1189 lines
40 KiB
Python
1189 lines
40 KiB
Python
# -*- coding: UTF-8 -*-
|
|
|
|
|
|
# Glasnost-XML
|
|
# By: Frederic Peters <fpeters@entrouvert.be>
|
|
# Emmanuel Raviart <eraviart@entrouvert.com>
|
|
#
|
|
# Copyright (C) 2003 Entr'ouvert & Emmanuel Raviart
|
|
# 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.
|
|
|
|
|
|
"""Descriptions Module
|
|
|
|
A description contains all informations, methods, etc, needed to properly
|
|
layout a XML data holder.
|
|
"""
|
|
|
|
|
|
import libxml2
|
|
|
|
import context
|
|
import dataholders
|
|
import elements
|
|
import html
|
|
import locations
|
|
import modules
|
|
import parsers
|
|
import xforms
|
|
|
|
|
|
class Control(elements.Element):
|
|
"""XForms Control."""
|
|
|
|
_inForm = False
|
|
isFieldLabelNeeded = True
|
|
|
|
def fillButtonLayout(self, layout):
|
|
"""Add the control to a buttons bar."""
|
|
|
|
return self.fillFieldValueLayout(self, layout)
|
|
|
|
def fillFieldChildLayout(self, layout):
|
|
filled = False
|
|
relevant = self.relevant
|
|
if not self.inForm and not relevant:
|
|
returnfilled
|
|
if relevant and self.isFieldLabelNeeded:
|
|
labelLayout = self.getFieldLabelLayout()
|
|
layout.append(labelLayout)
|
|
## balloonHelp = slot.getKind().balloonHelp
|
|
## if balloonHelp and not self.isReadOnly(slot):
|
|
## layout += X.div(_class = 'formHelp')(X.asIs(
|
|
## helpTextRegEx.sub(r'<a href="/help/\2">\1</a>', _(balloonHelp))))
|
|
cellLayout = self.getFieldValueLayout()
|
|
## if cellLayout is not None:
|
|
## if isinstance(cellLayout, X.array):
|
|
## tooltipCell = [x for x in cellLayout.children if x][0]
|
|
## else:
|
|
## tooltipCell = cellLayout
|
|
## if self.colSpan:
|
|
## cellLayout.setAttribute('class', ' '.join(
|
|
## [cellLayout.getAttribute('class'), 'fullwidth']))
|
|
|
|
## if slot.getObject().getError(slot.getPath()) and \
|
|
## not context.getVar('hideErrors'):
|
|
## layout.setAttribute('class', ' '.join(
|
|
## [layout.getAttribute('class'), 'error']))
|
|
## layout += self.getErrorLayout(slot, fields)
|
|
if cellLayout is not None:
|
|
layout.append(cellLayout)
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldLayout(self, layout):
|
|
# See WidgetMixin.getEditFieldLayout.
|
|
filled = False
|
|
if not self.inForm and not self.relevant:
|
|
return filled
|
|
divLayout = html.div(class_ = "field")
|
|
if self.fillFieldChildLayout(divLayout):
|
|
layout.append(divLayout)
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
filled = False
|
|
errorLayout = self.getErrorLayout()
|
|
if errorLayout is not None:
|
|
layout.append(errorLayout)
|
|
filled = True
|
|
helpLayout = self.getHelpLayout()
|
|
if helpLayout is not None:
|
|
layout.append(helpLayout)
|
|
filled = True
|
|
layout.append(html.input(
|
|
name = self.fieldName, type = "text", value = self.fieldValue))
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueLayout(self, layout):
|
|
filled = False
|
|
if self.inForm:
|
|
if self.relevant:
|
|
if self.readOnly:
|
|
filled = self.fillFieldValueReadOnlyLayout(layout)
|
|
else:
|
|
filled = self.fillFieldValueFormLayout(layout)
|
|
else:
|
|
filled = self.fillHiddenLayout(layout)
|
|
else:
|
|
if self.relevant:
|
|
filled = self.fillFieldValueStaticLayout(layout)
|
|
return filled
|
|
|
|
def fillFieldValueReadOnlyLayout(self, layout):
|
|
filled = False
|
|
filled = self.fillHiddenLayout(layout) or filled
|
|
filled = self.fillFieldValueStaticLayout(layout) or filled
|
|
return filled
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
filled = False
|
|
htmlValueNodes = self.htmlValueNodes
|
|
if htmlValueNodes:
|
|
for htmlValueNode in htmlValueNodes:
|
|
layout.append(htmlValueNode)
|
|
filled = True
|
|
return filled
|
|
|
|
def fillHiddenLayout(self, layout):
|
|
layout.append(html.input(
|
|
name = self.fieldName, type = "hidden", value = self.fieldValue))
|
|
return True
|
|
|
|
def getAppearance(self):
|
|
nodes = self.evaluateXpath("@appearance")
|
|
if nodes:
|
|
return nodes[0].content
|
|
else:
|
|
return None
|
|
|
|
def getChildInstanceDataXpath(self):
|
|
return self.instanceDataXpath
|
|
|
|
def getErrorLayout(self):
|
|
# FIXME
|
|
return None
|
|
|
|
def getFieldLabelLayout(self):
|
|
# See WidgetMixin.getHtmlFieldLabel
|
|
label = self.label
|
|
if label is None:
|
|
return None
|
|
labelLayout = html.span(
|
|
html.label(_(label), _(":"), " ", for_ = self.fieldName),
|
|
class_ = "label")
|
|
## if slot.getKind().isRequired and self.isInForm() \
|
|
## and not self.isReadOnly(slot):
|
|
## return X.array(label,
|
|
## X.span(_class = 'fieldRequired',
|
|
## title = _('Required'))(_('(Required)')))
|
|
return labelLayout
|
|
|
|
def getFieldName(self):
|
|
return self.instanceDataXpath
|
|
|
|
def getFieldValue(self):
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
fieldValue = ""
|
|
else:
|
|
valueType = self.valueType
|
|
fieldValue = valueType.convertValueNodeToHtmlAttributeValue(
|
|
valueNode)
|
|
return fieldValue
|
|
|
|
def getFieldValueLayout(self):
|
|
# See WidgetMixin.getHtmlFieldValue.
|
|
layout = []
|
|
if not self.fillFieldValueLayout(layout):
|
|
return None
|
|
# We have to enclose the field value in a tag with class 'cell' but we
|
|
# want a nice markup so we can't simply <div class="cell"> v </div> and
|
|
# go away with this.
|
|
attributes = {"class_": "cell"}
|
|
return html.enclose(*layout, **attributes)
|
|
|
|
def getHelpLayout(self):
|
|
return None
|
|
|
|
def getHtmlValueNodes(self):
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
return []
|
|
valueType = self.valueType
|
|
return valueType.convertValueNodeToHtmlNodes(valueNode)
|
|
|
|
def getInstanceDataXpath(self):
|
|
modelIdNodes = self.evaluateXpath("@model")
|
|
if modelIdNodes:
|
|
xpath = ""
|
|
else:
|
|
parentInstanceDataXpath = self.parent.childInstanceDataXpath
|
|
refNodes = self.evaluateXpath("@ref")
|
|
if not refNodes:
|
|
xpath = parentInstanceDataXpath
|
|
else:
|
|
ref = refNodes[0].content
|
|
if parentInstanceDataXpath:
|
|
xpath = "%s/%s" % (parentInstanceDataXpath, ref)
|
|
else:
|
|
xpath = ref
|
|
return xpath
|
|
|
|
def getLabel(self):
|
|
labelNodes = self.evaluateXpath("xforms:label")
|
|
if not labelNodes:
|
|
return None
|
|
labelNode = labelNodes[0]
|
|
refNodes = self.evaluateXpath("@ref", labelNode)
|
|
if refNodes:
|
|
ref = refNodes[0].content
|
|
#labelNodes = self.getValueNodes(subXpath = ref)
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
return None
|
|
currentActionHandler = context.getVar("currentActionHandler")
|
|
labelNodes = currentActionHandler.evaluateXpath(
|
|
ref, contextNode = valueNode)
|
|
if not labelNodes:
|
|
return None
|
|
labelNode = labelNodes[0]
|
|
label = labelNode.content
|
|
translatable = labelNode.nsProp(
|
|
"translatable", "http://abracadabra.entrouvert.org/0.0")
|
|
if translatable in ("1", "true"):
|
|
label = _(label)
|
|
return label
|
|
|
|
def getModel(self):
|
|
modelId = self.modelId
|
|
if modelId is None:
|
|
return None
|
|
return self.dataHolder.getModel(modelId)
|
|
|
|
def getModelId(self):
|
|
modelIdNodes = self.evaluateXpath("@model")
|
|
if modelIdNodes:
|
|
return modelIdNodes[0].content
|
|
if self.parent is None:
|
|
return None
|
|
return self.parent.modelId
|
|
|
|
def getValueNode(self):
|
|
valueNodes = self.getValueNodes()
|
|
if not valueNodes:
|
|
return None
|
|
return valueNodes[0]
|
|
|
|
def getValueNodes(self):
|
|
return context.getVar("currentActionHandler").evaluateXpath(
|
|
self.instanceDataXpath)
|
|
|
|
def getValueType(self):
|
|
return context.getVar("currentActionHandler").getTypeAtXpath(
|
|
self.instanceDataXpath)
|
|
|
|
def isInForm(self):
|
|
if self._inForm:
|
|
return True
|
|
parent = self.parent
|
|
if parent is None:
|
|
return False
|
|
return parent.inForm
|
|
|
|
def isReadOnly(self):
|
|
model = self.model
|
|
if model is not None and model.isReadOnly(self.instanceDataXpath):
|
|
return True
|
|
parent = self.parent
|
|
if parent is None:
|
|
return False
|
|
return parent.readOnly
|
|
|
|
def isRelevant(self):
|
|
model = self.model
|
|
if model is not None \
|
|
and not model.isRelevant(self.instanceDataXpath):
|
|
return False
|
|
parent = self.parent
|
|
if parent is None:
|
|
return True
|
|
return parent.relevant
|
|
|
|
def setFieldValue(self, fieldValue):
|
|
valueNode = self.valueNode
|
|
# FIXME
|
|
# assert valueNode is not None
|
|
# FIXME: Handle None fieldValue properly.
|
|
if valueNode is not None and fieldValue is not None:
|
|
valueNode.setContent(fieldValue)
|
|
|
|
def setInForm(self, inForm):
|
|
self._inForm = inForm
|
|
|
|
def setValueNode(self, valueNode):
|
|
nodes = context.getVar("currentActionHandler").evaluateXpath(
|
|
self.instanceDataXpath)
|
|
if nodes:
|
|
node = nodes[0]
|
|
node.replaceNode(valueNode)
|
|
node.freeNode()
|
|
else:
|
|
self.node.addChild(valueNode)
|
|
|
|
def submit(self):
|
|
fieldStorage = context.getVar("fieldStorage")
|
|
fieldName = self.fieldName
|
|
if fieldStorage.has_key(fieldName):
|
|
value = fieldStorage.getvalue(fieldName)
|
|
else:
|
|
value = None
|
|
valueType = self.valueType
|
|
if valueType is None:
|
|
context.getVar("logger").info(
|
|
"""Missing type for value "%s" in field "%s".""" % (
|
|
value, fieldName))
|
|
context.getVar("currentActionHandler").setContentAtXpath(
|
|
self.instanceDataXpath,
|
|
valueType.convertFieldStorageValueToContent(value))
|
|
|
|
appearance = property(getAppearance)
|
|
childInstanceDataXpath = property(getChildInstanceDataXpath)
|
|
fieldName = property(getFieldName)
|
|
fieldValue = property(getFieldValue, setFieldValue)
|
|
htmlValueNodes = property(getHtmlValueNodes)
|
|
inForm = property(isInForm, setInForm)
|
|
instanceDataXpath = property(getInstanceDataXpath)
|
|
label = property(getLabel)
|
|
model = property(getModel)
|
|
modelId = property(getModelId)
|
|
readOnly = property(isReadOnly)
|
|
relevant = property(isRelevant)
|
|
valueNode = property(getValueNode, setValueNode)
|
|
valueType = property(getValueType)
|
|
|
|
|
|
class ActionButton(Control):
|
|
fieldValue = None
|
|
isFieldLabelNeeded = False
|
|
|
|
def fillButtonLayout(self, layout):
|
|
filled = False
|
|
if not self.relevant:
|
|
return filled
|
|
action = self.action
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
if currentActionHandler.walk([action, 'exists']) \
|
|
and currentActionHandler.walk([action, 'isAuthorized']):
|
|
if self.inForm:
|
|
# self.fieldName doesn't exist for buttons.
|
|
layout.append(html.input(
|
|
class_ = 'button', name = self.action, type = 'submit',
|
|
value = self.label))
|
|
filled = True
|
|
else:
|
|
layout.append(html.a(
|
|
self.label, class_ = 'button',
|
|
href = currentActionHandler.dataHolder.getActionUri(
|
|
self.action)))
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
layout.append(html.a(
|
|
self.label,
|
|
href = currentActionHandler.dataHolder.getActionUri(self.action)))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
layout.append(html.a(
|
|
self.label,
|
|
href = currentActionHandler.dataHolder.getActionUri(self.action)))
|
|
return True
|
|
|
|
def fillHiddenLayout(self, layout):
|
|
return False
|
|
|
|
def getAction(self):
|
|
actionNodes = self.evaluateXpath('yep:action')
|
|
if not actionNodes:
|
|
return None
|
|
actionNode = actionNodes[0]
|
|
refNodes = self.evaluateXpath('@ref', actionNode)
|
|
if not refNodes:
|
|
return actionNodes[0].content
|
|
ref = refNodes[0].content
|
|
#actionNodes = self.getValueNodes(subXpath = ref)
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
return None
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
actionNodes = currentActionHandler.evaluateXpath(
|
|
ref, contextNode = valueNode)
|
|
if not actionNodes:
|
|
return None
|
|
return actionNodes[0].content
|
|
|
|
action = property(getAction)
|
|
|
|
|
|
class Custom(Control):
|
|
def fillFieldValueLayout(self, layout):
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
return getattr(currentActionHandler, self.functionName)(self, layout)
|
|
|
|
def getFunctionName(self):
|
|
nodes = self.evaluateXpath('@function')
|
|
if nodes:
|
|
return nodes[0].content
|
|
else:
|
|
return None
|
|
|
|
def submit(self):
|
|
pass
|
|
|
|
functionName = property(getFunctionName)
|
|
|
|
|
|
class Description(elements.Element):
|
|
externalModelLocations = None
|
|
|
|
def fillInstancePageBodyLayout(self, layout, inForm = False):
|
|
"""Layout page body."""
|
|
|
|
pageName = context.getVar('pageName')
|
|
pageNode = self.getPageNode(pageName)
|
|
if pageNode is None:
|
|
return False
|
|
|
|
bodyNodes = self.evaluateXpath('yep:body', pageNode)
|
|
if bodyNodes:
|
|
controlNodes = self.evaluateXpath('*', bodyNodes[0])
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, dataHolder = self)
|
|
control.inForm = inForm
|
|
control.fillFieldValueLayout(layout)
|
|
return True
|
|
else:
|
|
context.getVar('logger').info(
|
|
"""Missing body description for page named "%s".""" % pageName)
|
|
return False
|
|
|
|
def fillInstancePageLayout(self, layout):
|
|
pageName = context.getVar('pageName')
|
|
pageNode = self.getPageNode(pageName)
|
|
if pageNode is None:
|
|
return False
|
|
inForm = self.instanceLayoutRequiresForm()
|
|
if inForm:
|
|
currentActionHandler = context.getVar('currentActionHandler')
|
|
pageLayout = html.form(
|
|
action = currentActionHandler.dataHolder.getActionUri('submit'),
|
|
enctype = 'multipart/form-data', method = 'post')
|
|
layout.append(pageLayout)
|
|
if not context.getVar('canUseCookie'):
|
|
session = context.getVar('session')
|
|
if session is not None:
|
|
pageLayout.append(html.input(
|
|
name = 'sessionToken', type = 'hidden',
|
|
value = session.dataHolder.localId))
|
|
pageLayout.append(html.input(
|
|
name = 'pageName', type = 'hidden', value = pageName))
|
|
else:
|
|
pageLayout = layout
|
|
|
|
self.fillInstancePageBodyLayout(pageLayout, inForm = inForm)
|
|
|
|
# Layout page buttons bar.
|
|
buttonsBarLayout = html.div(class_ = 'buttons-bar')
|
|
actionButtonsBarLayout = html.span(class_ = 'action-buttons-bar')
|
|
buttonsBarLayout.append(actionButtonsBarLayout)
|
|
actionButtonsBarNodes = self.evaluateXpath(
|
|
'yep:actionButtonsBar', pageNode)
|
|
buttonsBarFilled = False
|
|
if actionButtonsBarNodes:
|
|
controlNodes = self.evaluateXpath(
|
|
'*', actionButtonsBarNodes[0])
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, dataHolder = self)
|
|
control.inForm = inForm
|
|
if control.fillButtonLayout(actionButtonsBarLayout):
|
|
buttonsBarFilled = True
|
|
if buttonsBarFilled:
|
|
pageLayout.append(buttonsBarLayout)
|
|
return True
|
|
|
|
def getModel(self, id):
|
|
modelNodes = self.evaluateXpath('xforms:model[@id = "%s"]' % id)
|
|
if modelNodes:
|
|
modelNode = modelNodes[0]
|
|
return xforms.Model(modelNode, self)
|
|
if self.externalModelLocations:
|
|
for externalModelLocation in self.externalModelLocations:
|
|
externalDescriptions = dataholders.walkToLocation(
|
|
externalModelLocation)
|
|
model = externalDescriptions.getModel(id)
|
|
if model is not None:
|
|
return model
|
|
return None
|
|
|
|
def getPageNames(self):
|
|
pageNameNodes = self.evaluateXpath('yep:page/@name')
|
|
return [pageNameNode.content for pageNameNode in pageNameNodes]
|
|
|
|
def getPageNode(self, pageName):
|
|
if pageName is None:
|
|
pageName = 'view'
|
|
pageNodes = self.evaluateXpath('yep:page[@name = "%s"]' % pageName)
|
|
if not pageNodes:
|
|
context.getVar('logger').info(
|
|
"""Missing description for page named "%s".""" % pageName)
|
|
return None
|
|
return pageNodes[0]
|
|
|
|
def instanceLayoutRequiresForm(self):
|
|
pageName = context.getVar('pageName')
|
|
pageNode = self.getPageNode(pageName)
|
|
if pageNode is None:
|
|
return False
|
|
pageTypeNodes = self.evaluateXpath('@type', contextNode = pageNode)
|
|
if pageTypeNodes:
|
|
pageType = pageTypeNodes[0].content
|
|
else:
|
|
pageType = None
|
|
return pageType == 'form'
|
|
|
|
def loadExternalModels(self):
|
|
externalModelLocationNodes = self.evaluateXpath('xforms:model/@src')
|
|
self.externalModelLocations = [
|
|
externalModelLocationNode.content
|
|
for externalModelLocationNode in externalModelLocationNodes]
|
|
for externalModelLocation in self.externalModelLocations:
|
|
dataholders.walkToLocation(externalModelLocation)
|
|
|
|
def submit(self):
|
|
pageName = context.getVar('pageName')
|
|
pageNode = self.getPageNode(pageName)
|
|
if pageNode is None:
|
|
context.getVar('logger').info(
|
|
"""Missing page named "%s".""" % pageName)
|
|
return
|
|
bodyNodes = self.evaluateXpath('yep:body', pageNode)
|
|
if not bodyNodes:
|
|
context.getVar('logger').info(
|
|
"""Missing body description for page named "%s"."""
|
|
% pageName)
|
|
controlNodes = self.evaluateXpath('*', bodyNodes[0])
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, dataHolder = self)
|
|
control.submit()
|
|
|
|
|
|
class DescriptionHolder(dataholders.Xml):
|
|
def setupXml(self):
|
|
dataholders.Xml.setupXml(self)
|
|
self.rootElement.loadExternalModels()
|
|
|
|
|
|
class Group(Control):
|
|
def fillButtonLayout(self, layout):
|
|
filled = False
|
|
if not self.inForm and not self.relevant:
|
|
return False
|
|
controlNodes = self.evaluateXpath('*')
|
|
for controlNode in controlNodes:
|
|
if controlNode.name == 'label':
|
|
continue
|
|
control = newControl(controlNode, parent = self)
|
|
if control.fillButtonLayout(layout):
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueLayout(self, layout):
|
|
# See ThingMixin.getEditLayout.
|
|
filled = False
|
|
if not self.inForm and not self.relevant:
|
|
return filled
|
|
controlNodes = self.evaluateXpath('*')
|
|
for controlNode in controlNodes:
|
|
if controlNode.name == 'label':
|
|
continue
|
|
control = newControl(controlNode, parent = self)
|
|
if control.fillFieldLayout(layout):
|
|
filled = True
|
|
return filled
|
|
|
|
def submit(self):
|
|
controlNodes = self.evaluateXpath('*')
|
|
for controlNode in controlNodes:
|
|
if controlNode.name == 'label':
|
|
continue
|
|
control = newControl(controlNode, parent = self)
|
|
control.submit()
|
|
|
|
|
|
class Input(Control):
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
if self.appearance == 'title':
|
|
layout.append(html.h2(self.fieldValue))
|
|
return True
|
|
return Control.fillFieldValueStaticLayout(self, layout)
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
filled = False
|
|
errorLayout = self.getErrorLayout()
|
|
if errorLayout is not None:
|
|
layout.append(errorLayout)
|
|
filled = True
|
|
helpLayout = self.getHelpLayout()
|
|
if helpLayout is not None:
|
|
layout.append(helpLayout)
|
|
filled = True
|
|
fieldName = self.fieldName
|
|
valueTypeName = self.valueType.name
|
|
if valueTypeName == 'xsd:date':
|
|
layout.append(html.input(
|
|
id = fieldName, maxlength = '10', name = fieldName,
|
|
size = '10', type = 'text', value = self.fieldValue))
|
|
layout.append(html.img(
|
|
id= '%s_trigger' % fieldName,
|
|
src = locations.getUri('/javascript/jscalendar/img.png'),
|
|
style = 'cursor: pointer;',
|
|
title = _('Date Selector'),
|
|
onload = """Calendar.setup({
|
|
inputField: "%s",
|
|
ifFormat: "%%d/%%m/%%Y",
|
|
button: "%s_trigger",
|
|
singleClick: true
|
|
})
|
|
""" % (fieldName, fieldName),
|
|
onmouseout="this.style.background=''",
|
|
onmouseover = "this.style.background='';"))
|
|
else:
|
|
layout.append(html.input(
|
|
name = fieldName, type = "text", value = self.fieldValue))
|
|
filled = True
|
|
return filled
|
|
|
|
# FIXME: Label is not a control.
|
|
class Label(Control):
|
|
def fillFieldChildLayout(self, layout):
|
|
label = self.label
|
|
if label is None:
|
|
label = N_("Unknown Label")
|
|
layout.append(label)
|
|
return True
|
|
|
|
|
|
class Repeat(Control):
|
|
index = None
|
|
fieldValue = None
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
## kind = slot.getKind()
|
|
## countName = slot.getFieldOptionName('count')
|
|
## count = kind.getItemsCount(slot, fields)
|
|
countName = "FIXME"
|
|
count = 0
|
|
valueNodes = self.getValueNodes()
|
|
count = max(1, count, len(valueNodes))
|
|
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
|
|
layout.append(html.input(
|
|
name = countName, type = "hidden", value = str(count)))
|
|
|
|
## if self.inline:
|
|
## fieldset = X.ul(_class = 'multi inline')
|
|
## else:
|
|
## fieldset = X.ul(_class = 'multi')
|
|
ul = html.ul(class_ = "multi")
|
|
layout.append(html.div(ul))
|
|
|
|
for i in range(count):
|
|
if i >= len(valueNodes):
|
|
self.index = None
|
|
else:
|
|
self.index = i
|
|
controlNodes = self.evaluateXpath("*")
|
|
li = html.li()
|
|
ul.append(li)
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, parent = self)
|
|
if control.isFieldLabelNeeded:
|
|
control.fillFieldLayout(li)
|
|
else:
|
|
control.fillFieldValueLayout(li)
|
|
|
|
## if kind.canAddItem(slot, fields):
|
|
## addButtonName = slot.getFieldOptionName('addButton')
|
|
## layout += X.buttonInForm('add', addButtonName)
|
|
## if self.apply:
|
|
## layout += X.buttonInForm('apply', 'applyButton')
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
filled = False
|
|
valueNodes = self.getValueNodes()
|
|
if not valueNodes:
|
|
return filled
|
|
ul = html.ul(class_ = "multi")
|
|
for i in range(len(valueNodes)):
|
|
self.index = i
|
|
controlNodes = self.evaluateXpath("*")
|
|
li = html.li()
|
|
ul.append(li)
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, parent = self)
|
|
if control.isFieldLabelNeeded:
|
|
if control.fillFieldLayout(li):
|
|
filled = True
|
|
else:
|
|
if control.fillFieldValueLayout(li):
|
|
filled = True
|
|
if filled:
|
|
layout.append(ul)
|
|
return filled
|
|
|
|
def fillHiddenLayout(self, layout):
|
|
filled = False
|
|
countName = "FIXME"
|
|
count = 0
|
|
valueNodes = self.getValueNodes()
|
|
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
|
|
controlNodes = self.evaluateXpath("*")
|
|
for controlNode in controlNodes:
|
|
control = newControl(controlNode, parent = self)
|
|
if control.fillHiddenLayout(layout):
|
|
filled = True
|
|
return filled
|
|
|
|
def getChildInstanceDataXpath(self):
|
|
return "%s[%d]" % (self.instanceDataXpath, self.index + 1)
|
|
|
|
childInstanceDataXpath = property(getChildInstanceDataXpath)
|
|
|
|
|
|
class Secret(Control):
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
layout.append(html.input(
|
|
name = self.fieldName, type = "password", value = self.fieldValue))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
layout.append("*" * len(self.fieldValue))
|
|
return True
|
|
|
|
|
|
class Select(Control):
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
ul = html.ul()
|
|
layout.append(ul)
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
value = None
|
|
else:
|
|
value = valueNode.content
|
|
if value is None:
|
|
itemValues = []
|
|
else:
|
|
itemValues = value.split(" ")
|
|
fieldName = self.fieldName
|
|
itemNodes = self.evaluateXpath("xforms:item")
|
|
for itemNode in itemNodes:
|
|
itemValueNodes = self.evaluateXpath("xforms:value", itemNode)
|
|
if not itemValueNodes:
|
|
continue
|
|
itemValue = itemValueNodes[0].content
|
|
if not itemValue:
|
|
continue
|
|
labelValueNodes = self.evaluateXpath("xforms:label", itemNode)
|
|
if labelValueNodes:
|
|
itemLabel = labelValueNodes[0].content
|
|
else:
|
|
itemLabel = None
|
|
if not itemLabel:
|
|
itemLabel = itemValue
|
|
itemFieldName = "%s_%s" % (fieldName, itemValue)
|
|
if itemValue in itemValues:
|
|
attributes = {"checked": "checked"}
|
|
else:
|
|
attributes = {}
|
|
ul.append(html.li(
|
|
html.input(
|
|
name = itemFieldName, type = "checkbox", value = itemValue,
|
|
**attributes),
|
|
html.label(_(itemLabel), for_ = itemFieldName)))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
filled = False
|
|
ul = html.ul()
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
value = None
|
|
else:
|
|
value = valueNode.content
|
|
if value is None:
|
|
itemValues = []
|
|
else:
|
|
itemValues = value.split(" ")
|
|
itemNodes = self.evaluateXpath("xforms:item")
|
|
for itemNode in itemNodes:
|
|
itemValueNodes = self.evaluateXpath("xforms:value", itemNode)
|
|
if not itemValueNodes:
|
|
continue
|
|
itemValue = itemValueNodes[0].content
|
|
if not itemValue in itemValues:
|
|
continue
|
|
labelValueNodes = self.evaluateXpath("xforms:label", itemNode)
|
|
if labelValueNodes:
|
|
itemLabel = labelValueNodes[0].content
|
|
else:
|
|
itemLabel = None
|
|
if not itemLabel:
|
|
itemLabel = itemValue
|
|
ul.append(html.li(_(itemLabel)))
|
|
filled = True
|
|
if filled:
|
|
layout.append(ul)
|
|
return filled
|
|
|
|
def submit(self):
|
|
fieldStorage = context.getVar("fieldStorage")
|
|
fieldName = self.fieldName
|
|
itemNodes = self.evaluateXpath("xforms:item")
|
|
splitedValue = []
|
|
for itemNode in itemNodes:
|
|
itemValueNodes = self.evaluateXpath("xforms:value", itemNode)
|
|
if not itemValueNodes:
|
|
continue
|
|
itemValue = itemValueNodes[0].content
|
|
if not itemValue:
|
|
continue
|
|
itemFieldName = "%s_%s" % (fieldName, itemValue)
|
|
if fieldStorage.has_key(itemFieldName) \
|
|
and fieldStorage.getvalue(itemFieldName) == itemValue:
|
|
splitedValue.append(itemValue)
|
|
context.getVar("currentActionHandler").setContentAtXpath(
|
|
self.instanceDataXpath, " ".join(splitedValue))
|
|
|
|
|
|
class Select1(Control):
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
appearance = self.appearance
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
value = None
|
|
else:
|
|
value = valueNode.content
|
|
fieldName = self.fieldName
|
|
if appearance == "full":
|
|
ul = html.ul()
|
|
layout.append(ul)
|
|
else: # appearance == "minimal"
|
|
select = html.select(name = fieldName)
|
|
layout.append(select)
|
|
itemNodes = self.evaluateXpath("xforms:item")
|
|
for itemNode in itemNodes:
|
|
itemValueNodes = self.evaluateXpath("xforms:value", itemNode)
|
|
if not itemValueNodes:
|
|
continue
|
|
itemValue = itemValueNodes[0].content
|
|
if not itemValue:
|
|
continue
|
|
labelValueNodes = self.evaluateXpath("xforms:label", itemNode)
|
|
if labelValueNodes:
|
|
itemLabel = labelValueNodes[0].content
|
|
else:
|
|
itemLabel = None
|
|
if not itemLabel:
|
|
itemLabel = itemValue
|
|
if appearance == "full":
|
|
if itemValue == value:
|
|
attributes = {"checked": "checked"}
|
|
else:
|
|
attributes = {}
|
|
ul.append(html.li(
|
|
html.input(
|
|
name = fieldName, type = "radio", value = itemValue,
|
|
**attributes),
|
|
html.label(_(itemLabel), for_ = fieldName)))
|
|
else: # appearance == "minimal"
|
|
if itemValue == value:
|
|
attributes = {"selected": "selected"}
|
|
else:
|
|
attributes = {}
|
|
select.append(html.option(
|
|
_(itemLabel), value = itemValue, **attributes))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
value = None
|
|
else:
|
|
value = valueNode.content
|
|
labelNodes = self.evaluateXpath(
|
|
"""xforms:item[xforms:value = "%s"]/xforms:label""" % value)
|
|
if labelNodes:
|
|
label = labelNodes[0].content
|
|
else:
|
|
label = value
|
|
layout.append(_(label))
|
|
return True
|
|
|
|
def submit(self):
|
|
fieldStorage = context.getVar("fieldStorage")
|
|
fieldName = self.fieldName
|
|
if fieldStorage.has_key(fieldName):
|
|
value = fieldStorage.getvalue(fieldName)
|
|
else:
|
|
value = ""
|
|
valueNodes = self.evaluateXpath("xforms:item/xforms:value")
|
|
values = [valueNode.content for valueNode in valueNodes]
|
|
if value in values:
|
|
context.getVar("currentActionHandler").setContentAtXpath(
|
|
self.instanceDataXpath, value)
|
|
|
|
|
|
class Spip(Control):
|
|
def getFieldLabelLayout(self):
|
|
return None
|
|
|
|
def fillFieldChildLayout(self, layout):
|
|
filled = False
|
|
cellLayout = self.getFieldValueLayout()
|
|
if cellLayout is not None:
|
|
layout.append(cellLayout)
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueLayout(self, layout):
|
|
return self.fillFieldValueStaticLayout(layout)
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
filled = False
|
|
content = self.node.content
|
|
if content:
|
|
htmlText = "<spip>%s</spip>" % parsers.makeHtmlFromSpip(content)
|
|
doc = libxml2.htmlParseDoc(htmlText, 'UTF-8')
|
|
node = doc.getRootElement().children
|
|
while node is not None:
|
|
layout.append(node)
|
|
node = node.next
|
|
filled = True
|
|
return filled
|
|
|
|
def submit(self):
|
|
pass
|
|
|
|
|
|
class Submit(Control):
|
|
fieldValue = None
|
|
isFieldLabelNeeded = False
|
|
|
|
def fillButtonLayout(self, layout):
|
|
filled = False
|
|
if not self.relevant:
|
|
return filled
|
|
# Unlike Submit Control, a Trigger never submits the form even inside
|
|
# a form.
|
|
if self.inForm:
|
|
# self.fieldName doesn't exist for buttons.
|
|
## layout.append(html.input(
|
|
## class_ = "button", name = self.fieldName, type = "submit",
|
|
## value = self.label))
|
|
layout.append(html.input(
|
|
class_ = "button", name = self.getSubmissionId(),
|
|
type = "submit", value = self.label))
|
|
filled = True
|
|
else:
|
|
layout.append(html.a(
|
|
self.label, class_ = "button",
|
|
href = self.getSubmissionActionUri()))
|
|
filled = True
|
|
return filled
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
layout.append(html.a(self.label, href = self.getSubmissionActionUri()))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
layout.append(html.a(self.label, href = self.getSubmissionActionUri()))
|
|
return True
|
|
|
|
def fillHiddenLayout(self, layout):
|
|
return False
|
|
|
|
def getSubmissionActionUri(self):
|
|
submissionId = self.getSubmissionId()
|
|
submissionNode = self.model.getSubmission(submissionId)
|
|
if submissionNode is None:
|
|
return None
|
|
actionNodes = self.evaluateXpath("@action", submissionNode)
|
|
if not actionNodes:
|
|
return None
|
|
return locations.getUri(actionNodes[0].content)
|
|
|
|
def getSubmissionId(self):
|
|
submissionIdNodes = self.evaluateXpath("@submission")
|
|
if submissionIdNodes:
|
|
submissionId = submissionIdNodes[0].content
|
|
else:
|
|
submissionId = None
|
|
return submissionId
|
|
|
|
|
|
class TextArea(Control):
|
|
def fillFieldValueFormLayout(self, layout):
|
|
filled = False
|
|
errorLayout = self.getErrorLayout()
|
|
if errorLayout is not None:
|
|
layout.append(errorLayout)
|
|
filled = True
|
|
helpLayout = self.getHelpLayout()
|
|
if helpLayout is not None:
|
|
layout.append(helpLayout)
|
|
filled = True
|
|
if self.appearance == "full":
|
|
attributes = {"cols": "80", "rows": "10"}
|
|
else:
|
|
attributes = {}
|
|
layout.append(html.textarea(
|
|
self.fieldValue, name = self.fieldName, **attributes))
|
|
filled = True
|
|
return filled
|
|
|
|
def getFieldValueLayout(self):
|
|
layout = Control.getFieldValueLayout(self)
|
|
if layout is None:
|
|
return None
|
|
if self.appearance == "full":
|
|
layoutClass = layout.getAttribute("class")
|
|
if layoutClass:
|
|
layoutClass = "%s %s" % (layoutClass, "fullwidth")
|
|
else:
|
|
layoutClass = "fullwidth"
|
|
layout.setAttribute("class", layoutClass)
|
|
return layout
|
|
|
|
|
|
class Trigger(Control):
|
|
fieldValue = None
|
|
isFieldLabelNeeded = False
|
|
|
|
def fillButtonLayout(self, layout):
|
|
if not self.relevant:
|
|
return False
|
|
# Unlike Submit Control, a Trigger never submits the form even inside
|
|
# a form.
|
|
layout.append(html.a(
|
|
self.label, class_ = "button",
|
|
href = self.getLoadResourceUri()))
|
|
return True
|
|
|
|
def fillFieldValueFormLayout(self, layout):
|
|
layout.append(self.getErrorLayout())
|
|
layout.append(self.getHelpLayout())
|
|
layout.append(html.a(self.label, href = self.getLoadResourceUri()))
|
|
return True
|
|
|
|
def fillFieldValueStaticLayout(self, layout):
|
|
layout.append(html.a(self.label, href = self.getLoadResourceUri()))
|
|
return True
|
|
|
|
def fillHiddenLayout(self, layout):
|
|
return False
|
|
|
|
def getLoadResourceUri(self):
|
|
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.getValueNodes(subXpath = ref)
|
|
valueNode = self.valueNode
|
|
if valueNode is None:
|
|
return None
|
|
currentActionHandler = context.getVar("currentActionHandler")
|
|
resourceNodes = currentActionHandler.evaluateXpath(
|
|
ref, contextNode = valueNode)
|
|
if not resourceNodes:
|
|
return None
|
|
resource = resourceNodes[0].content
|
|
return locations.getUri(resource)
|
|
|
|
|
|
controlClasses = {
|
|
"actionButton": ActionButton,
|
|
"custom": Custom,
|
|
"spip": Spip,
|
|
"xforms:group": Group,
|
|
"xforms:input": Input,
|
|
"xforms:label": Label,
|
|
"xforms:repeat": Repeat,
|
|
"xforms:secret": Secret,
|
|
"xforms:select": Select,
|
|
"xforms:select1": Select1,
|
|
"xforms:textarea": TextArea,
|
|
"xforms:trigger": Trigger,
|
|
"xforms:submit": Submit,
|
|
}
|
|
namespaceNames = {
|
|
"http://abracadabra.entrouvert.org/0.0": None,
|
|
"http://www.w3.org/2002/xforms": "xforms",
|
|
}
|
|
|
|
|
|
def newControl(node, dataHolder = None, parent = None):
|
|
name = None
|
|
while True:
|
|
namespaceUri = node.ns().content
|
|
if not namespaceUri in namespaceNames:
|
|
raise Exception(
|
|
"Unknown XML namespace URI = \"%s\"" % namespaceUri)
|
|
namespaceName = namespaceNames[namespaceUri]
|
|
if namespaceName is None:
|
|
name = node.name
|
|
else:
|
|
name = "%s:%s" % (namespaceName, node.name)
|
|
if name != "copy":
|
|
break
|
|
# The node is a copy of a real node => get real node instead.
|
|
if dataHolder is None:
|
|
description = parent.dataHolder
|
|
else:
|
|
description = dataHolder
|
|
originalIdNodes = description.evaluateXpath("@original", node)
|
|
originalId = originalIdNodes[0].content
|
|
originalNodes = description.evaluateXpath(
|
|
"//*[@yep:id = '%s']" % originalId)
|
|
node = originalNodes[0]
|
|
return controlClasses[name](
|
|
node, dataHolder = dataHolder, parent = parent)
|
|
|
|
modules.registerElementClass(
|
|
"http://abracadabra.entrouvert.org/0.0", "description", Description)
|
|
|
|
modules.registerXmlClass("description", DescriptionHolder)
|