Example #1
0
 def update_actions(self):
     """ Prepare form actions, this method should be called directly.
     ``Form.update`` calls this method during initialization."""
     self.actions = Actions(self, self.request)
     self.actions.update()
Example #2
0
class Form(object):
    """ A form

    ``id``: Form id

    ``name``: Form name

    ``label``: Form label

    ``description``: Form description

    ``prefix``: Form prefix, it used for html elements `id` generations.

    ``fields``: Form fields :py:class:`pform.Fieldset`

    ``buttons``: Form buttons :py:class:`pform.Buttons`

    ``actions``: Instance of :py:class:`pform.Actions` class

    ``widgets``: Instance of :py:class:`FormWidgets` class

    ``content``: Form content, it should be `None` or dictionary with
    data for fields.

    ``params``: Form request parameters

    ``action``: Form action, by default ``request.url``

    ``method``: HTML Form method (`post`, `get`)

    ``csrf``: Enable/disable form csrf protection

    ``csrf_name``: Form csrf field name

    ``csrf_token``: Form csrf token value
    """

    label = None
    description = ''
    prefix = 'form.'

    actions = None
    widgets = None

    buttons = None
    fields = Fieldset()

    content = None

    method = 'post'
    enctype = 'multipart/form-data'
    accept = None
    accept_charset = 'utf-8'
    params = None
    context = None
    klass = 'form-horizontal'

    csrf = False
    csrf_name = 'csrf-token'
    csrf_token = ''

    tmpl_view = 'form:form'
    tmpl_actions = 'form:form-actions'
    tmpl_widget = 'form:widget'

    __name__ = ''
    __parent__ = None
    __view_mapper__ = FormViewMapper

    def __init__(self, context, request, **kw):
        self.__dict__.update(kw)

        self.context = context
        self.request = request
        self.__parent__ = context

        if self.buttons is None:
            self.buttons = Buttons()

        # convert fields to Fieldset
        if not isinstance(self.fields, Fieldset):
            self.fields = Fieldset(*self.fields)

        # set tmpl_widget
        for fieldset in self.fields.fieldsets():
            for field in fieldset.fields():
                if field.cls.tmpl_widget is None:
                    field.cls.tmpl_widget = self.tmpl_widget

    @reify
    def id(self):
        return self.name.replace('.', '-')

    @reify
    def name(self):
        return self.prefix.strip('.')

    @reify
    def action(self):
        return self.request.url

    @reify
    def csrf_token(self):
        return self.request.session.get_csrf_token()

    def form_content(self):
        """ Return form content.
        By default it returns ``Form.content`` attribute. """
        return self.content

    def form_params(self):
        """ get form request params """
        if self.params is not None:
            if not isinstance(self.params, MultiDict):
                return MultiDict(self.params)
            return self.params

        if self.method == 'post':
            return self.request.POST
        elif self.method == 'get':
            return self.request.GET
        else:
            return self.params

    def update_widgets(self):
        """ prepare form widgets """
        self.widgets = FormWidgets(self.fields, self, self.request)
        self.widgets.update()

    def update_actions(self):
        """ Prepare form actions, this method should be called directly.
        ``Form.update`` calls this method during initialization."""
        self.actions = Actions(self, self.request)
        self.actions.update()

    def update_form(self, data=None):
        """ update form """
        if not self.content and data:
            self.content = data

        self.update_widgets()
        self.update_actions()

        ac_result = self.actions.execute()
        if IResponse.providedBy(ac_result):
            raise HTTPResponseIsReady(ac_result)

        result = self.update()
        if IResponse.providedBy(result):
            raise HTTPResponseIsReady(result)

        if result is None:
            result = {}

        if ac_result is not None:
            result.update(ac_result)

        return result

    def update(self):
        """ Update form """
        return {}

    def render(self):
        """ render form """
        return render(self.request, self.tmpl_view, self,
                      actions = self.actions,
                      widgets = self.widgets)

    def validate(self, data, errors):
        """ Custom form validation """

    def validate_form(self, data, errors):
        """ Form validation """
        self.validate_csrf_token()
        try:
            self.validate(data, errors)
        except Invalid as err:
            errors.append(err)

    def validate_csrf_token(self):
        """ csrf token validation """
        if self.csrf:
            token = self.form_params().get(self.csrf_name, None)
            if token is not None:
                if self.csrf_token == token:
                    return

            raise HTTPForbidden("Form authenticator is not found.")

    def extract(self):
        """ extract form values """
        return self.widgets.extract()

    def add_error_message(self, msg):
        """ add form error message """
        add_message(self.request, msg, 'form:error')

    def __call__(self):
        """ update form and render form to response """
        try:
            result = self.update_form()
        except HTTPResponseIsReady as result:
            return result.args[0]
        except HTTPException as result:
            return result

        response = self.request.registry.queryAdapterOrSelf(result, IResponse)
        if response is not None:
            return response

        body = self.render()

        response = self.request.response
        if isinstance(body, bytes):
            response.body = body
        else:
            response.text = body
        return response