Including Orchard.Forms

--HG--
branch : 1.x
This commit is contained in:
Sebastien Ros
2012-08-22 15:36:43 -07:00
parent a181a20e5d
commit ff37a20d46
14 changed files with 940 additions and 0 deletions

View 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

View 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>

View File

@@ -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")]

View 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>

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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);
}
}

View File

@@ -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; }
}
}

View 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;
}
}
}

View File

@@ -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;
}
}
}

View 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>

View 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>

View 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>