forms.py
563 lines
| 21.4 KiB
| text/x-python
|
PythonLexer
r1 | # -*- coding: utf-8 -*- | |||
r1271 | # Copyright (C) 2010-2017 RhodeCode GmbH | |||
r1 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# 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 Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
""" | ||||
this is forms validation classes | ||||
http://formencode.org/module-formencode.validators.html | ||||
for list off all availible validators | ||||
we can create our own validators | ||||
The table below outlines the options which can be used in a schema in addition to the validators themselves | ||||
pre_validators [] These validators will be applied before the schema | ||||
chained_validators [] These validators will be applied after the schema | ||||
allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present | ||||
filter_extra_fields False If True, then keys that aren't associated with a validator are removed | ||||
if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value. | ||||
ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already | ||||
<name> = formencode.validators.<name of validator> | ||||
<name> must equal form name | ||||
list=[1,2,3,4,5] | ||||
for SELECT use formencode.All(OneOf(list), Int()) | ||||
""" | ||||
r518 | import deform | |||
r519 | import logging | |||
import formencode | ||||
r518 | from pkg_resources import resource_filename | |||
r519 | from formencode import All, Pipe | |||
from pylons.i18n.translation import _ | ||||
from rhodecode import BACKENDS | ||||
from rhodecode.lib import helpers | ||||
from rhodecode.model import validators as v | ||||
log = logging.getLogger(__name__) | ||||
r518 | ||||
deform_templates = resource_filename('deform', 'templates') | ||||
rhodecode_templates = resource_filename('rhodecode', 'templates/forms') | ||||
search_path = (rhodecode_templates, deform_templates) | ||||
r519 | ||||
class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory): | ||||
""" Subclass of ZPTRendererFactory to add rhodecode context variables """ | ||||
def __call__(self, template_name, **kw): | ||||
kw['h'] = helpers | ||||
return self.load(template_name)(**kw) | ||||
r518 | ||||
r519 | form_renderer = RhodecodeFormZPTRendererFactory(search_path) | |||
deform.Form.set_default_renderer(form_renderer) | ||||
r1 | ||||
def LoginForm(): | ||||
class _LoginForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
username = v.UnicodeString( | ||||
strip=True, | ||||
min=1, | ||||
not_empty=True, | ||||
messages={ | ||||
'empty': _(u'Please enter a login'), | ||||
'tooShort': _(u'Enter a value %(min)i characters long or more') | ||||
} | ||||
) | ||||
password = v.UnicodeString( | ||||
strip=False, | ||||
min=3, | ||||
not_empty=True, | ||||
messages={ | ||||
'empty': _(u'Please enter a password'), | ||||
'tooShort': _(u'Enter %(min)i characters or more')} | ||||
) | ||||
remember = v.StringBoolean(if_missing=False) | ||||
chained_validators = [v.ValidAuth()] | ||||
return _LoginForm | ||||
def UserForm(edit=False, available_languages=[], old_data={}): | ||||
class _UserForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
username = All(v.UnicodeString(strip=True, min=1, not_empty=True), | ||||
v.ValidUsername(edit, old_data)) | ||||
if edit: | ||||
new_password = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=False) | ||||
) | ||||
password_confirmation = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=False), | ||||
) | ||||
admin = v.StringBoolean(if_missing=False) | ||||
else: | ||||
password = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=True) | ||||
) | ||||
password_confirmation = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=False) | ||||
) | ||||
password_change = v.StringBoolean(if_missing=False) | ||||
create_repo_group = v.StringBoolean(if_missing=False) | ||||
active = v.StringBoolean(if_missing=False) | ||||
firstname = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
lastname = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) | ||||
extern_name = v.UnicodeString(strip=True) | ||||
extern_type = v.UnicodeString(strip=True) | ||||
language = v.OneOf(available_languages, hideList=False, | ||||
testValueList=True, if_missing=None) | ||||
chained_validators = [v.ValidPasswordsMatch()] | ||||
return _UserForm | ||||
r1089 | def UserGroupForm(edit=False, old_data=None, allow_disabled=False): | |||
r224 | old_data = old_data or {} | |||
r1 | class _UserGroupForm(formencode.Schema): | |||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
users_group_name = All( | ||||
v.UnicodeString(strip=True, min=1, not_empty=True), | ||||
v.ValidUserGroup(edit, old_data) | ||||
) | ||||
user_group_description = v.UnicodeString(strip=True, min=1, | ||||
not_empty=False) | ||||
users_group_active = v.StringBoolean(if_missing=False) | ||||
if edit: | ||||
r224 | # this is user group owner | |||
user = All( | ||||
v.UnicodeString(not_empty=True), | ||||
v.ValidRepoUser(allow_disabled)) | ||||
r1 | return _UserGroupForm | |||
r224 | def RepoGroupForm(edit=False, old_data=None, available_groups=None, | |||
can_create_in_root=False, allow_disabled=False): | ||||
old_data = old_data or {} | ||||
available_groups = available_groups or [] | ||||
r1 | class _RepoGroupForm(formencode.Schema): | |||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | ||||
v.SlugifyName(),) | ||||
group_description = v.UnicodeString(strip=True, min=1, | ||||
not_empty=False) | ||||
group_copy_permissions = v.StringBoolean(if_missing=False) | ||||
group_parent_id = v.OneOf(available_groups, hideList=False, | ||||
testValueList=True, not_empty=True) | ||||
enable_locking = v.StringBoolean(if_missing=False) | ||||
r224 | chained_validators = [ | |||
v.ValidRepoGroup(edit, old_data, can_create_in_root)] | ||||
r1 | ||||
if edit: | ||||
r224 | # this is repo group owner | |||
user = All( | ||||
v.UnicodeString(not_empty=True), | ||||
v.ValidRepoUser(allow_disabled)) | ||||
r1 | ||||
return _RepoGroupForm | ||||
def RegisterForm(edit=False, old_data={}): | ||||
class _RegisterForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
username = All( | ||||
v.ValidUsername(edit, old_data), | ||||
v.UnicodeString(strip=True, min=1, not_empty=True) | ||||
) | ||||
password = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=True) | ||||
) | ||||
password_confirmation = All( | ||||
v.ValidPassword(), | ||||
v.UnicodeString(strip=False, min=6, not_empty=True) | ||||
) | ||||
active = v.StringBoolean(if_missing=False) | ||||
firstname = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
lastname = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data)) | ||||
chained_validators = [v.ValidPasswordsMatch()] | ||||
return _RegisterForm | ||||
def PasswordResetForm(): | ||||
class _PasswordResetForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
email = All(v.ValidSystemEmail(), v.Email(not_empty=True)) | ||||
return _PasswordResetForm | ||||
r224 | def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None, | |||
allow_disabled=False): | ||||
r1 | old_data = old_data or {} | |||
repo_groups = repo_groups or [] | ||||
landing_revs = landing_revs or [] | ||||
supported_backends = BACKENDS.keys() | ||||
class _RepoForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | ||||
r1644 | v.SlugifyName(), v.CannotHaveGitSuffix()) | |||
r1 | repo_group = All(v.CanWriteGroup(old_data), | |||
v.OneOf(repo_groups, hideList=True)) | ||||
repo_type = v.OneOf(supported_backends, required=False, | ||||
if_missing=old_data.get('repo_type')) | ||||
repo_description = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
repo_private = v.StringBoolean(if_missing=False) | ||||
repo_landing_rev = v.OneOf(landing_revs, hideList=True) | ||||
repo_copy_permissions = v.StringBoolean(if_missing=False) | ||||
clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False)) | ||||
repo_enable_statistics = v.StringBoolean(if_missing=False) | ||||
repo_enable_downloads = v.StringBoolean(if_missing=False) | ||||
repo_enable_locking = v.StringBoolean(if_missing=False) | ||||
if edit: | ||||
# this is repo owner | ||||
r224 | user = All( | |||
v.UnicodeString(not_empty=True), | ||||
v.ValidRepoUser(allow_disabled)) | ||||
r1 | clone_uri_change = v.UnicodeString( | |||
not_empty=False, if_missing=v.Missing) | ||||
chained_validators = [v.ValidCloneUri(), | ||||
v.ValidRepoName(edit, old_data)] | ||||
return _RepoForm | ||||
def RepoPermsForm(): | ||||
class _RepoPermsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
chained_validators = [v.ValidPerms(type_='repo')] | ||||
return _RepoPermsForm | ||||
def RepoGroupPermsForm(valid_recursive_choices): | ||||
class _RepoGroupPermsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
recursive = v.OneOf(valid_recursive_choices) | ||||
chained_validators = [v.ValidPerms(type_='repo_group')] | ||||
return _RepoGroupPermsForm | ||||
def UserGroupPermsForm(): | ||||
class _UserPermsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
chained_validators = [v.ValidPerms(type_='user_group')] | ||||
return _UserPermsForm | ||||
def RepoFieldForm(): | ||||
class _RepoFieldForm(formencode.Schema): | ||||
filter_extra_fields = True | ||||
allow_extra_fields = True | ||||
new_field_key = All(v.FieldKey(), | ||||
v.UnicodeString(strip=True, min=3, not_empty=True)) | ||||
new_field_value = v.UnicodeString(not_empty=False, if_missing=u'') | ||||
new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'], | ||||
if_missing='str') | ||||
new_field_label = v.UnicodeString(not_empty=False) | ||||
new_field_desc = v.UnicodeString(not_empty=False) | ||||
return _RepoFieldForm | ||||
def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(), | ||||
repo_groups=[], landing_revs=[]): | ||||
class _RepoForkForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True), | ||||
v.SlugifyName()) | ||||
repo_group = All(v.CanWriteGroup(), | ||||
v.OneOf(repo_groups, hideList=True)) | ||||
repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends)) | ||||
description = v.UnicodeString(strip=True, min=1, not_empty=True) | ||||
private = v.StringBoolean(if_missing=False) | ||||
copy_permissions = v.StringBoolean(if_missing=False) | ||||
fork_parent_id = v.UnicodeString() | ||||
chained_validators = [v.ValidForkName(edit, old_data)] | ||||
landing_rev = v.OneOf(landing_revs, hideList=True) | ||||
return _RepoForkForm | ||||
def ApplicationSettingsForm(): | ||||
class _ApplicationSettingsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False) | ||||
rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True) | ||||
rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
r1094 | rhodecode_create_personal_repo_group = v.StringBoolean(if_missing=False) | |||
rhodecode_personal_repo_group_pattern = v.UnicodeString(strip=True, min=1, not_empty=False) | ||||
r1 | ||||
return _ApplicationSettingsForm | ||||
def ApplicationVisualisationForm(): | ||||
class _ApplicationVisualisationForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
rhodecode_show_public_icon = v.StringBoolean(if_missing=False) | ||||
rhodecode_show_private_icon = v.StringBoolean(if_missing=False) | ||||
rhodecode_stylify_metatags = v.StringBoolean(if_missing=False) | ||||
rhodecode_repository_fields = v.StringBoolean(if_missing=False) | ||||
rhodecode_lightweight_journal = v.StringBoolean(if_missing=False) | ||||
rhodecode_dashboard_items = v.Int(min=5, not_empty=True) | ||||
rhodecode_admin_grid_items = v.Int(min=5, not_empty=True) | ||||
rhodecode_show_version = v.StringBoolean(if_missing=False) | ||||
rhodecode_use_gravatar = v.StringBoolean(if_missing=False) | ||||
rhodecode_markup_renderer = v.OneOf(['markdown', 'rst']) | ||||
rhodecode_gravatar_url = v.UnicodeString(min=3) | ||||
rhodecode_clone_uri_tmpl = v.UnicodeString(min=3) | ||||
rhodecode_support_url = v.UnicodeString() | ||||
rhodecode_show_revision_number = v.StringBoolean(if_missing=False) | ||||
rhodecode_show_sha_length = v.Int(min=4, not_empty=True) | ||||
return _ApplicationVisualisationForm | ||||
class _BaseVcsSettingsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
hooks_changegroup_repo_size = v.StringBoolean(if_missing=False) | ||||
hooks_changegroup_push_logger = v.StringBoolean(if_missing=False) | ||||
hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False) | ||||
r1570 | # PR/Code-review | |||
rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False) | ||||
rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False) | ||||
# hg | ||||
r1 | extensions_largefiles = v.StringBoolean(if_missing=False) | |||
r1738 | extensions_evolve = v.StringBoolean(if_missing=False) | |||
r1 | phases_publish = v.StringBoolean(if_missing=False) | |||
Martin Bornhold
|
r358 | rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False) | ||
r1 | ||||
r1570 | # git | |||
vcs_git_lfs_enabled = v.StringBoolean(if_missing=False) | ||||
# svn | ||||
r754 | vcs_svn_proxy_http_requests_enabled = v.StringBoolean(if_missing=False) | |||
vcs_svn_proxy_http_server_url = v.UnicodeString(strip=True, if_missing=None) | ||||
r1 | ||||
def ApplicationUiSettingsForm(): | ||||
class _ApplicationUiSettingsForm(_BaseVcsSettingsForm): | ||||
web_push_ssl = v.StringBoolean(if_missing=False) | ||||
paths_root_path = All( | ||||
v.ValidPath(), | ||||
v.UnicodeString(strip=True, min=1, not_empty=True) | ||||
) | ||||
r1563 | largefiles_usercache = All( | |||
v.ValidPath(), | ||||
r1570 | v.UnicodeString(strip=True, min=2, not_empty=True)) | |||
vcs_git_lfs_store_location = All( | ||||
v.ValidPath(), | ||||
v.UnicodeString(strip=True, min=2, not_empty=True)) | ||||
r1 | extensions_hgsubversion = v.StringBoolean(if_missing=False) | |||
extensions_hggit = v.StringBoolean(if_missing=False) | ||||
new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch') | ||||
new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag') | ||||
return _ApplicationUiSettingsForm | ||||
def RepoVcsSettingsForm(repo_name): | ||||
class _RepoVcsSettingsForm(_BaseVcsSettingsForm): | ||||
inherit_global_settings = v.StringBoolean(if_missing=False) | ||||
new_svn_branch = v.ValidSvnPattern( | ||||
section='vcs_svn_branch', repo_name=repo_name) | ||||
new_svn_tag = v.ValidSvnPattern( | ||||
section='vcs_svn_tag', repo_name=repo_name) | ||||
return _RepoVcsSettingsForm | ||||
def LabsSettingsForm(): | ||||
class _LabSettingsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
return _LabSettingsForm | ||||
r1034 | def ApplicationPermissionsForm( | |||
register_choices, password_reset_choices, extern_activate_choices): | ||||
r1 | class _DefaultPermissionsForm(formencode.Schema): | |||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
anonymous = v.StringBoolean(if_missing=False) | ||||
default_register = v.OneOf(register_choices) | ||||
default_register_message = v.UnicodeString() | ||||
r1034 | default_password_reset = v.OneOf(password_reset_choices) | |||
r1 | default_extern_activate = v.OneOf(extern_activate_choices) | |||
return _DefaultPermissionsForm | ||||
def ObjectPermissionsForm(repo_perms_choices, group_perms_choices, | ||||
user_group_perms_choices): | ||||
class _ObjectPermissionsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
overwrite_default_repo = v.StringBoolean(if_missing=False) | ||||
overwrite_default_group = v.StringBoolean(if_missing=False) | ||||
overwrite_default_user_group = v.StringBoolean(if_missing=False) | ||||
default_repo_perm = v.OneOf(repo_perms_choices) | ||||
default_group_perm = v.OneOf(group_perms_choices) | ||||
default_user_group_perm = v.OneOf(user_group_perms_choices) | ||||
return _ObjectPermissionsForm | ||||
def UserPermissionsForm(create_choices, create_on_write_choices, | ||||
repo_group_create_choices, user_group_create_choices, | ||||
fork_choices, inherit_default_permissions_choices): | ||||
class _DefaultPermissionsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
anonymous = v.StringBoolean(if_missing=False) | ||||
default_repo_create = v.OneOf(create_choices) | ||||
default_repo_create_on_write = v.OneOf(create_on_write_choices) | ||||
default_user_group_create = v.OneOf(user_group_create_choices) | ||||
default_repo_group_create = v.OneOf(repo_group_create_choices) | ||||
default_fork_create = v.OneOf(fork_choices) | ||||
default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices) | ||||
return _DefaultPermissionsForm | ||||
def UserIndividualPermissionsForm(): | ||||
class _DefaultPermissionsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
inherit_default_permissions = v.StringBoolean(if_missing=False) | ||||
return _DefaultPermissionsForm | ||||
def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()): | ||||
class _DefaultsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
default_repo_type = v.OneOf(supported_backends) | ||||
default_repo_private = v.StringBoolean(if_missing=False) | ||||
default_repo_enable_statistics = v.StringBoolean(if_missing=False) | ||||
default_repo_enable_downloads = v.StringBoolean(if_missing=False) | ||||
default_repo_enable_locking = v.StringBoolean(if_missing=False) | ||||
return _DefaultsForm | ||||
def AuthSettingsForm(): | ||||
class _AuthSettingsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
auth_plugins = All(v.ValidAuthPlugins(), | ||||
v.UniqueListFromString()(not_empty=True)) | ||||
return _AuthSettingsForm | ||||
def UserExtraEmailForm(): | ||||
class _UserExtraEmailForm(formencode.Schema): | ||||
email = All(v.UniqSystemEmail(), v.Email(not_empty=True)) | ||||
return _UserExtraEmailForm | ||||
def UserExtraIpForm(): | ||||
class _UserExtraIpForm(formencode.Schema): | ||||
ip = v.ValidIp()(not_empty=True) | ||||
return _UserExtraIpForm | ||||
r873 | ||||
r1 | def PullRequestForm(repo_id): | |||
r873 | class ReviewerForm(formencode.Schema): | |||
user_id = v.Int(not_empty=True) | ||||
reasons = All() | ||||
r1769 | mandatory = v.StringBoolean() | |||
r873 | ||||
r1 | class _PullRequestForm(formencode.Schema): | |||
allow_extra_fields = True | ||||
filter_extra_fields = True | ||||
r1769 | common_ancestor = v.UnicodeString(strip=True, required=True) | |||
r1 | source_repo = v.UnicodeString(strip=True, required=True) | |||
source_ref = v.UnicodeString(strip=True, required=True) | ||||
target_repo = v.UnicodeString(strip=True, required=True) | ||||
target_ref = v.UnicodeString(strip=True, required=True) | ||||
revisions = All(#v.NotReviewedRevisions(repo_id)(), | ||||
v.UniqueList()(not_empty=True)) | ||||
r873 | review_members = formencode.ForEach(ReviewerForm()) | |||
r1 | pullrequest_title = v.UnicodeString(strip=True, required=True) | |||
pullrequest_desc = v.UnicodeString(strip=True, required=False) | ||||
return _PullRequestForm | ||||
def IssueTrackerPatternsForm(): | ||||
class _IssueTrackerPatternsForm(formencode.Schema): | ||||
allow_extra_fields = True | ||||
filter_extra_fields = False | ||||
chained_validators = [v.ValidPattern()] | ||||
return _IssueTrackerPatternsForm | ||||