webform.py :  » Web-Frameworks » Nevow » Nevow-0.10.0 » formless » Python Open Source

Home
Python Open Source
1.3.1.2 Python
2.Ajax
3.Aspect Oriented
4.Blog
5.Build
6.Business Application
7.Chart Report
8.Content Management Systems
9.Cryptographic
10.Database
11.Development
12.Editor
13.Email
14.ERP
15.Game 2D 3D
16.GIS
17.GUI
18.IDE
19.Installer
20.IRC
21.Issue Tracker
22.Language Interface
23.Log
24.Math
25.Media Sound Audio
26.Mobile
27.Network
28.Parser
29.PDF
30.Project Management
31.RSS
32.Search
33.Security
34.Template Engines
35.Test
36.UML
37.USB Serial
38.Web Frameworks
39.Web Server
40.Web Services
41.Web Unit
42.Wiki
43.Windows
44.XML
Python Open Source » Web Frameworks » Nevow 
Nevow » Nevow 0.10.0 » formless » webform.py
# -*- test-case-name: formless.test.test_freeform -*-

# Copyright (c) 2004 Divmod.
# See LICENSE for details.


from __future__ import generators

import warnings
from zope.interface import implements,Interface

from twisted.python import components

from nevow import inevow
from nevow.stan import slot
from nevow import tags
from nevow import util
from nevow.context import NodeNotFound

from formless import iformless
from formless.formutils import FormDefaults,FormErrors,calculatePostURL,keyToXMLID,getError


from nevow.static import File

defaultCSS = File(util.resource_filename('formless', 'freeform-default.css'), 'text/css')


class DefaultRenderer(object):
    implements(inevow.IRenderer, iformless.ITypedRenderer)
    complexType = False
    def rend(self, context, data):
        return StringRenderer(data)

defaultBindingRenderer = DefaultRenderer()


class BaseInputRenderer(components.Adapter):
    implements(inevow.IRenderer, iformless.ITypedRenderer)
    complexType = False
    def rend(self, context, data):
        defaults = context.locate(iformless.IFormDefaults)
        value = defaults.getDefault(context.key, context)
        context.remember(data.typedValue, iformless.ITyped)

        if data.typedValue.getAttribute('immutable'):
            inp = tags.span(id=keyToXMLID(context.key))[value]
        else:
            ##value may be a deferred; make sure to wait on it properly before calling self.input
            ## TODO: If flattening this results in an empty string, return an empty string
            inp = tags.invisible(
                render=lambda c, value: self.input( context, tags.invisible(), data, data.name, value ),
                data=value)

        if data.typedValue.getAttribute('hidden') or data.typedValue.getAttribute('compact'):
            return inp

        context.fillSlots( 'label', data.label )
        context.fillSlots( 'name', data.name )
        context.fillSlots( 'input', inp )
        context.fillSlots( 'error', getError(context) )
        context.fillSlots( 'description', data.description )
        context.fillSlots( 'id', keyToXMLID(context.key) )
        context.fillSlots( 'value', value )

        return context.tag

    def input(self, context, slot, data, name, value):
        raise NotImplementedError, "Implement in subclass"

class PasswordRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        return [
            tags.input(id=keyToXMLID(context.key), name=name, type="password", _class="freeform-input-password"),
            " Again ",
            tags.input(name="%s____2" % name, type="password", _class="freeform-input-password"),
        ]


class PasswordEntryRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        return slot[
            tags.input(id=keyToXMLID(context.key), type='password', name=name,
                  _class='freeform-input-password')]


class StringRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        if data.typedValue.getAttribute('hidden'):
            T="hidden"
        else:
            T="text"
        return slot[
            tags.input(id=keyToXMLID(context.key), type=T, name=name, value=value,
                  _class='freeform-input-%s' % T)]


class TextRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        return slot[
            tags.textarea(id=keyToXMLID(context.key), name=name, _class="freeform-textarea", rows=8, cols=40)[
                value or '']]


class BooleanRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        ## The only difference here is the "checked" attribute; the value is still the same because
        ## we want true to be passed to the server when the checkbox is checked and the form
        ## is posted.
        node = tags.input(id=keyToXMLID(context.key), type="checkbox", name=name, value='True', _class="freeform-input-checkbox")
        if value:
            node(checked="checked")

        # HTML forms are so weak. If the checkbox is not checked, no value at all will be
        # in request.args with the name data.name. So let's force the value False to always
        # be in request.args[data.name]. If the checkbox is checked, the value True will
        # be first, and we will find that.
        return slot[node, tags.input(type="hidden", name=name, value="False")]


class FileUploadRenderer(BaseInputRenderer):
    def input(self, context, slot, data, name, value):
        return slot[tags.input(id=keyToXMLID(context.key), type="file", name=name,
                          _class='freeform-input-file')]


class ICurrentlySelectedValue(Interface):
    """The currently-selected-value for the ITypedRenderer being rendered.
    """


csv = ICurrentlySelectedValue
def valToKey(c, d):
    return iformless.ITyped(c).valueToKey(d)


def isSelected(c, d):
    if csv(c) == valToKey(c, d):
        return c.tag(selected='selected')
    return c.tag

    
def isChecked(c, d):
    if csv(c) == valToKey(c, d):
        return c.tag(checked='checked')
    return c.tag


class ChoiceRenderer(BaseInputRenderer):
    default_select = tags.select(id=slot('id'), name=slot('name'), render=tags.directive('sequence'))[
        tags.option(pattern="item", 
            value=valToKey, 
            render=isSelected)[
            lambda c, d: iformless.ITyped(c).stringify(d)]]

    def input(self, context, slot, data, name, value):
        tv = data.typedValue
        choices = tv.choices

        if value:
            context.remember(value, csv)
        else:
            context.remember('', csv)

        try:
            selector = context.tag.patternGenerator( 'selector' )
        except NodeNotFound:
            selector = self.default_select

        return selector(data=choices)


class RadioRenderer(ChoiceRenderer):
    default_select = tags.span(id=slot('id'), render=tags.directive('sequence'))[
        tags.div(pattern="item", _class="freeform-radio-option")[
            tags.input(type="radio", name=slot('name'), value=valToKey, render=isChecked)[
                lambda c, d: iformless.ITyped(c).stringify(d)]]]


class ObjectRenderer(components.Adapter):
    implements(inevow.IRenderer, iformless.ITypedRenderer)
    complexType = True
    def rend(self, context, data):
        configurable = context.locate(iformless.IConfigurable)
        return getattr(configurable, data.name)

class NullRenderer(components.Adapter):
    """Use a NullRenderer as the ITypedRenderer adapter when nothing should
    be included in the output.
    """
    implements(inevow.IRenderer, iformless.ITypedRenderer)
    def rend(self, context, data):
        return ''


class GroupBindingRenderer(components.Adapter):
    implements(inevow.IRenderer)

    def rend(self, context, data):
        context.remember(data, iformless.IBinding)

        from formless import configurable

        configurable = conf.GroupConfigurable(data.boundTo, data.typedValue.iface)
        context.remember(configurable, iformless.IConfigurable)

        bindingNames = configurable.getBindingNames(context)

        def generateBindings():
            for name in bindingNames:
                bnd = configurable.getBinding(context, name)
                renderer = iformless.IBindingRenderer(bnd, defaultBindingRenderer)
                renderer.isGrouped = True
                renderer.needsSkin = True
                yield tags.invisible(
                    data=bnd,
                    render=renderer,
                    key=name)

        return getError(context), tags.form(
            id=keyToXMLID(context.key),
            enctype="multipart/form-data",
            action=calculatePostURL(context, data),
            method="post",
            **{'accept-charset':'utf-8'})[
                tags.fieldset[
                    tags.legend(_class="freeform-form-label")[data.label],
                    tags.input(type='hidden', name='_charset_'),
                    generateBindings(),
                    tags.input(type="submit")]]


