1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/MoinForms.py Thu Nov 29 00:53:51 2012 +0100
1.3 @@ -0,0 +1,145 @@
1.4 +# -*- coding: iso-8859-1 -*-
1.5 +"""
1.6 + MoinMoin - MoinForms library
1.7 +
1.8 + @copyright: 2012 by Paul Boddie <paul@boddie.org.uk>
1.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.10 +"""
1.11 +
1.12 +from MoinMoin import wikiutil
1.13 +from StringIO import StringIO
1.14 +from MoinSupport import *
1.15 +import re
1.16 +
1.17 +__version__ = "0.1"
1.18 +
1.19 +# Common formatting functions.
1.20 +
1.21 +def formatForm(text, request, fmt, attrs=None, write=None):
1.22 +
1.23 + """
1.24 + Format the given 'text' using the specified 'request' and formatter 'fmt'.
1.25 + The optional 'attrs' can be used to control the presentation of the form.
1.26 +
1.27 + If the 'write' parameter is specified, use it to write output; otherwise,
1.28 + write output using the request.
1.29 + """
1.30 +
1.31 + write = write or request.write
1.32 + page = request.page
1.33 +
1.34 + fields = getFields(get_form(request))
1.35 +
1.36 + querystr = attrs and attrs.has_key("action") and ("action=%s" % attrs["action"]) or None
1.37 +
1.38 + write(fmt.rawHTML('<form method="post" action="%s">' %
1.39 + escattr(page.url(request, querystr))
1.40 + ))
1.41 +
1.42 + output = getFormOutput(text, fields)
1.43 + write(formatText(output, request, fmt))
1.44 +
1.45 + write(fmt.rawHTML('</form>'))
1.46 +
1.47 +def getFormOutput(text, fields):
1.48 +
1.49 + """
1.50 + Combine regions found in the given 'text' and then return them as a single
1.51 + block. The reason for doing this, as opposed to just passing each region to
1.52 + a suitable parser for formatting, is that form sections may break up
1.53 + regions, and such sections may not define separate subregions but instead
1.54 + act as a means of conditional inclusion of text into an outer region.
1.55 +
1.56 + The given 'fields' are used to populate fields provided in forms and to
1.57 + control whether sections are populated or not.
1.58 + """
1.59 +
1.60 + output = []
1.61 + section = fields
1.62 +
1.63 + for region in getRegions(text, True):
1.64 + format, attributes, body, header, close = getFragmentFromRegion(region)
1.65 +
1.66 + # NOTE: Need to adjust FormField macros to use hierarchical names.
1.67 +
1.68 + # Include bare regions as they are.
1.69 +
1.70 + if format is None:
1.71 + output.append(region)
1.72 +
1.73 + # Include form sections only if fields exist for those sections.
1.74 +
1.75 + elif format == "form":
1.76 + section_name = attributes.get("section")
1.77 + if section_name and section.has_key(section_name):
1.78 + output.append(header)
1.79 + output.append(getFormOutput(body, section[section_name]))
1.80 + output.append(close)
1.81 +
1.82 + # Inspect and include other regions.
1.83 +
1.84 + else:
1.85 + output.append(header)
1.86 + output.append(getFormOutput(body, section))
1.87 + output.append(close)
1.88 +
1.89 + return "".join(output)
1.90 +
1.91 +def getFields(d):
1.92 +
1.93 + """
1.94 + Return the form fields hierarchy for the given dictionary 'd'.
1.95 + """
1.96 +
1.97 + fields = {}
1.98 +
1.99 + for key, value in d.items():
1.100 +
1.101 + # Reproduce the original hierarchy of the fields.
1.102 +
1.103 + section = fields
1.104 + parts = key.split("/")
1.105 +
1.106 + for part in parts[:-1]:
1.107 + try:
1.108 + name, index = part.split("$", 1)
1.109 + index = int(index)
1.110 + except ValueError:
1.111 + name, index = part, None
1.112 +
1.113 + if not section.has_key(name):
1.114 + section[name] = {}
1.115 +
1.116 + if not section[name].has_key(index):
1.117 + section[name][index] = {}
1.118 +
1.119 + section = section[name][index]
1.120 +
1.121 + section[parts[-1]] = value
1.122 +
1.123 + return fields
1.124 +
1.125 +def formatFormForOutputType(text, request, mimetype, attrs=None, write=None):
1.126 +
1.127 + """
1.128 + Format the given 'text' using the specified 'request' for the given output
1.129 + 'mimetype'.
1.130 +
1.131 + The optional 'attrs' can be used to control the presentation of the form.
1.132 +
1.133 + If the 'write' parameter is specified, use it to write output; otherwise,
1.134 + write output using the request.
1.135 + """
1.136 +
1.137 + write = write or request.write
1.138 +
1.139 + if mimetype == "text/html":
1.140 + write('<html>')
1.141 + write('<body>')
1.142 + fmt = request.html_formatter
1.143 + fmt.setPage(request.page)
1.144 + formatForm(text, request, fmt, attrs, write)
1.145 + write('</body>')
1.146 + write('</html>')
1.147 +
1.148 +# vim: tabstop=4 expandtab shiftwidth=4
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/macros/FormField.py Thu Nov 29 00:53:51 2012 +0100
2.3 @@ -0,0 +1,147 @@
2.4 +# -*- coding: iso-8859-1 -*-
2.5 +"""
2.6 + MoinMoin - FormField Macro
2.7 +
2.8 + @copyright: 2012 by Paul Boddie <paul@boddie.org.uk>
2.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
2.10 +"""
2.11 +
2.12 +from MoinMoin import wikiutil
2.13 +from MoinSupport import *
2.14 +
2.15 +Dependencies = ['pages']
2.16 +
2.17 +escape = wikiutil.escape
2.18 +
2.19 +# Macro functions.
2.20 +
2.21 +def execute(macro, args):
2.22 +
2.23 + """
2.24 + Execute the 'macro' with the given 'args' to produce a form field element:
2.25 +
2.26 + * A field name
2.27 + * The WikiDict describing form fields
2.28 +
2.29 + The following optional named arguments are also supported:
2.30 +
2.31 + label=TEXT The label employed by button-like fields
2.32 +
2.33 + The nature of each field is described by a WikiDict entry for the given
2.34 + field name.
2.35 + """
2.36 +
2.37 + request = macro.request
2.38 + fmt = macro.formatter
2.39 + page = fmt.page
2.40 + _ = request.getText
2.41 +
2.42 + # Interpret the arguments.
2.43 + # NOTE: The argument parsing should really be more powerful in order to
2.44 + # NOTE: support labels.
2.45 +
2.46 + try:
2.47 + parsed_args = args and wikiutil.parse_quoted_separated(args, name_value=False) or []
2.48 + except AttributeError:
2.49 + parsed_args = args.split(",")
2.50 +
2.51 + parsed_args = [arg for arg in parsed_args if arg]
2.52 +
2.53 + # Get special arguments.
2.54 +
2.55 + name = None
2.56 + dictpage = None
2.57 + label = None
2.58 +
2.59 + for arg in parsed_args:
2.60 + if arg.startswith("name="):
2.61 + name = arg[5:]
2.62 +
2.63 + elif arg.startswith("dict="):
2.64 + dictpage = arg[5:]
2.65 +
2.66 + elif arg.startswith("label="):
2.67 + label = arg[6:]
2.68 +
2.69 + elif name is None:
2.70 + name = arg
2.71 +
2.72 + elif dictpage is None:
2.73 + dictpage = arg
2.74 +
2.75 + if not name:
2.76 + return showError(_("No field name specified."), request)
2.77 +
2.78 + if not dictpage:
2.79 + return showError(_("No WikiDict specified."), request)
2.80 +
2.81 + # Get the WikiDict and the field's definition.
2.82 +
2.83 + wikidict = getWikiDict(dictpage, request)
2.84 +
2.85 + if not wikidict:
2.86 + return showError(_("WikiDict %s cannot be loaded.") % dictpage, request)
2.87 +
2.88 + try:
2.89 + field_definition = wikidict[name]
2.90 + except KeyError:
2.91 + return showError(_("No entry for %s in %s.") % (name, dictpage), request)
2.92 +
2.93 + field_args = {}
2.94 +
2.95 + for field_arg in field_definition.split():
2.96 +
2.97 + # Record the key-value details.
2.98 +
2.99 + try:
2.100 + argname, argvalue = field_arg.split("=", 1)
2.101 + field_args[argname] = argvalue
2.102 +
2.103 + # Single keywords are interpreted as type descriptions.
2.104 +
2.105 + except ValueError:
2.106 + if not field_args.has_key("type"):
2.107 + field_args["type"] = field_arg
2.108 +
2.109 + # Obtain any request parameters corresponding to the field.
2.110 +
2.111 + form = get_form(request)
2.112 + value = form.get(name, [""])[0]
2.113 +
2.114 + # Render the field.
2.115 +
2.116 + type = field_args.get("type", "text")
2.117 +
2.118 + if type == "text":
2.119 + return fmt.rawHTML('<input name="%s" type="%s" size="%s" value="%s" />' % (
2.120 + escattr(name), escattr(type), escattr(field_args.get("size", "10")), escattr(value)
2.121 + ))
2.122 +
2.123 + elif type == "textarea":
2.124 + return fmt.rawHTML('<textarea name="%s" cols="%s" rows="%s">%s</textarea>' % (
2.125 + escattr(name), escattr(field_args.get("cols", "60")), escattr(field_args.get("rows", "5")), escape(value)
2.126 + ))
2.127 +
2.128 + elif type == "submit":
2.129 + return fmt.rawHTML('<input name="%s" type="submit" value="%s" />' % (
2.130 + escattr(name), escattr(_(label or name))
2.131 + ))
2.132 +
2.133 + # NOTE: Support select fields at the very least.
2.134 +
2.135 + else:
2.136 + return ''
2.137 +
2.138 +def showError(text, request):
2.139 + fmt = request.formatter
2.140 +
2.141 + output = []
2.142 + append = output.append
2.143 +
2.144 + append(fmt.span(on=1, attrs={"class" : "form-field-error"}))
2.145 + append(fmt.text(text))
2.146 + append(fmt.span(on=0))
2.147 +
2.148 + return "".join(output)
2.149 +
2.150 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/parsers/form.py Thu Nov 29 00:53:51 2012 +0100
3.3 @@ -0,0 +1,69 @@
3.4 +# -*- coding: iso-8859-1 -*-
3.5 +"""
3.6 + MoinMoin - form (MoinForms)
3.7 +
3.8 + @copyright: 2012 by Paul Boddie <paul@boddie.org.uk>
3.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
3.10 +"""
3.11 +
3.12 +from MoinForms import *
3.13 +from MoinSupport import *
3.14 +
3.15 +Dependencies = ["pages"]
3.16 +
3.17 +# Parser support.
3.18 +
3.19 +class Parser:
3.20 +
3.21 + "Manipulate and display forms."
3.22 +
3.23 + Dependencies = Dependencies
3.24 + extensions = []
3.25 +
3.26 + # Output content types preferred by this parser.
3.27 +
3.28 + output_mimetypes = ["text/html"]
3.29 +
3.30 + def __init__(self, raw, request, **kw):
3.31 +
3.32 + """
3.33 + Initialise the parser with the given 'raw' data, 'request' and any
3.34 + keyword arguments that may have been supplied.
3.35 + """
3.36 +
3.37 + self.raw = raw
3.38 + self.request = request
3.39 + attrs = parseAttributes(kw.get("format_args", ""), False)
3.40 +
3.41 + self.fragment = attrs.get("fragment")
3.42 +
3.43 + def format(self, fmt, write=None):
3.44 +
3.45 + """
3.46 + Format an event using the given formatter 'fmt'. If the 'write'
3.47 + parameter is specified, use it to write output; otherwise, write output
3.48 + using the request.
3.49 + """
3.50 +
3.51 + formatForm(self.raw, self.request, fmt, write=write)
3.52 +
3.53 + # Extra API methods.
3.54 +
3.55 + def formatForOutputType(self, mimetype, write=None):
3.56 +
3.57 + """
3.58 + Format an event for the given 'mimetype'. If the 'write' parameter is
3.59 + specified, use it to write output; otherwise, write output using the
3.60 + request.
3.61 + """
3.62 +
3.63 + formatFormForOutputType(self.raw, self.request, mimetype, write=write)
3.64 +
3.65 + # Class methods.
3.66 +
3.67 + def getOutputTypes(self):
3.68 + return self.output_mimetypes
3.69 +
3.70 + getOutputTypes = classmethod(getOutputTypes)
3.71 +
3.72 +# vim: tabstop=4 expandtab shiftwidth=4
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/setup.py Thu Nov 29 00:53:51 2012 +0100
4.3 @@ -0,0 +1,13 @@
4.4 +#! /usr/bin/env python
4.5 +
4.6 +from distutils.core import setup
4.7 +
4.8 +setup(
4.9 + name = "MoinForms",
4.10 + description = "Support HTML forms within MoinMoin pages",
4.11 + author = "Paul Boddie",
4.12 + author_email = "paul@boddie.org.uk",
4.13 + url = "http://moinmo.in/ParserMarket/MoinForms",
4.14 + version = "0.1",
4.15 + py_modules = ["MoinForms"]
4.16 + )