1798 lines
69 KiB
Python
1798 lines
69 KiB
Python
# -*- coding: UTF-8 -*-
|
|
|
|
|
|
# Expression
|
|
# By: Frederic Peters <fpeters@entrouvert.com>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
#
|
|
# Copyright (C) 2004 Entr'ouvert, Frederic Peters & Emmanuel Raviart
|
|
#
|
|
# This program 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 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# 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 General Public License
|
|
# along with this program; if not, write to the Free Software
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
|
|
"""XForms Module"""
|
|
|
|
import libxml2
|
|
|
|
import expression.core.elements as elements
|
|
import expression.core.environs as environs
|
|
import expression.core.html as html
|
|
import expression.core.locations as locations
|
|
import expression.core.logs as logs
|
|
import expression.core.namespaces as namespaces
|
|
import expression.core.stations as stations
|
|
import expression.core.widgets as widgets
|
|
import expression.core.xmlschemas as xmlschemas
|
|
|
|
|
|
class Element(elements.Element):
|
|
pass
|
|
|
|
|
|
class WidgetElement(widgets.WidgetMixin, Element):
|
|
def generateXmlContext(self, context, layout):
|
|
return False
|
|
|
|
def generateHtmlContextControlAlert(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
alert = self.getAlert(context, modelContext)
|
|
if alert is None:
|
|
return False
|
|
return alert.generateHtmlContextMessage(context, layout)
|
|
|
|
def generateHtmlContextControlHelp(self, context, layout):
|
|
help = self.help
|
|
if help is None:
|
|
return False
|
|
return help.generateHtmlContextMessage(context, layout)
|
|
|
|
def generateHtmlContextControlHint(self, context, layout):
|
|
hint = self.hint
|
|
if hint is None:
|
|
return False
|
|
return hint.generateHtmlContextMessage(context, layout)
|
|
|
|
def generateHtmlContextControlLabel(self, context, layout):
|
|
label = self.label
|
|
if label is None:
|
|
return False
|
|
return label.generateHtmlContextMessage(context, layout)
|
|
|
|
def getAlert(self, context, modelContext):
|
|
alertNodes = self.evaluateXpath("xforms:alert")
|
|
if not alertNodes:
|
|
return None
|
|
alertNode = alertNodes[0]
|
|
return self.newElement(alertNode)
|
|
|
|
def getFieldName(self, context, modelContext):
|
|
return self.getParent().getFieldName(context, modelContext)
|
|
|
|
def getHelp(self):
|
|
helpNodes = self.evaluateXpath("xforms:help")
|
|
if not helpNodes:
|
|
return None
|
|
helpNode = helpNodes[0]
|
|
return self.newElement(helpNode)
|
|
|
|
def getHint(self):
|
|
hintNodes = self.evaluateXpath("xforms:hint")
|
|
if not hintNodes:
|
|
return None
|
|
hintNode = hintNodes[0]
|
|
return self.newElement(hintNode)
|
|
|
|
def getComputedId(self, context, modelContext):
|
|
return self.getParent().getComputedId(context, modelContext)
|
|
|
|
def getInstanceData(self, context, modelContext):
|
|
instanceElement = modelContext.getInstanceElement()
|
|
valueNodes = instanceElement.evaluateXpath(self.getInstanceDataXpath(
|
|
context, modelContext))
|
|
if not valueNodes:
|
|
return None
|
|
return instanceElement.newElement(valueNodes[0])
|
|
|
|
def getInstanceDataNode(self, context, modelContext):
|
|
valueNodes = self.getInstanceDataNodes(context, modelContext)
|
|
if not valueNodes:
|
|
return None
|
|
return valueNodes[0]
|
|
|
|
def getInstanceDataNodes(self, context, modelContext):
|
|
instance = modelContext.getInstance()
|
|
valueNodes = instance.evaluateXpath(self.getInstanceDataXpath(context, modelContext))
|
|
return valueNodes
|
|
|
|
def getInstanceDataType(self, context, modelContext):
|
|
# FIXME: What to use for getTypeAtXpath first argument and
|
|
# TypeContext second argument? Is it really modelContext?
|
|
schemasContext = modelContext.schemas
|
|
if schemasContext is None:
|
|
return xmlschemas.TypeContext(
|
|
xmlschemas.String(), modelContext, previous = modelContext)
|
|
instanceElementType = modelContext.getInstanceElementType()
|
|
if instanceElementType is None:
|
|
return xmlschemas.TypeContext(
|
|
xmlschemas.String(), modelContext, previous = modelContext)
|
|
instanceDataType = instanceElementType.getTypeAtXpath(
|
|
modelContext, self.getInstanceDataXpath(context, modelContext))
|
|
if instanceDataType is None:
|
|
return xmlschemas.TypeContext(
|
|
xmlschemas.String(), modelContext, previous = modelContext)
|
|
return instanceDataType
|
|
|
|
def getInstanceDataXpath(self, context, modelContext):
|
|
bindIdNodes = self.evaluateXpath("@bind")
|
|
if bindIdNodes:
|
|
bindId = bindIdNodes[0].content
|
|
bind = modelContext.getBindById(bindId)
|
|
if bind is not None:
|
|
return "*/%s" % bind.nodeset
|
|
modelIdNodes = self.evaluateXpath("@model")
|
|
if modelIdNodes:
|
|
xpath = ""
|
|
elif self.getParent():
|
|
xpath = self.getParent().getChildInstanceDataXpath(context, modelContext)
|
|
else:
|
|
xpath = ""
|
|
nodesetNodes = self.evaluateXpath("@nodeset")
|
|
if nodesetNodes:
|
|
ref = nodesetNodes[0].content
|
|
# Quick & dirty hack, so that XForms expressions like:
|
|
# <xforms:repeat nodeset="yep:andcriteria | yep:falsecriteria">
|
|
# <xform:input>...</xforms:input>
|
|
# </xforms:repeat>
|
|
# give the following XPath:
|
|
# "(yep:andcriteria | yep:falsecriteria)[0]"
|
|
# "(yep:andcriteria | yep:falsecriteria)[1]"
|
|
# instead of:
|
|
# "yep:andcriteria | yep:falsecriteria[0]"
|
|
# "yep:andcriteria | yep:falsecriteria[1]"
|
|
if "|" in ref:
|
|
ref = "(%s)" % ref
|
|
else:
|
|
refNodes = self.evaluateXpath("@ref")
|
|
if refNodes:
|
|
ref = refNodes[0].content
|
|
else:
|
|
ref = ""
|
|
if xpath and ref:
|
|
xpath = "%s/%s" % (xpath, ref)
|
|
else:
|
|
xpath = "%s%s" % (xpath, ref)
|
|
return xpath
|
|
|
|
def getLabel(self):
|
|
labelNodes = self.evaluateXpath("xforms:label")
|
|
if not labelNodes:
|
|
return None
|
|
labelNode = labelNodes[0]
|
|
return self.newElement(labelNode)
|
|
|
|
def getRealNode(self, context, modelContext):
|
|
"""The real node is either the node of this element or the one refered
|
|
by the single-node binding attributes."""
|
|
|
|
# Works only for Single-node binding attributes. Don't use getRealNode
|
|
# for elements with node-set binding attributes.
|
|
instanceDataXpath = None
|
|
bindIdNodes = self.evaluateXpath("@bind")
|
|
if bindIdNodes:
|
|
bindId = bindIdNodes[0].content
|
|
bind = modelContext.getBindById(bindId)
|
|
if bind is not None:
|
|
instanceDataXpath = "*/%s" % bind.nodeset
|
|
refNodes = self.evaluateXpath("@ref")
|
|
if refNodes:
|
|
ref = refNodes[0].content
|
|
modelIdNodes = self.evaluateXpath("@model")
|
|
if modelIdNodes:
|
|
instanceDataXpath = ""
|
|
elif self.getParent():
|
|
instanceDataXpath = self.getParent().getChildInstanceDataXpath(
|
|
context, modelContext)
|
|
else:
|
|
instanceDataXpath = ""
|
|
if instanceDataXpath:
|
|
instanceDataXpath = "%s/%s" % (instanceDataXpath, ref)
|
|
else:
|
|
instanceDataXpath = ref
|
|
if instanceDataXpath is None:
|
|
return self.node
|
|
instance = modelContext.getInstance()
|
|
instanceDataNodes = instance.evaluateXpath(instanceDataXpath)
|
|
if not instanceDataNodes:
|
|
return self.node
|
|
return instanceDataNodes[0]
|
|
|
|
def newContext(self, specimen, *attributes, **keywords):
|
|
return WidgetElementContext(self, specimen, *attributes, **keywords)
|
|
|
|
def submitContext(self, context):
|
|
pass
|
|
|
|
help = property(getHelp)
|
|
hint = property(getHint)
|
|
label = property(getLabel)
|
|
|
|
|
|
class WidgetElementContext(html.WidgetElementContext):
|
|
pass
|
|
|
|
|
|
class Control(WidgetElement):
|
|
"""XForms Control."""
|
|
|
|
def generateXmlContext(self, context, layout):
|
|
# Generate a HTML form either when formCreationNeeded is True or when
|
|
# it is not specified (when a XForms control is encountered it is by
|
|
# default inside a form).
|
|
if context.formCreationNeeded is None or context.formCreationNeeded:
|
|
form = html.form(
|
|
action = locations.cleanUpUrl(context.getHttpPostUri(), "sessionToken"),
|
|
enctype = "multipart/form-data", method = "post")
|
|
oldFormCreationNeeded = context.formCreationNeeded
|
|
context.formCreationNeeded = False
|
|
context.inForm = True
|
|
filled = self.generateHtmlContextControl(context, form)
|
|
context.formCreationNeeded = oldFormCreationNeeded
|
|
context.inForm = False
|
|
if filled:
|
|
if not environs.getVar("canUseCookie") or environs.getVar("testCookieSupport"):
|
|
session = environs.getVar("session")
|
|
if session is not None and session.publishToken:
|
|
form.append(html.input(
|
|
name = "sessionToken", type = "hidden", value = session.token))
|
|
layout.append(form)
|
|
return filled
|
|
else:
|
|
return self.generateHtmlContextControl(context, layout)
|
|
|
|
def generateHtmlContextControl(self, context, layout):
|
|
inForm = context.inForm
|
|
modelContext = self.getModelContext(context)
|
|
relevant = self.isRelevant(context, modelContext)
|
|
if not inForm and not relevant:
|
|
return False
|
|
filled = False
|
|
if relevant:
|
|
groupingElementClass = self.htmlControlGroupingElementClass
|
|
groupingElement = groupingElementClass(class_ = self.getHtmlControlClass(context))
|
|
if self.generateHtmlContextControlLabel(context, groupingElement):
|
|
filled = True
|
|
groupingElementFilled = False
|
|
if inForm:
|
|
if self.generateHtmlContextControlHelp(context, groupingElement):
|
|
groupingElementFilled = True
|
|
if self.generateHtmlContextControlHint(context, groupingElement):
|
|
groupingElementFilled = True
|
|
if self.isReadOnly(context, modelContext):
|
|
if self.generateHtmlContextControlCoreReadOnly(context, groupingElement):
|
|
groupingElementFilled = True
|
|
elif self.generateHtmlContextControlCoreForm(context, groupingElement):
|
|
groupingElementFilled = True
|
|
if self.generateHtmlContextControlAlert(context, groupingElement):
|
|
groupingElementFilled = True
|
|
elif self.generateHtmlContextControlCoreStatic(context, groupingElement):
|
|
groupingElementFilled = True
|
|
if groupingElementFilled:
|
|
layout.append(groupingElement)
|
|
filled = True
|
|
# do not show irrelevant field values in html source, and no need to submit them
|
|
#elif inForm:
|
|
# if self.generateHtmlContextControlCoreHidden(context, layout):
|
|
# filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
layout.append(html.input(
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext), type = "text",
|
|
value = self.getFieldValue(context)))
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreHidden(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
layout.append(html.input(
|
|
name = self.getFieldName(context, modelContext), type = "hidden",
|
|
value = self.getFieldValue(context)))
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreReadOnly(self, context, layout):
|
|
filled = False
|
|
# no need to submit read-only field values -> no need for a hidden input field
|
|
#if self.generateHtmlContextControlCoreHidden(context, layout):
|
|
# filled = True
|
|
if self.generateHtmlContextControlCoreStatic(context, layout):
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
filled = False
|
|
htmlValueNodes = self.getHtmlValueNodes(context)
|
|
if htmlValueNodes:
|
|
for htmlValueNode in htmlValueNodes:
|
|
layout.append(htmlValueNode)
|
|
filled = True
|
|
return filled
|
|
|
|
def generatePlainTextContext(self, context):
|
|
return self.generatePlainTextContextControl(context)
|
|
|
|
def generatePlainTextContextControl(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
if not self.isRelevant(context, modelContext):
|
|
return u""
|
|
label = self.label
|
|
if label is None:
|
|
labelText = u""
|
|
else:
|
|
labelText = label.generatePlainTextContext(context)
|
|
coreText = self.generatePlainTextContextControlCore(context)
|
|
if labelText and coreText:
|
|
return u"%s %s" % (labelText, coreText)
|
|
else:
|
|
return u"%s%s" % (labelText, coreText)
|
|
|
|
def generatePlainTextContextControlCore(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
valueNode = self.getInstanceDataNode(context, modelContext)
|
|
if valueNode is None:
|
|
return u""
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
return valueType.convertValueNodeToPlainText(valueNode)
|
|
|
|
def getAlert(self, context, modelContext):
|
|
alerts = modelContext.alerts
|
|
if alerts is not None:
|
|
computedId = self.getComputedId(context, modelContext)
|
|
if computedId in alerts:
|
|
return AutomaticalAlert(alerts[computedId])
|
|
return WidgetElement.getAlert(self, context, modelContext)
|
|
|
|
def getAppearance(self):
|
|
nodes = self.evaluateXpath("@appearance")
|
|
if nodes:
|
|
return nodes[0].content
|
|
else:
|
|
return None
|
|
|
|
def getBind(self, context, modelContext):
|
|
bindNodes = self.evaluateXpath("@bind")
|
|
if bindNodes:
|
|
bindId = bindNodes[0].content
|
|
bind = modelContext.getBindById(bindId)
|
|
else:
|
|
refNodes = self.evaluateXpath("@nodeset | @ref")
|
|
if not refNodes:
|
|
return WidgetElement.getBind(self, context, modelContext)
|
|
bind = modelContext.getBindByInstanceDataXpath(
|
|
self.getInstanceDataXpath(context, modelContext))
|
|
return bind
|
|
|
|
def getComputedId(self, context, modelContext):
|
|
# FIXME: The try is a quick & dirty hack for the first group label of a description.
|
|
try:
|
|
instanceDataXpath = self.getRootInstanceDataXpath(context, modelContext)
|
|
except AttributeError:
|
|
logs.debug("getComputedId(%s, %s, %s): getInstanceDataXpath failed" % (
|
|
self, context, modelContext))
|
|
return ""
|
|
return instanceDataXpath.replace(":", "_")
|
|
|
|
def getFieldName(self, context, modelContext):
|
|
# FIXME: The try is a quick & dirty hack for the first group label of a description.
|
|
try:
|
|
instanceDataXpath = self.getRootInstanceDataXpath(context, modelContext)
|
|
except AttributeError:
|
|
logs.debug("getFieldName(%s, %s, %s): getInstanceDataXpath failed" % (
|
|
self, context, modelContext))
|
|
return ""
|
|
return instanceDataXpath.replace(":", "_")
|
|
|
|
def getFieldValue(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
valueNode = self.getInstanceDataNode(context, modelContext)
|
|
if valueNode is None:
|
|
fieldValue = ""
|
|
else:
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
fieldValue = valueType.convertValueNodeToHtmlAttributeValue(
|
|
valueNode)
|
|
return fieldValue
|
|
|
|
def getHtmlControlClass(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
computedId = self.getComputedId(context, modelContext)
|
|
type = None
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
if isinstance(valueType, xmlschemas.TypeContext):
|
|
type = "xsd-%s" % valueType.prototype.__class__.__name__.lower()
|
|
controlClass = "%s-%s" % (
|
|
namespaces.getName(self.node.ns().content),
|
|
self.node.name
|
|
)
|
|
specimenClass = "%s-%s" % (
|
|
namespaces.getName(context.specimen.node.ns().content),
|
|
context.specimen.node.name
|
|
)
|
|
ref = self.node.prop("ref")
|
|
if ref:
|
|
ref = ref.replace(":", "-").replace("/", "_")
|
|
customClass = self.node.prop("class")
|
|
required = None
|
|
if self.isRequired(context, modelContext):
|
|
required = "requiredField"
|
|
classes = [
|
|
controlClass,
|
|
specimenClass,
|
|
computedId,
|
|
ref,
|
|
type,
|
|
customClass,
|
|
required
|
|
]
|
|
return " ".join([c for c in classes if c])
|
|
|
|
def getHtmlControlGroupingElementClass(self):
|
|
return html.div
|
|
|
|
def getHtmlValueNodes(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
valueNode = self.getInstanceDataNode(context, modelContext)
|
|
if valueNode is None:
|
|
return []
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
if valueType is None:
|
|
return []
|
|
return valueType.convertValueNodeToHtmlNodes(valueNode)
|
|
|
|
def getModelId(self):
|
|
modelIdNodes = self.evaluateXpath("@model")
|
|
if modelIdNodes:
|
|
return modelIdNodes[0].content
|
|
return WidgetElement.getModelId(self)
|
|
|
|
def isReadOnly(self, context, modelContext):
|
|
bind = self.getBind(context, modelContext)
|
|
if bind is None:
|
|
return False
|
|
return bind.isReadOnly(
|
|
context, modelContext, self.getInstanceDataXpath(context, modelContext))
|
|
|
|
def isRelevant(self, context, modelContext):
|
|
bind = self.getBind(context, modelContext)
|
|
if bind is None:
|
|
return True
|
|
return bind.isRelevant(
|
|
context, modelContext, self.getInstanceDataXpath(context, modelContext))
|
|
|
|
def isRequired(self, context, modelContext):
|
|
bind = self.getBind(context, modelContext)
|
|
if bind is None:
|
|
return False
|
|
return bind.isRequired(
|
|
context, modelContext, self.getInstanceDataXpath(context, modelContext))
|
|
# FIXME: should also look in the schema: "xsd:element[not(@minOccurs) or @minOccurs > 0] | xsd:attribute[@use='required']"
|
|
|
|
def setFieldValue(self, context, modelContext, fieldValue):
|
|
valueNode = self.getInstanceDataNode(context, modelContext)
|
|
# FIXME
|
|
# assert valueNode is not None
|
|
# FIXME: Handle None fieldValue properly.
|
|
if valueNode is not None and fieldValue is not None:
|
|
# Note: Don't use setContent, because for some nodes it tries to
|
|
# convert entities references.
|
|
# valueNode.setContent(fieldValue)
|
|
if valueNode.children is not None:
|
|
childNode = valueNode.children
|
|
childNode.unlinkNode()
|
|
childNode.freeNodeList()
|
|
valueNode.addChild(valueNode.doc.newDocText(fieldValue))
|
|
|
|
## def setValueNode(self, context, modelContext, valueNode):
|
|
## nodes = environs.getVar("currentActionHandler").evaluateXpath(
|
|
## self.getInstanceDataXpath(context, modelContext))
|
|
## if nodes:
|
|
## node = nodes[0]
|
|
## node.replaceNode(valueNode)
|
|
## node.freeNode()
|
|
## else:
|
|
## self.node.addChild(valueNode)
|
|
|
|
def submitContext(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
if self.isRelevant(context, modelContext) and not self.isReadOnly(context, modelContext):
|
|
submission = environs.getVar("submission")
|
|
fieldName = self.getFieldName(context, modelContext)
|
|
value = submission.getField(fieldName)
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
if valueType is None:
|
|
logs.info("""Missing type for value "%s" in field "%s".""" % (value, fieldName))
|
|
try:
|
|
content = valueType.convertSubmissionValueToContent(value)
|
|
except ValueError:
|
|
computedId = self.getComputedId(context, modelContext)
|
|
modelContext.setErrorLabel(computedId, "emptyValue", N_("Unrecognized Value: %s") % value)
|
|
else:
|
|
context.specimen.setContentAtXpath(
|
|
self.getInstanceDataXpath(context, modelContext), content)
|
|
if not content and self.isRequired(context, modelContext):
|
|
computedId = self.getComputedId(context, modelContext)
|
|
modelContext.setErrorLabel(computedId, "emptyValue", N_("Missing Value"))
|
|
|
|
htmlControlGroupingElementClass = property(
|
|
getHtmlControlGroupingElementClass)
|
|
modelId = property(getModelId)
|
|
|
|
|
|
class Message(WidgetElement):
|
|
translatable = True
|
|
def generateHtmlContextMessage(self, context, layout):
|
|
span = html.span(class_ = self.getHtmlMessageClass())
|
|
if not self.generateHtmlContextMessageChildren(context, span):
|
|
return False
|
|
layout.append(span)
|
|
return True
|
|
|
|
def generateHtmlContextMessageChildren(self, context, layout):
|
|
filled = False
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
childNode = realNode.children
|
|
while childNode is not None:
|
|
if childNode.type in ("cdata", "text"):
|
|
content = childNode.content
|
|
layout.append(content)
|
|
filled = True
|
|
childNode = childNode.next
|
|
continue
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
if element.generateXmlContext(context, layout):
|
|
filled = True
|
|
childNode = childNode.next
|
|
return filled
|
|
|
|
def getHtmlMessageClass(self):
|
|
return self.node and self.node.prop("class") or self.htmlMessageClass
|
|
|
|
|
|
class Alert(Message):
|
|
htmlMessageClass = "xforms-alert"
|
|
|
|
|
|
class AutomaticalAlert(Alert):
|
|
message = None
|
|
|
|
def __init__(self, message):
|
|
# Dont' call inherited.
|
|
if message is not None:
|
|
self.message = message
|
|
|
|
def generateHtmlContextMessageChildren(self, context, layout):
|
|
# Dont' call inherited.
|
|
filled = False
|
|
if self.message:
|
|
layout.append(_(self.message))
|
|
filled = True
|
|
return filled
|
|
|
|
|
|
class Bind(Element):
|
|
def buildXformsModelContext(self, context):
|
|
nodeset = self.nodeset
|
|
if context.bindsByNodeset is None:
|
|
context.bindsByNodeset = {}
|
|
context.bindsByNodeset[nodeset] = self
|
|
id = self.id
|
|
if id is not None:
|
|
if context.bindsById is None:
|
|
context.bindsById = {}
|
|
context.bindsById[id] = self
|
|
|
|
def getNodeset(self):
|
|
sourceNodes = self.evaluateXpath("@nodeset")
|
|
if sourceNodes:
|
|
return sourceNodes[0].content
|
|
else:
|
|
return None
|
|
|
|
def isReadOnly(self, context, modelContext, instanceDataXpath):
|
|
sourceNodes = self.evaluateXpath("@readonly")
|
|
if sourceNodes:
|
|
formula = sourceNodes[0].content
|
|
instance = modelContext.getInstance()
|
|
# Note: we can't use instanceData as base for evaluateXpath,
|
|
# because it can be None. So we use instance, but we should change
|
|
# the paths in formula so that they are relative to instance
|
|
# instead of instanceData (dirty hack needed because of a stupid
|
|
# XForms design decision). FIXME: TODO
|
|
return bool(instance.evaluateXpath(formula))
|
|
else:
|
|
return False
|
|
|
|
def isRelevant(self, context, modelContext, instanceDataXpath):
|
|
sourceNodes = self.evaluateXpath("@relevant")
|
|
if sourceNodes:
|
|
formula = sourceNodes[0].content
|
|
instance = modelContext.getInstance()
|
|
# Note: we can't use instanceData as base for evaluateXpath,
|
|
# because it can be None. So we use instance, but we should change
|
|
# the paths in formula so that they are relative to instance
|
|
# instead of instanceData (dirty hack needed because of a stupid
|
|
# XForms design decision). FIXME: TODO
|
|
return bool(instance.evaluateXpath(formula))
|
|
else:
|
|
return True
|
|
|
|
def isRequired(self, context, modelContext, instanceDataXpath):
|
|
sourceNodes = self.evaluateXpath("@required")
|
|
if sourceNodes:
|
|
formula = sourceNodes[0].content
|
|
instance = modelContext.getInstance()
|
|
# Note: we can't use instanceData as base for evaluateXpath,
|
|
# because it can be None. So we use instance, but we should change
|
|
# the paths in formula so that they are relative to instance
|
|
# instead of instanceData (dirty hack needed because of a stupid
|
|
# XForms design decision). FIXME: TODO
|
|
return bool(instance.evaluateXpath(formula))
|
|
else:
|
|
return False
|
|
|
|
def getTypeName(self):
|
|
sourceNodes = self.evaluateXpath("@type")
|
|
if sourceNodes:
|
|
return sourceNodes[0].content
|
|
else:
|
|
return None
|
|
|
|
nodeset = property(getNodeset)
|
|
|
|
|
|
class Choices(WidgetElement):
|
|
def generateHtmlContextControlCheckBoxesForm(
|
|
self, context, layout, checkedValues):
|
|
filled = False
|
|
li = html.li(class_ = "xforms-choices")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
|
|
ulFilled = False
|
|
ul = html.ul()
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlCheckBoxesForm(
|
|
context, ul, checkedValues):
|
|
ulFilled = True
|
|
if ulFilled:
|
|
li.append(ul)
|
|
liFilled = True
|
|
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlCheckBoxesStatic(
|
|
self, context, layout, checkedValues):
|
|
filled = False
|
|
li = html.li(class_ = "xforms-choices")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
|
|
ulFilled = False
|
|
ul = html.ul()
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlCheckBoxesStatic(
|
|
context, ul, checkedValues):
|
|
ulFilled = True
|
|
if ulFilled:
|
|
li.append(ul)
|
|
liFilled = True
|
|
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlOptionsForm(self, context, layout, value):
|
|
filled = False
|
|
optgroup = html.optgroup(class_ = "xforms-choices")
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlOptionsForm(
|
|
context, optgroup, value):
|
|
filled = True
|
|
if filled:
|
|
self.label.generateHtmlContextMessageChildren(context, optgroup)
|
|
layout.append(optgroup)
|
|
return filled
|
|
|
|
def generateHtmlContextControlOptionsStatic(self, context, layout, value):
|
|
filled = False
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlOptionsStatic(
|
|
context, layout, value):
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlRadiosForm(self, context, layout, value):
|
|
filled = False
|
|
li = html.li(class_ = "xforms-choices")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
|
|
ulFilled = False
|
|
ul = html.ul()
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlRadiosForm(
|
|
context, ul, value):
|
|
ulFilled = True
|
|
if ulFilled:
|
|
li.append(ul)
|
|
liFilled = True
|
|
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlRadiosStatic(self, context, layout, value):
|
|
filled = False
|
|
itemOrChoicesNodes = self.evaluateXpath("xforms:item | xforms:choices")
|
|
for itemOrChoicesNode in itemOrChoicesNodes:
|
|
element = self.newElement(itemOrChoicesNode)
|
|
if element.generateHtmlContextControlRadiosStatic(
|
|
context, layout, value):
|
|
filled = True
|
|
return filled
|
|
|
|
|
|
class Group(Control):
|
|
|
|
def generateHtmlContextControlCore(self, context, layout):
|
|
filled = False
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type in ("cdata", "text"):
|
|
layout.append(childNode.content)
|
|
filled = True
|
|
childNode = childNode.next
|
|
continue
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
if element.generateXmlContext(context, layout):
|
|
filled = True
|
|
childNode = childNode.next
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
return self.generateHtmlContextControlCore(context, layout)
|
|
|
|
def generateHtmlContextControlCoreHidden(self, context, layout):
|
|
return self.generateHtmlContextControlCore(context, layout)
|
|
|
|
def generateHtmlContextControlCoreReadOnly(self, context, layout):
|
|
return self.generateHtmlContextControlCore(context, layout)
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
return self.generateHtmlContextControlCore(context, layout)
|
|
|
|
def submitContext(self, context):
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
elementContext = element.newContext(context.specimen, previous = context)
|
|
elementContext.submit()
|
|
childNode = childNode.next
|
|
|
|
|
|
class Help(Message):
|
|
htmlMessageClass = "xforms-help"
|
|
|
|
|
|
class Hint(Message):
|
|
htmlMessageClass = "xforms-hint"
|
|
|
|
|
|
class Input(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
fieldName = self.getFieldName(context, modelContext)
|
|
id = self.getComputedId(context, modelContext)
|
|
valueType = self.getInstanceDataType(context, modelContext)
|
|
if isinstance(valueType.prototype, xmlschemas.DateTime):
|
|
layout.append(html.input(
|
|
id = id, maxlength = "16", name = fieldName,
|
|
size = "16", type = "text",
|
|
value = self.getFieldValue(context)))
|
|
layout.append(html.img(
|
|
id= "%s_trigger" % id,
|
|
src = context.constructUri("/javascript/jscalendar/img.png"),
|
|
style = "cursor: pointer;",
|
|
title = _("Date Selector"),
|
|
onload = """Calendar.setup({
|
|
inputField: "%s",
|
|
ifFormat: "%%d/%%m/%%Y %%H:%%M",
|
|
button: "%s_trigger",
|
|
singleClick: true,
|
|
showsTime: true
|
|
})
|
|
""" % (id, id),
|
|
onmouseout="this.style.background=''",
|
|
onmouseover = "this.style.background='';"))
|
|
elif isinstance(valueType.prototype, xmlschemas.Date):
|
|
layout.append(html.input(
|
|
id = id, maxlength = "10", name = fieldName,
|
|
size = "10", type = "text",
|
|
value = self.getFieldValue(context)))
|
|
layout.append(html.img(
|
|
id= "%s_trigger" % id,
|
|
src = context.constructUri("/javascript/jscalendar/img.png"),
|
|
style = "cursor: pointer;",
|
|
title = _("Date Selector"),
|
|
onload = """Calendar.setup({
|
|
inputField: "%s",
|
|
ifFormat: "%%d/%%m/%%Y",
|
|
button: "%s_trigger",
|
|
singleClick: true
|
|
})
|
|
""" % (id, id),
|
|
onmouseout="this.style.background=''",
|
|
onmouseover = "this.style.background='';"))
|
|
elif isinstance(valueType.prototype, xmlschemas.Boolean):
|
|
input = html.input(id = id, name = fieldName, type = "checkbox", value = "1")
|
|
if self.getFieldValue(context) in ("1", "true"):
|
|
input.node.setProp("checked", "true")
|
|
layout.append(input)
|
|
else:
|
|
layout.append(html.input(
|
|
id = id, name = fieldName, type = "text",
|
|
value = self.getFieldValue(context)))
|
|
return True
|
|
|
|
|
|
class Instance(Element):
|
|
_rootElement = None
|
|
|
|
def buildXformsModelContext(self, context):
|
|
if context.defaultInstance is None:
|
|
context.defaultInstance = self
|
|
id = self.id
|
|
if id is not None:
|
|
if context.instancesById is None:
|
|
context.instancesById = {}
|
|
context.instancesById[id] = self
|
|
|
|
def getRootElement(self):
|
|
if self._rootElement is None:
|
|
rootNode = self.getRootNode()
|
|
if rootNode is None:
|
|
return None
|
|
# There doesn't exist any URI path to access a rootElement => no uriPathFragment.
|
|
self._rootElement = elements.newElement(rootNode, previous = self, owner = self)
|
|
rootElement = self._rootElement
|
|
return rootElement
|
|
|
|
def getRootNode(self):
|
|
rootNodes = self.evaluateXpath("*")
|
|
if not rootNodes:
|
|
return None
|
|
return rootNodes[0]
|
|
|
|
|
|
class DummyInstance(object):
|
|
"""This Instance class is used to encapsulate an Expression element, so that it behaves like
|
|
an XForms instance."""
|
|
|
|
_rootElement = None
|
|
|
|
def __init__(self, rootElement):
|
|
self._rootElement = rootElement
|
|
|
|
def buildXformsModelContext(self, context):
|
|
if context.defaultInstance is None:
|
|
context.defaultInstance = self
|
|
id = self.id
|
|
if id is not None:
|
|
if context.instancesById is None:
|
|
context.instancesById = {}
|
|
context.instancesById[id] = self
|
|
|
|
def evaluateXpath(self, xpath, contextNode = None):
|
|
return self._rootElement.evaluateXpath(xpath, contextNode)
|
|
|
|
def getRootElement(self):
|
|
return self._rootElement
|
|
|
|
def serialize(self):
|
|
return self._rootElement.serialize()
|
|
|
|
|
|
class Item(WidgetElement):
|
|
def generateHtmlContextControlCheckBoxesForm(
|
|
self, context, layout, checkedValues):
|
|
filled = False
|
|
li = html.li(class_ = "xforms-item")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
if self.value.generateHtmlContextValueCheckBoxForm(
|
|
context, li, checkedValues):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlCheckBoxesStatic(self, context, layout, checkedValues):
|
|
modelContext = self.getModelContext(context)
|
|
value = self.value
|
|
if value.getRealNode(context, modelContext).content not in checkedValues:
|
|
return False
|
|
filled = False
|
|
li = html.li(class_ = "xforms-item")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
if value.generateHtmlContextValueCheckBoxStatic(context, li, checkedValues):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlOptionsForm(self, context, layout, value):
|
|
filled = False
|
|
if self.value.generateHtmlContextValueOptionForm(
|
|
context, layout, value):
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlOptionsStatic(self, context, layout, value):
|
|
filled = False
|
|
if self.value.generateHtmlContextValueOptionStaticSimple(
|
|
context, layout, value
|
|
):
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlRadiosForm(self, context, layout, value):
|
|
filled = False
|
|
li = html.li(class_ = "xforms-item")
|
|
liFilled = False
|
|
if self.generateHtmlContextControlHelp(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlHint(context, li):
|
|
liFilled = True
|
|
if self.value.generateHtmlContextValueRadioForm(context, li, value):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlAlert(context, li):
|
|
liFilled = True
|
|
if self.generateHtmlContextControlLabel(context, li):
|
|
liFilled = True
|
|
if liFilled:
|
|
layout.append(li)
|
|
filled = True
|
|
return filled
|
|
|
|
def generateHtmlContextControlRadiosStatic(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
value = self.value
|
|
if value.getRealNode(context, modelContext).content != value:
|
|
return False
|
|
filled = False
|
|
if self.generateHtmlContextControlHelp(context, layout):
|
|
filled = True
|
|
if self.generateHtmlContextControlHint(context, layout):
|
|
filled = True
|
|
if value.generateHtmlContextValueRadioStatic(context, layout, value):
|
|
filled = True
|
|
if self.generateHtmlContextControlAlert(context, layout):
|
|
filled = True
|
|
if self.generateHtmlContextControlLabel(context, layout):
|
|
filled = True
|
|
return filled
|
|
|
|
def getComputedId(self, context, modelContext):
|
|
value = self.value.getRealNode(context, modelContext).content
|
|
return "%s_%s" % (self.getParent().getComputedId(context, modelContext), value)
|
|
|
|
def getValue(self):
|
|
valueNodes = self.evaluateXpath("xforms:value")
|
|
if not valueNodes:
|
|
return None
|
|
valueNode = valueNodes[0]
|
|
return self.newElement(valueNode)
|
|
|
|
value = property(getValue)
|
|
|
|
|
|
class Label(Message):
|
|
htmlMessageClass = "xforms-label"
|
|
|
|
def generateHtmlContextMessage(self, context, layout):
|
|
label = html.label(class_ = self.getHtmlMessageClass())
|
|
if context.inForm:
|
|
modelContext = self.getModelContext(context)
|
|
id = self.getComputedId(context, modelContext)
|
|
if id:
|
|
label.node.setProp("for", id)
|
|
if not self.generateHtmlContextMessageChildren(context, label):
|
|
return False
|
|
layout.append(label)
|
|
return True
|
|
|
|
|
|
class Model(WidgetElement):
|
|
def buildXformsContext(self, context):
|
|
modelContext = ModelContext(
|
|
self, context, previous = context, uriPathFragment = "model")
|
|
if context.xformsDefaultModel is None:
|
|
context.xformsDefaultModel = modelContext
|
|
id = self.id
|
|
if id is not None:
|
|
if context.xformsModelsById is None:
|
|
context.xformsModelsById = {}
|
|
context.xformsModelsById[id] = modelContext
|
|
schemaLocations = self.schemaLocations
|
|
for schemaLocation in schemaLocations:
|
|
schemaAbsolutePath = self.convertRelativeLocationToAbsolutePath(schemaLocation)
|
|
if schemaAbsolutePath is None:
|
|
logs.debug('Missing schema file "%s".' % schemaLocation)
|
|
continue
|
|
schemaHolder = xmlschemas.getSchemaHolder(schemaAbsolutePath)
|
|
if schemaHolder is None:
|
|
logs.debug('Missing schema file "%s".' % schemaLocation)
|
|
continue
|
|
schema = schemaHolder.getRootElement()
|
|
if environs.getVar("debug"):
|
|
if schema.validateElement(context.specimen.node):
|
|
logs.debug("%s is valid against %s." % (context.specimen, schema))
|
|
else:
|
|
logs.warning("%s is NOT valid against %s." % (context.specimen, schema))
|
|
if modelContext.schemas is None:
|
|
modelContext.schemas = xmlschemas.SchemasContext(
|
|
modelContext.specimen, previous = modelContext, uriPathFragment = "schemas")
|
|
schemasContext = modelContext.schemas
|
|
if schemasContext.schemas is None:
|
|
schemasContext.schemas = []
|
|
schemasContext.schemas.append(schema)
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
element.buildXformsModelContext(modelContext)
|
|
childNode = childNode.next
|
|
|
|
def generateXmlContext(self, context, layout):
|
|
self.buildXformsContext(context)
|
|
return False
|
|
|
|
def getInstanceNode(self, schemaNameWithNamespace):
|
|
instanceNodes = self.evaluateXpath(
|
|
"xforms:instance/%s" % schemaNameWithNamespace)
|
|
if not instanceNodes:
|
|
return None
|
|
return instanceNodes[0]
|
|
|
|
def getModelId(self):
|
|
return self.id
|
|
|
|
def getSchemaLocations(self):
|
|
schemaLocationsNodes = self.evaluateXpath("@schema")
|
|
if not schemaLocationsNodes:
|
|
return []
|
|
schemaLocations = schemaLocationsNodes[0].content
|
|
return schemaLocations.split()
|
|
|
|
def getSubmissionNode(self, id = None):
|
|
if id is None:
|
|
submissionNodes = self.evaluateXpath(
|
|
"xforms:submission[@id = '%s']" % id.replace("'", "'"))
|
|
else:
|
|
submissionNodes = self.evaluateXpath("xforms:submission")
|
|
if not submissionNodes:
|
|
return None
|
|
return submissionNodes[0]
|
|
|
|
modelId = property(getModelId)
|
|
schemaLocations = property(getSchemaLocations)
|
|
|
|
|
|
class ModelContext(stations.AbstractContext):
|
|
alerts = None
|
|
bindsById = None
|
|
bindsByNodeset = None
|
|
defaultInstance = None
|
|
embedder = None
|
|
embedderContext = None
|
|
errors = None
|
|
instancesById = None
|
|
schemas = None
|
|
|
|
def getBindById(self, id):
|
|
if self.bindsById is not None and id in self.bindsById:
|
|
return self.bindsById[id]
|
|
else:
|
|
return None
|
|
|
|
def getBindByInstanceDataXpath(self, instanceDataXpath):
|
|
if self.bindsByNodeset is None \
|
|
or not instanceDataXpath in self.bindsByNodeset:
|
|
return None
|
|
return self.bindsByNodeset[instanceDataXpath]
|
|
|
|
def getInstance(self):
|
|
id = None # FIXME
|
|
if id is None:
|
|
instance = self.defaultInstance
|
|
else:
|
|
instance = self.instancesById[id]
|
|
return instance
|
|
|
|
def getInstanceElement(self):
|
|
instance = self.getInstance()
|
|
if instance is None:
|
|
return None
|
|
return instance.getRootElement()
|
|
|
|
def getInstanceElementType(self):
|
|
instanceElement = self.getInstanceElement()
|
|
instanceElementType = instanceElement.type
|
|
if instanceElementType is not None:
|
|
return instanceElementType
|
|
if self.schemas is not None:
|
|
return self.schemas.getValueType(instanceElement)
|
|
return None
|
|
|
|
def setErrorLabel(self, computedId, symbol, label, override = False):
|
|
if self.errors is None:
|
|
self.errors = {}
|
|
if computedId not in self.errors or override:
|
|
self.errors[computedId] = symbol
|
|
if self.alerts is None:
|
|
self.alerts = {}
|
|
if computedId not in self.alerts or override:
|
|
self.alerts[computedId] = label
|
|
|
|
|
|
class Output(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
layout.append(html.span(self.getFieldValue(context)))
|
|
return True
|
|
|
|
def generateHtmlContextControlRequired(self, context, layout):
|
|
return False
|
|
|
|
def getHtmlControlGroupingElementClass(self):
|
|
return html.span
|
|
|
|
def isReadOnly(self, context, modelContext):
|
|
return True
|
|
|
|
def isRequired(self, context, modelContext):
|
|
return False
|
|
|
|
def submitContext(self, context):
|
|
pass
|
|
|
|
htmlControlGroupingElementClass = property(
|
|
getHtmlControlGroupingElementClass)
|
|
|
|
|
|
class Range(Input):
|
|
pass
|
|
|
|
|
|
class Repeat(Control):
|
|
index = None
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
countName = "%s-count" % self.getFieldName(context, modelContext)
|
|
count = 0
|
|
valueNodes = self.getInstanceDataNodes(context, modelContext)
|
|
count = max(0, count, len(valueNodes))
|
|
|
|
layout.append(html.input(name = countName, type = "hidden", value = str(count)))
|
|
|
|
ul = html.ul(class_ = "multi")
|
|
layout.append(html.div(ul))
|
|
|
|
filled = False
|
|
for i in range(count):
|
|
#~ if i >= len(valueNodes):
|
|
#~ self.index = None
|
|
#~ else:
|
|
#~ self.index = i
|
|
self.index = i
|
|
li = html.li()
|
|
ul.append(li)
|
|
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type in ("cdata", "text"):
|
|
li.append(childNode.content)
|
|
childNode = childNode.next
|
|
continue
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
filled |= element.generateXmlContext(context, li)
|
|
childNode = childNode.next
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreHidden(self, context, layout):
|
|
filled = False
|
|
modelContext = self.getModelContext(context)
|
|
countName = "%s-count" % self.getFieldName(context, modelContext)
|
|
count = 0
|
|
valueNodes = self.getInstanceDataNodes(context, modelContext)
|
|
count = max(0, count, len(valueNodes))
|
|
|
|
layout.append(html.input(name = countName, type = "hidden", value = str(count)))
|
|
|
|
for i in range(len(valueNodes)):
|
|
self.index = i
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
if element.generateHtmlContextControlCoreHidden(context, layout):
|
|
filled = True
|
|
childNode = childNode.next
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
filled = False
|
|
modelContext = self.getModelContext(context)
|
|
valueNodes = self.getInstanceDataNodes(context, modelContext)
|
|
if not valueNodes:
|
|
return filled
|
|
ul = html.ul(class_ = "multi")
|
|
for i in range(len(valueNodes)):
|
|
self.index = i
|
|
li = html.li()
|
|
ul.append(li)
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type in ("cdata", "text"):
|
|
li.append(childNode.content)
|
|
filled = True
|
|
childNode = childNode.next
|
|
continue
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
if element.generateXmlContext(context, li):
|
|
filled = True
|
|
childNode = childNode.next
|
|
if filled:
|
|
layout.append(ul)
|
|
return filled
|
|
|
|
def getFieldValue(self, context):
|
|
return None
|
|
|
|
def getChildInstanceDataXpath(self, context, modelContext):
|
|
return "%s[%d]" % (self.getInstanceDataXpath(context, modelContext), self.index + 1)
|
|
|
|
def submitContext(self, context):
|
|
modelContext = self.getModelContext(context)
|
|
countName = "%s-count" % self.getFieldName(context, modelContext)
|
|
count = 0
|
|
submission = environs.getVar("submission")
|
|
if submission.hasField(countName):
|
|
try:
|
|
count = int(submission.getField(countName))
|
|
except (TypeError, ValueError):
|
|
pass
|
|
|
|
for i in range(count):
|
|
self.index = i
|
|
childNode = self.node.children
|
|
while childNode is not None:
|
|
if childNode.type != "element":
|
|
childNode = childNode.next
|
|
continue
|
|
element = self.newElement(childNode)
|
|
elementContext = element.newContext(context.specimen, previous = context)
|
|
elementContext.submit()
|
|
childNode = childNode.next
|
|
|
|
|
|
class Secret(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
layout.append(html.input(
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext), type = "password",
|
|
value = self.getFieldValue(context)))
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
layout.append("*" * len(self.getFieldValue(context)))
|
|
return True
|
|
|
|
|
|
class Select(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
""" Renders as an HTML checkbox list appended to layout.
|
|
Returns whether something was appended to layout.
|
|
"""
|
|
selectedValues = self.getSelectedValues(context)
|
|
filled = False
|
|
ul = html.ul()
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlCheckBoxesForm(context, ul, selectedValues)
|
|
if filled:
|
|
layout.append(ul)
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
""" Renders as an HTML read-only list appended to layout.
|
|
Returns whether something was appended to layout.
|
|
"""
|
|
selectedValues = self.getSelectedValues(context)
|
|
filled = False
|
|
ul = html.ul()
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlCheckBoxesStatic(context, ul, selectedValues)
|
|
if filled:
|
|
layout.append(ul)
|
|
return filled
|
|
|
|
def getAvailableValues(self, context):
|
|
""" Returns a list with all available values.
|
|
"""
|
|
return [node.content for node in self.evaluateXpath(".//xforms:value")]
|
|
|
|
def getSelectedValues(self, context):
|
|
""" Returns the list of selected value strings.
|
|
"""
|
|
node = self.getInstanceDataNode(context, self.getModelContext(context))
|
|
return node and node.content.split(" ") or []
|
|
|
|
def getItemOrChoiceElements(self, context):
|
|
""" Returns a list of elements that provide methods:
|
|
- generateHtmlContextControlCheckBoxesForm
|
|
- generateHtmlContextControlCheckBoxesStatic
|
|
"""
|
|
return [self.newElement(node) for node in self.evaluateXpath("xforms:item | xforms:choices")]
|
|
|
|
def submitContext(self, context):
|
|
""" Updates the context specimen with submitted data for this control.
|
|
Sets an error if no data was submitted.
|
|
"""
|
|
submission = environs.getVar("submission")
|
|
modelContext = self.getModelContext(context)
|
|
fieldName = self.getFieldName(context, modelContext)
|
|
splitedValue = []
|
|
if submission.hasField(fieldName):
|
|
submissionValue = submission.getField(fieldName)
|
|
for itemValue in self.getAvailableValues(context):
|
|
if isinstance(submissionValue, list) and itemValue in submissionValue \
|
|
or itemValue == submissionValue:
|
|
splitedValue.append(itemValue)
|
|
context.specimen.setContentAtXpath(
|
|
self.getInstanceDataXpath(context, modelContext),
|
|
" ".join(splitedValue)
|
|
)
|
|
if not splitedValue and self.isRequired(context, modelContext):
|
|
computedId = self.getComputedId(context, modelContext)
|
|
modelContext.setErrorLabel(computedId, "emptyValue", N_("Missing Value"))
|
|
|
|
|
|
class Select1(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
""" Renders and appends to layout either as an HTML radio button list, or an HTML select control.
|
|
Returns whether something was appended to layout.
|
|
"""
|
|
modelContext = self.getModelContext(context)
|
|
value = self.getSelectedValue(context)
|
|
filled = False
|
|
appearance = self.getAppearance()
|
|
if appearance == "full":
|
|
ul = html.ul()
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlRadiosForm(context, ul, value)
|
|
if filled:
|
|
layout.append(ul)
|
|
else: # appearance == "minimal"
|
|
select = html.select(
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext)
|
|
)
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlOptionsForm(context, select, value)
|
|
if filled:
|
|
layout.append(select)
|
|
return filled
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
""" Renders and appends to layout as a list.
|
|
Returns whether something was appended to layout.
|
|
"""
|
|
modelContext = self.getModelContext(context)
|
|
value = self.getSelectedValue(context)
|
|
filled = False
|
|
appearance = self.getAppearance()
|
|
if appearance == "full":
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlRadiosStatic(context, layout, value)
|
|
else: # appearance == "minimal"
|
|
for element in self.getItemOrChoiceElements(context):
|
|
filled |= element.generateHtmlContextControlOptionsStatic(context, layout, value)
|
|
return filled
|
|
|
|
def getAvailableValues(self, context):
|
|
""" Returns a list with all available values.
|
|
"""
|
|
return [node.content for node in self.evaluateXpath(".//xforms:value")]
|
|
|
|
def getItemOrChoiceElements(self, context):
|
|
""" Returns a list of elements that provide methods:
|
|
- generateHtmlContextControlRadiosForm
|
|
- generateHtmlContextControlOptionsForm
|
|
- generateHtmlContextControlRadiosStatic
|
|
- generateHtmlContextControlOptionsStatic
|
|
"""
|
|
return [self.newElement(node) for node in self.evaluateXpath("xforms:item | xforms:choices")]
|
|
|
|
def getSelectedValue(self, context):
|
|
""" Returns the selected value.
|
|
"""
|
|
node = self.getInstanceDataNode(context, self.getModelContext(context))
|
|
return node and node.content or None
|
|
|
|
def submitContext(self, context):
|
|
""" Updates the context specimen with submitted data for this control.
|
|
Sets an error if the submitted value is not available.
|
|
Sets an error if no data was submitted.
|
|
"""
|
|
submission = environs.getVar("submission")
|
|
modelContext = self.getModelContext(context)
|
|
fieldName = self.getFieldName(context, modelContext)
|
|
value = submission.getField(fieldName, default = "")
|
|
context.specimen.setContentAtXpath(self.getInstanceDataXpath(context, modelContext), value)
|
|
if value not in self.getAvailableValues(context):
|
|
computedId = self.getComputedId(context, modelContext)
|
|
modelContext.setErrorLabel(
|
|
computedId, "wrongValue", N_("Wrong Value"))
|
|
elif not value and self.isRequired(context, modelContext):
|
|
computedId = self.getComputedId(context, modelContext)
|
|
modelContext.setErrorLabel(
|
|
computedId, "emptyValue", N_("Missing Value"))
|
|
|
|
|
|
class Submission(Element):
|
|
def buildXformsModelContext(self, context):
|
|
# FIXME: TODO
|
|
pass
|
|
|
|
|
|
class Submit(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
layout.append(html.input(
|
|
class_ = "button", name = self.getSubmissionId(), type = "submit",
|
|
value = self.label.node.content))
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
label = self.label
|
|
if label is None:
|
|
return False
|
|
a = html.a(class_ = 'button', href = self.getSubmissionActionUri(
|
|
context))
|
|
if not label.generateHtmlContextMessageChildren(context, a):
|
|
return False
|
|
layout.append(a)
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreHidden(self, context, layout):
|
|
return False
|
|
|
|
def generateHtmlContextControlLabel(self, context, layout):
|
|
return False
|
|
|
|
def getSubmissionActionUri(self, context):
|
|
# FIXME: TODO
|
|
return None
|
|
submissionId = self.getSubmissionId()
|
|
submissionNode = self.getModel(context).getSubmission(submissionId)
|
|
if submissionNode is None:
|
|
return None
|
|
actionNodes = self.evaluateXpath("@action", submissionNode)
|
|
if not actionNodes:
|
|
return None
|
|
return context.constructUri(actionNodes[0].content)
|
|
|
|
def getSubmissionId(self):
|
|
submissionIdNodes = self.evaluateXpath("@submission")
|
|
if submissionIdNodes:
|
|
submissionId = submissionIdNodes[0].content
|
|
else:
|
|
submissionId = None
|
|
return submissionId
|
|
|
|
def getFieldValue(self, context):
|
|
return None
|
|
|
|
|
|
class TextArea(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
if self.getAppearance() == "full":
|
|
attributes = {"cols": "80", "rows": "10"}
|
|
else:
|
|
attributes = {"cols": "60", "rows": "4"}
|
|
layout.append(html.textarea(
|
|
self.getFieldValue(context),
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext), **attributes))
|
|
return True
|
|
|
|
|
|
class Trigger(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
# Unlike Submit Control, a Trigger never submits the form even inside
|
|
# a form.
|
|
label = self.label
|
|
if label is None:
|
|
return False
|
|
a = html.a(
|
|
class_ = 'button', href = self.getLoadResourceUri(context))
|
|
if not label.generateHtmlContextMessageChildren(context, a):
|
|
return False
|
|
layout.append(a)
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreStatic(self, context, layout):
|
|
label = self.label
|
|
if label is None:
|
|
return False
|
|
a = html.a(
|
|
class_ = 'button', href = self.getLoadResourceUri(context))
|
|
if not label.generateHtmlContextMessageChildren(context, a):
|
|
return False
|
|
layout.append(a)
|
|
return True
|
|
|
|
def generateHtmlContextControlCoreHidden(self, context, layout):
|
|
return False
|
|
|
|
def generateHtmlContextControlLabel(self, context, layout):
|
|
return False
|
|
|
|
def getFieldValue(self, context):
|
|
return None
|
|
|
|
def getLoadResourceUri(self, context):
|
|
loadNodes = self.evaluateXpath("xforms:load[@ev:event='DOMActivate']")
|
|
if not loadNodes:
|
|
return None
|
|
loadNode = loadNodes[0]
|
|
resourceNodes = self.evaluateXpath("@resource", loadNode)
|
|
if not resourceNodes:
|
|
refNodes = self.evaluateXpath("@ref", loadNode)
|
|
if not refNodes:
|
|
return None
|
|
ref = refNodes[0].content
|
|
## resourceNodes = self.getInstanceDataNodes(context, subXpath = ref)
|
|
modelContext = self.getModelContext(context)
|
|
instanceDataNode = self.getInstanceDataNode(context, modelContext)
|
|
if instanceDataNode is None:
|
|
return None
|
|
instance = modelContext.getInstance()
|
|
resourceNodes = instance.evaluateXpath(ref, contextNode = instanceDataNode)
|
|
if not resourceNodes:
|
|
return None
|
|
resource = resourceNodes[0].content
|
|
return context.constructUri(resource)
|
|
|
|
|
|
class Upload(Control):
|
|
|
|
def generateHtmlContextControlCoreForm(self, context, layout):
|
|
modelContext = self.getModelContext(context)
|
|
fieldName = self.getFieldName(context, modelContext)
|
|
computedId = self.getComputedId(context, modelContext)
|
|
layout.append(html.input(
|
|
id_ = computedId, name = fieldName, type = "file"))
|
|
return True
|
|
|
|
|
|
class Value(WidgetElement):
|
|
def generateHtmlContextValueCheckBoxForm(self, context, layout, checkedValues):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue in checkedValues:
|
|
attributes = {"checked": "checked"}
|
|
else:
|
|
attributes = {}
|
|
layout.append(html.input(
|
|
class_ = "xforms-value",
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext), type = "checkbox",
|
|
value = itemValue, **attributes))
|
|
return True
|
|
|
|
def generateHtmlContextValueCheckBoxStatic(self, context, layout, checkedValues):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue in checkedValues:
|
|
class_ = "xforms-value-checkbox-checked"
|
|
else:
|
|
class_ = "xforms-value-checkbox-unchecked"
|
|
layout.append(html.span(class_ = class_))
|
|
return True
|
|
|
|
def generateHtmlContextValueOptionForm(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue == value:
|
|
attributes = {"selected": "selected"}
|
|
else:
|
|
attributes = {}
|
|
option = html.option(
|
|
class_ = "xforms-value", value = itemValue, **attributes)
|
|
layout.append(option)
|
|
self.getParent().label.generateHtmlContextMessageChildren(context, option)
|
|
return True
|
|
|
|
def generateHtmlContextValueOptionStatic(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue == value:
|
|
class_ = "xforms-value-option-selected"
|
|
else:
|
|
class_ = "xforms-value-option-unselected"
|
|
span = html.span(class_ = class_)
|
|
layout.append(span)
|
|
self.getParent().label.generateHtmlContextMessageChildren(context, span)
|
|
return True
|
|
|
|
def generateHtmlContextValueOptionStaticSimple(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue == value:
|
|
span = html.span()
|
|
layout.append(span)
|
|
self.getParent().label.generateHtmlContextMessageChildren(context, span)
|
|
return True
|
|
return False
|
|
|
|
def generateHtmlContextValueRadioForm(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue == value:
|
|
attributes = {"checked": "checked"}
|
|
else:
|
|
attributes = {}
|
|
layout.append(html.input(
|
|
class_ = "xforms-value",
|
|
id = self.getComputedId(context, modelContext),
|
|
name = self.getFieldName(context, modelContext), type = "radio",
|
|
value = itemValue, **attributes))
|
|
return True
|
|
|
|
def generateHtmlContextValueRadioStatic(self, context, layout, value):
|
|
modelContext = self.getModelContext(context)
|
|
realNode = self.getRealNode(context, modelContext)
|
|
itemValue = realNode.content
|
|
if itemValue == value:
|
|
class_ = "xforms-value-radio-checked"
|
|
else:
|
|
class_ = "xforms-value-radio-unchecked"
|
|
layout.append(html.span(class_ = class_))
|
|
return True
|
|
|
|
|
|
elements.registerElement(namespaces.xforms.uri, "alert", Alert)
|
|
elements.registerElement(namespaces.xforms.uri, "bind", Bind)
|
|
elements.registerElement(namespaces.xforms.uri, "choices", Choices)
|
|
elements.registerElement(namespaces.xforms.uri, "group", Group)
|
|
elements.registerElement(namespaces.xforms.uri, "help", Help)
|
|
elements.registerElement(namespaces.xforms.uri, "hint", Hint)
|
|
elements.registerElement(namespaces.xforms.uri, "input", Input)
|
|
elements.registerElement(namespaces.xforms.uri, "instance", Instance)
|
|
elements.registerElement(namespaces.xforms.uri, "item", Item)
|
|
elements.registerElement(namespaces.xforms.uri, "label", Label)
|
|
elements.registerElement(namespaces.xforms.uri, "model", Model)
|
|
elements.registerElement(namespaces.xforms.uri, "output", Output)
|
|
elements.registerElement(namespaces.xforms.uri, "range", Range)
|
|
elements.registerElement(namespaces.xforms.uri, "repeat", Repeat)
|
|
elements.registerElement(namespaces.xforms.uri, "secret", Secret)
|
|
elements.registerElement(namespaces.xforms.uri, "select", Select)
|
|
elements.registerElement(namespaces.xforms.uri, "select1", Select1)
|
|
elements.registerElement(namespaces.xforms.uri, "submission", Submission)
|
|
elements.registerElement(namespaces.xforms.uri, "submit", Submit)
|
|
elements.registerElement(namespaces.xforms.uri, "textarea", TextArea)
|
|
elements.registerElement(namespaces.xforms.uri, "trigger", Trigger)
|
|
elements.registerElement(namespaces.xforms.uri, "upload", Upload)
|
|
elements.registerElement(namespaces.xforms.uri, "value", Value)
|