NIHVIVO-3538 implemented a jquery plus java captcha on the contact form page

This commit is contained in:
tworrall 2012-03-08 20:44:21 +00:00
parent 06fbfcbb87
commit 5fea873ed2
5 changed files with 277 additions and 51 deletions

View file

@ -49,6 +49,7 @@ public class ContactMailController extends FreemarkerHttpServlet {
private final static String TEMPLATE_EMAIL = "contactForm-email.ftl"; private final static String TEMPLATE_EMAIL = "contactForm-email.ftl";
private final static String TEMPLATE_BACKUP = "contactForm-backup.ftl"; private final static String TEMPLATE_BACKUP = "contactForm-backup.ftl";
private final static String TEMPLATE_ERROR = "contactForm-error.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 PROPERTY_VITRO_HOME_DIR = "vitro.home.directory";
private static final String EMAIL_JOURNAL_FILE_DIR = "emailJournal"; 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 webuseremail = nonNullAndTrim(vreq, WEB_USEREMAIL_PARAM);
String comments = nonNullAndTrim(vreq, COMMENTS_PARAM); String comments = nonNullAndTrim(vreq, COMMENTS_PARAM);
String formType = nonNullAndTrim(vreq, "DeliveryType"); String formType = nonNullAndTrim(vreq, "DeliveryType");
String captchaInput = nonNullAndTrim(vreq, "defaultReal");
String captchaDisplay = nonNullAndTrim(vreq, "defaultRealHash");
if (validateInput(webusername, webuseremail, comments) != null) { String errorMsg = validateInput(webusername, webuseremail, comments, captchaInput, captchaDisplay);
return errorParametersNotValid();
if ( errorMsg != null) {
return errorParametersNotValid(errorMsg, webusername, webuseremail, comments);
} }
String spamReason = checkForSpam(comments, formType); String spamReason = checkForSpam(comments, formType);
@ -302,20 +307,28 @@ public class ContactMailController extends FreemarkerHttpServlet {
} }
private String validateInput(String webusername, String webuseremail, private String validateInput(String webusername, String webuseremail,
String comments) { String comments, String captchaInput, String captchaDisplay) {
if( webusername.isEmpty() ){ 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() ){ 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()) { 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; 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() { private ResponseValues errorNoSmtpServer() {
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("errorMessage", body.put("errorMessage",
@ -363,11 +385,14 @@ public class ContactMailController extends FreemarkerHttpServlet {
return new TemplateResponseValues(TEMPLATE_ERROR, body); return new TemplateResponseValues(TEMPLATE_ERROR, body);
} }
private ResponseValues errorParametersNotValid() { private ResponseValues errorParametersNotValid(String errorMsg, String webusername, String webuseremail, String comments) {
// rjy7 We should reload the form, not go to the error page!
Map<String, Object> body = new HashMap<String, Object>(); Map<String, Object> body = new HashMap<String, Object>();
body.put("errorMessage", "Invalid submission"); body.put("errorMessage", errorMsg);
return new TemplateResponseValues(TEMPLATE_ERROR, body); 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() { private ResponseValues errorSpam() {

View file

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

View file

@ -6,41 +6,9 @@ function ValidateForm(formName) {
errors = false; errors = false;
var errorList; 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 // Check for Email formatting
if (document.forms[formName].EmailFields) { 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 // build array of required fields
emailStr = document.forms[formName].EmailFields.value; emailStr = document.forms[formName].EmailFields.value;
emailFields = emailStr.split(','); emailFields = emailStr.split(',');

View file

@ -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 = '<div class="realperson-challenge"><div class="realperson-text">';
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, '&nbsp;') +
'&nbsp;&nbsp;';
}
html += '<br>';
}
html += '</div><div class="realperson-regen">' + inst.settings.regenerate +
'</div></div><input type="hidden" class="realperson-hash" name="' +
inst.settings.hashName.replace(/\{n\}/, target.attr('name')) +
'" value="' + this._hash(text) + '">';
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);

View file

@ -5,11 +5,17 @@
<section class="staticPageBackground feedbackForm" role="region"> <section class="staticPageBackground feedbackForm" role="region">
<h2>${title}</h2> <h2>${title}</h2>
<#if errorMessage?has_content>
<section id="error-alert"><img src="${urls.images}/iconAlert.png" role="error alert"/>
<p>${errorMessage}</p>
</section>
</#if>
<p>Thank you for your interest in ${siteName}. <p>Thank you for your interest in ${siteName}.
Please submit this form with questions, comments, or feedback about the content of this site. Please submit this form with questions, comments, or feedback about the content of this site.
</p> </p>
<form name="contact_form" id="contact_form" class="customForm" action="${formAction}" method="post" onsubmit="return ValidateForm('contact_form');" role="contact form"> <form name="contact_form" id="contact_form" class="customForm" action="${formAction!}" method="post" onSubmit="return ValidateForm('contact_form');" role="contact form">
<input type="hidden" name="RequiredFields" value="webusername,webuseremail,s34gfd88p9x1" /> <input type="hidden" name="RequiredFields" value="webusername,webuseremail,s34gfd88p9x1" />
<input type="hidden" name="RequiredFieldsNames" value="Name,Email address,Comments" /> <input type="hidden" name="RequiredFieldsNames" value="Name,Email address,Comments" />
<input type="hidden" name="EmailFields" value="webuseremail" /> <input type="hidden" name="EmailFields" value="webuseremail" />
@ -17,21 +23,34 @@
<input type="hidden" name="DeliveryType" value="contact" /> <input type="hidden" name="DeliveryType" value="contact" />
<label for="webusername">Full name <span class="requiredHint"> *</span></label> <label for="webusername">Full name <span class="requiredHint"> *</span></label>
<input type="text" name="webusername" /> <input type="text" name="webusername" value="${webusername!}"/>
<label for="webuseremail">Email address <span class="requiredHint"> *</span></label> <label for="webuseremail">Email address <span class="requiredHint"> *</span></label>
<input type="text" name="webuseremail" /> <input type="text" name="webuseremail" value="${webuseremail!}"/>
<label>Comments, questions, or suggestions <span class="requiredHint"> *</span></label> <label>Comments, questions, or suggestions <span class="requiredHint"> *</span></label>
<textarea name="s34gfd88p9x1" rows="10" cols="90"></textarea> <textarea name="s34gfd88p9x1" rows="10" cols="90">${comments!}</textarea>
<p><label class="realpersonLabel">Please enter the letters displayed below into the security field:</label>
<input type="text" id="defaultReal" name="defaultReal"></p>
<div class="buttons"> <div class="buttons">
<input id="submit" type="submit" value="Send Mail" /> <br /><input id="submit" type="submit" value="Send Mail" />
</div </div>
<p class="requiredHint">* required fields</p> <p class="requiredHint">* required fields</p>
</form> </form>
</section> </section>
${stylesheets.add('<link rel="stylesheet" href="${urls.base}/edit/forms/css/customForm.css" />')} ${stylesheets.add('<link rel="stylesheet" href="${urls.base}/templates/freemarker/edit/forms/css/customForm.css" />',
${scripts.add('<script type="text/javascript" src="${urls.base}/js/commentForm.js"></script>')} '<link rel="stylesheet" href="${urls.base}/css/jquery_plugins/jquery.realperson.css" />')}
${scripts.add('<script type="text/javascript" src="${urls.base}/js/commentForm.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/jquery_plugins/jquery.realperson.js"></script>',
'<script type="text/javascript" src="${urls.base}/js/jquery-ui/js/jquery-ui-1.8.9.custom.min.js"></script>')}
<script type="text/javascript">
$(function() {
$('#defaultReal').realperson();
});
</script>