<commit_before># This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico 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; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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 Indico; if not, see <http://www.gnu.org/licenses/>.

import weakref

from flask import request, session, flash, g
from flask_wtf import Form
from wtforms import ValidationError
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from wtforms.fields.core import FieldList
from wtforms.form import FormMeta
from wtforms.widgets.core import HiddenInput

from indico.core import signals
from indico.core.auth import multipass
from indico.util.i18n import _
from indico.util.signals import values_from_signal
from indico.util.string import strip_whitespace


class _DataWrapper(object):
    """Wrapper for the return value of generated_data properties"""
    def __init__(self, data):
        self.data = data

    def __repr__(self):
        return '<DataWrapper({!r})>'.format(self.data)


class generated_data(property):
    """property decorator for generated data in forms"""

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return _DataWrapper(self.fget(obj))


class IndicoFormMeta(FormMeta):
    def __call__(cls, *args, **kwargs):
        # If we are instantiating a form that was just extended, don't
        # send the signal again - it's pointless to extend the extended
        # form and doing so could actually result in infinite recursion
        # if the signal receiver didn't specify a sender.
        if kwargs.pop('__extended', False):
            return super(IndicoFormMeta, cls).__call__(*args, **kwargs)
        extra_fields = values_from_signal(signals.add_form_fields.send(cls))
        # If there are no extra fields, we don't need any custom logic
        # and simply create an instance of the original form.
        if not extra_fields:
            return super(IndicoFormMeta, cls).__call__(*args, **kwargs)
        kwargs['__extended'] = True
        ext_cls = type(b'_Extended' + cls.__name__, (cls,), {})
        for name, field in extra_fields:
            name = 'ext__' + name
            if hasattr(ext_cls, name):
                raise RuntimeError(u'Preference collision in {}: {}'.format(cls.__name__, name))
            setattr(ext_cls, name, field)
        return ext_cls(*args, **kwargs)


