diff --git a/src/Orchard.Web/Modules/Orchard.AntiSpam/Drivers/ReCaptchaPartDriver.cs b/src/Orchard.Web/Modules/Orchard.AntiSpam/Drivers/ReCaptchaPartDriver.cs index 569d55722..1cc37bbb1 100644 --- a/src/Orchard.Web/Modules/Orchard.AntiSpam/Drivers/ReCaptchaPartDriver.cs +++ b/src/Orchard.Web/Modules/Orchard.AntiSpam/Drivers/ReCaptchaPartDriver.cs @@ -17,9 +17,8 @@ namespace Orchard.AntiSpam.Drivers { public class ReCaptchaPartDriver : ContentPartDriver { private readonly INotifier _notifier; private readonly IWorkContextAccessor _workContextAccessor; - private const string ReCaptchaUrl = "http://www.google.com/recaptcha/api"; - private const string ReCaptchaSecureUrl = "https://www.google.com/recaptcha/api"; - + private const string ReCaptchaSecureUrl = "https://www.google.com/recaptcha/api/siteverify"; + public ReCaptchaPartDriver( INotifier notifier, IWorkContextAccessor workContextAccessor) { @@ -67,67 +66,55 @@ namespace Orchard.AntiSpam.Drivers { return null; } - var submitViewModel = new ReCaptchaPartSubmitViewModel(); + var context = workContext.HttpContext; - if(updater.TryUpdateModel(submitViewModel, String.Empty, null, null)) { - var context = workContext.HttpContext; + try { + var result = ExecuteValidateRequest( + settings.PrivateKey, + context.Request.ServerVariables["REMOTE_ADDR"], + context.Request.Form["g-recaptcha-response"] + ); - try { - var result = ExecuteValidateRequest( - settings.PrivateKey, - context.Request.ServerVariables["REMOTE_ADDR"], - submitViewModel.recaptcha_challenge_field, - submitViewModel.recaptcha_response_field - ); + ReCaptchaPartResponseModel responseModel = Newtonsoft.Json.JsonConvert.DeserializeObject(result); - if (!HandleValidateResponse(context, result)) { - _notifier.Error(T("The text you entered in the Captcha field does not match the image")); - updater.AddModelError("", T("The text you entered in the Captcha field does not match the image")); + if (!responseModel.success) { + for (int i = 0; i < responseModel.errorMessage.Length; i++) { + if (responseModel.errorMessage[i] == "missing-input-response") { + _notifier.Error(T("The Captcha field is required")); + } + else { + _notifier.Error(T("There was an error with the Captcha please try again")); + Logger.Error("Error occured while submitting a reCaptcha: " + responseModel.errorMessage[i]); + } } } - catch(Exception e) { - Logger.Error(e, "An unexcepted error occured while submitting a reCaptcha"); - updater.AddModelError("Parts_ReCaptcha_Fields", T("There was an error while validating the Captcha image")); - } + } + catch (Exception e) { + Logger.Error(e, "An unexcepted error occured while submitting a reCaptcha"); + updater.AddModelError("Parts_ReCaptcha_Fields", T("There was an error while validating the Captcha image")); } return Editor(part, shapeHelper); } - private static string ExecuteValidateRequest(string privateKey, string remoteip, string challenge, string response) { - WebRequest request = WebRequest.Create(ReCaptchaUrl + "/verify"); - request.Method = "POST"; + private static string ExecuteValidateRequest(string privateKey, string remoteip, string response) { + var postData = String.Format(CultureInfo.InvariantCulture, + "secret={0}&response={1}&remoteip={2}", + privateKey, + response, + remoteip + ); + + WebRequest request = WebRequest.Create(ReCaptchaSecureUrl + "?" + postData); + request.Method = "GET"; request.Timeout = 5000; //milliseconds request.ContentType = "application/x-www-form-urlencoded"; - var postData = String.Format(CultureInfo.InvariantCulture, - "privatekey={0}&remoteip={1}&challenge={2}&response={3}", - privateKey, - remoteip, - challenge, - response - ); - - byte[] content = Encoding.UTF8.GetBytes(postData); - using (Stream stream = request.GetRequestStream()) { - stream.Write(content, 0, content.Length); - } using (WebResponse webResponse = request.GetResponse()) { using (var reader = new StreamReader(webResponse.GetResponseStream())) { return reader.ReadToEnd(); } } } - - internal static bool HandleValidateResponse(HttpContextBase context, string response) { - if (!String.IsNullOrEmpty(response)) { - string[] results = response.Split('\n'); - if (results.Length > 0) { - bool rval = Convert.ToBoolean(results[0], CultureInfo.InvariantCulture); - return rval; - } - } - return false; - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AntiSpam/Services/MissingSettingsBanner.cs b/src/Orchard.Web/Modules/Orchard.AntiSpam/Services/MissingSettingsBanner.cs new file mode 100644 index 000000000..875981a95 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.AntiSpam/Services/MissingSettingsBanner.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Orchard.AntiSpam.Models; +using Orchard.ContentManagement; +using Orchard.Localization; +using Orchard.UI.Admin.Notification; +using Orchard.UI.Notify; +using System.Web.Mvc; + +namespace Orchard.AntiSpam.Services { + public class MissingSettingsBanner : INotificationProvider { + private readonly IOrchardServices _orchardServices; + + public MissingSettingsBanner(IOrchardServices orchardServices) { + _orchardServices = orchardServices; + T = NullLocalizer.Instance; + } + + public Localizer T { get; set; } + + public IEnumerable GetNotifications() { + var workContext = _orchardServices.WorkContext; + var RecaptchaSettings = workContext.CurrentSite.As(); + + if (RecaptchaSettings.PublicKey == null || RecaptchaSettings.PrivateKey == null) { + var urlHelper = new UrlHelper(workContext.HttpContext.Request.RequestContext); + var url = urlHelper.Action("Spam", "Admin", new { Area = "Settings" }); + yield return new NotifyEntry { Message = T("The ReCaptcha settings need to be configured.", url), Type = NotifyType.Warning }; + } + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.AntiSpam/ViewModels/ReCaptchaPartEditViewModel.cs b/src/Orchard.Web/Modules/Orchard.AntiSpam/ViewModels/ReCaptchaPartEditViewModel.cs index 4086f4bc9..8a55d7519 100644 --- a/src/Orchard.Web/Modules/Orchard.AntiSpam/ViewModels/ReCaptchaPartEditViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.AntiSpam/ViewModels/ReCaptchaPartEditViewModel.cs @@ -1,10 +1,14 @@ -namespace Orchard.AntiSpam.ViewModels { +using Newtonsoft.Json; + +namespace Orchard.AntiSpam.ViewModels { public class ReCaptchaPartEditViewModel { public string PublicKey { get; set; } } - public class ReCaptchaPartSubmitViewModel { - public string recaptcha_challenge_field { get; set; } - public string recaptcha_response_field { get; set; } + public class ReCaptchaPartResponseModel { + public bool success { get; set; } + + [JsonProperty("error-codes")] + public string[] errorMessage { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AntiSpam/Views/EditorTemplates/Parts.ReCaptcha.Fields.cshtml b/src/Orchard.Web/Modules/Orchard.AntiSpam/Views/EditorTemplates/Parts.ReCaptcha.Fields.cshtml index df17cb7a4..6d7ab4a1f 100644 --- a/src/Orchard.Web/Modules/Orchard.AntiSpam/Views/EditorTemplates/Parts.ReCaptcha.Fields.cshtml +++ b/src/Orchard.Web/Modules/Orchard.AntiSpam/Views/EditorTemplates/Parts.ReCaptcha.Fields.cshtml @@ -1,8 +1,32 @@ @model Orchard.AntiSpam.ViewModels.ReCaptchaPartEditViewModel - - \ No newline at end of file + +@{ + var publicKey = (string)Model.PublicKey; +} +@if (!String.IsNullOrWhiteSpace(publicKey)) { + + +
+
+ +
+} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Validators/ReCaptchaValidator.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/Validators/ReCaptchaValidator.cs index f83ac48a0..244468640 100644 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Validators/ReCaptchaValidator.cs +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Validators/ReCaptchaValidator.cs @@ -7,6 +7,7 @@ using System.Web; using Orchard.AntiSpam.Models; using Orchard.ContentManagement; using Orchard.DynamicForms.Elements; +using Orchard.DynamicForms.ViewModels; using Orchard.DynamicForms.Helpers; using Orchard.DynamicForms.Services; using Orchard.DynamicForms.Services.Models; @@ -19,7 +20,7 @@ namespace Orchard.DynamicForms.Validators { _workContextAccessor = workContextAccessor; } - private const string ReCaptchaUrl = "http://www.google.com/recaptcha/api"; + private const string ReCaptchaSecureUrl = "https://www.google.com/recaptcha/api/siteverify"; protected override void OnValidate(ReCaptcha element, ValidateInputContext context) { var workContext = _workContextAccessor.GetContext(); @@ -30,22 +31,32 @@ namespace Orchard.DynamicForms.Validators { } var httpContext = workContext.HttpContext; - var response = context.Values["recaptcha_response_field"]; - var challenge = context.Values["recaptcha_challenge_field"]; + var response = context.Values["g-recaptcha-response"]; if (context.ModelState.IsValid) { try { var result = ExecuteValidateRequest( settings.PrivateKey, httpContext.Request.ServerVariables["REMOTE_ADDR"], - challenge, response ); - if (!HandleValidateResponse(httpContext, result)) { - var validationSettings = element.ValidationSettings; - var validationMessage = validationSettings.CustomValidationMessage.WithDefault("The text you entered in the Captcha field does not match the image. Please try again."); - context.ModelState.AddModelError("recaptcha_response_field", T(validationMessage).Text); + ReCaptchaElementResponseModel responseModel = Newtonsoft.Json.JsonConvert.DeserializeObject(result); + + if (!responseModel.success) { + for (int i = 0; i < responseModel.errorMessage.Length; i++) { + if (responseModel.errorMessage[i] == "missing-input-response") { + var validationSettings = element.ValidationSettings; + var validationMessage = validationSettings.CustomValidationMessage.WithDefault("The Captcha field is required"); + context.ModelState.AddModelError("g-recaptcha-response", T(validationMessage).Text); + } + else { + var validationSettings = element.ValidationSettings; + var validationMessage = validationSettings.CustomValidationMessage.WithDefault("There was an error with the Captcha please try again"); + context.ModelState.AddModelError("g-recaptcha-response", T(validationMessage).Text); + Logger.Error("Error occured while submitting a reCaptcha: " + responseModel.errorMessage[i]); + } + } } } catch (Exception e) { @@ -55,40 +66,24 @@ namespace Orchard.DynamicForms.Validators { } } - private static string ExecuteValidateRequest(string privateKey, string remoteip, string challenge, string response) { - var request = WebRequest.Create(ReCaptchaUrl + "/verify"); - request.Method = "POST"; + private static string ExecuteValidateRequest(string privateKey, string remoteip, string response) { + var postData = String.Format(CultureInfo.InvariantCulture, + "secret={0}&response={1}&remoteip={2}", + privateKey, + response, + remoteip + ); + + WebRequest request = WebRequest.Create(ReCaptchaSecureUrl + "?" + postData); + request.Method = "GET"; request.Timeout = 5000; //milliseconds request.ContentType = "application/x-www-form-urlencoded"; - var postData = String.Format(CultureInfo.InvariantCulture, - "privatekey={0}&remoteip={1}&challenge={2}&response={3}", - privateKey, - remoteip, - challenge, - response - ); - - var content = Encoding.UTF8.GetBytes(postData); - using (var stream = request.GetRequestStream()) { - stream.Write(content, 0, content.Length); - } - using (var webResponse = request.GetResponse()) { + using (WebResponse webResponse = request.GetResponse()) { using (var reader = new StreamReader(webResponse.GetResponseStream())) { return reader.ReadToEnd(); } } } - - internal static bool HandleValidateResponse(HttpContextBase context, string response) { - if (!String.IsNullOrEmpty(response)) { - var results = response.Split('\n'); - if (results.Length > 0) { - var rval = Convert.ToBoolean(results[0], CultureInfo.InvariantCulture); - return rval; - } - } - return false; - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/ViewModels/ReCaptchaElementViewModel.cs b/src/Orchard.Web/Modules/Orchard.DynamicForms/ViewModels/ReCaptchaElementViewModel.cs new file mode 100644 index 000000000..9dd36fafa --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/ViewModels/ReCaptchaElementViewModel.cs @@ -0,0 +1,11 @@ +using Newtonsoft.Json; + +namespace Orchard.DynamicForms.ViewModels +{ + public class ReCaptchaElementResponseModel { + public bool success { get; set; } + + [JsonProperty("error-codes")] + public string[] errorMessage { get; set; } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DynamicForms/Views/Elements/ReCaptcha.cshtml b/src/Orchard.Web/Modules/Orchard.DynamicForms/Views/Elements/ReCaptcha.cshtml index ce715c192..b0f2da1f9 100644 --- a/src/Orchard.Web/Modules/Orchard.DynamicForms/Views/Elements/ReCaptcha.cshtml +++ b/src/Orchard.Web/Modules/Orchard.DynamicForms/Views/Elements/ReCaptcha.cshtml @@ -7,14 +7,33 @@ } @if (!String.IsNullOrWhiteSpace(publicKey)) { @tagBuilder.StartElement - - + + +
+
+ +
@tagBuilder.EndElement if (element.ValidationSettings.ShowValidationMessage == true) { - @Html.ValidationMessage("recaptcha_response_field") + @Html.ValidationMessage("g-recaptcha-response") } } \ No newline at end of file