mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 19:54:57 +08:00
Including Orchard.Forms
--HG-- branch : 1.x
This commit is contained in:
12
src/Orchard.Web/Modules/Orchard.Forms/Module.txt
Normal file
12
src/Orchard.Web/Modules/Orchard.Forms/Module.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
Name: Forms
|
||||
AntiForgery: enabled
|
||||
Author: The Orchard Team
|
||||
Website: http://orchardforms.codeplex.com
|
||||
Version: 1.5
|
||||
OrchardVersion: 1.4
|
||||
Description: Provides a system to publish and alter html forms.
|
||||
Features:
|
||||
Orchard.Forms:
|
||||
Name: Forms
|
||||
Description: Provides a system to publish and alter html forms.
|
||||
Category: Developer
|
142
src/Orchard.Web/Modules/Orchard.Forms/Orchard.Forms.csproj
Normal file
142
src/Orchard.Web/Modules/Orchard.Forms/Orchard.Forms.csproj
Normal file
@@ -0,0 +1,142 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProductVersion>9.0.30729</ProductVersion>
|
||||
<SchemaVersion>2.0</SchemaVersion>
|
||||
<ProjectGuid>{642A49D7-8752-4177-80D6-BFBBCFAD3DE0}</ProjectGuid>
|
||||
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>Orchard.Forms</RootNamespace>
|
||||
<AssemblyName>Orchard.Forms</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
|
||||
<MvcBuildViews>false</MvcBuildViews>
|
||||
<FileUpgradeFlags>
|
||||
</FileUpgradeFlags>
|
||||
<OldToolsVersion>4.0</OldToolsVersion>
|
||||
<UpgradeBackupLocation>
|
||||
</UpgradeBackupLocation>
|
||||
<TargetFrameworkProfile />
|
||||
<UseIISExpress>false</UseIISExpress>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="ClaySharp">
|
||||
<HintPath>..\..\..\..\lib\claysharp\ClaySharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations">
|
||||
<RequiredTargetFramework>3.5</RequiredTargetFramework>
|
||||
</Reference>
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Web.Abstractions" />
|
||||
<Reference Include="System.Web.Routing" />
|
||||
<Reference Include="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<SpecificVersion>False</SpecificVersion>
|
||||
<HintPath>..\..\..\..\lib\aspnetmvc\System.Web.WebPages.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Web.config" />
|
||||
<Content Include="Views\Web.config" />
|
||||
<Content Include="Scripts\Web.config" />
|
||||
<Content Include="Styles\Web.config" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Content Include="Module.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Orchard\Orchard.Framework.csproj">
|
||||
<Project>{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}</Project>
|
||||
<Name>Orchard.Framework</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\Core\Orchard.Core.csproj">
|
||||
<Project>{9916839C-39FC-4CEB-A5AF-89CA7E87119F}</Project>
|
||||
<Name>Orchard.Core</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Services\FormProcessor.cs" />
|
||||
<Compile Include="Services\IFormEvents.cs" />
|
||||
<Compile Include="Shapes\EditorShapes.cs" />
|
||||
<Compile Include="Services\IFormProvider.cs" />
|
||||
<Compile Include="Services\DefaultFormManager.cs" />
|
||||
<Compile Include="Services\IFormManager.cs" />
|
||||
<Compile Include="Shapes\PropertyItemsBehavior.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target> -->
|
||||
<Target Name="AfterBuild" DependsOnTargets="AfterBuildCompiler">
|
||||
<PropertyGroup>
|
||||
<AreasManifestDir>$(ProjectDir)\..\Manifests</AreasManifestDir>
|
||||
</PropertyGroup>
|
||||
<!-- If this is an area child project, uncomment the following line:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Child" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
-->
|
||||
<!-- If this is an area parent project, uncomment the following lines:
|
||||
<CreateAreaManifest AreaName="$(AssemblyName)" AreaType="Parent" AreaPath="$(ProjectDir)" ManifestPath="$(AreasManifestDir)" ContentFiles="@(Content)" />
|
||||
<CopyAreaManifests ManifestPath="$(AreasManifestDir)" CrossCopy="false" RenameViews="true" />
|
||||
-->
|
||||
</Target>
|
||||
<Target Name="AfterBuildCompiler" Condition="'$(MvcBuildViews)'=='true'">
|
||||
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(ProjectDir)\..\$(ProjectName)" />
|
||||
</Target>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
|
||||
<WebProjectProperties>
|
||||
<UseIIS>False</UseIIS>
|
||||
<AutoAssignPort>True</AutoAssignPort>
|
||||
<DevelopmentServerPort>45979</DevelopmentServerPort>
|
||||
<DevelopmentServerVPath>/</DevelopmentServerVPath>
|
||||
<IISUrl>
|
||||
</IISUrl>
|
||||
<NTLMAuthentication>False</NTLMAuthentication>
|
||||
<UseCustomServer>True</UseCustomServer>
|
||||
<CustomServerUrl>http://orchard.codeplex.com</CustomServerUrl>
|
||||
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
|
||||
</WebProjectProperties>
|
||||
</FlavorProperties>
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
@@ -0,0 +1,35 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("Orchard.Forms")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyProduct("Orchard")]
|
||||
[assembly: AssemblyCopyright("Copyright <20> Outercurve Foundation 2009")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("7d9d33f6-ee65-4d41-b4c3-edbadaf0a658")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Revision and Build Numbers
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("1.5")]
|
||||
[assembly: AssemblyFileVersion("1.5")]
|
25
src/Orchard.Web/Modules/Orchard.Forms/Scripts/Web.config
Normal file
25
src/Orchard.Web/Modules/Orchard.Forms/Scripts/Web.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Logging;
|
||||
|
||||
namespace Orchard.Forms.Services {
|
||||
public class DefaultFormManager : IFormManager {
|
||||
private readonly IEnumerable<IFormProvider> _formProviders;
|
||||
private readonly IEnumerable<IFormEventHandler> _formEventHandlers;
|
||||
private readonly IShapeFactory _shapeFactory;
|
||||
|
||||
public ILogger Logger { get; set; }
|
||||
|
||||
public DefaultFormManager(IEnumerable<IFormProvider> formProviders, IEnumerable<IFormEventHandler> formEventHandlers, IShapeFactory shapeFactory) {
|
||||
_formProviders = formProviders;
|
||||
_formEventHandlers = formEventHandlers;
|
||||
_shapeFactory = shapeFactory;
|
||||
}
|
||||
|
||||
public dynamic Build(string formName, string prefix = "") {
|
||||
var context = new DescribeContext();
|
||||
foreach (var provider in _formProviders) {
|
||||
provider.Describe(context);
|
||||
}
|
||||
|
||||
var descriptor = context.Describe().FirstOrDefault(x => x.Name == formName);
|
||||
|
||||
if (descriptor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var shape = descriptor.Shape(_shapeFactory);
|
||||
var buildingContext = new BuildingContext { Shape = shape };
|
||||
|
||||
_formEventHandlers.Invoke(dispatch => dispatch.Building(buildingContext), Logger);
|
||||
// alter the shapes tree (validation, ajax, ...));
|
||||
_formEventHandlers.Invoke(dispatch => dispatch.Built(buildingContext), Logger);
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
public dynamic Bind(dynamic formShape, IValueProvider valueProvider, string prefix = "") {
|
||||
Action<object> process = shape => BindValue(shape, valueProvider, prefix);
|
||||
FormNodesProcessor.ProcessForm(formShape, process);
|
||||
|
||||
return formShape;
|
||||
}
|
||||
|
||||
|
||||
private static void BindValue(dynamic shape, IValueProvider valueProvider, string prefix) {
|
||||
// if the shape has a Name property, look for a value in
|
||||
// the ValueProvider
|
||||
var name = shape.Name;
|
||||
if (name != null) {
|
||||
ValueProviderResult value = valueProvider.GetValue(prefix + name);
|
||||
if (value != null) {
|
||||
if (shape.Metadata.Type == "Checkbox" || shape.Metadata.Type == "Radio") {
|
||||
shape.Checked = Convert.ToString(shape.Value) == Convert.ToString(value.AttemptedValue);
|
||||
}
|
||||
else {
|
||||
shape.Value = value.AttemptedValue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Validate(ValidatingContext context) {
|
||||
_formEventHandlers.Invoke(dispatch => dispatch.Validating(context), Logger);
|
||||
_formEventHandlers.Invoke(dispatch => dispatch.Validated(context), Logger);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using Orchard.DisplayManagement;
|
||||
|
||||
namespace Orchard.Forms.Services {
|
||||
public static class FormNodesProcessor {
|
||||
public static void ProcessForm(dynamic shape, Action<object> process) {
|
||||
// if it's not a shape, ignore
|
||||
// e.g., SelectListItem
|
||||
if (!(shape is IShape)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// execute external code on this node
|
||||
process(shape);
|
||||
|
||||
// recursively process child nodes
|
||||
if (shape.Items != null) {
|
||||
foreach (var item in shape.Items) {
|
||||
ProcessForm(item, process);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
using System.Web.Mvc;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.Forms.Services {
|
||||
public interface IFormEventHandler : IEventHandler {
|
||||
void Building(BuildingContext context);
|
||||
void Built(BuildingContext context);
|
||||
void Validating(ValidatingContext context);
|
||||
void Validated(ValidatingContext context);
|
||||
}
|
||||
|
||||
public class FormHandler : IFormEventHandler {
|
||||
|
||||
public virtual void Building(BuildingContext context) {}
|
||||
|
||||
public virtual void Built(BuildingContext context) {}
|
||||
|
||||
public virtual void Validating(ValidatingContext context) {}
|
||||
|
||||
public virtual void Validated(ValidatingContext context) {}
|
||||
}
|
||||
|
||||
public class BuildingContext {
|
||||
public dynamic Shape { get; set; }
|
||||
}
|
||||
|
||||
public class ValidatingContext {
|
||||
public string FormName { get; set; }
|
||||
public IValueProvider ValueProvider { get; set; }
|
||||
public ModelStateDictionary ModelState { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace Orchard.Forms.Services {
|
||||
public interface IFormManager : IDependency {
|
||||
dynamic Build(string formName, string prefix = "");
|
||||
dynamic Bind(dynamic formShape, IValueProvider state, string prefix = "");
|
||||
void Validate(ValidatingContext context);
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.Events;
|
||||
|
||||
namespace Orchard.Forms.Services {
|
||||
public interface IFormProvider : IEventHandler {
|
||||
void Describe(DescribeContext context);
|
||||
}
|
||||
|
||||
public class DescribeContext {
|
||||
private readonly Dictionary<string, FormDescriptor> _descriptors = new Dictionary<string, FormDescriptor>();
|
||||
|
||||
public IList<FormDescriptor> Describe() {
|
||||
return _descriptors.Select(x => x.Value).ToList();
|
||||
}
|
||||
|
||||
public DescribeContext Form(string name, Func<IShapeFactory, dynamic> shape) {
|
||||
_descriptors[name] = new FormDescriptor { Name = name, Shape = shape };
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public class FormDescriptor {
|
||||
public string Name { get; set; }
|
||||
public Func<IShapeFactory, dynamic> Shape { get; set; }
|
||||
}
|
||||
}
|
404
src/Orchard.Web/Modules/Orchard.Forms/Shapes/EditorShapes.cs
Normal file
404
src/Orchard.Web/Modules/Orchard.Forms/Shapes/EditorShapes.cs
Normal file
@@ -0,0 +1,404 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using Orchard.DisplayManagement;
|
||||
using Orchard.DisplayManagement.Descriptors;
|
||||
using Orchard.DisplayManagement.Shapes;
|
||||
using Orchard.UI;
|
||||
|
||||
// ReSharper disable InconsistentNaming
|
||||
|
||||
namespace Orchard.Forms.Shapes {
|
||||
public class EditorShapes : IShapeTableProvider {
|
||||
private readonly ITagBuilderFactory _tagBuilderFactory;
|
||||
|
||||
public EditorShapes(ITagBuilderFactory tagBuilderFactory) {
|
||||
_tagBuilderFactory = tagBuilderFactory;
|
||||
}
|
||||
|
||||
public void Discover(ShapeTableBuilder builder) {
|
||||
// hack: This is important when using the Input shape directly, but it doesn't come into play
|
||||
// when using a 'master' shape yet.
|
||||
builder.Describe("Input").Configure(descriptor => descriptor.Wrappers.Add("InputWrapper"));
|
||||
builder.Describe("SelectList").Configure(descriptor => descriptor.Wrappers.Add("InputWrapper"));
|
||||
builder.Describe("Textarea").Configure(descriptor => descriptor.Wrappers.Add("InputWrapper"));
|
||||
builder.Describe("Form").OnCreating(ctx => ctx.Behaviors.Add(new PropertiesAreItems()));
|
||||
builder.Describe("Fieldset").OnCreating(ctx => ctx.Behaviors.Add(new PropertiesAreItems()));
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void InputLabel(HtmlHelper Html, TextWriter Output, dynamic Shape, string For, object Title) {
|
||||
if (Title != null) {
|
||||
var label = _tagBuilderFactory.Create(Shape, "label");
|
||||
label.MergeAttribute("for", For);
|
||||
label.InnerHtml = Html.Encode(Title);
|
||||
Output.WriteLine(label.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void InputHint(dynamic Display, dynamic Shape, HtmlHelper Html, TextWriter Output, object Description/*, object ActionLinkValue, object ActionLink*/) {
|
||||
// <span class="hint">
|
||||
if (Description != null) {
|
||||
var span = _tagBuilderFactory.Create(Shape, "span");
|
||||
span.AddCssClass("hint");
|
||||
var html = Html.Encode(Description);
|
||||
html = html.Replace("\n", "</span><span class=\"hint\">");
|
||||
span.InnerHtml = html;
|
||||
Output.WriteLine(span.ToString(TagRenderMode.Normal));
|
||||
}
|
||||
// action link
|
||||
if (Shape.ActionLinkValue != null) {
|
||||
if (Shape.ActionLink is string) {
|
||||
Shape.ActionLink = new { Action = Shape.ActionLink };
|
||||
}
|
||||
Output.WriteLine("<p>" + Display.Link(RouteValues: Shape.ActionLink, Value: Shape.ActionLinkValue) + "</p>");
|
||||
}
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void InputRequiredMarker(TextWriter Output, HtmlHelper Html, dynamic Shape, object ErrorMessage, bool? IsValid) {
|
||||
if (!IsValid.GetValueOrDefault(true) && ErrorMessage != null) {
|
||||
var span = _tagBuilderFactory.Create(Shape, "span");
|
||||
span.AddCssClass(HtmlHelper.ValidationMessageCssClassName);
|
||||
span.InnerHtml = Html.Encode(Shape.ErrorMessage);
|
||||
Output.WriteLine(span.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void InputWrapper(
|
||||
HtmlHelper Html,
|
||||
TextWriter Output,
|
||||
dynamic Display,
|
||||
dynamic Shape,
|
||||
string Type,
|
||||
string Id,
|
||||
string Name,
|
||||
object Title,
|
||||
string TitleDisplay,
|
||||
string EnabledBy,
|
||||
bool? EnableWrapper) {
|
||||
|
||||
/*
|
||||
* #id #type #name #title #title_display #field_prefix #field_suffix #description #required
|
||||
* #title_display=={before,after,invisible,attribute,none}
|
||||
*
|
||||
* {form_element} ==InputWrapper
|
||||
* <div attributes id="#id" class="form-type-#type form-item-#name">
|
||||
* {form_element_label} ==InputLabel
|
||||
* <label class="option?#title_display==after element-invisible?#title_display==invisible" for="#id">
|
||||
* #title
|
||||
* {form_required_marker} ==InputRequiredMarker
|
||||
* <span class='form-required'>This field is required.</span>
|
||||
* {/form_required_marker}?#required
|
||||
* </label>
|
||||
* {/form_element_label}?#title
|
||||
* <span class="field-prefix">#field_prefix</span>?#field_prefix
|
||||
* #child_content
|
||||
* <span class="field-suffix">#field_suffix</span>?#field_suffix
|
||||
* {form_element_label/}?#title_display==after
|
||||
* <div class="description">#description</div>?
|
||||
* </div>
|
||||
* {/form_element}
|
||||
*/
|
||||
|
||||
|
||||
// Wraps an input with metadata as follows (most of it is optional and won't render if not present):
|
||||
/*
|
||||
<div data-controllerid="{EnabledByInputId}">
|
||||
<label for="inputfieldname">{DisplayName}</label>
|
||||
{ChildContent}
|
||||
{ValidationMessage}
|
||||
<span class="hint">{Description}</span>
|
||||
<p><a href="{ActionLinkRouteUrl}">{ActionLinkText}</a></p>
|
||||
</div>
|
||||
* Note: For a checkbox, the label comes after
|
||||
* Note: Localizable strings should already be localized by this point.
|
||||
* When using this via an html.editor() call, the content has already been localized
|
||||
* by the generic MVC template.
|
||||
*/
|
||||
|
||||
if (!EnableWrapper.GetValueOrDefault(true)) {
|
||||
Output.WriteLine(Shape.Metadata.ChildContent);
|
||||
return;
|
||||
}
|
||||
|
||||
// surrounding div
|
||||
var div = new TagBuilder("div");
|
||||
if (TitleDisplay == "attribute") {
|
||||
if (Title != null) {
|
||||
div.MergeAttribute("title", Display(Title).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(EnabledBy)) {
|
||||
// note: the value should be the html ID of the input.
|
||||
// When using this via html.editor() the id is automatically converted from the property name
|
||||
// to the field id before it gets to the shape.
|
||||
div.MergeAttribute("data-controllerid", EnabledBy);
|
||||
}
|
||||
Output.WriteLine(div.ToString(TagRenderMode.StartTag));
|
||||
|
||||
var isCheckbox = new [] {"checkbox", "radio"}.Any(x => x.Equals(Type, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
IHtmlString labelHtml = null;
|
||||
if (Title != null) {
|
||||
labelHtml = Display.InputLabel(For: Id, Title: Title, Input: Shape, Classes: isCheckbox ? new[] { "forcheckbox" } : null);
|
||||
|
||||
if (TitleDisplay == "before" || (string.IsNullOrEmpty(TitleDisplay) && !isCheckbox)) {
|
||||
Output.Write(labelHtml);
|
||||
}
|
||||
}
|
||||
Output.WriteLine(Shape.Metadata.ChildContent);
|
||||
if (Title != null && (TitleDisplay == "after" || (string.IsNullOrEmpty(TitleDisplay) && isCheckbox))) {
|
||||
Output.Write(labelHtml);
|
||||
}
|
||||
|
||||
Output.WriteLine(Display.InputRequiredMarker(Input: Shape));
|
||||
Output.WriteLine(Display.InputHint(Input: Shape, Description: Shape.Description, ActionLink: Shape.ActionLink, ActionLinkValue: Shape.ActionLinkValue));
|
||||
|
||||
Output.WriteLine(div.ToString(TagRenderMode.EndTag));
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void Form(HtmlHelper Html, Action<object> Output, dynamic Display, dynamic Shape, string Method, string Action) {
|
||||
// (todo) design markup
|
||||
OrchardTagBuilder tag = _tagBuilderFactory.Create(Shape, "form");
|
||||
|
||||
tag.MergeAttribute("action", Action ?? Html.ViewContext.HttpContext.Request.Url.PathAndQuery);
|
||||
tag.MergeAttribute("method", Method ?? "POST");
|
||||
|
||||
Output(tag.ToString(TagRenderMode.StartTag));
|
||||
foreach (var item in Ordered(Shape.Items)) {
|
||||
Output(Display(item));
|
||||
}
|
||||
Output(tag.ToString(TagRenderMode.EndTag));
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Hidden(dynamic Display, dynamic Shape) {
|
||||
return DisplayShapeAsInput(Display, Shape, "hidden");
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void Fieldset(Action<object> Output, dynamic Display, dynamic Shape, object Title) {
|
||||
// (todo) design markup
|
||||
OrchardTagBuilder tag = _tagBuilderFactory.Create(Shape, "fieldset");
|
||||
Output(tag.ToString(TagRenderMode.StartTag));
|
||||
if (Title != null) {
|
||||
Output("<legend>");
|
||||
Output(Display(Title));
|
||||
Output("</legend>");
|
||||
}
|
||||
foreach (var item in Ordered(Shape.Items)) {
|
||||
Output(Display(item));
|
||||
}
|
||||
Output(tag.ToString(TagRenderMode.EndTag));
|
||||
}
|
||||
|
||||
private static Tuple<string, object> Positionify(dynamic item, int naturalIndex) {
|
||||
if (item is IShape) {
|
||||
return new Tuple<string, object>(((IShape)item).Metadata.Position, item);
|
||||
}
|
||||
if (item is Tuple<string, object>) {
|
||||
return item;
|
||||
}
|
||||
else {
|
||||
// non-shape items are given a position equal to their index in the list, giving the shapes a way of mingling among them
|
||||
return new Tuple<string, object>(naturalIndex.ToString(CultureInfo.InvariantCulture), item);
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<object> Ordered(IEnumerable<object> items) {
|
||||
return items.Select(Positionify).OrderBy(p => p.Item1, new FlatPositionComparer()).Select(p => p.Item2);
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Textbox(dynamic Display, dynamic Shape) {
|
||||
Shape.Classes.Add("text-box");
|
||||
return DisplayShapeAsInput(Display, Shape, "text");
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void Textarea(
|
||||
TextWriter Output,
|
||||
dynamic Display,
|
||||
dynamic Shape, string Name, string Value, int Size = 0, int Rows = 0) {
|
||||
var select = (TagBuilder)_tagBuilderFactory.Create(Shape, "textarea");
|
||||
select.AddCssClass("text-box");
|
||||
|
||||
if (Name != null) {
|
||||
select.MergeAttribute("name", Name, false);
|
||||
}
|
||||
|
||||
if (Size > 0) {
|
||||
select.MergeAttribute("size", Size.ToString(), false);
|
||||
}
|
||||
|
||||
if (Rows > 0) {
|
||||
select.MergeAttribute("rows", Rows.ToString(), false);
|
||||
}
|
||||
|
||||
Output.Write(select.ToString(TagRenderMode.StartTag));
|
||||
Output.WriteLine(Value);
|
||||
Output.WriteLine(select.ToString(TagRenderMode.EndTag));
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Submit(dynamic Display, dynamic Shape) {
|
||||
Shape.Classes.Add("primaryAction button");
|
||||
// (todo) this might not need full wrapper in hindsight?
|
||||
return DisplayShapeAsInput(Display, Shape, "submit");
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Markup(dynamic Display, dynamic Shape, string Value) {
|
||||
return new MvcHtmlString(Value);
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Button(dynamic Display, dynamic Shape) {
|
||||
// (todo) this might not need full wrapper in hindsight?
|
||||
return DisplayShapeAsInput(Display, Shape, "button");
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Input(HtmlHelper Html, dynamic Shape, dynamic Display, string Type, string Name, dynamic Value, bool? Checked) {
|
||||
var tag = (TagBuilder)_tagBuilderFactory.Create(Shape, "input");
|
||||
tag.MergeAttribute("type", Type, false);
|
||||
if (Name != null) {
|
||||
tag.MergeAttribute("name", Name, false);
|
||||
}
|
||||
if (Value != null) {
|
||||
Value = Value is string ? Value : Display(Value);
|
||||
tag.MergeAttribute("value", Convert.ToString(Value), false);
|
||||
}
|
||||
if (Checked.GetValueOrDefault()) {
|
||||
tag.MergeAttribute("checked", "checked");
|
||||
}
|
||||
return new HtmlString(tag.ToString(TagRenderMode.SelfClosing));
|
||||
}
|
||||
|
||||
private static IHtmlString DisplayShapeAsInput(dynamic Display, dynamic Shape, string inputType) {
|
||||
Shape.Metadata.Alternates.Clear();
|
||||
Shape.Type = inputType;
|
||||
Shape.Metadata.Type = "Input";
|
||||
|
||||
// HACK: This code shouldn't know about this, but cascading shapes doesn't work well with wrappers and other metadata yet
|
||||
// todo: alternates, etc?
|
||||
Shape.Metadata.Wrappers.Clear();
|
||||
Shape.Metadata.Wrappers.Add("InputWrapper");
|
||||
|
||||
var display = Display(Shape);
|
||||
// hack: so that InputWrapper doesn't render a 2nd time on the master shape
|
||||
// todo: alternates, etc?
|
||||
Shape.Metadata.Wrappers.Clear();
|
||||
return display;
|
||||
}
|
||||
|
||||
private static string ListItemToOption(SelectListItem item) {
|
||||
var option = new TagBuilder("option");
|
||||
option.InnerHtml = HttpUtility.HtmlEncode(item.Text);
|
||||
|
||||
if (item.Value != null) {
|
||||
option.Attributes["value"] = item.Value;
|
||||
}
|
||||
if (item.Selected) {
|
||||
option.Attributes["selected"] = "selected";
|
||||
}
|
||||
return option.ToString(TagRenderMode.Normal);
|
||||
}
|
||||
|
||||
private static object GetSelectProperty(object obj, string propertyName) {
|
||||
if (string.IsNullOrEmpty(propertyName)) {
|
||||
return obj.ToString();
|
||||
}
|
||||
var pi = obj.GetType().GetProperty(propertyName);
|
||||
return pi.GetValue(obj, new object[] { });
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public void SelectList(
|
||||
TextWriter Output,
|
||||
dynamic Display,
|
||||
dynamic Shape,
|
||||
IEnumerable<object> Items,
|
||||
string DataTextField,
|
||||
string DataValueField,
|
||||
string Name,
|
||||
int Size = 0,
|
||||
bool Multiple = false
|
||||
) {
|
||||
var select = (TagBuilder)_tagBuilderFactory.Create(Shape, "select");
|
||||
|
||||
if (Name != null) {
|
||||
select.MergeAttribute("name", Name, false);
|
||||
}
|
||||
|
||||
if (Size > 1) {
|
||||
select.MergeAttribute("size", Size.ToString(), false);
|
||||
}
|
||||
|
||||
if (Multiple) {
|
||||
select.MergeAttribute("multiple", "multiple", false);
|
||||
}
|
||||
|
||||
Output.WriteLine(select.ToString(TagRenderMode.StartTag));
|
||||
|
||||
string selectedValue = Convert.ToString(Shape.Value);
|
||||
var selectedValues = selectedValue.Split(new[] { ',' });
|
||||
|
||||
foreach (var item in Items) {
|
||||
var selectItem = item as SelectListItem;
|
||||
if (selectItem == null) {
|
||||
selectItem = new SelectListItem();
|
||||
if (item is string) {
|
||||
var itemStr = (string)item;
|
||||
selectItem.Text = itemStr;
|
||||
selectItem.Selected = (itemStr == Convert.ToString(Shape.Value));
|
||||
}
|
||||
else {
|
||||
selectItem.Text = Convert.ToString(GetSelectProperty(item, DataTextField));
|
||||
if (DataValueField != null) {
|
||||
var value = GetSelectProperty(item, DataValueField);
|
||||
selectItem.Value = Convert.ToString(value);
|
||||
selectItem.Selected = (value == Shape.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
selectItem.Selected = selectedValues.Any(x => x == selectItem.Value);
|
||||
}
|
||||
|
||||
Output.WriteLine(ListItemToOption(selectItem));
|
||||
}
|
||||
|
||||
Output.WriteLine(select.ToString(TagRenderMode.EndTag));
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Checkbox(dynamic Display, dynamic Shape) {
|
||||
return DisplayShapeAsInput(Display, Shape, "checkbox");
|
||||
}
|
||||
|
||||
[Shape]
|
||||
public IHtmlString Radio(dynamic Display, dynamic Shape) {
|
||||
return DisplayShapeAsInput(Display, Shape, "radio");
|
||||
}
|
||||
|
||||
static TagBuilder GetTagBuilder(string tagName, string id, IEnumerable<string> classes, IDictionary<string, string> attributes) {
|
||||
var tagBuilder = new TagBuilder(tagName);
|
||||
tagBuilder.MergeAttributes(attributes, false);
|
||||
foreach (var cssClass in classes ?? Enumerable.Empty<string>())
|
||||
tagBuilder.AddCssClass(cssClass);
|
||||
if (!string.IsNullOrWhiteSpace(id))
|
||||
tagBuilder.GenerateId(id);
|
||||
return tagBuilder;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Orchard.DisplayManagement;
|
||||
using ClaySharp;
|
||||
|
||||
namespace Orchard.Forms.Shapes {
|
||||
public class PropertiesAreItems : ClayBehavior {
|
||||
public override object SetMember(Func<object> proceed, dynamic self, string name, object value) {
|
||||
Patch(self, name, value);
|
||||
return proceed();
|
||||
}
|
||||
|
||||
public override object SetIndex(Func<object> proceed, dynamic self, IEnumerable<object> keys, object value) {
|
||||
if (keys.Count() == 1 && keys.All(k => k is string))
|
||||
Patch(self, System.Convert.ToString(keys.Single()), value);
|
||||
return proceed();
|
||||
}
|
||||
|
||||
public override object InvokeMember(Func<object> proceed, dynamic self, string name, INamedEnumerable<object> args) {
|
||||
if (args.Count() == 1 && args.Named.Count() == 0)
|
||||
Patch(self, name, args.Single());
|
||||
return proceed();
|
||||
}
|
||||
|
||||
readonly IDictionary<string, object> _assigned = new Dictionary<string, object>();
|
||||
private void Patch(dynamic self, string name, object value) {
|
||||
if (!name.StartsWith("_"))
|
||||
return;
|
||||
|
||||
object priorValue;
|
||||
if (_assigned.TryGetValue(name, out priorValue) && priorValue != null) {
|
||||
// it's a no-op to reassign same value to a prop
|
||||
if (priorValue == value)
|
||||
return;
|
||||
|
||||
self.Items.Remove(priorValue);
|
||||
}
|
||||
if (value is IShape) {
|
||||
self.Items.Add(value);
|
||||
}
|
||||
_assigned[name] = value;
|
||||
}
|
||||
}
|
||||
}
|
25
src/Orchard.Web/Modules/Orchard.Forms/Styles/Web.config
Normal file
25
src/Orchard.Web/Modules/Orchard.Forms/Styles/Web.config
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<!-- iis6 - for any request in this location, return via managed static file handler -->
|
||||
<add path="*" verb="*" type="System.Web.StaticFileHandler" />
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<staticContent>
|
||||
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="7.00:00:00" />
|
||||
</staticContent>
|
||||
|
||||
<handlers accessPolicy="Script,Read">
|
||||
<!--
|
||||
iis7 - for any request to a file exists on disk, return it via native http module.
|
||||
accessPolicy 'Script' is to allow for a managed 404 page.
|
||||
-->
|
||||
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
41
src/Orchard.Web/Modules/Orchard.Forms/Views/Web.config
Normal file
41
src/Orchard.Web/Modules/Orchard.Forms/Views/Web.config
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
<appSettings>
|
||||
<add key="webpages:Enabled" value="false" />
|
||||
</appSettings>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
</httpHandlers>
|
||||
|
||||
<!--
|
||||
Enabling request validation in view pages would cause validation to occur
|
||||
after the input has already been processed by the controller. By default
|
||||
MVC performs request validation before a controller processes the input.
|
||||
To change this behavior apply the ValidateInputAttribute to a
|
||||
controller or action.
|
||||
-->
|
||||
<pages
|
||||
validateRequest="false"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" namespace="System.Web.Mvc" tagPrefix="mvc" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false"/>
|
||||
<handlers>
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
|
||||
<bindingRedirect oldVersion="2.0.0.0" newVersion="3.0.0.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
41
src/Orchard.Web/Modules/Orchard.Forms/Web.config
Normal file
41
src/Orchard.Web/Modules/Orchard.Forms/Web.config
Normal file
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0"?>
|
||||
<configuration>
|
||||
|
||||
<configSections>
|
||||
<sectionGroup name="system.web.webPages.razor" type="System.Web.WebPages.Razor.Configuration.RazorWebSectionGroup, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
|
||||
<remove name="host" />
|
||||
<remove name="pages" />
|
||||
<section name="host" type="System.Web.WebPages.Razor.Configuration.HostSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
<section name="pages" type="System.Web.WebPages.Razor.Configuration.RazorPagesSection, System.Web.WebPages.Razor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" />
|
||||
</sectionGroup>
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<pages pageBaseType="Orchard.Mvc.ViewEngines.Razor.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="System.Web.Mvc" />
|
||||
<add namespace="System.Web.Mvc.Ajax" />
|
||||
<add namespace="System.Web.Mvc.Html" />
|
||||
<add namespace="System.Web.Routing" />
|
||||
<add namespace="System.Web.WebPages" />
|
||||
<add namespace="System.Linq"/>
|
||||
<add namespace="System.Collections.Generic"/>
|
||||
<add namespace="Orchard.Mvc.Html"/>
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<system.web>
|
||||
<compilation targetFramework="4.0">
|
||||
<assemblies>
|
||||
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
<add assembly="System.Data.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
|
||||
<add assembly="System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
|
||||
<add assembly="System.Web.WebPages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
|
||||
</assemblies>
|
||||
</compilation>
|
||||
</system.web>
|
||||
|
||||
</configuration>
|
Reference in New Issue
Block a user