class BaseBindingRenderer(components.Adapter):
    implements(inevow.IRenderer)

    isGrouped = False
    needsSkin = False
    def calculateDefaultSkin(self, context):
        if self.isGrouped:
            frm = tags.invisible
            butt = ''
            fld = tags.invisible
        else:
            frm = tags.form(
                id=slot('form-id'),
                name=slot('form-id'),
                action=slot('form-action'),
                method="post",
                enctype="multipart/form-data",
                **{'accept-charset':'utf-8'}
                )
            butt = slot('form-button')
            fld = tags.fieldset[tags.input(type='hidden', name='_charset_')]

        ## Provide default skin since no skin was provided for us.
        context.tag.clear()[
            frm[fld[tags.legend(_class="freeform-form-label")[ slot('form-label') ],
                    tags.div(_class="freeform-form-description")[slot('form-description')],
                    tags.div(_class="freeform-form-error")[ slot('form-error') ],

                    slot('form-arguments'), butt ]]]

    def fillForm(self, context, data):
        context.fillSlots( 'form-id', keyToXMLID(context.key) )
        context.fillSlots( 'form-action', calculatePostURL(context, data) )
        context.fillSlots( 'form-name', data.name )
        context.fillSlots( 'form-error', getError(context) )


class PropertyBindingRenderer(BaseBindingRenderer):
    def rend(self, context, data):
        context.remember(data, iformless.IBinding)
        context.remember(data.typedValue, iformless.ITyped)
        typedRenderer = iformless.ITypedRenderer(data.typedValue, defaultBindingRenderer)
        if typedRenderer.complexType:
            return tags.invisible(data=data, render=typedRenderer)

        if self.needsSkin or not context.tag.children:
            self.calculateDefaultSkin(context)

        if self.isGrouped or data.typedValue.getAttribute('immutable'):
            subm = ''
        else:
            subm = tags.input(type="submit", name="change", value="Change")

        self.fillForm(context, data)
        context.fillSlots( 'form-label', '' )
        context.fillSlots( 'form-description', '' )

        try:
            content_pattern = context.tag.patternGenerator( 'binding' )
        except NodeNotFound:
            content_pattern = freeformDefaultContentPattern

        context.fillSlots(
            'form-arguments',
            content_pattern(
                data=data, render=typedRenderer, key=data.name))

        context.fillSlots('form-button', subm)

        return context.tag


freeformDefaultContentPattern = tags.invisible[
    tags.label(_class="freeform-label", _for=slot('id'))[ slot('label') ],
    tags.span(_class="freeform-input")[ slot('input') ],
    tags.div(_class="freeform-error")[ slot('error') ],
    tags.div(_class="freeform-description")[tags.label(_for=slot('id'))[ slot('description') ]]].freeze()


class MethodBindingRenderer(BaseBindingRenderer):
    def rend(self, context, data):
        if data.getAttribute('invisible'):
            return ''

        context.remember(data, iformless.IBinding)

        if self.needsSkin or not context.tag.children:
            self.calculateDefaultSkin(context)

        self.fillForm(context, data)
        context.fillSlots( 'form-label', data.label )
        context.fillSlots( 'form-description', data.description )
        context.fillSlots( 'form-arguments', list(self.generateArguments(context, data.getArgs())))

        if not self.isGrouped:
            try:
                button_pattern = context.tag.onePattern( 'form-button' )
            except NodeNotFound:
                button_pattern = tags.invisible[ slot('input') ]

            button_pattern.fillSlots( 'input', tags.input(type='submit', value=data.action or data.label, name=data.name, class_="freeform-button") )

            context.fillSlots( 'form-button', button_pattern )

        return context.tag(key=None)

    def generateArguments(self, context, args):
        default_content_pattern = None
        content_pattern = None
        for argument in args:
            try:
                content_pattern = context.tag.patternGenerator( 'argument!!%s' % argument.name )
            except NodeNotFound:
                if default_content_pattern is None:
                    try:
                        default_content_pattern = context.tag.patternGenerator( 'argument' )
                    except NodeNotFound:
                        default_content_pattern = freeformDefaultContentPattern
                content_pattern = default_content_pattern
            renderer = iformless.ITypedRenderer(
                argument.typedValue, defaultBindingRenderer)
            pat = content_pattern(
                key=argument.name,
                data=argument,
                render=renderer,
                remember={iformless.ITyped: argument.typedValue})
            context.fillSlots( 'argument!!%s' % argument.name, pat )
            yield pat


