mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-08 00:14:31 +08:00
Compare commits
11 Commits
issue/8786
...
issue/8752
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f66516c87f | ||
![]() |
3ec2daa9d6 | ||
![]() |
2c3fe26900 | ||
![]() |
bf028e0316 | ||
![]() |
7b503709a5 | ||
![]() |
81570fab35 | ||
![]() |
0d9fccb624 | ||
![]() |
fdbb06ba8d | ||
![]() |
15cad85d1e | ||
![]() |
0b86413e60 | ||
![]() |
3a6810ec67 |
@@ -1,4 +1,5 @@
|
||||
using System.Net;
|
||||
using System;
|
||||
using System.Net;
|
||||
using NUnit.Framework;
|
||||
using Orchard.Warmup.Services;
|
||||
|
||||
@@ -20,7 +21,7 @@ namespace Orchard.Tests.Modules.Warmup {
|
||||
|
||||
[Test]
|
||||
public void StatusCodeShouldBe404ForUnexistingResources() {
|
||||
var download = _webDownloader.Download("http://orchardproject.net/yepyep");
|
||||
var download = _webDownloader.Download("https://orchardcore.net/" + Guid.NewGuid());
|
||||
Assert.That(download, Is.Not.Null);
|
||||
Assert.That(download.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
|
||||
Assert.That(download.Content, Is.Null);
|
||||
@@ -28,7 +29,7 @@ namespace Orchard.Tests.Modules.Warmup {
|
||||
|
||||
[Test]
|
||||
public void StatusCodeShouldBe200ForValidRequests() {
|
||||
var download = _webDownloader.Download("http://orchardproject.net/");
|
||||
var download = _webDownloader.Download("https://orchardcore.net/");
|
||||
Assert.That(download, Is.Not.Null);
|
||||
Assert.That(download.StatusCode, Is.EqualTo(HttpStatusCode.OK));
|
||||
Assert.That(download.Content, Is.Not.Empty);
|
||||
|
@@ -1,74 +1,75 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Core.Contents.Settings;
|
||||
using Orchard.Core.Navigation.Models;
|
||||
using Orchard.Core.Navigation.Services;
|
||||
using Orchard.Core.Navigation.ViewModels;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.UI;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.Utility;
|
||||
using System;
|
||||
using Orchard.ContentManagement.Handlers;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Exceptions;
|
||||
using Orchard.ContentManagement.Aspects;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Core.Contents.Settings;
|
||||
using Orchard.Data;
|
||||
using System.Web.Routing;
|
||||
using Orchard.Exceptions;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.Mvc.Extensions;
|
||||
using Orchard.Mvc.Html;
|
||||
using Orchard.Security;
|
||||
using Orchard.UI;
|
||||
using Orchard.UI.Navigation;
|
||||
using Orchard.UI.Notify;
|
||||
using Orchard.Utility;
|
||||
using Orchard.Utility.Extensions;
|
||||
|
||||
namespace Orchard.Core.Navigation.Controllers {
|
||||
[ValidateInput(false)]
|
||||
public class AdminController : Controller, IUpdateModel {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
private readonly IAuthorizer _authorizer;
|
||||
private readonly INotifier _notifier;
|
||||
private readonly IMenuService _menuService;
|
||||
private readonly INavigationManager _navigationManager;
|
||||
private readonly IEnumerable<IContentHandler> _handlers;
|
||||
private readonly IMenuManager _menuManager;
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly ITransactionManager _transactionManager;
|
||||
|
||||
public AdminController(
|
||||
IOrchardServices orchardServices,
|
||||
IContentManager contentManager,
|
||||
ITransactionManager transactionManager,
|
||||
IMenuService menuService,
|
||||
IMenuManager menuManager,
|
||||
INavigationManager navigationManager,
|
||||
IEnumerable<IContentHandler> handlers) {
|
||||
_contentManager = contentManager;
|
||||
_transactionManager = transactionManager;
|
||||
_contentManager = orchardServices.ContentManager;
|
||||
_transactionManager = orchardServices.TransactionManager;
|
||||
_authorizer = orchardServices.Authorizer;
|
||||
_notifier = orchardServices.Notifier;
|
||||
_menuService = menuService;
|
||||
_menuManager = menuManager;
|
||||
_navigationManager = navigationManager;
|
||||
_handlers = handlers;
|
||||
|
||||
Services = orchardServices;
|
||||
T = NullLocalizer.Instance;
|
||||
Logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
public Localizer T { get; set; }
|
||||
public ILogger Logger { get; set; }
|
||||
public IOrchardServices Services { get; set; }
|
||||
|
||||
public ActionResult Index(NavigationManagementViewModel model, int? menuId) {
|
||||
var menus = Services.ContentManager.Query("Menu").List().ToList()
|
||||
var menus = _contentManager.Query("Menu").List().ToList()
|
||||
.OrderBy(x => x.ContentManager.GetItemMetadata(x).DisplayText);
|
||||
|
||||
if (!menus.Any()) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, T("Not allowed to manage menus"))) {
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, T("Not allowed to manage menus"))) {
|
||||
return new HttpUnauthorizedResult();
|
||||
}
|
||||
|
||||
return RedirectToAction("Create", "Admin", new { area = "Contents", id = "Menu", returnUrl = Request.RawUrl });
|
||||
}
|
||||
|
||||
var allowedMenus = menus.Where(menu => Services.Authorizer.Authorize(Permissions.ManageMenus, menu)).ToList();
|
||||
var allowedMenus = menus.Where(menu => _authorizer.Authorize(Permissions.ManageMenus, menu)).ToList();
|
||||
|
||||
if (!allowedMenus.Any()) {
|
||||
return new HttpUnauthorizedResult();
|
||||
@@ -87,7 +88,11 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
}
|
||||
|
||||
if (model.MenuItemEntries == null || !model.MenuItemEntries.Any()) {
|
||||
model.MenuItemEntries = _menuService.GetMenuParts(currentMenu.Id).Select(CreateMenuItemEntries).OrderBy(menuPartEntry => menuPartEntry.Position, new FlatPositionComparer()).ToList();
|
||||
model.MenuItemEntries = _menuService
|
||||
.GetMenuParts(currentMenu.Id)
|
||||
.Select(CreateMenuItemEntries)
|
||||
.OrderBy(menuPartEntry => menuPartEntry.Position, new FlatPositionComparer())
|
||||
.ToList();
|
||||
}
|
||||
|
||||
model.MenuItemDescriptors = _menuManager.GetMenuItemTypes();
|
||||
@@ -100,7 +105,10 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
|
||||
[HttpPost, ActionName("Index")]
|
||||
public ActionResult IndexPOST(IList<MenuItemEntry> menuItemEntries, int? menuId) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, (menuId.HasValue) ? _menuService.GetMenu(menuId.Value) : null, T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(
|
||||
Permissions.ManageMenus,
|
||||
menuId.HasValue ? _menuService.GetMenu(menuId.Value) : null,
|
||||
T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
// See https://github.com/OrchardCMS/Orchard/issues/948
|
||||
@@ -123,25 +131,15 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
return RedirectToAction("Index", new { menuId });
|
||||
}
|
||||
|
||||
private MenuItemEntry CreateMenuItemEntries(MenuPart menuPart) {
|
||||
return new MenuItemEntry {
|
||||
MenuItemId = menuPart.Id,
|
||||
IsMenuItem = menuPart.Is<MenuItemPart>(),
|
||||
Text = menuPart.MenuText,
|
||||
Position = menuPart.MenuPosition,
|
||||
Url = menuPart.Is<MenuItemPart>()
|
||||
? menuPart.As<MenuItemPart>().Url
|
||||
: _navigationManager.GetUrl(null, Services.ContentManager.GetItemMetadata(menuPart).DisplayRouteValues),
|
||||
ContentItem = menuPart.ContentItem,
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Delete(int id) {
|
||||
|
||||
MenuPart menuPart = _menuService.Get(id);
|
||||
int? menuId = null;
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, (menuPart != null) ? _menuService.GetMenu(menuPart.Menu.Id) : null, T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(
|
||||
Permissions.ManageMenus,
|
||||
menuPart == null ? null : _menuService.GetMenu(menuPart.Menu.Id),
|
||||
T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
if (menuPart != null) {
|
||||
@@ -155,7 +153,8 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
|
||||
foreach (var menuItem in menuItems.Concat(new[] { menuPart })) {
|
||||
// if the menu item is a concrete content item, don't delete it, just unreference the menu
|
||||
if (!menuPart.ContentItem.TypeDefinition.Settings.ContainsKey("Stereotype") || menuPart.ContentItem.TypeDefinition.Settings["Stereotype"] != "MenuItem") {
|
||||
if (!menuPart.ContentItem.TypeDefinition.Settings.ContainsKey("Stereotype")
|
||||
|| menuPart.ContentItem.TypeDefinition.Settings["Stereotype"] != "MenuItem") {
|
||||
menuPart.Menu = null;
|
||||
}
|
||||
else {
|
||||
@@ -168,26 +167,18 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
return RedirectToAction("Index", new { menuId });
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
|
||||
public ActionResult CreateMenuItem(string id, int menuId, string returnUrl) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, _menuService.GetMenu(menuId), T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, _menuService.GetMenu(menuId), T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
// create a new temporary menu item
|
||||
var menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
var menuPart = _contentManager.New<MenuPart>(id);
|
||||
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
|
||||
// load the menu
|
||||
var menu = Services.ContentManager.Get(menuId);
|
||||
var menu = _contentManager.Get(menuId);
|
||||
|
||||
if (menu == null)
|
||||
return HttpNotFound();
|
||||
@@ -196,7 +187,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
// filter the content items for this specific menu
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu));
|
||||
menuPart.Menu = menu;
|
||||
var model = Services.ContentManager.BuildEditor(menuPart);
|
||||
var model = _contentManager.BuildEditor(menuPart);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
@@ -206,32 +197,32 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
}
|
||||
|
||||
Logger.Error(T("Creating menu item failed: {0}", exception.Message).Text);
|
||||
Services.Notifier.Error(T("Creating menu item failed: {0}", exception.Message));
|
||||
_notifier.Error(T("Creating menu item failed: {0}", exception.Message));
|
||||
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost, ActionName("CreateMenuItem")]
|
||||
public ActionResult CreateMenuItemPost(string id, int menuId, string returnUrl) {
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, _menuService.GetMenu(menuId), T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, _menuService.GetMenu(menuId), T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
var menuPart = Services.ContentManager.New<MenuPart>(id);
|
||||
var menuPart = _contentManager.New<MenuPart>(id);
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
// load the menu
|
||||
var menu = Services.ContentManager.Get(menuId);
|
||||
var menu = _contentManager.Get(menuId);
|
||||
if (menu == null)
|
||||
return HttpNotFound();
|
||||
|
||||
menuPart.Menu = menu;
|
||||
var model = Services.ContentManager.UpdateEditor(menuPart, this);
|
||||
var model = _contentManager.UpdateEditor(menuPart, this);
|
||||
menuPart.MenuPosition = Position.GetNext(_navigationManager.BuildMenu(menu));
|
||||
Services.ContentManager.Create(menuPart);
|
||||
_contentManager.Create(menuPart);
|
||||
if (!ModelState.IsValid) {
|
||||
Services.TransactionManager.Cancel();
|
||||
_transactionManager.Cancel();
|
||||
return View(model);
|
||||
}
|
||||
Services.Notifier.Information(T("Your {0} has been added.", menuPart.TypeDefinition.DisplayName));
|
||||
_notifier.Information(T("Your {0} has been added.", menuPart.TypeDefinition.DisplayName));
|
||||
return this.RedirectLocal(returnUrl, () => RedirectToAction("Index"));
|
||||
}
|
||||
|
||||
@@ -241,7 +232,7 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
if (contentItem == null)
|
||||
return HttpNotFound();
|
||||
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, contentItem.Content.MenuPart.Menu, T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, contentItem.Content.MenuPart.Menu, T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var model = _contentManager.BuildEditor(contentItem);
|
||||
@@ -252,19 +243,81 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
[Mvc.FormValueRequired("submit.Save")]
|
||||
public ActionResult EditPOST(int id, string returnUrl) {
|
||||
return EditPOST(id, returnUrl, contentItem => {
|
||||
if (!contentItem.Has<IPublishingControlAspect>() && !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable)
|
||||
if (!contentItem.Has<IPublishingControlAspect>()
|
||||
&& !contentItem.TypeDefinition.Settings.GetModel<ContentTypeSettings>().Draftable
|
||||
&& contentItem.IsPublished())
|
||||
_contentManager.Publish(contentItem);
|
||||
});
|
||||
}
|
||||
private ActionResult EditPOST(int id, string returnUrl, Action<ContentItem> conditionallyPublish) {
|
||||
var contentItem = _contentManager.Get(id, VersionOptions.DraftRequired);
|
||||
|
||||
if (contentItem == null)
|
||||
[HttpPost]
|
||||
// Copy of Contents/AdminController/Publish, but with different permission check and redirect.
|
||||
public ActionResult Publish(int id) {
|
||||
var menuPart = _contentManager.GetLatest<MenuPart>(id);
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
|
||||
if (!Services.Authorizer.Authorize(Permissions.ManageMenus, contentItem.Content.MenuPart.Menu, T("Couldn't manage the main menu")))
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, menuPart.Menu, T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
_contentManager.Publish(menuPart.ContentItem);
|
||||
|
||||
_notifier.Information(
|
||||
string.IsNullOrWhiteSpace(menuPart.MenuText)
|
||||
? string.IsNullOrWhiteSpace(menuPart.TypeDefinition.DisplayName)
|
||||
? T("Your content has been published.")
|
||||
: T("Your {0} has been published.", menuPart.TypeDefinition.DisplayName)
|
||||
: T("'{0}' has been published.", menuPart.MenuText));
|
||||
|
||||
return RedirectToAction("Index", new { menuId = menuPart.Menu.Id });
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
// Copy of Contents/AdminController/Unpublish, but with different permission check and redirect.
|
||||
public ActionResult Unpublish(int id) {
|
||||
var menuPart = _contentManager.GetLatest<MenuPart>(id);
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, menuPart.Menu, T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
_contentManager.Unpublish(menuPart.ContentItem);
|
||||
|
||||
_notifier.Information(
|
||||
string.IsNullOrWhiteSpace(menuPart.MenuText)
|
||||
? string.IsNullOrWhiteSpace(menuPart.TypeDefinition.DisplayName)
|
||||
? T("Your content has been unpublished.")
|
||||
: T("Your {0} has been unpublished.", menuPart.TypeDefinition.DisplayName)
|
||||
: T("'{0}' has been unpublished.", menuPart.MenuText));
|
||||
|
||||
return RedirectToAction("Index", new { menuId = menuPart.Menu.Id });
|
||||
}
|
||||
|
||||
private MenuItemEntry CreateMenuItemEntries(MenuPart menuPart) {
|
||||
return new MenuItemEntry {
|
||||
MenuItemId = menuPart.Id,
|
||||
IsMenuItem = menuPart.Is<MenuItemPart>(),
|
||||
Text = menuPart.MenuText,
|
||||
Position = menuPart.MenuPosition,
|
||||
Url = menuPart.Is<MenuItemPart>()
|
||||
? menuPart.As<MenuItemPart>().Url
|
||||
: _navigationManager.GetUrl(null, _contentManager.GetItemMetadata(menuPart).DisplayRouteValues),
|
||||
ContentItem = menuPart.ContentItem,
|
||||
};
|
||||
}
|
||||
|
||||
private ActionResult EditPOST(int id, string returnUrl, Action<ContentItem> conditionallyPublish) {
|
||||
var menuPart = _contentManager.GetDraftRequired<MenuPart>(id);
|
||||
|
||||
if (menuPart == null)
|
||||
return HttpNotFound();
|
||||
|
||||
if (!_authorizer.Authorize(Permissions.ManageMenus, menuPart.Menu, T("Couldn't manage the menu")))
|
||||
return new HttpUnauthorizedResult();
|
||||
|
||||
var contentItem = menuPart.ContentItem;
|
||||
|
||||
string previousRoute = null;
|
||||
if (contentItem.Has<IAliasAspect>()
|
||||
&& !string.IsNullOrWhiteSpace(returnUrl)
|
||||
@@ -289,11 +342,22 @@ namespace Orchard.Core.Navigation.Controllers {
|
||||
returnUrl = Url.ItemDisplayUrl(contentItem);
|
||||
}
|
||||
|
||||
Services.Notifier.Information(string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
|
||||
? T("Your content has been saved.")
|
||||
: T("Your {0} has been saved.", contentItem.TypeDefinition.DisplayName));
|
||||
_notifier.Information(
|
||||
string.IsNullOrWhiteSpace(menuPart.MenuText)
|
||||
? string.IsNullOrWhiteSpace(contentItem.TypeDefinition.DisplayName)
|
||||
? T("Your content has been saved.")
|
||||
: T("Your {0} has been saved.", contentItem.TypeDefinition.DisplayName)
|
||||
: T("'{0}' has been saved.", menuPart.MenuText));
|
||||
|
||||
return this.RedirectLocal(returnUrl, () => RedirectToAction("Edit", new RouteValueDictionary { { "Id", contentItem.Id } }));
|
||||
return RedirectToAction("Index", new { menuId = menuPart.Menu.Id });
|
||||
}
|
||||
|
||||
bool IUpdateModel.TryUpdateModel<TModel>(TModel model, string prefix, string[] includeProperties, string[] excludeProperties) {
|
||||
return TryUpdateModel(model, prefix, includeProperties, excludeProperties);
|
||||
}
|
||||
|
||||
void IUpdateModel.AddModelError(string key, LocalizedString errorMessage) {
|
||||
ModelState.AddModelError(key, errorMessage.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,8 @@ namespace Orchard.Core.Navigation.Services {
|
||||
|
||||
public IEnumerable<MenuPart> GetMenuParts(int menuId) {
|
||||
return _contentManager
|
||||
.Query<MenuPart, MenuPartRecord>()
|
||||
.Where( x => x.MenuId == menuId)
|
||||
.Query<MenuPart, MenuPartRecord>(VersionOptions.Latest)
|
||||
.Where(x => x.MenuId == menuId)
|
||||
.List();
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,6 @@
|
||||
@model NavigationManagementViewModel
|
||||
|
||||
@using Orchard.ContentManagement;
|
||||
@using Orchard.Core.Navigation.ViewModels;
|
||||
@using Orchard.Utility.Extensions;
|
||||
|
||||
@@ -115,8 +117,21 @@
|
||||
<span class="navigation-position"><input type="text" class="text" name="@Html.NameOf(m => m.MenuItemEntries[i].Position)" value="@menuPartEntry.Position" /></span>
|
||||
<span class="navigation-actions">
|
||||
<input type="hidden" name="@Html.NameOf(m => m.MenuItemEntries[i].MenuItemId)" value="@menuPartEntry.MenuItemId" />
|
||||
@Html.ItemEditLink(T("Edit").Text, menuPartEntry.ContentItem, new { returnUrl = Request.RawUrl })@T(" | ")
|
||||
@Html.Link(T("Delete").Text, Url.ItemRemoveUrl(menuPartEntry.ContentItem,null), new { itemprop = "RemoveUrl UnsafeUrl" })
|
||||
@{
|
||||
var menuItemHasPublished = menuPartEntry.ContentItem.HasPublished();
|
||||
}
|
||||
|
||||
@Html.Link(
|
||||
menuItemHasPublished ? T("Unpublish").Text : T("Publish").Text,
|
||||
Url.Action(
|
||||
menuItemHasPublished ? "Unpublish": "Publish",
|
||||
"Admin",
|
||||
new { area = "Navigation", id = menuPartEntry.ContentItem.Id }),
|
||||
new { itemprop = "UnsafeUrl" })
|
||||
@T(" | ")
|
||||
@Html.ItemEditLink(T("Edit").Text, menuPartEntry.ContentItem, new { returnUrl = Request.RawUrl })
|
||||
@T(" | ")
|
||||
@Html.Link(T("Delete").Text, Url.ItemRemoveUrl(menuPartEntry.ContentItem, null), new { itemprop = "RemoveUrl UnsafeUrl" })
|
||||
</span>
|
||||
</div>
|
||||
|
||||
|
@@ -89,6 +89,8 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
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");
|
||||
@@ -141,10 +143,10 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -194,6 +196,11 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
|
||||
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
|
||||
@@ -225,6 +232,10 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
path = ConvertToRelativeUriPath(path);
|
||||
newPath = ConvertToRelativeUriPath(newPath);
|
||||
|
||||
if (FileSystemStorageProvider.FolderNameContainsInvalidCharacters(GetFolderName(newPath))) {
|
||||
throw new InvalidNameCharacterException("The new directory name contains invalid character(s)");
|
||||
}
|
||||
|
||||
if (!path.EndsWith("/"))
|
||||
path += "/";
|
||||
|
||||
@@ -260,6 +271,10 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
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));
|
||||
|
||||
@@ -284,6 +299,10 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
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");
|
||||
}
|
||||
@@ -371,10 +390,7 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
_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('/');
|
||||
@@ -399,11 +415,12 @@ namespace Orchard.Azure.Services.FileSystems {
|
||||
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;
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@@ -340,7 +340,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" />
|
||||
|
@@ -467,4 +467,4 @@ namespace Orchard.DynamicForms.Services {
|
||||
return validatorElementType == elementType || validatorElementType.IsAssignableFrom(elementType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -4,46 +4,41 @@ using Orchard.ContentManagement.Handlers;
|
||||
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) {
|
||||
|
@@ -6,6 +6,7 @@ using Orchard.Fields.Settings;
|
||||
using Orchard.Localization;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Security.Policy;
|
||||
|
||||
namespace Orchard.Fields.Drivers {
|
||||
public class LinkFieldDriver : ContentFieldDriver<LinkField> {
|
||||
@@ -57,11 +58,33 @@ namespace Orchard.Fields.Drivers {
|
||||
if (settings.Required && String.IsNullOrWhiteSpace(field.Value)) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("Url is required for {0}", T(field.DisplayName)));
|
||||
}
|
||||
else if (!String.IsNullOrWhiteSpace(field.Value) && !Uri.IsWellFormedUriString(field.Value, UriKind.RelativeOrAbsolute)) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("{0} is an invalid url.", field.Value));
|
||||
}
|
||||
else if (settings.LinkTextMode == LinkTextMode.Required && String.IsNullOrWhiteSpace(field.Text)) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("Text is required for {0}.", T(field.DisplayName)));
|
||||
} else if (!String.IsNullOrWhiteSpace(field.Value)) {
|
||||
// Check if it's a valid uri, considering that there may be the link to an anchor only
|
||||
// e.g.: field.Value = "#divId"
|
||||
// Take everything before the first "#" character and check if it's a valid uri.
|
||||
// If there is no character before the first "#", consider the value as a valid one (because it is a reference to a div inside the same page)
|
||||
if (field.Value.StartsWith("#")) {
|
||||
// The field value is a tag id reference
|
||||
// For html 5, a tag id is valid as long as it doesn't contain white spaces
|
||||
if (field.Value.IndexOf(' ') >= 0) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("{0} is an invalid url.", field.Value));
|
||||
}
|
||||
} else {
|
||||
var urlAndRef = field.Value.Split(new char[] { '#' }, 2);
|
||||
|
||||
// Since field value is a proper url and not a tag id only, assume the first part of the array is the actual url to link to
|
||||
if (!String.IsNullOrWhiteSpace(urlAndRef[0]) && !Uri.IsWellFormedUriString(urlAndRef[0], UriKind.RelativeOrAbsolute)) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("{0} is an invalid url.", field.Value));
|
||||
} else if (urlAndRef.Length > 1) {
|
||||
// The second part of the url is the id reference
|
||||
// For html 5, a tag id is valid as long as it doesn't contain white spaces
|
||||
if (urlAndRef[1].IndexOf(' ') >= 0) {
|
||||
updater.AddModelError(GetPrefix(field, part), T("{0} is an invalid url.", field.Value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,28 +7,16 @@ namespace Orchard.Fields.Fields {
|
||||
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) : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -25,7 +25,7 @@
|
||||
</div>
|
||||
<div class="editor-field">
|
||||
@(settings.Required ? Html.TextBoxFor(m => m.Value, new { @class = "text large", required = "required" }) : Html.TextBoxFor(m => m.Value, new { @class = "text large" }))
|
||||
<span class="hint">@T("A valid url, i.e. http://orchardproject.net, /content/file.pdf, ...")</span>
|
||||
<span class="hint">@T("A valid url, i.e. http://orchardproject.net, /content/file.pdf, #some_id, ...")</span>
|
||||
</div>
|
||||
@if (settings.LinkTextMode == LinkTextMode.Optional || settings.LinkTextMode == LinkTextMode.Required) {
|
||||
<div class="editor-label">
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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,4 +30,4 @@ 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
|
||||
|
@@ -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="Models\TransliterationSpecificationRecord.cs" />
|
||||
<Compile Include="Providers\ContentLocalizationTokens.cs" />
|
||||
<Compile Include="Selectors\ContentCultureSelector.cs" />
|
||||
@@ -118,6 +119,7 @@
|
||||
<Compile Include="Services\LocalizationService.cs" />
|
||||
<Compile Include="Services\TransliterationService.cs" />
|
||||
<Compile Include="Events\TransliterationSlugEventHandler.cs" />
|
||||
<Compile Include="Services\Utils.cs" />
|
||||
<Compile Include="ViewModels\ContentLocalizationsViewModel.cs" />
|
||||
<Compile Include="ViewModels\EditLocalizationViewModel.cs" />
|
||||
<Compile Include="ViewModels\CreateTransliterationViewModel.cs" />
|
||||
@@ -196,6 +198,9 @@
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\UserCultureSelector.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
@@ -229,4 +234,4 @@
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
@@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using System.Web;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Environment.Extensions;
|
||||
@@ -19,7 +18,8 @@ namespace Orchard.Localization.Selectors {
|
||||
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 @@ namespace Orchard.Localization.Selectors {
|
||||
|
||||
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 @@ namespace Orchard.Localization.Selectors {
|
||||
return cookiePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using Orchard.Autoroute.Models;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.Localization.Models;
|
||||
|
||||
@@ -10,5 +11,7 @@ namespace Orchard.Localization.Services {
|
||||
void SetContentCulture(IContent content, string culture);
|
||||
IEnumerable<LocalizationPart> GetLocalizations(IContent content);
|
||||
IEnumerable<LocalizationPart> GetLocalizations(IContent content, VersionOptions versionOptions);
|
||||
bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute);
|
||||
bool TryGetRouteForUrl(string url, out AutoroutePart route);
|
||||
}
|
||||
}
|
||||
|
@@ -1,53 +1,59 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning: Returns only the first item of same culture localizations.
|
||||
/// </summary>
|
||||
public LocalizationPart GetLocalizedContentItem(IContent content, string culture) =>
|
||||
GetLocalizedContentItem(content, culture, null);
|
||||
|
||||
public LocalizationPart GetLocalizedContentItem(IContent content, string culture) {
|
||||
// Warning: Returns only the first of same culture localizations.
|
||||
return GetLocalizedContentItem(content, culture, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Warning: Returns only the first item of same culture localizations.
|
||||
/// </summary>
|
||||
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<LocalizationPart>();
|
||||
|
||||
if (localized == null) return null;
|
||||
if (localized == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var masterContentItemId = localized.HasTranslationGroup ? localized.Record.MasterContentItemId : localized.Id;
|
||||
|
||||
// Warning: Returns only the first of same culture localizations.
|
||||
return _contentManager
|
||||
.Query<LocalizationPart>(versionOptions, content.ContentItem.ContentType)
|
||||
.Where<LocalizationPartRecord>(l =>
|
||||
(l.Id == masterContentItemId || l.MasterContentItemId == masterContentItemId) &&
|
||||
l.CultureId == cultureRecord.Id)
|
||||
.Where<LocalizationPartRecord>(localization =>
|
||||
(localization.Id == masterContentItemId || localization.MasterContentItemId == masterContentItemId)
|
||||
&& localization.CultureId == cultureRecord.Id)
|
||||
.Slice(1)
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
public string GetContentCulture(IContent content) {
|
||||
var localized = content.As<LocalizationPart>();
|
||||
|
||||
return localized?.Culture == null ?
|
||||
_cultureManager.GetSiteCulture() :
|
||||
localized.Culture.Culture;
|
||||
}
|
||||
public string GetContentCulture(IContent content) =>
|
||||
content.As<LocalizationPart>()?.Culture?.Culture ?? _cultureManager.GetSiteCulture();
|
||||
|
||||
public void SetContentCulture(IContent content, string culture) {
|
||||
var localized = content.As<LocalizationPart>();
|
||||
@@ -57,11 +63,14 @@ namespace Orchard.Localization.Services {
|
||||
localized.Culture = _cultureManager.GetCultureByName(culture);
|
||||
}
|
||||
|
||||
public IEnumerable<LocalizationPart> GetLocalizations(IContent content) {
|
||||
// Warning: May contain more than one localization of the same culture.
|
||||
return GetLocalizations(content, null);
|
||||
}
|
||||
/// <summary>
|
||||
/// Warning: May contain more than one localization of the same culture.
|
||||
/// </summary>
|
||||
public IEnumerable<LocalizationPart> GetLocalizations(IContent content) => GetLocalizations(content, null);
|
||||
|
||||
/// <summary>
|
||||
/// Warning: May contain more than one localization of the same culture.
|
||||
/// </summary>
|
||||
public IEnumerable<LocalizationPart> GetLocalizations(IContent content, VersionOptions versionOptions) {
|
||||
if (content.ContentItem.Id == 0) return Enumerable.Empty<LocalizationPart>();
|
||||
|
||||
@@ -76,16 +85,58 @@ namespace Orchard.Localization.Services {
|
||||
if (localized.HasTranslationGroup) {
|
||||
int masterContentItemId = localized.MasterContentItem.ContentItem.Id;
|
||||
|
||||
query = query.Where<LocalizationPartRecord>(l =>
|
||||
l.Id != contentItemId && // Exclude the content
|
||||
(l.Id == masterContentItemId || l.MasterContentItemId == masterContentItemId));
|
||||
query = query.Where<LocalizationPartRecord>(localization =>
|
||||
localization.Id != contentItemId && // Exclude the content
|
||||
(localization.Id == masterContentItemId || localization.MasterContentItemId == masterContentItemId));
|
||||
}
|
||||
else {
|
||||
query = query.Where<LocalizationPartRecord>(l => l.MasterContentItemId == contentItemId);
|
||||
query = query.Where<LocalizationPartRecord>(localization => localization.MasterContentItemId == contentItemId);
|
||||
}
|
||||
|
||||
// Warning: May contain more than one localization of the same culture.
|
||||
return query.List().ToList();
|
||||
}
|
||||
|
||||
public bool TryGetRouteForUrl(string url, out AutoroutePart route) {
|
||||
route = _contentManager.Query<AutoroutePart, AutoroutePartRecord>()
|
||||
.ForVersion(VersionOptions.Published)
|
||||
.Where(r => r.DisplayAlias == url)
|
||||
.List()
|
||||
.FirstOrDefault();
|
||||
|
||||
route = route ?? _homeAliasService.GetHomePage(VersionOptions.Latest).As<AutoroutePart>();
|
||||
|
||||
return route != null;
|
||||
}
|
||||
|
||||
public bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute) {
|
||||
if (!routableContent.Parts.Any(p => p.Is<ILocalizableAspect>())) {
|
||||
localizedRoute = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
IEnumerable<LocalizationPart> 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<AutoroutePart>();
|
||||
|
||||
return localizedRoute != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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, "");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,34 @@
|
||||
@using Orchard.Localization.Services
|
||||
|
||||
@{
|
||||
var currentCulture = WorkContext.CurrentCulture;
|
||||
var supportedCultures = WorkContext.Resolve<ICultureManager>().ListCultures().ToList();
|
||||
}
|
||||
|
||||
<div id="culture-selection">
|
||||
<ul>
|
||||
@foreach (var supportedCulture in supportedCultures)
|
||||
{
|
||||
var url = Url.Action(
|
||||
"ChangeCulture",
|
||||
"UserCultureSelector",
|
||||
new
|
||||
{
|
||||
area = "Orchard.Localization",
|
||||
culture = supportedCulture,
|
||||
returnUrl = Html.ViewContext.HttpContext.Request.RawUrl
|
||||
});
|
||||
|
||||
<li>
|
||||
@if (supportedCulture.Equals(currentCulture))
|
||||
{
|
||||
<a href="@url">@T("{0} (current)", supportedCulture)</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<a href="@url">@supportedCulture</a>
|
||||
}
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
@@ -3,15 +3,14 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Logging;
|
||||
using Orchard.MediaLibrary.Models;
|
||||
using Orchard.MediaLibrary.Services;
|
||||
using Orchard.MediaLibrary.ViewModels;
|
||||
using Orchard.Themes;
|
||||
using Orchard.UI.Admin;
|
||||
using Orchard.MediaLibrary.Models;
|
||||
using Orchard.Localization;
|
||||
using System.Linq;
|
||||
using Orchard.FileSystems.Media;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.MediaLibrary.Controllers {
|
||||
[Admin, Themed(false)]
|
||||
@@ -107,10 +106,16 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
url = mediaPart.FileName,
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, "Unexpected exception when uploading a media.");
|
||||
catch (InvalidNameCharacterException) {
|
||||
statuses.Add(new {
|
||||
error = T(ex.Message).Text,
|
||||
error = T("The file name contains invalid character(s)").Text,
|
||||
progress = 1.0,
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Logger.Error(ex, T("Unexpected exception when uploading a media.").Text);
|
||||
statuses.Add(new {
|
||||
error = ex.Message,
|
||||
progress = 1.0,
|
||||
});
|
||||
}
|
||||
@@ -130,7 +135,7 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
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();
|
||||
}
|
||||
@@ -138,7 +143,7 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
var statuses = new List<object>();
|
||||
|
||||
var settings = Services.WorkContext.CurrentSite.As<MediaLibrarySettingsPart>();
|
||||
|
||||
|
||||
// Loop through each file in the request
|
||||
for (int i = 0; i < HttpContext.Request.Files.Count; i++) {
|
||||
// Pointer to file
|
||||
@@ -146,7 +151,8 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
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";
|
||||
}
|
||||
|
||||
@@ -184,7 +190,7 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
});
|
||||
}
|
||||
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,
|
||||
|
@@ -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 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
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 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
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 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
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 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
}
|
||||
|
||||
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 @@ namespace Orchard.MediaLibrary.Controllers {
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
@@ -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<MediaPart> {
|
||||
private readonly IAuthenticationService _authenticationService;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
@@ -58,6 +58,8 @@ namespace Orchard.MediaLibrary.MediaFileName
|
||||
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,14 +67,18 @@ namespace Orchard.MediaLibrary.MediaFileName
|
||||
_notifier.Add(NotifyType.Information, 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 (Exception) {
|
||||
updater.AddModelError("MediaFileNameEditorSettings.FileName", T("Unable to rename file"));
|
||||
catch (InvalidNameCharacterException) {
|
||||
updater.AddModelError(fieldName, T("The file name contains invalid character(s)."));
|
||||
}
|
||||
catch (Exception exception) {
|
||||
updater.AddModelError(fieldName, T("Unable to rename file: {0}", exception.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
});
|
||||
}
|
||||
|
@@ -6,13 +6,13 @@ using System.Web;
|
||||
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 @@ namespace Orchard.MediaLibrary.Services {
|
||||
private readonly IStorageProvider _storageProvider;
|
||||
private readonly IEnumerable<IMediaFactorySelector> _mediaFactorySelectors;
|
||||
private readonly IMediaFolderProvider _mediaFolderProvider;
|
||||
private static char[] HttpUnallowed = new char[] { '<', '>', '*', '%', '&', ':', '\\', '?', '#' };
|
||||
|
||||
public MediaLibraryService(
|
||||
IOrchardServices orchardServices,
|
||||
@@ -145,12 +144,6 @@ namespace Orchard.MediaLibrary.Services {
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -177,9 +170,9 @@ namespace Orchard.MediaLibrary.Services {
|
||||
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;
|
||||
@@ -256,7 +249,7 @@ namespace Orchard.MediaLibrary.Services {
|
||||
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) ?? "";
|
||||
@@ -268,7 +261,7 @@ namespace Orchard.MediaLibrary.Services {
|
||||
isMyfolder = true;
|
||||
}
|
||||
|
||||
if(isMyfolder) {
|
||||
if (isMyfolder) {
|
||||
return _orchardServices.Authorizer.Authorize(Permissions.ManageOwnMedia);
|
||||
}
|
||||
else { // other
|
||||
|
@@ -7,7 +7,6 @@ using Orchard.Forms.Services;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.Projections.FilterEditors.Forms {
|
||||
|
||||
public class StringFilterForm : IFormProvider {
|
||||
public const string FormName = "StringFilter";
|
||||
|
||||
@@ -20,44 +19,44 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
}
|
||||
|
||||
public void Describe(DescribeContext context) {
|
||||
Func<IShapeFactory, object> form =
|
||||
shape => {
|
||||
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"
|
||||
));
|
||||
|
||||
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.")
|
||||
)
|
||||
);
|
||||
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.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
|
||||
});
|
||||
return f;
|
||||
}
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
context.Form(FormName, form);
|
||||
context.Form(FormName, (Func<IShapeFactory, object>)form);
|
||||
|
||||
}
|
||||
|
||||
@@ -65,6 +64,11 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
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 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
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<string, Action<IHqlExpressionFactory>>(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 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
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 @@ namespace Orchard.Projections.FilterEditors.Forms {
|
||||
NotStarts,
|
||||
Ends,
|
||||
NotEnds,
|
||||
NotContains,
|
||||
ContainsAnyIfProvided
|
||||
NotContains
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using Orchard.ContentManagement;
|
||||
using Orchard.ContentManagement.MetaData;
|
||||
using Orchard.Core.Common.Models;
|
||||
using Orchard.Core.Contents.Extensions;
|
||||
@@ -15,13 +14,16 @@ namespace Orchard.Projections {
|
||||
public class Migrations : DataMigrationImpl {
|
||||
private readonly IRepository<MemberBindingRecord> _memberBindingRepository;
|
||||
private readonly IRepository<LayoutRecord> _layoutRepository;
|
||||
|
||||
private readonly IRepository<FilterRecord> _filterRepository;
|
||||
|
||||
public Migrations(
|
||||
IRepository<MemberBindingRecord> memberBindingRepository,
|
||||
IRepository<LayoutRecord> layoutRepository) {
|
||||
IRepository<LayoutRecord> layoutRepository,
|
||||
IRepository<FilterRecord> filterRepository) {
|
||||
_memberBindingRepository = memberBindingRepository;
|
||||
_layoutRepository = layoutRepository;
|
||||
_filterRepository = filterRepository;
|
||||
|
||||
T = NullLocalizer.Instance;
|
||||
}
|
||||
|
||||
@@ -359,15 +361,30 @@ namespace Orchard.Projections {
|
||||
}
|
||||
|
||||
public int UpdateFrom5() {
|
||||
SchemaBuilder.AlterTable("LayoutRecord", t => t.AddColumn<string>("GUIdentifier",
|
||||
column => column.WithLength(68)));
|
||||
SchemaBuilder.AlterTable("LayoutRecord", t => t
|
||||
.AddColumn<string>("GUIdentifier", column => column.WithLength(68)));
|
||||
|
||||
var layoutRecords = _layoutRepository.Table.Where(l => l.GUIdentifier == null || l.GUIdentifier == "").ToList();
|
||||
foreach (var layout in layoutRecords) {
|
||||
layout.GUIdentifier = Guid.NewGuid().ToString();
|
||||
layout.GUIdentifier = Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
return 6;
|
||||
}
|
||||
|
||||
public int UpdateFrom6() {
|
||||
// This casts 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 = "<Operator>ContainsAnyIfProvided</Operator>";
|
||||
var filterRecordsToUpdate = _filterRepository.Table.Where(f => f.State.Contains(formStateToReplace)).ToList();
|
||||
foreach (var filter in filterRecordsToUpdate) {
|
||||
filter.State = filter.State.Replace(
|
||||
formStateToReplace,
|
||||
"<Operator>ContainsAny</Operator><IgnoreFilterIfValueIsEmpty>true</IgnoreFilterIfValueIsEmpty>");
|
||||
}
|
||||
|
||||
return 7;
|
||||
}
|
||||
}
|
||||
}
|
@@ -34,7 +34,7 @@ namespace Orchard.Exceptions {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sender is IEventBus && exception is OrchardFatalException) {
|
||||
if (sender is IEventBus && exception is OrchardFatalException) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Orchard.Exceptions {
|
||||
}
|
||||
|
||||
private static bool IsFatal(Exception exception) {
|
||||
return
|
||||
return
|
||||
exception is OrchardSecurityException ||
|
||||
exception is StackOverflowException ||
|
||||
exception is AccessViolationException ||
|
||||
|
@@ -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 {
|
||||
|
@@ -4,15 +4,22 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Hosting;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Validation;
|
||||
using Orchard.Exceptions;
|
||||
using Orchard.Localization;
|
||||
using Orchard.Utility.Extensions;
|
||||
using Orchard.Validation;
|
||||
|
||||
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 @@ namespace Orchard.FileSystems.Media {
|
||||
appPath = HostingEnvironment.ApplicationVirtualPath;
|
||||
}
|
||||
if (!appPath.EndsWith("/"))
|
||||
appPath = appPath + '/';
|
||||
appPath += '/';
|
||||
if (!appPath.StartsWith("/"))
|
||||
appPath = '/' + appPath;
|
||||
|
||||
@@ -39,21 +46,21 @@ namespace Orchard.FileSystems.Media {
|
||||
|
||||
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:
|
||||
/*
|
||||
<component instance-scope="per-lifetime-scope"
|
||||
type="Orchard.FileSystems.Media.FileSystemStorageProvider, Orchard.Framework"
|
||||
service="Orchard.FileSystems.Media.IStorageProvider">
|
||||
/// <summary>
|
||||
/// The public setter allows injecting this from Sites.MyTenant.Config or Sites.config, by using an AutoFac
|
||||
/// component. See the example below.
|
||||
/// </summary>
|
||||
/*
|
||||
<component
|
||||
instance-scope="per-lifetime-scope"
|
||||
service="Orchard.FileSystems.Media.IStorageProvider"
|
||||
type="Orchard.FileSystems.Media.FileSystemStorageProvider, Orchard.Framework">
|
||||
<properties>
|
||||
<property name="MaxPathLength" value="500" />
|
||||
</properties>
|
||||
</component>
|
||||
|
||||
*/
|
||||
}
|
||||
*/
|
||||
public int MaxPathLength { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps a relative path into the storage path.
|
||||
@@ -215,6 +222,12 @@ namespace Orchard.FileSystems.Media {
|
||||
/// <param name="path">The relative path to the folder to be created.</param>
|
||||
/// <exception cref="ArgumentException">If the folder already exists.</exception>
|
||||
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 @@ namespace Orchard.FileSystems.Media {
|
||||
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 @@ namespace Orchard.FileSystems.Media {
|
||||
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 @@ namespace Orchard.FileSystems.Media {
|
||||
/// <exception cref="ArgumentException">If the file already exists.</exception>
|
||||
/// <returns>The created file.</returns>
|
||||
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 @@ namespace Orchard.FileSystems.Media {
|
||||
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 {
|
||||
|
@@ -128,7 +128,7 @@ namespace Orchard.FileSystems.Media {
|
||||
void SaveStream(string path, Stream inputStream);
|
||||
|
||||
/// <summary>
|
||||
/// Combines to paths.
|
||||
/// Combines two paths.
|
||||
/// </summary>
|
||||
/// <param name="path1">The parent path.</param>
|
||||
/// <param name="path2">The child path.</param>
|
||||
|
@@ -0,0 +1,7 @@
|
||||
using System;
|
||||
|
||||
namespace Orchard.FileSystems.Media {
|
||||
public class InvalidNameCharacterException : ArgumentException {
|
||||
public InvalidNameCharacterException(string message) : base(message) { }
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
@@ -45,7 +45,6 @@
|
||||
<CodeAnalysisRuleSet>..\OrchardBasicCorrectness.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
<UseVSHostingProcess>false</UseVSHostingProcess>
|
||||
<NoWarn></NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
@@ -159,6 +158,7 @@
|
||||
<Compile Include="Data\Migration\Schema\DropUniqueConstraintCommand.cs" />
|
||||
<Compile Include="Environment\Extensions\Models\LifecycleStatus.cs" />
|
||||
<Compile Include="Environment\ShellBuilders\ICompositionStrategy.cs" />
|
||||
<Compile Include="FileSystems\Media\InvalidNameCharacterException.cs" />
|
||||
<Compile Include="Mvc\ModelBinders\BooleanBinderProvider.cs" />
|
||||
<Compile Include="Mvc\Updater.cs" />
|
||||
<Compile Include="Recipes\Models\ConfigurationContext.cs" />
|
||||
@@ -685,6 +685,7 @@
|
||||
<Compile Include="Messaging\Services\IMessageManager.cs" />
|
||||
<Compile Include="Messaging\Services\IMessagingChannel.cs" />
|
||||
<Compile Include="IWorkContextAccessor.cs" />
|
||||
<Compile Include="Utility\Extensions\NameValueCollectionExtensions.cs" />
|
||||
<Compile Include="Utility\Extensions\VirtualPathProviderExtensions.cs" />
|
||||
<Compile Include="Utility\NamedReaderWriterLock.cs" />
|
||||
<Compile Include="Utility\ReflectionHelper.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());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user