mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Compare commits
13 Commits
issue/6748
...
issue/8816
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
871639c9ec | ||
|
|
8309d086ae | ||
|
|
7b01ebbad0 | ||
|
|
4da215ef81 | ||
|
|
4be9398e8c | ||
|
|
b6a89aa94b | ||
|
|
8e5a6a1f66 | ||
|
|
8f93170486 | ||
|
|
81570fab35 | ||
|
|
0d9fccb624 | ||
|
|
fdbb06ba8d | ||
|
|
15cad85d1e | ||
|
|
0b86413e60 |
24
.github/workflows/compile.yml
vendored
24
.github/workflows/compile.yml
vendored
@@ -26,11 +26,33 @@ jobs:
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
|
||||
- name: Compile
|
||||
run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:MvcBuildViews=true /p:TreatWarningsAsErrors=true -WarnAsError
|
||||
run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:TreatWarningsAsErrors=true -WarnAsError /p:MvcBuildViews=true
|
||||
|
||||
- name: Test
|
||||
run: msbuild Orchard.proj /m /v:minimal /t:Test
|
||||
|
||||
- name: Run Orchard setup
|
||||
run: |
|
||||
$commandFile = 'src/Orchard.Web/bin/setup-commands.txt'
|
||||
New-Item -Path $commandFile -ItemType File -Force
|
||||
Set-Content -Path $commandFile -Value 'setup /SiteName:Orchard /AdminUsername:admin /AdminPassword:Password1! /DatabaseProvider:SqlCe /Recipe:Default'
|
||||
& 'src/Orchard.Web/bin/Orchard.exe' @$commandFile
|
||||
|
||||
- name: Run code generation
|
||||
run: |
|
||||
$commandFile = 'src/Orchard.Web/bin/codegen-commands.txt'
|
||||
New-Item -Path $commandFile -ItemType File -Force
|
||||
Set-Content -Path $commandFile -Value @'
|
||||
feature enable Orchard.CodeGeneration
|
||||
codegen module Orchard.CodeGeneration.TestModule
|
||||
codegen theme Orchard.CodeGeneration.TestTheme /CreateProject:true
|
||||
codegen moduletests Orchard.CodeGeneration.TestModule
|
||||
'@
|
||||
& 'src/Orchard.Web/bin/Orchard.exe' @$commandFile
|
||||
|
||||
- name: Compile with generated projects
|
||||
run: msbuild Orchard.proj /m /v:minimal /t:Compile /p:TreatWarningsAsErrors=true -WarnAsError /NoWarn:CS2008
|
||||
|
||||
compile-node:
|
||||
name: Compile client-side assets
|
||||
defaults:
|
||||
|
||||
26
README.md
26
README.md
@@ -1,16 +1,14 @@
|
||||
# Orchard
|
||||
|
||||
Orchard is a free, open source, community-focused Content Management System built on the ASP.NET MVC platform.
|
||||
Orchard is a free, open source, community-focused Content Management System built on the ASP.NET MVC platform. You are looking at Orchard 1, the older, .NET Framework-based version that has been in development since 2009.
|
||||
|
||||
[](https://gitter.im/OrchardCMS/Orchard?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
If you're starting a new project (or maintaining a project built on Orchard 1), you should check out [Orchard Core](https://github.com/OrchardCMS/OrchardCore), the new generation of Orchard built on ASP.NET Core. You can also try it for free on [DotNest.com](https://dotnest.com)!
|
||||
|
||||
You can try it for free on [DotNest.com](https://dotnest.com) or on Microsoft Azure by clicking on this button.
|
||||
|
||||
[](https://portal.azure.com/#create/OutercurveFoundation.OrchardCMS)
|
||||
Join the community discussion on [Discord](https://orchardcore.net/discord), where we also have a [channel dedicated to Orchard 1](https://discord.com/channels/551136772243980291/551137194689953848).
|
||||
|
||||
## About The Orchard Project
|
||||
|
||||
#### Please visit our website at https://orchardproject.net for the most current information about this project.
|
||||
#### Please visit our website at https://orchardcore.net for the most current information about this project.
|
||||
|
||||
Orchard is a free, open source, community-focused **Content Management System** built on the ASP.NET MVC platform.
|
||||
|
||||
@@ -24,12 +22,12 @@ Our mission is to empower our users and foster a dedicated and diverse community
|
||||
|
||||
Orchard is currently in version **[1.10.3](https://github.com/OrchardCMS/Orchard/releases/tag/1.10.3)**: It contains bugfixes and the more impactful changes and new features added in the latest major version (*1.10*).
|
||||
|
||||
We invite participation by the developer community in shaping the project’s direction, so that we can publicly validate our designs and development approach.
|
||||
We invite participation by the developer community in shaping the project's direction, so that we can publicly validate our designs and development approach.
|
||||
All our releases are available on our [Releases](https://github.com/OrchardCMS/Orchard/releases) page, and we encourage interested developers to check out the source code on the Orchard GitHub site and get involved with the project.
|
||||
|
||||
* [Download the latest release](https://github.com/OrchardCMS/Orchard/releases)
|
||||
* [Feature roadmap](https://docs.orchardproject.net/en/latest/Documentation/Feature-roadmap/)
|
||||
* [Docs and designs/specs](https://docs.orchardproject.net)
|
||||
* [Feature roadmap](https://docs.orchardcore.net/projects/O1/en/latest/Documentation/Feature-roadmap/)
|
||||
* [Docs and designs/specs](https://docs.orchardcore.net/projects/O1/en/latest/)
|
||||
|
||||
## How To Get Involved
|
||||
|
||||
@@ -42,11 +40,7 @@ There are many ways you can contribute to Orchard:
|
||||
* [Find and file a bug](https://github.com/OrchardCMS/Orchard/issues)
|
||||
* [Propose a feature idea](https://github.com/OrchardCMS/Orchard/issues/new)
|
||||
* [Ask and answer questions on Stack Overflow](https://stackoverflow.com/questions/tagged/orchardcms)
|
||||
* [Participate in our gitter.im chatroom](https://gitter.im/OrchardCMS/Orchard)
|
||||
* [Submit a pull request](https://docs.orchardproject.net/en/latest/Documentation/Contributing-patches/)
|
||||
* [Join us on Discord](https://orchardcore.net/discord)
|
||||
* [Submit a pull request](https://docs.orchardcore.net/projects/O1/en/latest/Documentation/Contributing-patches/)
|
||||
* [Translate Orchard](https://crowdin.com/project/orchard-cms)
|
||||
* [Contribute modules and themes to our gallery](https://gallery.orchardproject.net/)
|
||||
|
||||
## The Future Of Orchard CMS: Orchard Core
|
||||
|
||||
As the underlying frameworks (.NET, ASP.NET and ASP.NET MVC) are constantly evolving, Orchard of course keeps track of the changes and improvements of these: Orchard Core is the next generation of Orchard releases that is based on [ASP.NET Core](https://www.asp.net/core). Just like the current Orchard project, it's fully [open-source and is publicly available on GitHub](https://github.com/OrchardCMS/OrchardCore). Orchard Core (as a framework) is being built from scratch: it's still in development and does not share any of its code base (at least directly) with the current versions (1.x) of Orchard.
|
||||
* [Contribute modules and themes to our gallery](https://gallery.orchardproject.net)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>..\..\..\OrchardBasicCorrectness.ruleset</CodeAnalysisRuleSet>
|
||||
<CodeAnalysisRuleSet>..\..\..\..\OrchardBasicCorrectness.ruleset</CodeAnalysisRuleSet>
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
@@ -35,16 +35,16 @@
|
||||
<Prefer32Bit>false</Prefer32Bit>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Autofac">
|
||||
<Reference Include="Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\..\packages\Autofac.3.5.2\lib\net40\Autofac.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.2.1510.2205, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\..\packages\Moq.4.2.1510.2205\lib\net40\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NHibernate">
|
||||
<HintPath>..\..\..\..\packages\NHibernate.4.0.1.4000\lib\net40\NHibernate.dll</HintPath>
|
||||
<Reference Include="NHibernate, Version=4.1.0.4000, Culture=neutral, PublicKeyToken=aa95f207798dfdb4, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\..\packages\NHibernate.4.1.2.4000\lib\net40\NHibernate.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="nunit.framework">
|
||||
<Reference Include="nunit.framework, Version=2.5.10.11092, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\..\..\packages\NUnit.2.5.10.11092\lib\nunit.framework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Autofac" version="3.5.2" targetFramework="net48" />
|
||||
<package id="Moq" version="4.2.1510.2205" targetFramework="net48" />
|
||||
<package id="NHibernate" version="4.1.2.4000" targetFramework="net48" />
|
||||
<package id="NUnit" version="2.5.10.11092" targetFramework="net48" />
|
||||
</packages>
|
||||
@@ -161,6 +161,8 @@ namespace Orchard.CodeGeneration.Commands {
|
||||
templateText = templateText.Replace("$$ModuleTypeLibGuid$$", Guid.NewGuid().ToString());
|
||||
File.WriteAllText(propertiesPath + "\\AssemblyInfo.cs", templateText);
|
||||
content.Add(propertiesPath + "\\AssemblyInfo.cs");
|
||||
File.WriteAllText(testsPath + "packages.config", File.ReadAllText(_codeGenTemplatePath + "ModuleTestsPackagesConfig.txt"));
|
||||
content.Add(testsPath + "packages.config");
|
||||
|
||||
var itemGroup = CreateProjectItemGroup(testsPath, content, folders);
|
||||
|
||||
@@ -168,7 +170,7 @@ namespace Orchard.CodeGeneration.Commands {
|
||||
csprojText = csprojText.Replace("$$ProjectName$$", projectName);
|
||||
csprojText = csprojText.Replace("$$TestsProjectGuid$$", projectGuid);
|
||||
csprojText = csprojText.Replace("$$FileIncludes$$", itemGroup ?? "");
|
||||
csprojText = csprojText.Replace("$$OrchardReferences$$", GetOrchardReferences());
|
||||
csprojText = csprojText.Replace("$$OrchardReferences$$", GetOrchardReferences(modulesFolderRelativeDepth: 3));
|
||||
|
||||
File.WriteAllText(testsPath + projectName + ".csproj", csprojText);
|
||||
|
||||
@@ -327,15 +329,17 @@ namespace Orchard.CodeGeneration.Commands {
|
||||
return text;
|
||||
}
|
||||
|
||||
private static string GetOrchardReferences() {
|
||||
private static string GetOrchardReferences(int modulesFolderRelativeDepth = 2) {
|
||||
var frameworkRelativeDepth = string.Join("\\", Enumerable.Repeat("..", modulesFolderRelativeDepth + 1));
|
||||
var coreRelativeDepth = string.Join("\\", Enumerable.Repeat("..", modulesFolderRelativeDepth));
|
||||
return IsSourceEnlistment() ?
|
||||
@"<ProjectReference Include=""..\..\..\Orchard\Orchard.Framework.csproj"">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
$@"<ProjectReference Include=""{frameworkRelativeDepth}\Orchard\Orchard.Framework.csproj"">
|
||||
<Project>{{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
<Private>$(MvcBuildViews)</Private>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include=""..\..\Core\Orchard.Core.csproj"">
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<ProjectReference Include=""{coreRelativeDepth}\Core\Orchard.Core.csproj"">
|
||||
<Project>{{9916839C-39FC-4CEB-A5AF-89CA7E87119F}}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
<Private>$(MvcBuildViews)</Private>
|
||||
</ProjectReference>" :
|
||||
|
||||
@@ -88,6 +88,7 @@
|
||||
<Compile Include="Services\CodeGenerationCommandInterpreter.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="CodeGenerationTemplates\ModuleTestsPackagesConfig.txt" />
|
||||
<Content Include="CodeGenerationTemplates\ModuleStylesMinCss.txt" />
|
||||
<Content Include="CodeGenerationTemplates\ModuleStylesCss.txt" />
|
||||
<Content Include="CodeGenerationTemplates\ModuleStylesLess.txt" />
|
||||
@@ -158,4 +159,4 @@
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -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>
|
||||
@@ -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