diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs
deleted file mode 100644
index cdd14b43d54..00000000000
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Helpers/NameValueCollectionExtensions.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Specialized;
-using System.Linq;
-using System.Web;
-
-namespace Orchard.DynamicForms.Helpers {
- public static class NameValueCollectionExtensions {
- public static string ToQueryString(this NameValueCollection nameValues) {
- return String.Join("&", (from string name in nameValues select String.Concat(name, "=", HttpUtility.UrlEncode(nameValues[name]))).ToArray());
- }
- }
-}
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj b/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj
index 3954c9a070b..1b17c9c0718 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Orchard.DynamicForms.csproj
@@ -340,7 +340,6 @@
-
diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs
index 7c602486905..603f5099d7e 100644
--- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs
+++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Services/FormService.cs
@@ -467,4 +467,4 @@ private static bool IsFormElementType(IElementValidator validator, Type elementT
return validatorElementType == elementType || validatorElementType.IsAssignableFrom(elementType);
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs b/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs
new file mode 100644
index 00000000000..2a4543c289b
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Controllers/UserCultureSelectorController.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Web.Mvc;
+using Orchard.Autoroute.Models;
+using Orchard.CulturePicker.Services;
+using Orchard.Environment.Extensions;
+using Orchard.Localization.Providers;
+using Orchard.Localization.Services;
+using Orchard.Mvc.Extensions;
+
+namespace Orchard.Localization.Controllers {
+ [OrchardFeature("Orchard.Localization.CultureSelector")]
+ public class UserCultureSelectorController : Controller {
+ private readonly ILocalizationService _localizationService;
+ private readonly ICultureStorageProvider _cultureStorageProvider;
+ public IOrchardServices Services { get; set; }
+
+ public UserCultureSelectorController(
+ IOrchardServices services,
+ ILocalizationService localizationService,
+ ICultureStorageProvider cultureStorageProvider) {
+ Services = services;
+ _localizationService = localizationService;
+ _cultureStorageProvider = cultureStorageProvider;
+ }
+
+ public ActionResult ChangeCulture(string culture) {
+ if (string.IsNullOrEmpty(culture)) {
+ throw new ArgumentNullException(culture);
+ }
+
+ var returnUrl = Utils.GetReturnUrl(Services.WorkContext.HttpContext.Request);
+ if (string.IsNullOrEmpty(returnUrl))
+ returnUrl = "";
+
+ if (_localizationService.TryGetRouteForUrl(returnUrl, out AutoroutePart currentRoutePart)
+ && _localizationService.TryFindLocalizedRoute(currentRoutePart.ContentItem, culture, out AutoroutePart localizedRoutePart)) {
+ returnUrl = localizedRoutePart.Path;
+ }
+
+ _cultureStorageProvider.SetCulture(culture);
+ if (!returnUrl.StartsWith("~/")) {
+ returnUrl = "~/" + returnUrl;
+ }
+
+ return this.RedirectLocal(returnUrl);
+ }
+ }
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Module.txt b/src/Orchard.Web/Modules/Orchard.Localization/Module.txt
index f9fd36172ca..26cf3fe4285 100644
--- a/src/Orchard.Web/Modules/Orchard.Localization/Module.txt
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Module.txt
@@ -9,7 +9,7 @@ Features:
Orchard.Localization:
Description: Enables localization of content items.
Category: Content
- Dependencies: Settings
+ Dependencies: Settings, Orchard.Autoroute
Name: Content Localization
Orchard.Localization.DateTimeFormat:
Description: Enables PO-based translation of date/time formats and names of days and months.
@@ -30,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
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj b/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj
index 3718e30d9ed..771a933d610 100644
--- a/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Orchard.Localization.csproj
@@ -89,10 +89,11 @@
+
-
+
@@ -118,6 +119,7 @@
+
@@ -196,6 +198,9 @@
+
+
+
10.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)
@@ -229,4 +234,4 @@
-
+
\ No newline at end of file
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs b/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs
index d39abbb5b67..00c82464f87 100644
--- a/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Selectors/CookieCultureSelector.cs
@@ -1,4 +1,3 @@
-using System;
using System.Web;
using Orchard.Environment.Configuration;
using Orchard.Environment.Extensions;
@@ -19,7 +18,8 @@ public class CookieCultureSelector : ICultureSelector, ICultureStorageProvider {
private const string AdminCookieName = "OrchardCurrentCulture-Admin";
private const int DefaultExpireTimeYear = 1;
- public CookieCultureSelector(IHttpContextAccessor httpContextAccessor,
+ public CookieCultureSelector(
+ IHttpContextAccessor httpContextAccessor,
IClock clock,
ShellSettings shellSettings) {
_httpContextAccessor = httpContextAccessor;
@@ -36,11 +36,10 @@ public void SetCulture(string culture) {
var cookie = new HttpCookie(cookieName, culture) {
Expires = _clock.UtcNow.AddYears(DefaultExpireTimeYear),
+ Domain = httpContext.Request.IsLocal ? null : httpContext.Request.Url.Host
};
- cookie.Domain = !httpContext.Request.IsLocal ? httpContext.Request.Url.Host : null;
-
- if (!String.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) {
+ if (!string.IsNullOrEmpty(_shellSettings.RequestUrlPrefix)) {
cookie.Path = GetCookiePath(httpContext);
}
@@ -73,4 +72,4 @@ private string GetCookiePath(HttpContextBase httpContext) {
return cookiePath;
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs
index f2ace3a9cb5..04cda8d60b6 100644
--- a/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/ILocalizationService.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using Orchard.Autoroute.Models;
using Orchard.ContentManagement;
using Orchard.Localization.Models;
@@ -10,5 +11,7 @@ public interface ILocalizationService : IDependency {
void SetContentCulture(IContent content, string culture);
IEnumerable GetLocalizations(IContent content);
IEnumerable GetLocalizations(IContent content, VersionOptions versionOptions);
+ bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute);
+ bool TryGetRouteForUrl(string url, out AutoroutePart route);
}
}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs
index e8baceda04d..97bcb0ae80b 100644
--- a/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/LocalizationService.cs
@@ -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;
}
+ ///
+ /// Warning: Returns only the first item of same culture localizations.
+ ///
+ public LocalizationPart GetLocalizedContentItem(IContent content, string culture) =>
+ GetLocalizedContentItem(content, culture, null);
- public LocalizationPart GetLocalizedContentItem(IContent content, string culture) {
- // Warning: Returns only the first of same culture localizations.
- return GetLocalizedContentItem(content, culture, null);
- }
-
+ ///
+ /// Warning: Returns only the first item of same culture localizations.
+ ///
public LocalizationPart GetLocalizedContentItem(IContent content, string culture, VersionOptions versionOptions) {
var cultureRecord = _cultureManager.GetCultureByName(culture);
- if (cultureRecord == null) return null;
+ if (cultureRecord == null) {
+ return null;
+ }
var localized = content.As();
- if (localized == null) return null;
+ if (localized == null) {
+ return null;
+ }
var masterContentItemId = localized.HasTranslationGroup ? localized.Record.MasterContentItemId : localized.Id;
- // Warning: Returns only the first of same culture localizations.
return _contentManager
.Query(versionOptions, content.ContentItem.ContentType)
- .Where(l =>
- (l.Id == masterContentItemId || l.MasterContentItemId == masterContentItemId) &&
- l.CultureId == cultureRecord.Id)
+ .Where(localization =>
+ (localization.Id == masterContentItemId || localization.MasterContentItemId == masterContentItemId)
+ && localization.CultureId == cultureRecord.Id)
.Slice(1)
.FirstOrDefault();
}
- public string GetContentCulture(IContent content) {
- var localized = content.As();
-
- return localized?.Culture == null ?
- _cultureManager.GetSiteCulture() :
- localized.Culture.Culture;
- }
+ public string GetContentCulture(IContent content) =>
+ content.As()?.Culture?.Culture ?? _cultureManager.GetSiteCulture();
public void SetContentCulture(IContent content, string culture) {
var localized = content.As();
@@ -57,11 +63,14 @@ public void SetContentCulture(IContent content, string culture) {
localized.Culture = _cultureManager.GetCultureByName(culture);
}
- public IEnumerable GetLocalizations(IContent content) {
- // Warning: May contain more than one localization of the same culture.
- return GetLocalizations(content, null);
- }
+ ///
+ /// Warning: May contain more than one localization of the same culture.
+ ///
+ public IEnumerable GetLocalizations(IContent content) => GetLocalizations(content, null);
+ ///
+ /// Warning: May contain more than one localization of the same culture.
+ ///
public IEnumerable GetLocalizations(IContent content, VersionOptions versionOptions) {
if (content.ContentItem.Id == 0) return Enumerable.Empty();
@@ -76,16 +85,58 @@ public IEnumerable GetLocalizations(IContent content, VersionO
if (localized.HasTranslationGroup) {
int masterContentItemId = localized.MasterContentItem.ContentItem.Id;
- query = query.Where(l =>
- l.Id != contentItemId && // Exclude the content
- (l.Id == masterContentItemId || l.MasterContentItemId == masterContentItemId));
+ query = query.Where(localization =>
+ localization.Id != contentItemId && // Exclude the content
+ (localization.Id == masterContentItemId || localization.MasterContentItemId == masterContentItemId));
}
else {
- query = query.Where(l => l.MasterContentItemId == contentItemId);
+ query = query.Where(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()
+ .ForVersion(VersionOptions.Published)
+ .Where(r => r.DisplayAlias == url)
+ .List()
+ .FirstOrDefault();
+
+ route = route ?? _homeAliasService.GetHomePage(VersionOptions.Latest).As();
+
+ return route != null;
+ }
+
+ public bool TryFindLocalizedRoute(ContentItem routableContent, string cultureName, out AutoroutePart localizedRoute) {
+ if (!routableContent.Parts.Any(p => p.Is())) {
+ localizedRoute = null;
+
+ return false;
+ }
+
+ IEnumerable localizations = GetLocalizations(routableContent, VersionOptions.Published);
+
+ ILocalizableAspect localizationPart = null, siteCultureLocalizationPart = null;
+ foreach (var localization in localizations) {
+ if (localization.Culture.Culture.Equals(cultureName, StringComparison.InvariantCultureIgnoreCase)) {
+ localizationPart = localization;
+
+ break;
+ }
+
+ if (localization.Culture == null && siteCultureLocalizationPart == null) {
+ siteCultureLocalizationPart = localization;
+ }
+ }
+
+ if (localizationPart == null) {
+ localizationPart = siteCultureLocalizationPart;
+ }
+
+ localizedRoute = localizationPart?.As();
+
+ return localizedRoute != null;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs b/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs
new file mode 100644
index 00000000000..c69c4e5df33
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Services/Utils.cs
@@ -0,0 +1,31 @@
+using System.Web;
+
+namespace Orchard.CulturePicker.Services {
+ public static class Utils {
+ public static string GetReturnUrl(HttpRequestBase request) {
+ if (request.UrlReferrer == null) {
+ return "";
+ }
+
+ string localUrl = GetAppRelativePath(request.UrlReferrer.AbsolutePath, request);
+ return HttpUtility.UrlDecode(localUrl);
+ }
+
+ public static string GetAppRelativePath(string logicalPath, HttpRequestBase request) {
+ if (request.ApplicationPath == null) {
+ return "";
+ }
+
+ logicalPath = logicalPath.ToLower();
+ string appPath = request.ApplicationPath.ToLower();
+ if (appPath != "/") {
+ appPath += "/";
+ }
+ else {
+ return logicalPath.Substring(1);
+ }
+
+ return logicalPath.Replace(appPath, "");
+ }
+ }
+}
diff --git a/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml b/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml
new file mode 100644
index 00000000000..71f4f80844e
--- /dev/null
+++ b/src/Orchard.Web/Modules/Orchard.Localization/Views/UserCultureSelector.cshtml
@@ -0,0 +1,34 @@
+@using Orchard.Localization.Services
+
+@{
+ var currentCulture = WorkContext.CurrentCulture;
+ var supportedCultures = WorkContext.Resolve().ListCultures().ToList();
+}
+
+
+
+ @foreach (var supportedCulture in supportedCultures)
+ {
+ var url = Url.Action(
+ "ChangeCulture",
+ "UserCultureSelector",
+ new
+ {
+ area = "Orchard.Localization",
+ culture = supportedCulture,
+ returnUrl = Html.ViewContext.HttpContext.Request.RawUrl
+ });
+
+ -
+ @if (supportedCulture.Equals(currentCulture))
+ {
+ @T("{0} (current)", supportedCulture)
+ }
+ else
+ {
+ @supportedCulture
+ }
+
+ }
+
+
diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj
index 57312d77f90..56e9fbc43bf 100644
--- a/src/Orchard/Orchard.Framework.csproj
+++ b/src/Orchard/Orchard.Framework.csproj
@@ -685,6 +685,7 @@
+
diff --git a/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs b/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs
new file mode 100644
index 00000000000..f4606979042
--- /dev/null
+++ b/src/Orchard/Utility/Extensions/NameValueCollectionExtensions.cs
@@ -0,0 +1,12 @@
+using System.Collections.Specialized;
+using System.Linq;
+using System.Web;
+
+namespace Orchard.Utility.Extensions {
+ public static class NameValueCollectionExtensions {
+ public static string ToQueryString(this NameValueCollection nameValues) =>
+ string.Join(
+ "&",
+ (from string name in nameValues select string.Concat(name, "=", HttpUtility.UrlEncode(nameValues[name]))).ToArray());
+ }
+}