class ButtonRenderer(components.Adapter):
    implements(inevow.IRenderer)

    def rend(self, context, data):
        return tags.input(id=keyToXMLID(context.key), type='submit', value=data.label, name=data.name, class_="freeform-button")


freeformDefaultForm = tags.div(_class="freeform-form").freeze()


def renderForms(configurableKey='', bindingNames=None, bindingDefaults=None):
    """Render forms for either the named configurable, or, if no configurableKey is given,
    the main configurable. If no bindingNames are given, forms will be
    rendered for all bindings described by the configurable.

    @param configurableKey: The name of the configurable to render.  The empty
    string indicates ctx.locate(IRenderer).

    @param bindingNames: The names of the bindings to render.  None indicates
    all bindings.

    @param bindingDefaults: A dict mapping bindingName: bindingDefault.  For example,
    given the TypedInterface::

            >>> class IMyForm(annotate.TypedInterface):
            ...     def doSomething(self, name=annotate.String()):
            ...         pass
            ...     doSomething = annotate.autocallable(doSomething)
            ...     def doNothing(self name=annotate.String()):
            ...         pass
            ...     doNothing = annotate.autocallable(doNothing)
            ...     def doMoreThings(self name=annotate.String(), things=annotate.String()):
            ...         pass
            ...     doMoreThings = annotate.autocallable(doMoreThings)

        One might call renderForms() like this::

            return webform.renderForms(
                '',
                bindingDefaults={'doSomething': {'name': 'jimbo'},
                                 # Change 'name' default, don't change 'things'
                                 'doMoreThings': {'things': 'jimbo'}
                                 })

        This would cause a form to be rendered which will call doSomething when
        submitted, and would have "jimbo" filled out as the default value for
        the name field, as well as a form which will call doMoreThings (with no
        default value filled in for 'name' but 'jimbo' filled in for 'things').
    """

    assert bindingNames is None or bindingDefaults is None, "Only specify bindingNames or bindingDefaults"

    if bindingNames is not None:
        bindingDefaults = dict.fromkeys(bindingNames, {})

    def formRenderer(ctx, data):
        cf = ctx.locate(iformless.IConfigurableFactory)
        return util.maybeDeferred(cf.locateConfigurable, ctx, configurableKey
                                  ).addCallback(_formRenderIt)

    def _formRenderIt(configurable):
        def _innerFormRenderIt(context, data):
            tag = context.tag
            # Remember the key for when the form post URL is generated.
            context.remember(configurableKey, iformless.IConfigurableKey)
            if configurable is None:
                warnings.warn(
                    "No configurable was found which provides enough type information for freeform to be able to render forms")
                yield ''
                return
            context.remember(configurable, iformless.IConfigurable)

            formDefaults = iformless.IFormDefaults(context)

            if bindingDefaults is None:
                available = configurable.getBindingNames(context)
            else:
                available = bindingDefaults.iterkeys()

            def _callback(binding):
                renderer = iformless.IBindingRenderer(binding, defaultBindingRenderer)
                try:
                    binding_pattern = tag.patternGenerator( 'freeform-form!!%s' % name )
                except NodeNotFound:
                    try:
                        binding_pattern = tag.patternGenerator( 'freeform-form' )
                    except NodeNotFound:
                        binding_pattern = freeformDefaultForm
                        
                if binding_pattern is freeformDefaultForm:
                    renderer.needsSkin = True
                return binding_pattern(data=binding, render=renderer, key=name)

            for name in available:
                if bindingDefaults is not None:
                    defs = formDefaults.getAllDefaults(name)
                    defs.update(bindingDefaults[name])

                d = util.maybeDeferred(configurable.getBinding, context, name)
                d.addCallback(_callback)
                yield d

        return _innerFormRenderIt
    return tags.invisible(render=formRenderer)

www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.