class IndicoForm(Form):
    __metaclass__ = IndicoFormMeta

    class Meta:
        def bind_field(self, form, unbound_field, options):
            # We don't set default filters for query-based fields as it breaks them if no query_factory is set
            # while the Form is instantiated. Also, it's quite pointless for those fields...
            # FieldList simply doesn't support filters.
            no_filter_fields = (QuerySelectField, FieldList)
            filters = [strip_whitespace] if not issubclass(unbound_field.field_class, no_filter_fields) else []
            filters += unbound_field.kwargs.get('filters', [])
            bound = unbound_field.bind(form=form, filters=filters, **options)
            bound.get_form = weakref.ref(form)  # GC won't collect the form if we don't use a weakref
            return bound

    def generate_csrf_token(self, csrf_context=None):
        if not self.csrf_enabled:
            return None
        return session.csrf_token

    def validate_csrf_token(self, field):
        if self.csrf_enabled and field.data != session.csrf_token:
            if not g.get('flashed_csrf_message'):
                # Only flash the message once per request. We may end up in here
                # multiple times if `validate()` is called more than once
                flash(_(u'It looks like there was a problem with your current session. Please submit the form again.'),
                      'error')
                g.flashed_csrf_message = True
            raise ValidationError(_(u'CSRF token missing'))

    def validate(self):
        valid = super(IndicoForm, self).validate()
        return valid and all(values_from_signal(signals.form_validated.send(self), single_value=True))

    def populate_obj(self, obj, fields=None, skip=None, existing_only=False):
        """Populates the given object with form data.

        If `fields` is set, only fields from that list are populated.<commit_msg>Add IndicoForm.post_validate callback<commit_after># This file is part of Indico.
# Copyright (C) 2002 - 2015 European Organization for Nuclear Research (CERN).
#
# Indico 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; either version 3 of the
# License, or (at your option) any later version.
#
# Indico 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 Indico; if not, see <http://www.gnu.org/licenses/>.

import weakref

from flask import request, session, flash, g
from flask_wtf import Form
from wtforms import ValidationError
from wtforms.ext.sqlalchemy.fields import QuerySelectField
from wtforms.fields.core import FieldList
from wtforms.form import FormMeta
from wtforms.widgets.core import HiddenInput

from indico.core import signals
from indico.core.auth import multipass
from indico.util.i18n import _
from indico.util.signals import values_from_signal
from indico.util.string import strip_whitespace


class _DataWrapper(object):
    """Wrapper for the return value of generated_data properties"""
    def __init__(self, data):
        self.data = data

    def __repr__(self):
        return '<DataWrapper({!r})>'.format(self.data)


class generated_data(property):
    """property decorator for generated data in forms"""

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return _DataWrapper(self.fget(obj))


class IndicoFormMeta(FormMeta):
    def __call__(cls, *args, **kwargs):
        # If we are instantiating a form that was just extended, don't
        # send the signal again - it's pointless to extend the extended
        # form and doing so could actually result in infinite recursion
        # if the signal receiver didn't specify a sender.
        if kwargs.pop('__extended', False):
            return super(IndicoFormMeta, cls).__call__(*args, **kwargs)
        extra_fields = values_from_signal(signals.add_form_fields.send(cls))
        # If there are no extra fields, we don't need any custom logic
        # and simply create an instance of the original form.
        if not extra_fields:
            return super(IndicoFormMeta, cls).__call__(*args, **kwargs)
        kwargs['__extended'] = True
        ext_cls = type(b'_Extended' + cls.__name__, (cls,), {})
        for name, field in extra_fields:
            name = 'ext__' + name
            if hasattr(ext_cls, name):
                raise RuntimeError(u'Preference collision in {}: {}'.format(cls.__name__, name))
            setattr(ext_cls, name, field)
        return ext_cls(*args, **kwargs)


class IndicoForm(Form):
    __metaclass__ = IndicoFormMeta

    class Meta:
        def bind_field(self, form, unbound_field, options):
            # We don't set default filters for query-based fields as it breaks them if no query_factory is set
            # while the Form is instantiated. Also, it's quite pointless for those fields...
            # FieldList simply doesn't support filters.
            no_filter_fields = (QuerySelectField, FieldList)
            filters = [strip_whitespace] if not issubclass(unbound_field.field_class, no_filter_fields) else []
            filters += unbound_field.kwargs.get('filters', [])
            bound = unbound_field.bind(form=form, filters=filters, **options)
            bound.get_form = weakref.ref(form)  # GC won't collect the form if we don't use a weakref
            return bound

    def generate_csrf_token(self, csrf_context=None):
        if not self.csrf_enabled:
            return None
        return session.csrf_token

    def validate_csrf_token(self, field):
        if self.csrf_enabled and field.data != session.csrf_token:
            if not g.get('flashed_csrf_message'):
                # Only flash the message once per request. We may end up in here
                # multiple times if `validate()` is called more than once
                flash(_(u'It looks like there was a problem with your current session. Please submit the form again.'),
                      'error')
                g.flashed_csrf_message = True
            raise ValidationError(_(u'CSRF token missing'))

    def validate(self):
        valid = super(IndicoForm, self).validate()
        if not valid:
            return False
        if not all(values_from_signal(signals.form_validated.send(self), single_value=True)):
            return False
        self.post_validate()
        return True

    def post_validate(self):
        """Called after the form was successfully validated.

        This method is a good place e.g. to override the data of fields in
        certain cases without going through the hassle of generated_data.
        """

    def populate_obj(self, obj, fields=None, skip=None, existing_only=False):
        """Populates the given object with form data.

        If `fields` is set, only fields from that list are populated.
{'fraction_non_alphanumeric': 0.07898600371929138, 'fraction_numerical': 0.0021532739551727515, 'mean_word_length': 2.9406093328191285, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6780805', 'n_tokens_mistral': 2819, 'n_tokens_neox': 2697, 'n_words': 1040}