From 5fea873ed2fb601c4826bd7deb0e10810c1481b1 Mon Sep 17 00:00:00 2001 From: tworrall Date: Thu, 8 Mar 2012 20:44:21 +0000 Subject: [PATCH] NIHVIVO-3538 implemented a jquery plus java captcha on the contact form page --- .../freemarker/ContactMailController.java | 45 ++++- .../css/jquery_plugins/jquery.realperson.css | 31 +++ webapp/web/js/commentForm.js | 34 +--- .../js/jquery_plugins/jquery.realperson.js | 183 ++++++++++++++++++ .../body/contactForm/contactForm-form.ftl | 35 +++- 5 files changed, 277 insertions(+), 51 deletions(-) create mode 100644 webapp/web/css/jquery_plugins/jquery.realperson.css create mode 100644 webapp/web/js/jquery_plugins/jquery.realperson.js diff --git a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java index 311774956..13774949c 100644 --- a/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java +++ b/webapp/src/edu/cornell/mannlib/vitro/webapp/controller/freemarker/ContactMailController.java @@ -49,6 +49,7 @@ public class ContactMailController extends FreemarkerHttpServlet { private final static String TEMPLATE_EMAIL = "contactForm-email.ftl"; private final static String TEMPLATE_BACKUP = "contactForm-backup.ftl"; private final static String TEMPLATE_ERROR = "contactForm-error.ftl"; + private final static String TEMPLATE_FORM = "contactForm-form.ftl"; private static final String PROPERTY_VITRO_HOME_DIR = "vitro.home.directory"; private static final String EMAIL_JOURNAL_FILE_DIR = "emailJournal"; @@ -75,9 +76,13 @@ public class ContactMailController extends FreemarkerHttpServlet { String webuseremail = nonNullAndTrim(vreq, WEB_USEREMAIL_PARAM); String comments = nonNullAndTrim(vreq, COMMENTS_PARAM); String formType = nonNullAndTrim(vreq, "DeliveryType"); + String captchaInput = nonNullAndTrim(vreq, "defaultReal"); + String captchaDisplay = nonNullAndTrim(vreq, "defaultRealHash"); - if (validateInput(webusername, webuseremail, comments) != null) { - return errorParametersNotValid(); + String errorMsg = validateInput(webusername, webuseremail, comments, captchaInput, captchaDisplay); + + if ( errorMsg != null) { + return errorParametersNotValid(errorMsg, webusername, webuseremail, comments); } String spamReason = checkForSpam(comments, formType); @@ -302,20 +307,28 @@ public class ContactMailController extends FreemarkerHttpServlet { } private String validateInput(String webusername, String webuseremail, - String comments) { + String comments, String captchaInput, String captchaDisplay) { if( webusername.isEmpty() ){ - return "A proper webusername field was not found in the form submitted."; + return "Please enter a value in the Full name field."; } if( webuseremail.isEmpty() ){ - return "A proper webuser email field was not found in the form submitted."; + return "Please enter a valid email address."; } if (comments.isEmpty()) { - return "The proper comments field was not found in the form submitted."; + return "Please enter your comments or questions in the space provided."; } + if (captchaInput.isEmpty()) { + return "Please enter the contents of the gray box in the security field provided."; + } + + if ( !captchaHash(captchaInput).equals(captchaDisplay) ) { + return "The value you entered in the security field did not match the letters displayed in the gray box."; + } + return null; } @@ -347,6 +360,15 @@ public class ContactMailController extends FreemarkerHttpServlet { } + private String captchaHash(String value) { + int hash = 5381; + value = value.toUpperCase(); + for(int i = 0; i < value.length(); i++) { + hash = ((hash << 5) + hash) + value.charAt(i); + } + return String.valueOf(hash); + } + private ResponseValues errorNoSmtpServer() { Map body = new HashMap(); body.put("errorMessage", @@ -363,11 +385,14 @@ public class ContactMailController extends FreemarkerHttpServlet { return new TemplateResponseValues(TEMPLATE_ERROR, body); } - private ResponseValues errorParametersNotValid() { - // rjy7 We should reload the form, not go to the error page! + private ResponseValues errorParametersNotValid(String errorMsg, String webusername, String webuseremail, String comments) { Map body = new HashMap(); - body.put("errorMessage", "Invalid submission"); - return new TemplateResponseValues(TEMPLATE_ERROR, body); + body.put("errorMessage", errorMsg); + body.put("formAction", "submitFeedback"); + body.put("webusername", webusername); + body.put("webuseremail", webuseremail); + body.put("comments", comments); + return new TemplateResponseValues(TEMPLATE_FORM, body); } private ResponseValues errorSpam() { diff --git a/webapp/web/css/jquery_plugins/jquery.realperson.css b/webapp/web/css/jquery_plugins/jquery.realperson.css new file mode 100644 index 000000000..35deac801 --- /dev/null +++ b/webapp/web/css/jquery_plugins/jquery.realperson.css @@ -0,0 +1,31 @@ +/* Real Person jQuery plugin styles v1.0.1. */ +.realperson-challenge { + display: inline-block; + background-color: #ddd; + width: 150px; + padding-top: 10px; + padding-left: 12px; +} +.realperson-text { + font-family: "Courier New",monospace; + font-size: 6px; + font-weight: bold; + letter-spacing: -1px; + line-height: 3px; + color: #000; +} +.realperson-regen { + padding-top: 4px; + padding-right: 6px; + font-size: 12px; + text-align: center; + cursor: pointer; +} +.realpersonLabel { + display: block; +} +#defaultReal { + margin-left: 20px; + vertical-align: top; + margin-top: 3px; +} diff --git a/webapp/web/js/commentForm.js b/webapp/web/js/commentForm.js index e3c3400bc..182cf9b27 100644 --- a/webapp/web/js/commentForm.js +++ b/webapp/web/js/commentForm.js @@ -6,41 +6,9 @@ function ValidateForm(formName) { errors = false; var errorList; - if (document.forms[formName].RequiredFields) { - errorList = 'Please fill out the following required fields:\n'; - // build array of required fields - reqStr = document.forms[formName].RequiredFields.value; - requiredFields = reqStr.split(','); - // build array holding the names of required fields as - // displayed in error box - if (document.forms[formName].RequiredFieldsNames) { - reqNameStr = document.forms[formName].RequiredFieldsNames.value; - } else { - reqNameStr = document.forms[formName].RequiredFields.value; - } - requiredNames = reqNameStr.split(','); - // Loop through form elements, checking for required fields - while ((x < document.forms[formName].elements.length)) { - if (document.forms[formName].elements[x].name == requiredFields[y]) { - if (document.forms[formName].elements[x].value == '') { - errorList += requiredNames[y] + '\n'; - errors = true; - } - y++; - } - x++; - } - if (errors) { - alert(errorList); - return false; - } - x = 0; - y = 0; - } - // Check for Email formatting if (document.forms[formName].EmailFields) { - errorList = 'Please format your e-mail address as: \"userid@institution.edu\" or enter another complete email address'; + errorList = '\nPlease format your e-mail address as:\n \"userid@institution.edu\" or enter another complete and valid email address'; // build array of required fields emailStr = document.forms[formName].EmailFields.value; emailFields = emailStr.split(','); diff --git a/webapp/web/js/jquery_plugins/jquery.realperson.js b/webapp/web/js/jquery_plugins/jquery.realperson.js new file mode 100644 index 000000000..9f0c715f8 --- /dev/null +++ b/webapp/web/js/jquery_plugins/jquery.realperson.js @@ -0,0 +1,183 @@ +/* http://keith-wood.name/realPerson.html + Real Person Form Submission for jQuery v1.0.1. + Written by Keith Wood (kwood{at}iinet.com.au) June 2009. + Dual licensed under the GPL (http://dev.jquery.com/browser/trunk/jquery/GPL-LICENSE.txt) and + MIT (http://dev.jquery.com/browser/trunk/jquery/MIT-LICENSE.txt) licenses. + Please attribute the author if you use it. */ + +(function($) { // Hide scope, no $ conflict + +var PROP_NAME = 'realPerson'; + +/* Real person manager. */ +function RealPerson() { + this._defaults = { + length: 6, // Number of characters to use + includeNumbers: false, // True to use numbers as well as letters + regenerate: 'Click to change', // Instruction text to regenerate + hashName: '{n}Hash' // Name of the hash value field to compare with, + // use {n} to substitute with the original field name + }; +} + +var CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; +var DOTS = [ + [' * ', ' * * ', ' * * ', ' * * ', ' ***** ', '* *', '* *'], + ['****** ', '* *', '* *', '****** ', '* *', '* *', '****** '], + [' ***** ', '* *', '* ', '* ', '* ', '* *', ' ***** '], + ['****** ', '* *', '* *', '* *', '* *', '* *', '****** '], + ['*******', '* ', '* ', '**** ', '* ', '* ', '*******'], + ['*******', '* ', '* ', '**** ', '* ', '* ', '* '], + [' ***** ', '* *', '* ', '* ', '* ***', '* *', ' ***** '], + ['* *', '* *', '* *', '*******', '* *', '* *', '* *'], + ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '*******'], + [' *', ' *', ' *', ' *', ' *', '* *', ' ***** '], + ['* *', '* ** ', '* ** ', '** ', '* ** ', '* ** ', '* *'], + ['* ', '* ', '* ', '* ', '* ', '* ', '*******'], + ['* *', '** **', '* * * *', '* * *', '* *', '* *', '* *'], + ['* *', '** *', '* * *', '* * *', '* * *', '* **', '* *'], + [' ***** ', '* *', '* *', '* *', '* *', '* *', ' ***** '], + ['****** ', '* *', '* *', '****** ', '* ', '* ', '* '], + [' ***** ', '* *', '* *', '* *', '* * *', '* * ', ' **** *'], + ['****** ', '* *', '* *', '****** ', '* * ', '* * ', '* *'], + [' ***** ', '* *', '* ', ' ***** ', ' *', '* *', ' ***** '], + ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', ' * '], + ['* *', '* *', '* *', '* *', '* *', '* *', ' ***** '], + ['* *', '* *', ' * * ', ' * * ', ' * * ', ' * * ', ' * '], + ['* *', '* *', '* *', '* * *', '* * * *', '** **', '* *'], + ['* *', ' * * ', ' * * ', ' * ', ' * * ', ' * * ', '* *'], + ['* *', ' * * ', ' * * ', ' * ', ' * ', ' * ', ' * '], + ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '*******'], + [' *** ', ' * * ', '* *', '* *', '* *', ' * * ', ' *** '], + [' * ', ' ** ', ' * * ', ' * ', ' * ', ' * ', '*******'], + [' ***** ', '* *', ' *', ' * ', ' ** ', ' ** ', '*******'], + [' ***** ', '* *', ' *', ' ** ', ' *', '* *', ' ***** '], + [' * ', ' ** ', ' * * ', ' * * ', '*******', ' * ', ' * '], + ['*******', '* ', '****** ', ' *', ' *', '* *', ' ***** '], + [' **** ', ' * ', '* ', '****** ', '* *', '* *', ' ***** '], + ['*******', ' * ', ' * ', ' * ', ' * ', ' * ', '* '], + [' ***** ', '* *', '* *', ' ***** ', '* *', '* *', ' ***** '], + [' ***** ', '* *', '* *', ' ******', ' *', ' * ', ' **** ']]; + +$.extend(RealPerson.prototype, { + /* Class name added to elements to indicate already configured with real person. */ + markerClassName: 'hasRealPerson', + + /* Override the default settings for all real person instances. + @param settings (object) the new settings to use as defaults + @return (RealPerson) this object */ + setDefaults: function(settings) { + $.extend(this._defaults, settings || {}); + return this; + }, + + /* Attach the real person functionality to an input field. + @param target (element) the control to affect + @param settings (object) the custom options for this instance */ + _attachRealPerson: function(target, settings) { + target = $(target); + if (target.hasClass(this.markerClassName)) { + return; + } + target.addClass(this.markerClassName); + var inst = {settings: $.extend({}, this._defaults)}; + $.data(target[0], PROP_NAME, inst); + this._changeRealPerson(target, settings); + }, + + /* Reconfigure the settings for a real person control. + @param target (element) the control to affect + @param settings (object) the new options for this instance or + (string) an individual property name + @param value (any) the individual property value (omit if settings is an object) */ + _changeRealPerson: function(target, settings, value) { + target = $(target); + if (!target.hasClass(this.markerClassName)) { + return; + } + settings = settings || {}; + if (typeof settings == 'string') { + var name = settings; + settings = {}; + settings[name] = value; + } + var inst = $.data(target[0], PROP_NAME); + $.extend(inst.settings, settings); + target.prevAll('.realperson-challenge,.realperson-hash').remove().end(). + before(this._generateHTML(target, inst)); + }, + + /* Generate the additional content for this control. + @param target (jQuery) the input field + @param inst (object) the current instance settings + @return (string) the additional content */ + _generateHTML: function(target, inst) { + var text = ''; + for (var i = 0; i < inst.settings.length; i++) { + text += CHARS.charAt(Math.floor(Math.random() * + (inst.settings.includeNumbers ? 36 : 26))); + } + var html = '
'; + for (var i = 0; i < DOTS[0].length; i++) { + for (var j = 0; j < text.length; j++) { + html += DOTS[CHARS.indexOf(text.charAt(j))][i].replace(/ /g, ' ') + + '  '; + } + html += '
'; + } + html += '
' + inst.settings.regenerate + + '
'; + return html; + }, + + /* Remove the real person functionality from a control. + @param target (element) the control to affect */ + _destroyRealPerson: function(target) { + target = $(target); + if (!target.hasClass(this.markerClassName)) { + return; + } + target.removeClass(this.markerClassName). + prevAll('.realperson-challenge,.realperson-hash').remove(); + $.removeData(target[0], PROP_NAME); + }, + + /* Compute a hash value for the given text. + @param value (string) the text to hash + @return the corresponding hash value */ + _hash: function(value) { + var hash = 5381; + for (var i = 0; i < value.length; i++) { + hash = ((hash << 5) + hash) + value.charCodeAt(i); + } + return hash; + } +}); + +/* Attach the real person functionality to a jQuery selection. + @param command (string) the command to run (optional, default 'attach') + @param options (object) the new settings to use for these instances (optional) + @return (jQuery) for chaining further calls */ +$.fn.realperson = function(options) { + var otherArgs = Array.prototype.slice.call(arguments, 1); + return this.each(function() { + if (typeof options == 'string') { + $.realperson['_' + options + 'RealPerson']. + apply($.realperson, [this].concat(otherArgs)); + } + else { + $.realperson._attachRealPerson(this, options || {}); + } + }); +}; + +/* Initialise the real person functionality. */ +$.realperson = new RealPerson(); // singleton instance + +$('.realperson-challenge').live('click', function() { + $(this).next().next().realperson('change'); +}); + +})(jQuery); diff --git a/webapp/web/templates/freemarker/body/contactForm/contactForm-form.ftl b/webapp/web/templates/freemarker/body/contactForm/contactForm-form.ftl index db4501468..7c36a3676 100644 --- a/webapp/web/templates/freemarker/body/contactForm/contactForm-form.ftl +++ b/webapp/web/templates/freemarker/body/contactForm/contactForm-form.ftl @@ -5,11 +5,17 @@

${title}

+ <#if errorMessage?has_content> +
+

${errorMessage}

+
+ +

Thank you for your interest in ${siteName}. Please submit this form with questions, comments, or feedback about the content of this site.

-
+ @@ -17,21 +23,34 @@ - + - + - + + + +

+ +

- -
+

* required fields

-${stylesheets.add('')} -${scripts.add('')} +${stylesheets.add('', + '')} +${scripts.add('', + '', + '')} +