178 lines
7.3 KiB
Python
178 lines
7.3 KiB
Python
# -*- coding: utf-8 -*-
|
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
|
|
|
###############################################################################
|
|
# ScribeEngine - Open Source Blog Software #
|
|
# --------------------------------------------------------------------------- #
|
|
# Copyright (c) 2010 Raoul Snyman #
|
|
# --------------------------------------------------------------------------- #
|
|
# 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 of the License. #
|
|
# #
|
|
# 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; if not, write to the Free Software Foundation, Inc., 59 #
|
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
###############################################################################
|
|
|
|
"""
|
|
Validation helper classes and methods.
|
|
"""
|
|
|
|
import logging
|
|
|
|
from formencode.htmlfill import FillingParser
|
|
from decorator import decorator
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
class ClassFillingParser(FillingParser):
|
|
"""
|
|
This parser inherits from the base htmlfill.FillingParser, but overrides
|
|
the behaviour.
|
|
|
|
If it encounters the class `error_${name}` on an element and the name given
|
|
is in the error dictionary, then the `form-error` class is added to the
|
|
element. Otherwise, the class is removed.
|
|
|
|
If it encounters the class `errormessage_${name}` on an element and the name
|
|
given is in the error dictionary, then the content of the element is
|
|
replaced with the error from the error dictionary. Otherwise, the element
|
|
is removed.
|
|
|
|
The class `error_` (ie, no name) will give any form errors.
|
|
"""
|
|
def __init__(self, *args, **kwargs):
|
|
"""
|
|
Set up the filling parser.
|
|
"""
|
|
self.formname = kwargs.pop(u'formname', None)
|
|
self.in_errormessage = None
|
|
return FillingParser.__init__(self, *args, **kwargs)
|
|
|
|
def handle_starttag(self, tag, attrs, startend=False):
|
|
"""
|
|
Handle the start of an HTML tag.
|
|
"""
|
|
# Find any class attribute on the element
|
|
class_attr = None
|
|
for key, value in attrs:
|
|
if key == u'class':
|
|
class_attr = value
|
|
break
|
|
# Only have to handle the case where there is a class.
|
|
if class_attr:
|
|
classes = class_attr.split(u' ')
|
|
for class_name in classes:
|
|
# Replace error_${name} with "error" if name is in error dict
|
|
if class_name.startswith(u'error_'):
|
|
classes = [cls for cls in classes if cls != class_name]
|
|
field = class_name[6:]
|
|
if field in self.errors:
|
|
classes.append(u'form-error')
|
|
self.set_attr(attrs, u'class', u' '.join(classes))
|
|
self.write_tag(tag, attrs, startend)
|
|
self.skip_next = True
|
|
return
|
|
# Replace the contents of elements with class
|
|
# errormessage_${name} with the error from the error
|
|
# dictionary (or delete the element entirely if there is no
|
|
# such error).
|
|
if class_name.startswith(u'errormessage_'):
|
|
field = class_name[13:]
|
|
self.in_errormessage = tag
|
|
self.skip_error = True
|
|
# form errors
|
|
if not field:
|
|
field = None
|
|
if field in self.errors:
|
|
classes = [cls for cls in classes if cls != class_name]
|
|
classes.append(u'form-error')
|
|
self.set_attr(attrs, u'class', u' '.join(classes))
|
|
self.write_tag(tag, attrs, startend)
|
|
self.write_text(htmlliteral(unicode(self.errors[field])).text)
|
|
self.write_text(u'</%s>' % tag)
|
|
self.skip_next = True
|
|
return
|
|
return FillingParser.handle_starttag(self, tag, attrs, startend=False)
|
|
|
|
def handle_endtag(self, tag):
|
|
"""
|
|
Handle the ending HTML tag.
|
|
"""
|
|
FillingParser.handle_endtag(self, tag)
|
|
# We're handling skipping of contents of an element with
|
|
# errormessage_* on it.
|
|
#
|
|
# After we encounter the end tag, we can stop ignoring elements.
|
|
if self.in_errormessage == tag:
|
|
self.skip_error = False
|
|
self.in_errormessage = None
|
|
self.skip_next = True
|
|
|
|
@classmethod
|
|
def html_error_fill(cls, form, errors_and_values):
|
|
"""
|
|
Create the custom ClassFillingParser, and pass through the values,
|
|
errors, and formname from the errors dictionary.
|
|
|
|
Converts the incoming form from UTF-8-encoded byte strings to Unicode
|
|
strings.
|
|
"""
|
|
p = cls(defaults=errors_and_values[u'form_values'],
|
|
errors=errors_and_values[u'form_errors'],
|
|
auto_error_formatter=False)
|
|
p.feed(form.decode(u'utf-8'))
|
|
p.close()
|
|
return p.text().encode(u'utf-8')
|
|
|
|
|
|
def jsvalidate(form_id):
|
|
"""
|
|
This decorator is used to generate JavaScript for client-side validate.
|
|
|
|
``form_id``
|
|
The HTML ``id`` of the form to be validated.
|
|
"""
|
|
def entangle(func, self, *args, **kwargs):
|
|
default_jscript = u"""/* jQuery Validation */
|
|
$ProjectHQ.Events.bind_load(function () {
|
|
$("#%s").validate({
|
|
errorClass: "form-error",
|
|
errorContainer: "#form-errors",
|
|
errorLabelContainer: "#form-errors > ul",
|
|
wrapper: "li",
|
|
rules: {
|
|
%s
|
|
},
|
|
messages: {
|
|
%s
|
|
}
|
|
});
|
|
});"""
|
|
validators = []
|
|
messages = []
|
|
jsschema = func(self, *args, **kwargs)
|
|
for name, validator in jsschema.iteritems():
|
|
validators.append(u'%s: {%s}' % (name, validator.to_javascript()))
|
|
if validator.get_message() is not None:
|
|
validator_message = validator.get_message()
|
|
if isinstance(validator_message, basestring):
|
|
messages.append(u'%s: "%s"' % (name, validator.get_message()))
|
|
else:
|
|
validator_messages = []
|
|
for key, value in validator_message.iteritems():
|
|
validator_messages.append(u'%s: "%s"' % (key, value))
|
|
messages.append(u'%s: {\n %s\n }' %
|
|
(name, ',\n '.join(validator_messages)))
|
|
jscript = default_jscript % (form_id, u',\n '.join(validators),
|
|
u',\n '.join(messages))
|
|
return jscript
|
|
return decorator(entangle)
|
|
|