diff --git a/js/validate/jquery.validate.js b/js/validate/jquery.validate.js new file mode 100644 index 0000000..e402ea8 --- /dev/null +++ b/js/validate/jquery.validate.js @@ -0,0 +1,1146 @@ +/* + * jQuery validation plug-in 1.7 + * + * http://bassistance.de/jquery-plugins/jquery-plugin-validation/ + * http://docs.jquery.com/Plugins/Validation + * + * Copyright (c) 2006 - 2008 Jörn Zaefferer + * + * $Id: jquery.validate.js 6403 2009-06-17 14:27:16Z joern.zaefferer $ + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + */ + +(function($) { + +$.extend($.fn, { + // http://docs.jquery.com/Plugins/Validation/validate + validate: function( options ) { + + // if nothing is selected, return nothing; can't chain anyway + if (!this.length) { + options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" ); + return; + } + + // check if a validator for this form was already created + var validator = $.data(this[0], 'validator'); + if ( validator ) { + return validator; + } + + validator = new $.validator( options, this[0] ); + $.data(this[0], 'validator', validator); + + if ( validator.settings.onsubmit ) { + + // allow suppresing validation by adding a cancel class to the submit button + this.find("input, button").filter(".cancel").click(function() { + validator.cancelSubmit = true; + }); + + // when a submitHandler is used, capture the submitting button + if (validator.settings.submitHandler) { + this.find("input, button").filter(":submit").click(function() { + validator.submitButton = this; + }); + } + + // validate the form on submit + this.submit( function( event ) { + if ( validator.settings.debug ) + // prevent form submit to be able to see console output + event.preventDefault(); + + function handle() { + if ( validator.settings.submitHandler ) { + if (validator.submitButton) { + // insert a hidden input as a replacement for the missing submit button + var hidden = $("").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm); + } + validator.settings.submitHandler.call( validator, validator.currentForm ); + if (validator.submitButton) { + // and clean up afterwards; thanks to no-block-scope, hidden can be referenced + hidden.remove(); + } + return false; + } + return true; + } + + // prevent submit for invalid forms or custom submit handlers + if ( validator.cancelSubmit ) { + validator.cancelSubmit = false; + return handle(); + } + if ( validator.form() ) { + if ( validator.pendingRequest ) { + validator.formSubmitted = true; + return false; + } + return handle(); + } else { + validator.focusInvalid(); + return false; + } + }); + } + + return validator; + }, + // http://docs.jquery.com/Plugins/Validation/valid + valid: function() { + if ( $(this[0]).is('form')) { + return this.validate().form(); + } else { + var valid = true; + var validator = $(this[0].form).validate(); + this.each(function() { + valid &= validator.element(this); + }); + return valid; + } + }, + // attributes: space seperated list of attributes to retrieve and remove + removeAttrs: function(attributes) { + var result = {}, + $element = this; + $.each(attributes.split(/\s/), function(index, value) { + result[value] = $element.attr(value); + $element.removeAttr(value); + }); + return result; + }, + // http://docs.jquery.com/Plugins/Validation/rules + rules: function(command, argument) { + var element = this[0]; + + if (command) { + var settings = $.data(element.form, 'validator').settings; + var staticRules = settings.rules; + var existingRules = $.validator.staticRules(element); + switch(command) { + case "add": + $.extend(existingRules, $.validator.normalizeRule(argument)); + staticRules[element.name] = existingRules; + if (argument.messages) + settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages ); + break; + case "remove": + if (!argument) { + delete staticRules[element.name]; + return existingRules; + } + var filtered = {}; + $.each(argument.split(/\s/), function(index, method) { + filtered[method] = existingRules[method]; + delete existingRules[method]; + }); + return filtered; + } + } + + var data = $.validator.normalizeRules( + $.extend( + {}, + $.validator.metadataRules(element), + $.validator.classRules(element), + $.validator.attributeRules(element), + $.validator.staticRules(element) + ), element); + + // make sure required is at front + if (data.required) { + var param = data.required; + delete data.required; + data = $.extend({required: param}, data); + } + + return data; + } +}); + +// Custom selectors +$.extend($.expr[":"], { + // http://docs.jquery.com/Plugins/Validation/blank + blank: function(a) {return !$.trim("" + a.value);}, + // http://docs.jquery.com/Plugins/Validation/filled + filled: function(a) {return !!$.trim("" + a.value);}, + // http://docs.jquery.com/Plugins/Validation/unchecked + unchecked: function(a) {return !a.checked;} +}); + +// constructor for validator +$.validator = function( options, form ) { + this.settings = $.extend( true, {}, $.validator.defaults, options ); + this.currentForm = form; + this.init(); +}; + +$.validator.format = function(source, params) { + if ( arguments.length == 1 ) + return function() { + var args = $.makeArray(arguments); + args.unshift(source); + return $.validator.format.apply( this, args ); + }; + if ( arguments.length > 2 && params.constructor != Array ) { + params = $.makeArray(arguments).slice(1); + } + if ( params.constructor != Array ) { + params = [ params ]; + } + $.each(params, function(i, n) { + source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n); + }); + return source; +}; + +$.extend($.validator, { + + defaults: { + messages: {}, + groups: {}, + rules: {}, + errorClass: "error", + validClass: "valid", + errorElement: "label", + focusInvalid: true, + errorContainer: $( [] ), + errorLabelContainer: $( [] ), + onsubmit: true, + ignore: [], + ignoreTitle: false, + onfocusin: function(element) { + this.lastActive = element; + + // hide error label and remove error class on focus if enabled + if ( this.settings.focusCleanup && !this.blockFocusCleanup ) { + this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass ); + this.errorsFor(element).hide(); + } + }, + onfocusout: function(element) { + if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) { + this.element(element); + } + }, + onkeyup: function(element) { + if ( element.name in this.submitted || element == this.lastElement ) { + this.element(element); + } + }, + onclick: function(element) { + // click on selects, radiobuttons and checkboxes + if ( element.name in this.submitted ) + this.element(element); + // or option elements, check parent select in that case + else if (element.parentNode.name in this.submitted) + this.element(element.parentNode); + }, + highlight: function( element, errorClass, validClass ) { + $(element).addClass(errorClass).removeClass(validClass); + }, + unhighlight: function( element, errorClass, validClass ) { + $(element).removeClass(errorClass).addClass(validClass); + } + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults + setDefaults: function(settings) { + $.extend( $.validator.defaults, settings ); + }, + + messages: { + required: "This field is required.", + remote: "Please fix this field.", + email: "Please enter a valid email address.", + url: "Please enter a valid URL.", + date: "Please enter a valid date.", + dateISO: "Please enter a valid date (ISO).", + number: "Please enter a valid number.", + digits: "Please enter only digits.", + creditcard: "Please enter a valid credit card number.", + equalTo: "Please enter the same value again.", + accept: "Please enter a value with a valid extension.", + maxlength: $.validator.format("Please enter no more than {0} characters."), + minlength: $.validator.format("Please enter at least {0} characters."), + rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."), + range: $.validator.format("Please enter a value between {0} and {1}."), + max: $.validator.format("Please enter a value less than or equal to {0}."), + min: $.validator.format("Please enter a value greater than or equal to {0}.") + }, + + autoCreateRanges: false, + + prototype: { + + init: function() { + this.labelContainer = $(this.settings.errorLabelContainer); + this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm); + this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer ); + this.submitted = {}; + this.valueCache = {}; + this.pendingRequest = 0; + this.pending = {}; + this.invalid = {}; + this.reset(); + + var groups = (this.groups = {}); + $.each(this.settings.groups, function(key, value) { + $.each(value.split(/\s/), function(index, name) { + groups[name] = key; + }); + }); + var rules = this.settings.rules; + $.each(rules, function(key, value) { + rules[key] = $.validator.normalizeRule(value); + }); + + function delegate(event) { + var validator = $.data(this[0].form, "validator"), + eventType = "on" + event.type.replace(/^validate/, ""); + validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] ); + } + $(this.currentForm) + .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate) + .validateDelegate(":radio, :checkbox, select, option", "click", delegate); + + if (this.settings.invalidHandler) + $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler); + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/form + form: function() { + this.checkForm(); + $.extend(this.submitted, this.errorMap); + this.invalid = $.extend({}, this.errorMap); + if (!this.valid()) + $(this.currentForm).triggerHandler("invalid-form", [this]); + this.showErrors(); + return this.valid(); + }, + + checkForm: function() { + this.prepareForm(); + for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) { + this.check( elements[i] ); + } + return this.valid(); + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/element + element: function( element ) { + element = this.clean( element ); + this.lastElement = element; + this.prepareElement( element ); + this.currentElements = $(element); + var result = this.check( element ); + if ( result ) { + delete this.invalid[element.name]; + } else { + this.invalid[element.name] = true; + } + if ( !this.numberOfInvalids() ) { + // Hide error containers on last error + this.toHide = this.toHide.add( this.containers ); + } + this.showErrors(); + return result; + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/showErrors + showErrors: function(errors) { + if(errors) { + // add items to error list and map + $.extend( this.errorMap, errors ); + this.errorList = []; + for ( var name in errors ) { + this.errorList.push({ + message: errors[name], + element: this.findByName(name)[0] + }); + } + // remove items from success list + this.successList = $.grep( this.successList, function(element) { + return !(element.name in errors); + }); + } + this.settings.showErrors + ? this.settings.showErrors.call( this, this.errorMap, this.errorList ) + : this.defaultShowErrors(); + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/resetForm + resetForm: function() { + if ( $.fn.resetForm ) + $( this.currentForm ).resetForm(); + this.submitted = {}; + this.prepareForm(); + this.hideErrors(); + this.elements().removeClass( this.settings.errorClass ); + }, + + numberOfInvalids: function() { + return this.objectLength(this.invalid); + }, + + objectLength: function( obj ) { + var count = 0; + for ( var i in obj ) + count++; + return count; + }, + + hideErrors: function() { + this.addWrapper( this.toHide ).hide(); + }, + + valid: function() { + return this.size() == 0; + }, + + size: function() { + return this.errorList.length; + }, + + focusInvalid: function() { + if( this.settings.focusInvalid ) { + try { + $(this.findLastActive() || this.errorList.length && this.errorList[0].element || []) + .filter(":visible") + .focus() + // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find + .trigger("focusin"); + } catch(e) { + // ignore IE throwing errors when focusing hidden elements + } + } + }, + + findLastActive: function() { + var lastActive = this.lastActive; + return lastActive && $.grep(this.errorList, function(n) { + return n.element.name == lastActive.name; + }).length == 1 && lastActive; + }, + + elements: function() { + var validator = this, + rulesCache = {}; + + // select all valid inputs inside the form (no submit or reset buttons) + // workaround $Query([]).add until http://dev.jquery.com/ticket/2114 is solved + return $([]).add(this.currentForm.elements) + .filter(":input") + .not(":submit, :reset, :image, [disabled]") + .not( this.settings.ignore ) + .filter(function() { + !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this); + + // select only the first element for each name, and only those with rules specified + if ( this.name in rulesCache || !validator.objectLength($(this).rules()) ) + return false; + + rulesCache[this.name] = true; + return true; + }); + }, + + clean: function( selector ) { + return $( selector )[0]; + }, + + errors: function() { + return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext ); + }, + + reset: function() { + this.successList = []; + this.errorList = []; + this.errorMap = {}; + this.toShow = $([]); + this.toHide = $([]); + this.currentElements = $([]); + }, + + prepareForm: function() { + this.reset(); + this.toHide = this.errors().add( this.containers ); + }, + + prepareElement: function( element ) { + this.reset(); + this.toHide = this.errorsFor(element); + }, + + check: function( element ) { + element = this.clean( element ); + + // if radio/checkbox, validate first element in group instead + if (this.checkable(element)) { + element = this.findByName( element.name )[0]; + } + + var rules = $(element).rules(); + var dependencyMismatch = false; + for( method in rules ) { + var rule = { method: method, parameters: rules[method] }; + try { + var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters ); + + // if a method indicates that the field is optional and therefore valid, + // don't mark it as valid when there are no other rules + if ( result == "dependency-mismatch" ) { + dependencyMismatch = true; + continue; + } + dependencyMismatch = false; + + if ( result == "pending" ) { + this.toHide = this.toHide.not( this.errorsFor(element) ); + return; + } + + if( !result ) { + this.formatAndAdd( element, rule ); + return false; + } + } catch(e) { + this.settings.debug && window.console && console.log("exception occured when checking element " + element.id + + ", check the '" + rule.method + "' method", e); + throw e; + } + } + if (dependencyMismatch) + return; + if ( this.objectLength(rules) ) + this.successList.push(element); + return true; + }, + + // return the custom message for the given element and validation method + // specified in the element's "messages" metadata + customMetaMessage: function(element, method) { + if (!$.metadata) + return; + + var meta = this.settings.meta + ? $(element).metadata()[this.settings.meta] + : $(element).metadata(); + + return meta && meta.messages && meta.messages[method]; + }, + + // return the custom message for the given element name and validation method + customMessage: function( name, method ) { + var m = this.settings.messages[name]; + return m && (m.constructor == String + ? m + : m[method]); + }, + + // return the first defined argument, allowing empty strings + findDefined: function() { + for(var i = 0; i < arguments.length; i++) { + if (arguments[i] !== undefined) + return arguments[i]; + } + return undefined; + }, + + defaultMessage: function( element, method) { + return this.findDefined( + this.customMessage( element.name, method ), + this.customMetaMessage( element, method ), + // title is never undefined, so handle empty string as undefined + !this.settings.ignoreTitle && element.title || undefined, + $.validator.messages[method], + "Warning: No message defined for " + element.name + "" + ); + }, + + formatAndAdd: function( element, rule ) { + var message = this.defaultMessage( element, rule.method ), + theregex = /\$?\{(\d+)\}/g; + if ( typeof message == "function" ) { + message = message.call(this, rule.parameters, element); + } else if (theregex.test(message)) { + message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters); + } + this.errorList.push({ + message: message, + element: element + }); + + this.errorMap[element.name] = message; + this.submitted[element.name] = message; + }, + + addWrapper: function(toToggle) { + if ( this.settings.wrapper ) + toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) ); + return toToggle; + }, + + defaultShowErrors: function() { + for ( var i = 0; this.errorList[i]; i++ ) { + var error = this.errorList[i]; + this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass ); + this.showLabel( error.element, error.message ); + } + if( this.errorList.length ) { + this.toShow = this.toShow.add( this.containers ); + } + if (this.settings.success) { + for ( var i = 0; this.successList[i]; i++ ) { + this.showLabel( this.successList[i] ); + } + } + if (this.settings.unhighlight) { + for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) { + this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass ); + } + } + this.toHide = this.toHide.not( this.toShow ); + this.hideErrors(); + this.addWrapper( this.toShow ).show(); + }, + + validElements: function() { + return this.currentElements.not(this.invalidElements()); + }, + + invalidElements: function() { + return $(this.errorList).map(function() { + return this.element; + }); + }, + + showLabel: function(element, message) { + var label = this.errorsFor( element ); + if ( label.length ) { + // refresh error/success class + label.removeClass().addClass( this.settings.errorClass ); + + // check if we have a generated label, replace the message then + label.attr("generated") && label.html(message); + } else { + // create label + label = $("<" + this.settings.errorElement + "/>") + .attr({"for": this.idOrName(element), generated: true}) + .addClass(this.settings.errorClass) + .html(message || ""); + if ( this.settings.wrapper ) { + // make sure the element is visible, even in IE + // actually showing the wrapped element is handled elsewhere + label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent(); + } + if ( !this.labelContainer.append(label).length ) + this.settings.errorPlacement + ? this.settings.errorPlacement(label, $(element) ) + : label.insertAfter(element); + } + if ( !message && this.settings.success ) { + label.text(""); + typeof this.settings.success == "string" + ? label.addClass( this.settings.success ) + : this.settings.success( label ); + } + this.toShow = this.toShow.add(label); + }, + + errorsFor: function(element) { + var name = this.idOrName(element); + return this.errors().filter(function() { + return $(this).attr('for') == name; + }); + }, + + idOrName: function(element) { + return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name); + }, + + checkable: function( element ) { + return /radio|checkbox/i.test(element.type); + }, + + findByName: function( name ) { + // select by name and filter by form for performance over form.find("[name=...]") + var form = this.currentForm; + return $(document.getElementsByName(name)).map(function(index, element) { + return element.form == form && element.name == name && element || null; + }); + }, + + getLength: function(value, element) { + switch( element.nodeName.toLowerCase() ) { + case 'select': + return $("option:selected", element).length; + case 'input': + if( this.checkable( element) ) + return this.findByName(element.name).filter(':checked').length; + } + return value.length; + }, + + depend: function(param, element) { + return this.dependTypes[typeof param] + ? this.dependTypes[typeof param](param, element) + : true; + }, + + dependTypes: { + "boolean": function(param, element) { + return param; + }, + "string": function(param, element) { + return !!$(param, element.form).length; + }, + "function": function(param, element) { + return param(element); + } + }, + + optional: function(element) { + return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch"; + }, + + startRequest: function(element) { + if (!this.pending[element.name]) { + this.pendingRequest++; + this.pending[element.name] = true; + } + }, + + stopRequest: function(element, valid) { + this.pendingRequest--; + // sometimes synchronization fails, make sure pendingRequest is never < 0 + if (this.pendingRequest < 0) + this.pendingRequest = 0; + delete this.pending[element.name]; + if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) { + $(this.currentForm).submit(); + this.formSubmitted = false; + } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) { + $(this.currentForm).triggerHandler("invalid-form", [this]); + this.formSubmitted = false; + } + }, + + previousValue: function(element) { + return $.data(element, "previousValue") || $.data(element, "previousValue", { + old: null, + valid: true, + message: this.defaultMessage( element, "remote" ) + }); + } + + }, + + classRuleSettings: { + required: {required: true}, + email: {email: true}, + url: {url: true}, + date: {date: true}, + dateISO: {dateISO: true}, + dateDE: {dateDE: true}, + number: {number: true}, + numberDE: {numberDE: true}, + digits: {digits: true}, + creditcard: {creditcard: true} + }, + + addClassRules: function(className, rules) { + className.constructor == String ? + this.classRuleSettings[className] = rules : + $.extend(this.classRuleSettings, className); + }, + + classRules: function(element) { + var rules = {}; + var classes = $(element).attr('class'); + classes && $.each(classes.split(' '), function() { + if (this in $.validator.classRuleSettings) { + $.extend(rules, $.validator.classRuleSettings[this]); + } + }); + return rules; + }, + + attributeRules: function(element) { + var rules = {}; + var $element = $(element); + + for (method in $.validator.methods) { + var value = $element.attr(method); + if (value) { + rules[method] = value; + } + } + + // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs + if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) { + delete rules.maxlength; + } + + return rules; + }, + + metadataRules: function(element) { + if (!$.metadata) return {}; + + var meta = $.data(element.form, 'validator').settings.meta; + return meta ? + $(element).metadata()[meta] : + $(element).metadata(); + }, + + staticRules: function(element) { + var rules = {}; + var validator = $.data(element.form, 'validator'); + if (validator.settings.rules) { + rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {}; + } + return rules; + }, + + normalizeRules: function(rules, element) { + // handle dependency check + $.each(rules, function(prop, val) { + // ignore rule when param is explicitly false, eg. required:false + if (val === false) { + delete rules[prop]; + return; + } + if (val.param || val.depends) { + var keepRule = true; + switch (typeof val.depends) { + case "string": + keepRule = !!$(val.depends, element.form).length; + break; + case "function": + keepRule = val.depends.call(element, element); + break; + } + if (keepRule) { + rules[prop] = val.param !== undefined ? val.param : true; + } else { + delete rules[prop]; + } + } + }); + + // evaluate parameters + $.each(rules, function(rule, parameter) { + rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter; + }); + + // clean number parameters + $.each(['minlength', 'maxlength', 'min', 'max'], function() { + if (rules[this]) { + rules[this] = Number(rules[this]); + } + }); + $.each(['rangelength', 'range'], function() { + if (rules[this]) { + rules[this] = [Number(rules[this][0]), Number(rules[this][1])]; + } + }); + + if ($.validator.autoCreateRanges) { + // auto-create ranges + if (rules.min && rules.max) { + rules.range = [rules.min, rules.max]; + delete rules.min; + delete rules.max; + } + if (rules.minlength && rules.maxlength) { + rules.rangelength = [rules.minlength, rules.maxlength]; + delete rules.minlength; + delete rules.maxlength; + } + } + + // To support custom messages in metadata ignore rule methods titled "messages" + if (rules.messages) { + delete rules.messages; + } + + return rules; + }, + + // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true} + normalizeRule: function(data) { + if( typeof data == "string" ) { + var transformed = {}; + $.each(data.split(/\s/), function() { + transformed[this] = true; + }); + data = transformed; + } + return data; + }, + + // http://docs.jquery.com/Plugins/Validation/Validator/addMethod + addMethod: function(name, method, message) { + $.validator.methods[name] = method; + $.validator.messages[name] = message != undefined ? message : $.validator.messages[name]; + if (method.length < 3) { + $.validator.addClassRules(name, $.validator.normalizeRule(name)); + } + }, + + methods: { + + // http://docs.jquery.com/Plugins/Validation/Methods/required + required: function(value, element, param) { + // check if dependency is met + if ( !this.depend(param, element) ) + return "dependency-mismatch"; + switch( element.nodeName.toLowerCase() ) { + case 'select': + // could be an array for select-multiple or a string, both are fine this way + var val = $(element).val(); + return val && val.length > 0; + case 'input': + if ( this.checkable(element) ) + return this.getLength(value, element) > 0; + default: + return $.trim(value).length > 0; + } + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/remote + remote: function(value, element, param) { + if ( this.optional(element) ) + return "dependency-mismatch"; + + var previous = this.previousValue(element); + if (!this.settings.messages[element.name] ) + this.settings.messages[element.name] = {}; + previous.originalMessage = this.settings.messages[element.name].remote; + this.settings.messages[element.name].remote = previous.message; + + param = typeof param == "string" && {url:param} || param; + + if ( previous.old !== value ) { + previous.old = value; + var validator = this; + this.startRequest(element); + var data = {}; + data[element.name] = value; + $.ajax($.extend(true, { + url: param, + mode: "abort", + port: "validate" + element.name, + dataType: "json", + data: data, + success: function(response) { + validator.settings.messages[element.name].remote = previous.originalMessage; + var valid = response === true; + if ( valid ) { + var submitted = validator.formSubmitted; + validator.prepareElement(element); + validator.formSubmitted = submitted; + validator.successList.push(element); + validator.showErrors(); + } else { + var errors = {}; + var message = (previous.message = response || validator.defaultMessage( element, "remote" )); + errors[element.name] = $.isFunction(message) ? message(value) : message; + validator.showErrors(errors); + } + previous.valid = valid; + validator.stopRequest(element, valid); + } + }, param)); + return "pending"; + } else if( this.pending[element.name] ) { + return "pending"; + } + return previous.valid; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/minlength + minlength: function(value, element, param) { + return this.optional(element) || this.getLength($.trim(value), element) >= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/maxlength + maxlength: function(value, element, param) { + return this.optional(element) || this.getLength($.trim(value), element) <= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/rangelength + rangelength: function(value, element, param) { + var length = this.getLength($.trim(value), element); + return this.optional(element) || ( length >= param[0] && length <= param[1] ); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/min + min: function( value, element, param ) { + return this.optional(element) || value >= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/max + max: function( value, element, param ) { + return this.optional(element) || value <= param; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/range + range: function( value, element, param ) { + return this.optional(element) || ( value >= param[0] && value <= param[1] ); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/email + email: function(value, element) { + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/ + return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/url + url: function(value, element) { + // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/ + return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/date + date: function(value, element) { + return this.optional(element) || !/Invalid|NaN/.test(new Date(value)); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/dateISO + dateISO: function(value, element) { + return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/number + number: function(value, element) { + return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/digits + digits: function(value, element) { + return this.optional(element) || /^\d+$/.test(value); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/creditcard + // based on http://en.wikipedia.org/wiki/Luhn + creditcard: function(value, element) { + if ( this.optional(element) ) + return "dependency-mismatch"; + // accept only digits and dashes + if (/[^0-9-]+/.test(value)) + return false; + var nCheck = 0, + nDigit = 0, + bEven = false; + + value = value.replace(/\D/g, ""); + + for (var n = value.length - 1; n >= 0; n--) { + var cDigit = value.charAt(n); + var nDigit = parseInt(cDigit, 10); + if (bEven) { + if ((nDigit *= 2) > 9) + nDigit -= 9; + } + nCheck += nDigit; + bEven = !bEven; + } + + return (nCheck % 10) == 0; + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/accept + accept: function(value, element, param) { + param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif"; + return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i")); + }, + + // http://docs.jquery.com/Plugins/Validation/Methods/equalTo + equalTo: function(value, element, param) { + // bind to the blur event of the target in order to revalidate whenever the target field is updated + // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead + var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() { + $(element).valid(); + }); + return value == target.val(); + } + + } + +}); + +// deprecated, use $.validator.format instead +$.format = $.validator.format; + +})(jQuery); + +// ajax mode: abort +// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]}); +// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort() +;(function($) { + var ajax = $.ajax; + var pendingRequests = {}; + $.ajax = function(settings) { + // create settings for compatibility with ajaxSetup + settings = $.extend(settings, $.extend({}, $.ajaxSettings, settings)); + var port = settings.port; + if (settings.mode == "abort") { + if ( pendingRequests[port] ) { + pendingRequests[port].abort(); + } + return (pendingRequests[port] = ajax.apply(this, arguments)); + } + return ajax.apply(this, arguments); + }; +})(jQuery); + +// provides cross-browser focusin and focusout events +// IE has native support, in other browsers, use event caputuring (neither bubbles) + +// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation +// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target +;(function($) { + // only implement if not provided by jQuery core (since 1.4) + // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs + if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) { + $.each({ + focus: 'focusin', + blur: 'focusout' + }, function( original, fix ){ + $.event.special[fix] = { + setup:function() { + this.addEventListener( original, handler, true ); + }, + teardown:function() { + this.removeEventListener( original, handler, true ); + }, + handler: function(e) { + arguments[0] = $.event.fix(e); + arguments[0].type = fix; + return $.event.handle.apply(this, arguments); + } + }; + function handler(e) { + e = $.event.fix(e); + e.type = fix; + return $.event.handle.call(this, e); + } + }); + }; + $.extend($.fn, { + validateDelegate: function(delegate, type, handler) { + return this.bind(type, function(event) { + var target = $(event.target); + if (target.is(delegate)) { + return handler.apply(target, arguments); + } + }); + } + }); +})(jQuery); diff --git a/user.inc.php b/user.inc.php index a1027de..6724ead 100644 --- a/user.inc.php +++ b/user.inc.php @@ -576,46 +576,61 @@ function user_judge_registration_status() return "open"; } -$user_personal_fields_map = array( +$user_fields_map = array( + /* Account -- Email requirement is set based on username, which + * is always required. Password is not required unless they type + * in the field, in which case the form validator kicks + * (checks pass1==pass2 and all that) */ +// 'email' => array('email'), + /* Personal */ 'salutation' => array('salutation'), 'name' => array('firstname','lastname'), - 'email' => array('email'), 'sex' => array('sex'), 'phonehome' => array('phonehome'), - 'phonework' => array('phonework'), 'phonecell' => array('phonecell'), - 'fax' => array('fax'), - 'org' => array('organization'), 'birthdate' => array('birthdate'), 'lang' => array('lang'), 'address' => array('address', 'address2', 'postalcode'), 'city' => array('city'), 'province' => array('province'), - 'firstaid' => array('firstaid','cpr')); + 'firstaid' => array('firstaid','cpr'), + /* Organization */ + 'org' => array('organization'), + 'phonework' => array('phonework'), + 'fax' => array('fax'), +); -function user_personal_fields($role) +/* Return fields to show based on role. In the user editor, many + * fields are always shown and some have hard-coded requirements, but + * any in this list can be made optionally-required or not shown + * at all */ +function user_fields_enabled($role) { - global $config, $user_personal_fields_map; - $ret = array('firstname','lastname','email'); + global $config, $user_fields_map; + $ret = array('firstname','lastname'); $fields = $config["{$role}_personal_fields"]; if($fields != '') { $fields = split(',', $fields); foreach($fields as $f) { - $ret = array_merge($ret, $user_personal_fields_map[$f]); + $ret = array_merge($ret, $user_fields_map[$f]); } } return $ret; } -function user_personal_required_fields($role) +/* Return required fields. Some fields are always shown and can be + * set to required. Some have hard-coded requirement status. This is only + * for the fields where the requirement can be configured. Not for ALL fields + * the user sees */ +function user_fields_required($role) { - global $config, $user_personal_fields_map; - $ret = array('firstname','lastname','email'); + global $config, $user_fields_map; + $ret = array('firstname','lastname','username'); $required = $config["{$role}_personal_required"]; if($required != '') { $fields = split(',', $required); foreach($fields as $f) { - $ret = array_merge($ret, $user_personal_fields_map[$f]); + $ret = array_merge($ret, $user_fields_map[$f]); } } /* Filter some elements that are never required. @@ -625,26 +640,6 @@ function user_personal_required_fields($role) return $ret; } -function user_personal_info_status(&$u) -{ - $required = array(); - foreach(array_keys($u['roles']) as $r) { - $required = array_merge($required, - user_personal_required_fields($r)); - } - foreach($required as $r) { - $val = trim($u[$r]); - - if(strlen($val) > 0) { - /* Ok */ - } else { - return 'incomplete'; - } - } - /* FIXME: somehow call the $role _status_update() function to update - * the individual [$role]['complete'] entry? */ - return 'complete'; -} /* user_{$role}_login() is called with a full $u loaded */ diff --git a/user_account.php b/user_account.php index d07b04c..19afcb1 100644 --- a/user_account.php +++ b/user_account.php @@ -47,7 +47,7 @@ function user_account_check_username($accounts_id, $username) { if(!account_valid_user($username)) return false; - $u = mysql_real_escape_string($u); + $u = mysql_real_escape_string($username); $q = mysql_query("SELECT id FROM accounts WHERE username='$u' AND deleted='no' AND id!=$accounts_id"); if(mysql_num_rows($q) != 0) return false; @@ -142,6 +142,14 @@ case 'save': + +".i18n("Account/Login Information")."
"; - ?> +

-

+
+
@@ -266,7 +275,7 @@ $.validator.addMethod("checking",function(value, element) { return username_checking; }); -$().ready(function() { +$(document).ready(function() { $("#accountform").validate({ rules: { email: { @@ -321,6 +330,9 @@ $().ready(function() { } }); + user_update_tab_status('account'); + + /* Code to disable the username box, only included if the password hasn't expired */ var username_link = $("#username_link").is(":checked"); diff --git a/user_edit.inc.php b/user_edit.inc.php new file mode 100644 index 0000000..25027c2 --- /dev/null +++ b/user_edit.inc.php @@ -0,0 +1,137 @@ + + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +?> +'; + return; + } + + $req = in_array($fname, $required) ? REQUIREDFIELD : ''; + + $c = ($label == '') ? '' : ':'; + echo ""; + + echo ''; +} + +function user_account_status(&$u, $a = null) +{ + if(is_array($u)) { + $a = account_load($u['accounts_id']); + } + + if($a['username'] == '') + return 'incomplete'; + + return 'complete'; +} + +function user_personal_info_status(&$u) +{ + $required = array(); + foreach(array_keys($u['roles']) as $r) { + $required = array_merge($required, user_fields_required($r)); + } + $our_fields = array('salutation', 'firstname','lastname','address', + 'address2','city','province','postalcode', + 'phonehome','phonecell','language','sex', + 'firstaid','cpr'); + $required = array_intersect($our_fields, $required); + + foreach($required as $r) { + if(trim($u[$r]) == '') return 'incomplete'; + } + /* FIXME: somehow call the $role _status_update() function to update + * the individual [$role]['complete'] entry? */ + return 'complete'; +} + +function user_organization_status(&$u) +{ + $required = array(); + foreach(array_keys($u['roles']) as $r) { + $required = array_merge($required, user_fields_required($r)); + } + $our_fields = array('organization', 'phonework','fax'); + $required = array_intersect($our_fields, $required); + + foreach($required as $r) { + if(trim($u[$r]) == '') return 'incomplete'; + } + + /* FIXME: somehow call the $role _status_update() function to update + * the individual [$role]['complete'] entry? */ + return 'complete'; +} + +function user_roles_status(&$u) +{ + return 'complete'; +} + + +?> diff --git a/user_edit.php b/user_edit.php new file mode 100644 index 0000000..7793848 --- /dev/null +++ b/user_edit.php @@ -0,0 +1,279 @@ + + Copyright (C) 2005 James Grant + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +?> + array( + 'label' => 'Fair Information', + 'types' => array('fair'), + 'file' => 'fair_info.php', + ), + 'fairstatsgathering' => array( + 'label' => 'Fair Stats Gathering', + 'types' => array('fair'), + 'file' => 'fair_stats_select.php', + ), + 'account' => array( + 'label' => 'Account/Login', + 'name' => 'Account/Login/Password', + 'types' => array('student','judge','committee','volunteer','sponsor','fair'), + 'file' => 'user_account.php', + 'status_func' => 'user_account_status', + ), + 'personal' => array( + 'label' => 'Personal', + 'name' => 'Personal Information', + 'types' => array('student','judge','committee','volunteer','sponsor','fair'), + 'file' => 'user_personal.php', + 'status_func' => 'user_personal_info_status', + ), + 'organization' => array( + 'label' => 'Organization', + 'name' => 'Organization Information', + 'types' => array('judge','volunteer','sponsor'), + 'file' => 'user_organization.php', + 'status_func' => 'user_organization_status', + ), + 'roles' => array( + 'label' => 'Roles', + 'types' => array('student','judge','committee','volunteer','sponsor','fair'), + 'file' => 'user_roles.php', + 'status_func' => 'user_roles_status', + ), + 'judgeother' => array( + 'label' => 'Judge Other', + 'types' => array('judge'), + 'file' => 'judge_other.php', + ), + 'judgeexpertise' => array( + 'label' => 'Expertise', + 'types' => array('judge'), + 'file' => 'judge_expertise.php', + ), + 'judgeavailability' => array( + 'label' => 'Time Avail.', + 'types' => array('judge'), + 'file' => 'judge_availability.php', + ), + 'judgesa' => array( + 'label' => 'Special Awards', + 'types' => array('judge'), + 'file' => 'judge_special_awards.php', + ), + 'volunteerpos' => array( + 'label' => 'Volunteer Positions', + 'types' => array('volunteer'), + 'file' => 'volunteer_position.php', + ), + 'fairstats' => array( + 'label' => 'Fair Statistics and Information', + 'types' => array('fair'), + 'file' => 'fair_stats.php', + ), + ); + +$u = user_load($edit_id); +$types = array_keys($u['roles']); +$selected = $_GET['tab']; +if(!array_key_exists($selected, $tabs)) { + if(in_array('fair', $types) ) + $selected = 'fairinfo'; + else + $selected = 'personal'; +} + +$fields = array(); +$required = array(); +foreach(array_keys($u['roles']) as $r) { + $fields = array_merge($fields, user_fields_enabled($r)); + $required = array_merge($required, user_fields_required($r)); +} + +/* Disable some of the tabs */ +if($config['judges_availability_enable'] != 'yes') $tabs['judgeavailability']['disabled'] = true; +$a = array_intersect(array('organization','phonework','fax'), $fields); +if(count($a) == 0) { + /* No organization stuff is enabled */ + $tabs['organization']['disabled'] = true; +} + + +send_header(i18n("User Editor").": {$u['name']}"); + + +/* Setup tabs */ +echo '
'; +echo '
    '; +/* Always show a registration summary */ +echo '
  • '.i18n('Registration Summary').'
  • '; +$index = 1; +$selected_index = 0; +/* Show all other enabled tabs */ +foreach($tabs as $k=>$t) { + /* Make sure the tab is enabled */ + if($t['disabled'] == true) continue; + /* Make sure the user has the right type to see the tab */ + $i = array_intersect($t['types'], $types); + if(count($i) == 0) { + /* Turn off the tab, so in future iterations of the tabs + * list we only ahve to check enabled */ + $tabs[$k]['disabled'] = true; + continue; + } + + $tabs[$k]['index'] = $index; + $tabs_key[$index] = $k; + + if($k == $selected) $selected_index = $index; + $index++; + + /* Show the tab */ + $href = "{$t['file']}?id=$id"; + echo "
  • ".i18n($t['label'])."
  • "; +} + +?> +
+
+ +

+
+ +
'; + switch($type) { + case 'textbox': + echo ""; + break; + + case 'province': + emit_province_selector($fname, $u[$fname]); + break; + + case 'yesno': + echo ""; + break; + case 'sex': + echo ""; + echo "\n"; + foreach($config['languages'] AS $l=>$ln) { + if($u['lang']==$l) $sel="selected=\"selected\""; else $sel=""; + echo "\n"; + } + echo ""; + break; + } + echo '
+$t) { + /* Enabled has been modified now for this user */ + if($t['disabled'] == true) continue; + + /* Get the status */ + if(is_callable($t['status_func'])) { + $s = call_user_func($t['status_func'], $u); + $tabs[$k]['status'] = ($s == 'complete') ? 'complete' : 'incomplete'; + } else { + $tabs[$k]['status'] = 'incomplete'; + } + + /* Link to switch to the tab */ + $n = ($t['name'] != '') ? $t['name'] : $t['label']; + ?> + + + + + + + + + +

+ + + + + + + + diff --git a/user_organization.php b/user_organization.php new file mode 100644 index 0000000..93fa7d0 --- /dev/null +++ b/user_organization.php @@ -0,0 +1,170 @@ + + Copyright (C) 2005 James Grant + Copyright (C) 2007 David Grant + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation, version 2. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +?> + + + +

-

+
+ + + + + + + + + +
Organization
+ +
+" /> + +
+ + + + + + diff --git a/user_personal.php b/user_personal.php index 683c09b..d5d1ed1 100644 --- a/user_personal.php +++ b/user_personal.php @@ -25,44 +25,11 @@ array('name' => 'Salutation'), - 'firstname' => array('name' => 'First Name'), - 'lastname' => array('name' => 'Last Name'), - 'address' => array('name' => 'Address 1'), - 'address2' => array('name' => 'Address 2'), - 'city' => array('name' => 'City'), - 'lang' => array('name' => 'Preferred Language'), - 'province' => array('name' => $config['provincestate']), - 'organization' => array('name' => 'Organization'), - 'sex' => array('name' => 'Gender'), - 'firstaid' => array ('name' => 'First Aid Training', - 'type' => 'yesno'), - 'cpr' => array ('name' => 'CPR Training', - 'type' => 'yesno'), - 'phonehome' => array('name' => 'Phone (Home)', - 'regexp' => '^[1-9][0-9]{2}-[1-9][0-9]{2}-[0-9]{4}( x[0-9]{1,5})?$', - 'format' => '\'NNN-NNN-NNNN\' or \'NNN-NNN-NNNN xEXT\'',), - 'phonecell' => array('name' => 'Phone (Cell)', - 'regexp' => '^[1-9][0-9]{2}-[1-9][0-9]{2}-[0-9]{4}$', - 'format' => '\'NNN-NNN-NNNN\'',), - 'phonework' => array('name' => 'Phone (Work)', - 'regexp' => '^[1-9][0-9]{2}-[1-9][0-9]{2}-[0-9]{4}( x[0-9]{1,5})?$', - 'format' => '\'NNN-NNN-NNNN\' or \'NNN-NNN-NNNN xEXT\'',), - 'fax' => array('name' => 'Fax', - 'regexp' => '^[1-9][0-9]{2}-[1-9][0-9]{2}-[0-9]{4}$', - 'format' => '\'NNN-NNN-NNNN\'',), - 'postalcode' => array('name' => $config['postalzip'], - 'regexp' => '^(([A-Za-z][0-9][A-Za-z]( )?[0-9][A-Za-z][0-9])|([0-9]{5}))$', - 'format' => '\'ANA NAN\' or \'ANANAN\' or \'NNNNN\'',), - -); - - $edit_id = isset($_GET['users_id']) ? intval($_GET['users_id']) : $_SESSION['users_id']; if($edit_id != $_SESSION['users_id']) user_auth_required('admin'); @@ -71,46 +38,35 @@ else $u = user_load($edit_id); /* Load the fields the user can edit, and theones that are required */ - $fields = array(); - $required = array(); - $errorfields = array(); - foreach(array_keys($u['roles']) as $r) { +$fields = array(); +$required = array(); +foreach(array_keys($u['roles']) as $r) { $fields = array_merge($fields, - user_personal_fields($r)); + user_fields_enabled($r)); $required = array_merge($required, - user_personal_required_fields($r)); - } - /* true/false strings for form validation */ - $vreq = array(); + user_fields_required($r)); +} + +/* Filter fields, only the ones we care about */ +$our_fields = array('salutation', 'firstname','lastname','address', + 'address2','city','province','postalcode', + 'phonehome','phonecell','language','sex', + 'firstaid','cpr'); +$fields = array_intersect($our_fields, $fields); +$required = array_intersect($our_fields, $required); switch($_GET['action']) { case 'save': - $users_id = intval($_POST['users_id']); - /* Only admin can pass in a different users_id */ - if($users_id != $_SESSION['users_id']) { - user_auth_required('admin'); - } - $u = user_load($users_id); - $save = true; - /* Set values */ + + /* Cleanup POST data */ foreach($fields as $f) { $u[$f] = stripslashes($_POST[$f]); - /* Allow the user to clear a field regardless of regex */ + /* Allow the user to clear a field */ if($u[$f] == '') continue; - - /* See if this field has a validate */ - if(isset($user_personal_fields[$f]['regexp'])) { - /* Match the regex */ - if(!ereg($user_personal_fields[$f]['regexp'], $u[$f])) { - /* Bad */ - error_("Invalid format for $f expecting ({$user_personal_fields[$f]['format']})"); - $save = false; - $errorfields[] = $f; - } - } } +if(0) { if(array_key_exists('committee', $u['roles'])) { /* Trying to save a committee member eh? Well, we established above * that we're allowed to be here, so go ahead and save it */ @@ -149,6 +105,7 @@ case 'save': } } } +} if($save == true) { @@ -158,165 +115,65 @@ case 'save': //reload the user record because we dont know if we saved or didnt save above, we just want //to know what the user looks like _now_ - $u = user_load($users_id); + $u = user_load($u['id']); + + /* Update the status */ $newstatus=user_personal_info_status($u); - echo "\n"; - exit; - } - - -//send the header -display_messages(); -echo "

".i18n("Personal Information")."

"; -echo "
"; - -$newstatus=user_personal_info_status($u); ?> - + "; + exit; +} + + if(count($u['roles']) > 1) { $str=''; foreach(array_keys($u['roles']) as $r) { $str.= (($str=='')?'':', ').i18n($roles[$r]['name']); } - echo notice(i18n('This user has multiple roles, the fields shown below are a combination of every role. Some may not apply to some roles. This user has the following roles:').' '.$str); +// echo notice(i18n('This user has multiple roles, the fields shown below are a combination of every role. Some may not apply to some roles. This user has the following roles:').' '.$str); } -function item(&$u, $label, $fname, $type='textbox') -{ - global $required, $fields, $config; - global $vreq; - - if(!in_array($fname, $fields)) { - $vreq[$fname] = 'false'; - echo ''; - return; - } - - /* vreq is true/false for the form validator */ - if(in_array($fname, $required)) { - $vreq[$fname] = 'true'; - $req = REQUIREDFIELD; - } else { - $vreq[$fname] = 'false'; - $req = ''; - } - - $c = ($label == '') ? '' : ':'; - echo ""; - - echo ''; - switch($type) { - case 'textbox': - echo ""; - break; - - case 'province': - emit_province_selector($fname, $u[$fname]); - break; - - case 'yesno': - echo ""; - break; - case 'sex': - echo ""; - echo "\n"; - foreach($config['languages'] AS $l=>$ln) { - if($u['lang']==$l) $sel="selected=\"selected\""; else $sel=""; - echo "\n"; - } - echo ""; - break; - } - echo ''; - -} -/* - -" /> - -Address
- -*/ - ?> +

-

+
+ +
- - - + + + - - - - - + + + + + */ ?> - - + + */ ?> - - - - - - */ -?> - - - - - + + + + +
Name
Address
" />
Phone
" />
Other Information
" />
Organization
"; - /* Committee specific fields */ if(array_key_exists('committee', $u['roles']) && false ) { @@ -346,6 +203,15 @@ if(array_key_exists('committee', $u['roles']) && false ) { echo ''; } +function vreq($field) +{ + global $required; + /* Return 'true' or 'false' as text for the + * validator plugin to use for the 'required' param */ + if(in_array($field, $required)) return 'true'; + return 'false'; +} + ?> " /> @@ -353,6 +219,13 @@ if(array_key_exists('committee', $u['roles']) && false ) {
diff --git a/user_activate.php b/user_roles.php similarity index 68% rename from user_activate.php rename to user_roles.php index 67b3d05..56a586c 100644 --- a/user_activate.php +++ b/user_roles.php @@ -23,33 +23,24 @@ */ ?> "; - display_messages(); - echo "

".i18n("Role and Account Management")."

"; - echo "
"; - } else { - send_header("Role and Account Management", - array("Main" => "user_main.php") - ); - } - ?> + +

-

+ +'; + echo '
  • '.i18n("An Active Role indicates you would like to participate in the %1 %2 as that role (Judge, Volunteer, etc.)",array($config['FAIRYEAR'],$config['fairname'])); + echo '
  • '.i18n("A Deactivated Role indicates you cannot participate in the deactivated roles this year, but would like remain on the mailing lists for future years. You can activate your deactivated role at any time."); + echo '
  • '.i18n("The Remove Role button completely deletes the role from your account. You will not receive future emails for the removed role. This action cannot be undone."); + echo '
  • '.i18n("The Delete Entire Account button at the bottom of the page completely deletes your entire account. You will not receive any future email for any roles. It completely removes you from the system. This action cannot be undone."); + echo ''; +?> + - - + +
    "; - - echo '
      '; - echo '
    • '.i18n("An Active Role indicates you would like to participate in the %1 %2 as that role (Judge, Volunteer, etc.)",array($config['FAIRYEAR'],$config['fairname'])); - echo '
    • '.i18n("A Deactivated Role indicates you cannot participate in the deactivated roles this year, but would like remain on the mailing lists for future years. You can activate your deactivated role at any time."); - echo '
    • '.i18n("The Remove Role button completely deletes the role from your account. You will not receive future emails for the removed role. This action cannot be undone."); - echo '
    • '.i18n("The Delete Entire Account button below completely deletes your entire account. You will not receive any future email for any roles. It completely removes you from the system. This action cannot be undone."); - echo '
    '; - - echo ""; - echo ""; - echo ""; - echo "
  • "; - - if($_SESSION['embed'] != true) send_footer(); +} ?> + + + +
    +');" + type="submit" value=""> +
    +