mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2026-02-09 09:16:41 +08:00
Merge branch '1.x' of https://git01.codeplex.com/orchard into 1.x
This commit is contained in:
@@ -49,6 +49,7 @@
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="System.Runtime.Caching" />
|
||||
<Reference Include="System.Web.ApplicationServices" />
|
||||
<Reference Include="System.Web.DynamicData" />
|
||||
<Reference Include="System.Web.Entity" />
|
||||
|
||||
@@ -1,51 +1,84 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Runtime.Caching;
|
||||
using System.Web;
|
||||
using Orchard.Environment.Configuration;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.Caching.Services {
|
||||
public class DefaultCacheStorageProvider : ICacheStorageProvider {
|
||||
// The technique of signaling tenant-specific cache entries to be invalidated comes from: http://stackoverflow.com/a/22388943/220230
|
||||
// Singleton so signals can be stored for the shell lifetime.
|
||||
public class DefaultCacheStorageProvider : ICacheStorageProvider, ISingletonDependency {
|
||||
private event EventHandler Signaled;
|
||||
|
||||
private readonly ShellSettings _shellSettings;
|
||||
private readonly IClock _clock;
|
||||
// MemoryCache is optimal with one instance, see: http://stackoverflow.com/questions/8463962/using-multiple-instances-of-memorycache/13425322#13425322
|
||||
private readonly MemoryCache _cache = MemoryCache.Default;
|
||||
|
||||
public DefaultCacheStorageProvider(ShellSettings shellSettings, IClock clock) {
|
||||
_shellSettings = shellSettings;
|
||||
_clock = clock;
|
||||
}
|
||||
|
||||
public void Put(string key, object value) {
|
||||
HttpRuntime.Cache.Insert(
|
||||
key,
|
||||
value,
|
||||
null,
|
||||
System.Web.Caching.Cache.NoAbsoluteExpiration,
|
||||
System.Web.Caching.Cache.NoSlidingExpiration,
|
||||
System.Web.Caching.CacheItemPriority.Normal,
|
||||
null);
|
||||
// Keys are already prefixed by DefaultCacheService so no need to do it here again.
|
||||
_cache.Set(key, value, GetCacheItemPolicy(MemoryCache.InfiniteAbsoluteExpiration));
|
||||
}
|
||||
|
||||
public void Put(string key, object value, TimeSpan validFor) {
|
||||
HttpRuntime.Cache.Insert(
|
||||
key,
|
||||
value,
|
||||
null,
|
||||
System.Web.Caching.Cache.NoAbsoluteExpiration,
|
||||
validFor,
|
||||
System.Web.Caching.CacheItemPriority.Normal,
|
||||
null);
|
||||
_cache.Set(key, value, GetCacheItemPolicy(new DateTimeOffset(_clock.UtcNow).ToOffset(validFor)));
|
||||
}
|
||||
|
||||
public void Remove(string key) {
|
||||
HttpRuntime.Cache.Remove(key);
|
||||
_cache.Remove(key);
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
var all = HttpRuntime.Cache
|
||||
.AsParallel()
|
||||
.Cast<DictionaryEntry>()
|
||||
.Select(x => x.Key.ToString())
|
||||
.ToList();
|
||||
|
||||
foreach (var key in all) {
|
||||
Remove(key);
|
||||
if (Signaled != null) {
|
||||
Signaled(null, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
public object Get(string key) {
|
||||
return HttpRuntime.Cache.Get(key);
|
||||
return _cache.Get(key);
|
||||
}
|
||||
|
||||
private CacheItemPolicy GetCacheItemPolicy(DateTimeOffset absoluteExpiration) {
|
||||
var cacheItemPolicy = new CacheItemPolicy();
|
||||
|
||||
cacheItemPolicy.AbsoluteExpiration = absoluteExpiration;
|
||||
cacheItemPolicy.SlidingExpiration = MemoryCache.NoSlidingExpiration;
|
||||
cacheItemPolicy.ChangeMonitors.Add(new TenantCacheClearMonitor(this));
|
||||
|
||||
return cacheItemPolicy;
|
||||
}
|
||||
|
||||
public class TenantCacheClearMonitor : ChangeMonitor {
|
||||
private readonly DefaultCacheStorageProvider _storageProvider;
|
||||
|
||||
private readonly string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
||||
public override string UniqueId {
|
||||
get { return _uniqueId; }
|
||||
}
|
||||
|
||||
public TenantCacheClearMonitor(DefaultCacheStorageProvider storageProvider) {
|
||||
_storageProvider = storageProvider;
|
||||
_storageProvider.Signaled += OnSignalRaised;
|
||||
base.InitializationComplete();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
base.Dispose();
|
||||
_storageProvider.Signaled -= OnSignalRaised;
|
||||
}
|
||||
|
||||
private void OnSignalRaised(object sender, EventArgs e) {
|
||||
// Cache objects are obligated to remove entry upon change notification.
|
||||
base.OnChanged(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using Orchard.DynamicForms.Elements;
|
||||
using Orchard.Forms.Services;
|
||||
using Orchard.Layouts.Framework.Drivers;
|
||||
using DescribeContext = Orchard.Forms.Services.DescribeContext;
|
||||
|
||||
namespace Orchard.DynamicForms.Drivers {
|
||||
public class EmailFieldDriver : FormsElementDriver<EmailField>{
|
||||
public EmailFieldDriver(IFormManager formManager) : base(formManager) {}
|
||||
|
||||
protected override EditorResult OnBuildEditor(EmailField element, ElementEditorContext context) {
|
||||
var autoLabelEditor = BuildForm(context, "AutoLabel");
|
||||
var emailFieldValidation = BuildForm(context, "EmailFieldValidation", "Validation:10");
|
||||
|
||||
return Editor(context, autoLabelEditor, emailFieldValidation);
|
||||
}
|
||||
|
||||
protected override void DescribeForm(DescribeContext context) {
|
||||
context.Form("EmailFieldValidation", factory => {
|
||||
var shape = (dynamic)factory;
|
||||
var form = shape.Fieldset(
|
||||
Id: "EmailFieldValidation",
|
||||
_IsRequired: shape.Checkbox(
|
||||
Id: "IsRequired",
|
||||
Name: "IsRequired",
|
||||
Title: "Required",
|
||||
Value: "true",
|
||||
Description: T("Tick this checkbox to make this email field a required field.")),
|
||||
_MaximumLength: shape.Textbox(
|
||||
Id: "MaximumLength",
|
||||
Name: "MaximumLength",
|
||||
Title: "Maximum Length",
|
||||
Classes: new[] { "text", "medium", "tokenized" },
|
||||
Description: T("The maximum length allowed.")),
|
||||
_CompareWith: shape.Textbox(
|
||||
Id: "CompareWith",
|
||||
Name: "CompareWith",
|
||||
Title: "Compare With",
|
||||
Classes: new[] { "text", "medium", "tokenized" },
|
||||
Description: T("The name of another field whose value must match with this email field.")),
|
||||
_CustomValidationMessage: shape.Textbox(
|
||||
Id: "CustomValidationMessage",
|
||||
Name: "CustomValidationMessage",
|
||||
Title: "Custom Validation Message",
|
||||
Classes: new[] { "text", "large", "tokenized" },
|
||||
Description: T("Optionally provide a custom validation message.")),
|
||||
_ShowValidationMessage: shape.Checkbox(
|
||||
Id: "ShowValidationMessage",
|
||||
Name: "ShowValidationMessage",
|
||||
Title: "Show Validation Message",
|
||||
Value: "true",
|
||||
Description: T("Autogenerate a validation message when a validation error occurs for the current field. Alternatively, to control the placement of the validation message you can use the ValidationMessage element instead.")));
|
||||
|
||||
return form;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Orchard.DynamicForms.Validators.Settings;
|
||||
|
||||
namespace Orchard.DynamicForms.Elements {
|
||||
public class EmailField : LabeledFormElement {
|
||||
public EmailFieldValidationSettings ValidationSettings {
|
||||
get { return State.GetModel<EmailFieldValidationSettings>(""); }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,10 +141,12 @@
|
||||
<Compile Include="Controllers\SubmissionAdminController.cs" />
|
||||
<Compile Include="Controllers\AdminController.cs" />
|
||||
<Compile Include="Controllers\FormController.cs" />
|
||||
<Compile Include="Drivers\EmailFieldDriver.cs" />
|
||||
<Compile Include="Drivers\PasswordFieldDriver.cs" />
|
||||
<Compile Include="Drivers\QueryDriver.cs" />
|
||||
<Compile Include="Drivers\ValidationMessageDriver.cs" />
|
||||
<Compile Include="Drivers\ValidationSummaryDriver.cs" />
|
||||
<Compile Include="Elements\EmailField.cs" />
|
||||
<Compile Include="Elements\PasswordField.cs" />
|
||||
<Compile Include="Elements\Query.cs" />
|
||||
<Compile Include="Elements\ValidationMessage.cs" />
|
||||
@@ -166,12 +168,15 @@
|
||||
<Compile Include="Services\IValidationRule.cs" />
|
||||
<Compile Include="ValidationRules\Mandatory.cs" />
|
||||
<Compile Include="ValidationRules\Compare.cs" />
|
||||
<Compile Include="ValidationRules\EmailAddress.cs" />
|
||||
<Compile Include="ValidationRules\RegularExpression.cs" />
|
||||
<Compile Include="ValidationRules\StringLength.cs" />
|
||||
<Compile Include="Validators\CheckBoxValidator.cs" />
|
||||
<Compile Include="ValidationRules\Required.cs" />
|
||||
<Compile Include="Services\ValidationRule.cs" />
|
||||
<Compile Include="Validators\EmailFieldValidator.cs" />
|
||||
<Compile Include="Validators\Settings\CheckBoxValidationSettings.cs" />
|
||||
<Compile Include="Validators\Settings\EmailFieldValidationSettings.cs" />
|
||||
<Compile Include="Validators\Settings\PasswordFieldValidationSettings.cs" />
|
||||
<Compile Include="Validators\Settings\TextFieldValidationSettings.cs" />
|
||||
<Compile Include="Services\Models\ValidationSettingsBase.cs" />
|
||||
@@ -384,6 +389,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Element-Form-Query-RadioList.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Element-Form-EmailField.Design.cshtml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Element-Form-EmailField.cshtml" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
|
||||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
|
||||
|
||||
@@ -18,7 +18,7 @@ using Orchard.Layouts.Framework.Serialization;
|
||||
using Orchard.Layouts.Helpers;
|
||||
using Orchard.Layouts.Models;
|
||||
using Orchard.Layouts.Services;
|
||||
using Orchard.Mvc;
|
||||
using Orchard.Localization.Services;
|
||||
using Orchard.Services;
|
||||
|
||||
namespace Orchard.DynamicForms.Services {
|
||||
@@ -30,32 +30,37 @@ namespace Orchard.DynamicForms.Services {
|
||||
private readonly IContentManager _contentManager;
|
||||
private readonly IContentDefinitionManager _contentDefinitionManager;
|
||||
private readonly IBindingManager _bindingManager;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IDynamicFormEventHandler _formEventHandler;
|
||||
private readonly Lazy<IEnumerable<IElementValidator>> _validators;
|
||||
private readonly IDateLocalizationServices _dateLocalizationServices;
|
||||
private readonly IOrchardServices _services;
|
||||
private readonly ICultureAccessor _cultureAccessor;
|
||||
|
||||
public FormService(
|
||||
ILayoutSerializer serializer,
|
||||
IClock clock,
|
||||
IRepository<Submission> submissionRepository,
|
||||
IFormElementEventHandler elementHandlers,
|
||||
IContentManager contentManager,
|
||||
IContentDefinitionManager contentDefinitionManager,
|
||||
IBindingManager bindingManager,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
IDynamicFormEventHandler formEventHandler,
|
||||
Lazy<IEnumerable<IElementValidator>> validators) {
|
||||
Lazy<IEnumerable<IElementValidator>> validators,
|
||||
IDateLocalizationServices dateLocalizationServices,
|
||||
IOrchardServices services,
|
||||
ICultureAccessor cultureAccessor) {
|
||||
|
||||
_serializer = serializer;
|
||||
_clock = clock;
|
||||
_submissionRepository = submissionRepository;
|
||||
_elementHandlers = elementHandlers;
|
||||
_contentManager = contentManager;
|
||||
_contentManager = services.ContentManager;
|
||||
_contentDefinitionManager = contentDefinitionManager;
|
||||
_bindingManager = bindingManager;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_formEventHandler = formEventHandler;
|
||||
_validators = validators;
|
||||
_dateLocalizationServices = dateLocalizationServices;
|
||||
_services = services;
|
||||
_cultureAccessor = cultureAccessor;
|
||||
}
|
||||
|
||||
public Form FindForm(LayoutPart layoutPart, string formName = null) {
|
||||
@@ -173,7 +178,7 @@ namespace Orchard.DynamicForms.Services {
|
||||
}
|
||||
|
||||
// Collect any remaining form values not handled by any specific element.
|
||||
var requestForm = _httpContextAccessor.Current().Request.Form;
|
||||
var requestForm = _services.WorkContext.HttpContext.Request.Form;
|
||||
var blackList = new[] {"__RequestVerificationToken", "formName", "contentId"};
|
||||
foreach (var key in
|
||||
from string key in requestForm
|
||||
@@ -200,7 +205,7 @@ namespace Orchard.DynamicForms.Services {
|
||||
}
|
||||
|
||||
dataTable.Columns.Add("Id");
|
||||
dataTable.Columns.Add("CreatedUtc", typeof (DateTime));
|
||||
dataTable.Columns.Add("Created");
|
||||
foreach (var columnName in columnNames) {
|
||||
dataTable.Columns.Add(columnName);
|
||||
}
|
||||
@@ -209,7 +214,7 @@ namespace Orchard.DynamicForms.Services {
|
||||
var dataRow = dataTable.NewRow();
|
||||
|
||||
dataRow["Id"] = record.Item1.Id;
|
||||
dataRow["CreatedUtc"] = record.Item1.CreatedUtc;
|
||||
dataRow["Created"] = _dateLocalizationServices.ConvertToSiteTimeZone(record.Item1.CreatedUtc).ToString(_cultureAccessor.CurrentCulture);
|
||||
foreach (var columnName in columnNames) {
|
||||
var value = record.Item2[columnName];
|
||||
dataRow[columnName] = value;
|
||||
|
||||
@@ -22,4 +22,8 @@
|
||||
***************************************************************/
|
||||
.form-field-element {
|
||||
margin: 0.8em 0 0 0;
|
||||
}
|
||||
|
||||
.form-field-element-enumeration select {
|
||||
margin: 1.5em 0 0 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Orchard.DynamicForms.Helpers;
|
||||
using Orchard.DynamicForms.Services;
|
||||
using Orchard.DynamicForms.Services.Models;
|
||||
using Orchard.Localization;
|
||||
|
||||
namespace Orchard.DynamicForms.ValidationRules {
|
||||
public class EmailAddress : ValidationRule {
|
||||
public EmailAddress() {
|
||||
RegexOptions = RegexOptions.Singleline | RegexOptions.IgnoreCase;
|
||||
Pattern = @"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$";
|
||||
}
|
||||
|
||||
public string Pattern { get; set; }
|
||||
public RegexOptions RegexOptions { get; set; }
|
||||
|
||||
public override void Validate(ValidateInputContext context) {
|
||||
if (!Regex.IsMatch(context.AttemptedValue, Pattern, RegexOptions)) {
|
||||
var message = GetValidationMessage(context);
|
||||
context.ModelState.AddModelError(context.FieldName, message.Text);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RegisterClientAttributes(RegisterClientValidationAttributesContext context) {
|
||||
context.ClientAttributes["data-val-regex"] = GetValidationMessage(context).Text;
|
||||
context.ClientAttributes["data-val-regex-pattern"] = Pattern;
|
||||
}
|
||||
|
||||
private LocalizedString GetValidationMessage(ValidationContext context) {
|
||||
return T(ErrorMessage.WithDefault("{0} is not a valid email address."), context.FieldName, Pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Orchard.DynamicForms.Elements;
|
||||
using Orchard.DynamicForms.Services;
|
||||
using Orchard.DynamicForms.ValidationRules;
|
||||
|
||||
namespace Orchard.DynamicForms.Validators {
|
||||
public class EmailFieldValidator : ElementValidator<EmailField> {
|
||||
private readonly IValidationRuleFactory _validationRuleFactory;
|
||||
public EmailFieldValidator(IValidationRuleFactory validationRuleFactory) {
|
||||
_validationRuleFactory = validationRuleFactory;
|
||||
}
|
||||
|
||||
protected override IEnumerable<IValidationRule> GetValidationRules(EmailField element) {
|
||||
var settings = element.ValidationSettings;
|
||||
|
||||
if (settings.IsRequired == true)
|
||||
yield return _validationRuleFactory.Create<Required>(settings.CustomValidationMessage);
|
||||
|
||||
if (settings.MaximumLength != null) {
|
||||
yield return _validationRuleFactory.Create<StringLength>(r => {
|
||||
r.Maximum = settings.MaximumLength;
|
||||
r.ErrorMessage = settings.CustomValidationMessage;
|
||||
});
|
||||
}
|
||||
|
||||
yield return _validationRuleFactory.Create<EmailAddress>(settings.CustomValidationMessage);
|
||||
|
||||
if (!String.IsNullOrWhiteSpace(settings.CompareWith)) {
|
||||
yield return _validationRuleFactory.Create<Compare>(r => {
|
||||
r.TargetName = settings.CompareWith;
|
||||
r.ErrorMessage = settings.CustomValidationMessage;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Orchard.DynamicForms.Services.Models;
|
||||
|
||||
namespace Orchard.DynamicForms.Validators.Settings {
|
||||
public class EmailFieldValidationSettings : ValidationSettingsBase {
|
||||
public bool? IsRequired { get; set; }
|
||||
public int? MaximumLength { get; set; }
|
||||
public string CompareWith { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
@using Orchard.DisplayManagement.Shapes
|
||||
@using Orchard.DynamicForms.Elements
|
||||
@using Orchard.Layouts.Helpers
|
||||
@using Orchard.Layouts.Settings
|
||||
@{
|
||||
var element = (EmailField)Model.Element;
|
||||
var commonSettings = element.State.GetModel<CommonElementSettings>();
|
||||
var tagBuilder = (OrchardTagBuilder)TagBuilderExtensions.AddCommonElementAttributes(new OrchardTagBuilder("input"), Model);
|
||||
|
||||
tagBuilder.AddCssClass("text design");
|
||||
tagBuilder.Attributes["type"] = "email";
|
||||
tagBuilder.Attributes["value"] = element.Value;
|
||||
tagBuilder.Attributes["name"] = element.Name;
|
||||
}
|
||||
|
||||
@if (element.ShowLabel) {
|
||||
<div>
|
||||
<label for="@commonSettings.Id">@element.Label</label>
|
||||
@Html.Raw(tagBuilder.ToString(TagRenderMode.SelfClosing))
|
||||
</div>
|
||||
}
|
||||
else {
|
||||
@Html.Raw(tagBuilder.ToString(TagRenderMode.SelfClosing))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
@using Orchard.DisplayManagement.Shapes
|
||||
@using Orchard.DynamicForms.Elements
|
||||
@using Orchard.Layouts.Helpers
|
||||
@using Orchard.Layouts.Settings
|
||||
@{
|
||||
var element = (EmailField)Model.Element;
|
||||
var commonSettings = element.State.GetModel<CommonElementSettings>();
|
||||
var tagBuilder = (OrchardTagBuilder)TagBuilderExtensions.AddCommonElementAttributes(new OrchardTagBuilder("input"), Model);
|
||||
|
||||
tagBuilder.AddCssClass("text");
|
||||
tagBuilder.Attributes["type"] = "email";
|
||||
tagBuilder.Attributes["name"] = element.Name;
|
||||
tagBuilder.AddClientValidationAttributes((IDictionary<string, string>)Model.ClientValidationAttributes);
|
||||
|
||||
if (!ViewData.ModelState.IsValidField(element.Name)) {
|
||||
tagBuilder.AddCssClass("input-validation-error");
|
||||
}
|
||||
}
|
||||
|
||||
@if (element.ShowLabel) {
|
||||
<label for="@commonSettings.Id">@element.Label</label>
|
||||
}
|
||||
@Html.Raw(tagBuilder.ToString(TagRenderMode.SelfClosing))
|
||||
@if (element.ValidationSettings.ShowValidationMessage == true) {
|
||||
@Html.ValidationMessage(element.Name)
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Orchard.Layouts.Services {
|
||||
public class CultureAccessor : ICultureAccessor {
|
||||
private readonly IWorkContextAccessor _wca;
|
||||
private readonly Lazy<CultureInfo> _currentCulture;
|
||||
|
||||
public CultureAccessor(IWorkContextAccessor wca) {
|
||||
_wca = wca;
|
||||
_currentCulture = new Lazy<CultureInfo>(() => new CultureInfo(_wca.GetContext().CurrentCulture));
|
||||
}
|
||||
|
||||
public CultureInfo CurrentCulture {
|
||||
get { return new CultureInfo(_wca.GetContext().CurrentCulture); }
|
||||
get { return _currentCulture.Value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user