PageRenderTime 68ms CodeModel.GetById 43ms RepoModel.GetById 0ms app.codeStats 0ms

/src/packages/flaskext/wtf/__init__.py

https://github.com/netconstructor/DeforestationAnalysisTool
Python | 226 lines | 121 code | 50 blank | 55 comment | 14 complexity | 4b622cc509b0247b81942bcb38b2cb51 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskext.wtf
  4. ~~~~~~~~~~~~
  5. Flask-WTF extension
  6. :copyright: (c) 2010 by Dan Jacob.
  7. :license: BSD, see LICENSE for more details.
  8. """
  9. import warnings
  10. import uuid
  11. from wtforms.fields import BooleanField, DecimalField, DateField, \
  12. DateTimeField, FieldList, FloatField, FormField, \
  13. HiddenField, IntegerField, PasswordField, RadioField, SelectField, \
  14. SelectMultipleField, SubmitField, TextField, TextAreaField
  15. from wtforms.validators import Email, email, EqualTo, equal_to, \
  16. IPAddress, ip_address, Length, length, NumberRange, number_range, \
  17. Optional, optional, Required, required, Regexp, regexp, \
  18. URL, url, AnyOf, any_of, NoneOf, none_of
  19. from wtforms.widgets import CheckboxInput, FileInput, HiddenInput, \
  20. ListWidget, PasswordInput, RadioInput, Select, SubmitInput, \
  21. TableWidget, TextArea, TextInput
  22. from wtforms.fields import FileField as _FileField
  23. try:
  24. import sqlalchemy
  25. _is_sqlalchemy = True
  26. except ImportError:
  27. _is_sqlalchemy = False
  28. from wtforms import Form as BaseForm
  29. from wtforms import fields, widgets, validators, ValidationError
  30. from flask import request, session, current_app
  31. from jinja2 import Markup
  32. from flaskext.wtf import html5
  33. from flaskext.wtf import recaptcha
  34. from flaskext.wtf.recaptcha.fields import RecaptchaField
  35. from flaskext.wtf.recaptcha.widgets import RecaptchaWidget
  36. from flaskext.wtf.recaptcha.validators import Recaptcha
  37. fields.RecaptchaField = RecaptchaField
  38. widgets.RecaptchaWidget = RecaptchaWidget
  39. validators.Recaptcha = Recaptcha
  40. from flaskext.wtf.file import FileField
  41. from flaskext.wtf.file import FileAllowed, FileRequired, file_allowed, \
  42. file_required
  43. fields.FileField = FileField
  44. validators.file_allowed = file_allowed
  45. validators.file_required = file_required
  46. validators.FileAllowed = FileAllowed
  47. validators.FileRequired = FileRequired
  48. __all__ = ['Form', 'ValidationError',
  49. 'fields', 'validators', 'widgets', 'html5']
  50. __all__ += fields.__all__
  51. __all__ += validators.__all__
  52. __all__ += widgets.__all__
  53. __all__ += recaptcha.__all__
  54. if _is_sqlalchemy:
  55. from wtforms.ext.sqlalchemy.fields import QuerySelectField, \
  56. QuerySelectMultipleField
  57. __all__ += ['QuerySelectField',
  58. 'QuerySelectMultipleField']
  59. for field in (QuerySelectField,
  60. QuerySelectMultipleField):
  61. setattr(fields, field.__name__, field)
  62. def _generate_csrf_token():
  63. return str(uuid.uuid4())
  64. class Form(BaseForm):
  65. """
  66. Subclass of WTForms **Form** class. The main difference is that
  67. **request.form** is passed as `formdata` argument to constructor
  68. so can handle request data implicitly.
  69. In addition this **Form** implementation has automatic CSRF handling.
  70. """
  71. csrf = fields.HiddenField()
  72. def __init__(self, formdata=None, *args, **kwargs):
  73. csrf_enabled = kwargs.pop('csrf_enabled', None)
  74. if csrf_enabled is None:
  75. csrf_enabled = current_app.config.get('CSRF_ENABLED', True)
  76. self.csrf_enabled = csrf_enabled
  77. self.csrf_session_key = kwargs.pop('csrf_session_key', None)
  78. if self.csrf_session_key is None:
  79. self.csrf_session_key = \
  80. current_app.config.get('CSRF_SESSION_KEY', '_csrf_token')
  81. csrf_token = session.get(self.csrf_session_key, None)
  82. if csrf_token is None:
  83. csrf_token = self.reset_csrf()
  84. super(Form, self).__init__(formdata, csrf=csrf_token, *args, **kwargs)
  85. def is_submitted(self):
  86. """
  87. Checks if form has been submitted. The default case is if the HTTP
  88. method is **PUT** or **POST**.
  89. """
  90. return request and request.method in ("PUT", "POST")
  91. def process(self, formdata=None, obj=None, **kwargs):
  92. if self.is_submitted():
  93. if formdata is None:
  94. formdata = request.form
  95. # ensure csrf validation occurs ONLY when formdata is passed
  96. # in case "csrf" is the only field in the form
  97. if not formdata and not request.files:
  98. self.csrf_is_valid = False
  99. else:
  100. self.csrf_is_valid = None
  101. super(Form, self).process(formdata, obj, **kwargs)
  102. @property
  103. def csrf_token(self):
  104. """
  105. Renders CSRF field inside a hidden DIV.
  106. :deprecated: Use **hidden_tag** instead.
  107. """
  108. warnings.warn("csrf_token is deprecated. Use hidden_tag instead",
  109. DeprecationWarning)
  110. return self.hidden_tag('csrf')
  111. def reset_csrf(self):
  112. """
  113. Resets the CSRF token in the session. If you are reusing the form
  114. in the same view (i.e. you are not redirecting somewhere else)
  115. it's recommended you call this before rendering the form.
  116. """
  117. csrf_token = _generate_csrf_token()
  118. session[self.csrf_session_key] = csrf_token
  119. return csrf_token
  120. def validate_csrf(self, field):
  121. if not self.csrf_enabled:
  122. return
  123. csrf_token = session.pop(self.csrf_session_key, None)
  124. is_valid = field.data and \
  125. field.data == csrf_token and \
  126. self.csrf_is_valid is not False
  127. # reset this field, otherwise stale token is displayed
  128. field.data = self.reset_csrf()
  129. # we set this flag to ensure consistent behaviour when
  130. # calling validate() more than once
  131. self.csrf_is_valid = bool(is_valid)
  132. if not is_valid:
  133. raise ValidationError, "Missing or invalid CSRF token"
  134. def hidden_tag(self, *fields):
  135. """
  136. Wraps hidden fields in a hidden DIV tag, in order to keep XHTML
  137. compliance.
  138. .. versionadded:: 0.3
  139. :param fields: list of hidden field names. If not provided will render
  140. all hidden fields, including the CSRF field.
  141. """
  142. if not fields:
  143. fields = [f for f in self if isinstance(f, HiddenField)]
  144. rv = [u'<div style="display:none;">']
  145. for field in fields:
  146. if isinstance(field, basestring):
  147. field = getattr(self, field)
  148. rv.append(unicode(field))
  149. rv.append(u"</div>")
  150. return Markup(u"".join(rv))
  151. def validate_on_submit(self):
  152. """
  153. Checks if form has been submitted and if so runs validate. This is
  154. a shortcut, equivalent to ``form.is_submitted() and form.validate()``
  155. """
  156. return self.is_submitted() and self.validate()