Improved management of indexes; always provide str and not unicode strings as index values; search screen may now include javascripts and css like those required by the date chooser; removed CSS parser, basic XmlUnmarshaller can do it by itself.
This commit is contained in:
parent
2124cffa5e
commit
a30949a621
|
@ -688,7 +688,13 @@ class Type:
|
||||||
if not self.editDefault:
|
if not self.editDefault:
|
||||||
# Return self.default, of self.default() if it is a method
|
# Return self.default, of self.default() if it is a method
|
||||||
if type(self.default) == types.FunctionType:
|
if type(self.default) == types.FunctionType:
|
||||||
return self.default(obj.appy())
|
appyObj = obj.appy()
|
||||||
|
try:
|
||||||
|
return self.default(appyObj)
|
||||||
|
except Exception, e:
|
||||||
|
appyObj.log('Exception while getting default value ' \
|
||||||
|
'of field "%s": %s.' % (self.name, str(e)))
|
||||||
|
return None
|
||||||
else:
|
else:
|
||||||
return self.default
|
return self.default
|
||||||
# If value is editable, get the default value from the tool
|
# If value is editable, get the default value from the tool
|
||||||
|
@ -704,6 +710,11 @@ class Type:
|
||||||
if self.isEmptyValue(value): return ''
|
if self.isEmptyValue(value): return ''
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
def getIndexType(self):
|
||||||
|
'''Returns the name of the technical, Zope-level index type for this
|
||||||
|
field.'''
|
||||||
|
return 'FieldIndex'
|
||||||
|
|
||||||
def getIndexValue(self, obj, forSearch=False):
|
def getIndexValue(self, obj, forSearch=False):
|
||||||
'''This method returns a version for this field value on p_obj that is
|
'''This method returns a version for this field value on p_obj that is
|
||||||
ready for indexing purposes. Needs to be overridden by some child
|
ready for indexing purposes. Needs to be overridden by some child
|
||||||
|
@ -1130,6 +1141,13 @@ class String(Type):
|
||||||
res = str(value)
|
res = str(value)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def getIndexValue(self, obj, forSearch=False):
|
||||||
|
'''For indexing purposes, we return only strings, not unicodes.'''
|
||||||
|
res = Type.getIndexValue(self, obj, forSearch)
|
||||||
|
if isinstance(res, unicode):
|
||||||
|
res = res.encode('utf-8')
|
||||||
|
return res
|
||||||
|
|
||||||
def getPossibleValues(self,obj,withTranslations=False,withBlankValue=False):
|
def getPossibleValues(self,obj,withTranslations=False,withBlankValue=False):
|
||||||
'''Returns the list of possible values for this field if it is a
|
'''Returns the list of possible values for this field if it is a
|
||||||
selection field. If p_withTranslations is True,
|
selection field. If p_withTranslations is True,
|
||||||
|
@ -1236,6 +1254,14 @@ class String(Type):
|
||||||
value = [value]
|
value = [value]
|
||||||
exec 'obj.%s = value' % self.name
|
exec 'obj.%s = value' % self.name
|
||||||
|
|
||||||
|
def getIndexType(self):
|
||||||
|
'''Index type varies depending on String parameters.'''
|
||||||
|
# If String.isSelect, be it multivalued or not, we define a ZCTextIndex:
|
||||||
|
# this way we can use AND/OR operator.
|
||||||
|
if self.isSelect or (self.format in (String.TEXT, String.XHTML)):
|
||||||
|
return 'ZCTextIndex'
|
||||||
|
return Type.getIndexType(self)
|
||||||
|
|
||||||
class Boolean(Type):
|
class Boolean(Type):
|
||||||
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
def __init__(self, validator=None, multiplicity=(0,1), index=None,
|
||||||
default=None, optional=False, editDefault=False, show=True,
|
default=None, optional=False, editDefault=False, show=True,
|
||||||
|
@ -1291,10 +1317,11 @@ class Date(Type):
|
||||||
masterValue, focus, historized, True)
|
masterValue, focus, historized, True)
|
||||||
|
|
||||||
def getCss(self, layoutType):
|
def getCss(self, layoutType):
|
||||||
if layoutType == 'edit': return ('jscalendar/calendar-system.css',)
|
if (layoutType == 'edit') and self.calendar:
|
||||||
|
return ('jscalendar/calendar-system.css',)
|
||||||
|
|
||||||
def getJs(self, layoutType):
|
def getJs(self, layoutType):
|
||||||
if layoutType == 'edit':
|
if (layoutType == 'edit') and self.calendar:
|
||||||
return ('jscalendar/calendar_stripped.js',
|
return ('jscalendar/calendar_stripped.js',
|
||||||
'jscalendar/calendar-en.js')
|
'jscalendar/calendar-en.js')
|
||||||
|
|
||||||
|
|
|
@ -247,6 +247,8 @@ class PloneInstaller:
|
||||||
appyType.template)
|
appyType.template)
|
||||||
if os.path.exists(fileName):
|
if os.path.exists(fileName):
|
||||||
setattr(self.appyTool, attrName, fileName)
|
setattr(self.appyTool, attrName, fileName)
|
||||||
|
self.appyTool.log('Imported "%s" in the tool in ' \
|
||||||
|
'attribute "%s"'% (fileName,attrName))
|
||||||
else:
|
else:
|
||||||
self.appyTool.log('Template "%s" was not found!' % \
|
self.appyTool.log('Template "%s" was not found!' % \
|
||||||
fileName, type='error')
|
fileName, type='error')
|
||||||
|
@ -353,7 +355,7 @@ class PloneInstaller:
|
||||||
workflow = wfMethod(self, workflowName)
|
workflow = wfMethod(self, workflowName)
|
||||||
wfTool._setObject(workflowName, workflow)
|
wfTool._setObject(workflowName, workflow)
|
||||||
else:
|
else:
|
||||||
self.log('%s already in workflows.' % workflowName)
|
self.appyTool.log('%s already in workflows.' % workflowName)
|
||||||
# Link the workflow to the current content type
|
# Link the workflow to the current content type
|
||||||
wfTool.setChainForPortalTypes([contentType], workflowName)
|
wfTool.setChainForPortalTypes([contentType], workflowName)
|
||||||
return wfTool
|
return wfTool
|
||||||
|
@ -404,14 +406,10 @@ class PloneInstaller:
|
||||||
indexInfo = {}
|
indexInfo = {}
|
||||||
for className, appyTypes in self.attributes.iteritems():
|
for className, appyTypes in self.attributes.iteritems():
|
||||||
for appyType in appyTypes:
|
for appyType in appyTypes:
|
||||||
if appyType.indexed:
|
if not appyType.indexed or (appyType.name == 'title'): continue
|
||||||
n = appyType.name
|
n = appyType.name
|
||||||
indexName = 'get%s%s' % (n[0].upper(), n[1:])
|
indexName = 'get%s%s' % (n[0].upper(), n[1:])
|
||||||
indexType = 'FieldIndex'
|
indexInfo[indexName] = appyType.getIndexType()
|
||||||
if (appyType.type == 'String') and appyType.isSelect and \
|
|
||||||
appyType.isMultiValued():
|
|
||||||
indexType = 'ZCTextIndex'
|
|
||||||
indexInfo[indexName] = indexType
|
|
||||||
if indexInfo:
|
if indexInfo:
|
||||||
PloneInstaller.updateIndexes(self.ploneSite, indexInfo, self)
|
PloneInstaller.updateIndexes(self.ploneSite, indexInfo, self)
|
||||||
|
|
||||||
|
@ -454,11 +452,9 @@ class PloneInstaller:
|
||||||
frontPageName = self.productName + 'FrontPage'
|
frontPageName = self.productName + 'FrontPage'
|
||||||
site.manage_changeProperties(default_page=frontPageName)
|
site.manage_changeProperties(default_page=frontPageName)
|
||||||
|
|
||||||
def log(self, msg): print msg
|
def info(self, msg): return self.appyTool.log(msg)
|
||||||
def info(self, msg): return self.log(msg)
|
|
||||||
|
|
||||||
def install(self):
|
def install(self):
|
||||||
self.log("Installation of %s:" % self.productName)
|
|
||||||
if self.minimalistPlone: self.customizePlone()
|
if self.minimalistPlone: self.customizePlone()
|
||||||
self.installRootFolder()
|
self.installRootFolder()
|
||||||
self.installTypes()
|
self.installTypes()
|
||||||
|
@ -470,7 +466,7 @@ class PloneInstaller:
|
||||||
self.manageIndexes()
|
self.manageIndexes()
|
||||||
self.manageLanguages()
|
self.manageLanguages()
|
||||||
self.finalizeInstallation()
|
self.finalizeInstallation()
|
||||||
self.log("Installation of %s done." % self.productName)
|
self.appyTool.log("Installation of %s done." % self.productName)
|
||||||
|
|
||||||
def uninstallTool(self):
|
def uninstallTool(self):
|
||||||
site = self.ploneSite
|
site = self.ploneSite
|
||||||
|
@ -496,10 +492,8 @@ class PloneInstaller:
|
||||||
nvProps.manage_changeProperties(**{'idsNotToList': current})
|
nvProps.manage_changeProperties(**{'idsNotToList': current})
|
||||||
|
|
||||||
def uninstall(self):
|
def uninstall(self):
|
||||||
self.log("Uninstallation of %s:" % self.productName)
|
|
||||||
self.uninstallTool()
|
self.uninstallTool()
|
||||||
self.log("Uninstallation of %s done." % self.productName)
|
return 'Done.'
|
||||||
return self.toLog.getvalue()
|
|
||||||
|
|
||||||
# Stuff for tracking user activity ---------------------------------------------
|
# Stuff for tracking user activity ---------------------------------------------
|
||||||
loggedUsers = {}
|
loggedUsers = {}
|
||||||
|
|
|
@ -165,10 +165,12 @@ class ToolMixin(BaseMixin):
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getSearchInfo(self, contentType, refInfo=None):
|
def getSearchInfo(self, contentType, refInfo=None):
|
||||||
'''Returns a tuple:
|
'''Returns, as a dict:
|
||||||
1) The list of searchable fields (ie fields among all indexed fields)
|
- the list of searchable fields (= some fields among all indexed
|
||||||
2) The number of columns for layouting those fields.'''
|
fields);
|
||||||
|
- the number of columns for layouting those fields.'''
|
||||||
fields = []
|
fields = []
|
||||||
|
fieldDicts = []
|
||||||
if refInfo:
|
if refInfo:
|
||||||
# The search is triggered from a Ref field.
|
# The search is triggered from a Ref field.
|
||||||
refField = self.getRefInfo(refInfo)[1]
|
refField = self.getRefInfo(refInfo)[1]
|
||||||
|
@ -180,9 +182,12 @@ class ToolMixin(BaseMixin):
|
||||||
fieldNames = getattr(at, 'searchFieldsFor%s' % contentType,())
|
fieldNames = getattr(at, 'searchFieldsFor%s' % contentType,())
|
||||||
nbOfColumns = getattr(at, 'numberOfSearchColumnsFor%s' %contentType)
|
nbOfColumns = getattr(at, 'numberOfSearchColumnsFor%s' %contentType)
|
||||||
for name in fieldNames:
|
for name in fieldNames:
|
||||||
appyType = self.getAppyType(name, asDict=True,className=contentType)
|
appyType = self.getAppyType(name,asDict=False,className=contentType)
|
||||||
|
appyDict = self.getAppyType(name, asDict=True,className=contentType)
|
||||||
fields.append(appyType)
|
fields.append(appyType)
|
||||||
return fields, nbOfColumns
|
fieldDicts.append(appyDict)
|
||||||
|
return {'fields': fields, 'nbOfColumns': nbOfColumns,
|
||||||
|
'fieldDicts': fieldDicts}
|
||||||
|
|
||||||
def getImportElements(self, contentType):
|
def getImportElements(self, contentType):
|
||||||
'''Returns the list of elements that can be imported from p_path for
|
'''Returns the list of elements that can be imported from p_path for
|
||||||
|
@ -336,13 +341,12 @@ class ToolMixin(BaseMixin):
|
||||||
if not searchName:
|
if not searchName:
|
||||||
# It is the global search for all objects pf p_contentType
|
# It is the global search for all objects pf p_contentType
|
||||||
searchName = contentType
|
searchName = contentType
|
||||||
s = self.REQUEST.SESSION
|
|
||||||
uids = {}
|
uids = {}
|
||||||
i = -1
|
i = -1
|
||||||
for obj in res.objects:
|
for obj in res.objects:
|
||||||
i += 1
|
i += 1
|
||||||
uids[startNumber+i] = obj.UID()
|
uids[startNumber+i] = obj.UID()
|
||||||
s['search_%s' % searchName] = uids
|
self.REQUEST.SESSION['search_%s' % searchName] = uids
|
||||||
return res.__dict__
|
return res.__dict__
|
||||||
|
|
||||||
def getResultColumnsNames(self, contentType, refField):
|
def getResultColumnsNames(self, contentType, refField):
|
||||||
|
@ -452,15 +456,6 @@ class ToolMixin(BaseMixin):
|
||||||
return pythonClass.maySearch(self.appy())
|
return pythonClass.maySearch(self.appy())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def userMayNavigate(self, obj):
|
|
||||||
'''This method checks if the currently logged user can display the
|
|
||||||
navigation panel within the portlet. This is done by calling method
|
|
||||||
"mayNavigate" on the currently shown object. If no such method
|
|
||||||
exists, we return True.'''
|
|
||||||
appyObj = obj.appy()
|
|
||||||
if hasattr(appyObj, 'mayNavigate'): return appyObj.mayNavigate()
|
|
||||||
return True
|
|
||||||
|
|
||||||
def onImportObjects(self):
|
def onImportObjects(self):
|
||||||
'''This method is called when the user wants to create objects from
|
'''This method is called when the user wants to create objects from
|
||||||
external data.'''
|
external data.'''
|
||||||
|
@ -614,7 +609,7 @@ class ToolMixin(BaseMixin):
|
||||||
field, this method returns information about this reference: the
|
field, this method returns information about this reference: the
|
||||||
source content type and the Ref field (Appy type). If p_refInfo is
|
source content type and the Ref field (Appy type). If p_refInfo is
|
||||||
not given, we search it among search criteria in the session.'''
|
not given, we search it among search criteria in the session.'''
|
||||||
if not refInfo:
|
if not refInfo and (self.REQUEST.get('search', None) == '_advanced'):
|
||||||
criteria = self.REQUEST.SESSION.get('searchCriteria', None)
|
criteria = self.REQUEST.SESSION.get('searchCriteria', None)
|
||||||
if criteria and criteria.has_key('_ref'): refInfo = criteria['_ref']
|
if criteria and criteria.has_key('_ref'): refInfo = criteria['_ref']
|
||||||
if not refInfo: return ('', None)
|
if not refInfo: return ('', None)
|
||||||
|
@ -847,6 +842,8 @@ class ToolMixin(BaseMixin):
|
||||||
session.invalidate()
|
session.invalidate()
|
||||||
from Products.CMFPlone import transaction_note
|
from Products.CMFPlone import transaction_note
|
||||||
transaction_note('Logged out')
|
transaction_note('Logged out')
|
||||||
|
self.getProductConfig().logger.info('User "%s" has been logged out.' % \
|
||||||
|
userId)
|
||||||
# Remove user from variable "loggedUsers"
|
# Remove user from variable "loggedUsers"
|
||||||
from appy.gen.plone25.installer import loggedUsers
|
from appy.gen.plone25.installer import loggedUsers
|
||||||
if loggedUsers.has_key(userId): del loggedUsers[userId]
|
if loggedUsers.has_key(userId): del loggedUsers[userId]
|
||||||
|
|
|
@ -276,16 +276,22 @@ class BaseMixin:
|
||||||
res[appyType.name] = appyType.getValue(self)
|
res[appyType.name] = appyType.getValue(self)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def addDataChange(self, changes):
|
def addDataChange(self, changes, notForPreviouslyEmptyValues=False):
|
||||||
'''This method allows to add "manually" a data change into the objet's
|
'''This method allows to add "manually" a data change into the objet's
|
||||||
history. Indeed, data changes are "automatically" recorded only when
|
history. Indeed, data changes are "automatically" recorded only when
|
||||||
a HTTP form is uploaded, not if, in the code, a setter is called on
|
a HTTP form is uploaded, not if, in the code, a setter is called on
|
||||||
a field. The method is also called by the method historizeData below,
|
a field. The method is also called by m_historizeData below, that
|
||||||
that performs "automatic" recording when a HTTP form is uploaded.'''
|
performs "automatic" recording when a HTTP form is uploaded. Field
|
||||||
|
changes for which the previous value was empty are not recorded into
|
||||||
|
the history if p_notForPreviouslyEmptyValues is True.'''
|
||||||
# Add to the p_changes dict the field labels
|
# Add to the p_changes dict the field labels
|
||||||
for fieldName in changes.iterkeys():
|
for fieldName in changes.keys():
|
||||||
appyType = self.getAppyType(fieldName)
|
appyType = self.getAppyType(fieldName)
|
||||||
changes[fieldName] = (changes[fieldName], appyType.labelId)
|
if notForPreviouslyEmptyValues and \
|
||||||
|
appyType.isEmptyValue(changes[fieldName], self):
|
||||||
|
del changes[fieldName]
|
||||||
|
else:
|
||||||
|
changes[fieldName] = (changes[fieldName], appyType.labelId)
|
||||||
# Create the event to record in the history
|
# Create the event to record in the history
|
||||||
DateTime = self.getProductConfig().DateTime
|
DateTime = self.getProductConfig().DateTime
|
||||||
state = self.portal_workflow.getInfoFor(self, 'review_state')
|
state = self.portal_workflow.getInfoFor(self, 'review_state')
|
||||||
|
@ -474,21 +480,23 @@ class BaseMixin:
|
||||||
res.append(appyType)
|
res.append(appyType)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getCssAndJs(self, layoutType, page):
|
def getCssAndJs(self, fields, layoutType):
|
||||||
'''Returns the CSS and Javascript files that need to be loaded by the
|
'''Gets the list of Javascript and CSS files required by Appy types
|
||||||
p_page for the given p_layoutType.'''
|
p_fields when shown on p_layoutType.'''
|
||||||
|
# lists css and js below are not sets, because order of Javascript
|
||||||
|
# inclusion can be important, and this could be losed by using sets.
|
||||||
css = []
|
css = []
|
||||||
js = []
|
js = []
|
||||||
for appyType in self.getAppyTypes(layoutType, page):
|
for field in fields:
|
||||||
typeCss = appyType.getCss(layoutType)
|
fieldCss = field.getCss(layoutType)
|
||||||
if typeCss:
|
if fieldCss:
|
||||||
for tcss in typeCss:
|
for fcss in fieldCss:
|
||||||
if tcss not in css: css.append(tcss)
|
if fcss not in css: css.append(fcss)
|
||||||
typeJs = appyType.getJs(layoutType)
|
fieldJs = field.getJs(layoutType)
|
||||||
if typeJs:
|
if fieldJs:
|
||||||
for tjs in typeJs:
|
for fjs in fieldJs:
|
||||||
if tjs not in js: js.append(tjs)
|
if fjs not in js: js.append(fjs)
|
||||||
return css, js
|
return {'css':css, 'js':js}
|
||||||
|
|
||||||
def getAppyTypesFromNames(self, fieldNames, asDict=True, addTitle=True):
|
def getAppyTypesFromNames(self, fieldNames, asDict=True, addTitle=True):
|
||||||
'''Gets the Appy types named p_fieldNames. If 'title' is not among
|
'''Gets the Appy types named p_fieldNames. If 'title' is not among
|
||||||
|
@ -766,12 +774,27 @@ class BaseMixin:
|
||||||
res = True
|
res = True
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
def mayNavigate(self):
|
||||||
|
'''May the currently logged user see the navigation panel linked to
|
||||||
|
this object?'''
|
||||||
|
appyObj = self.appy()
|
||||||
|
if hasattr(appyObj, 'mayNavigate'): return appyObj.mayNavigate()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def mayDelete(self):
|
||||||
|
'''May the currently logged user delete this object? This condition
|
||||||
|
comes as an addition/refinement to the corresponding workflow
|
||||||
|
permission.'''
|
||||||
|
appyObj = self.appy()
|
||||||
|
if hasattr(appyObj, 'mayDelete'): return appyObj.mayDelete()
|
||||||
|
return True
|
||||||
|
|
||||||
def executeAppyAction(self, actionName, reindex=True):
|
def executeAppyAction(self, actionName, reindex=True):
|
||||||
'''Executes action with p_fieldName on this object.'''
|
'''Executes action with p_fieldName on this object.'''
|
||||||
appyType = self.getAppyType(actionName)
|
appyType = self.getAppyType(actionName)
|
||||||
actionRes = appyType(self.appy())
|
actionRes = appyType(self.appy())
|
||||||
if self.getParentNode().get(self.id):
|
if self.getParentNode().get(self.id):
|
||||||
# Else, it means that the action has led to self's removal.
|
# Else, it means that the action has led to self's deletion.
|
||||||
self.reindexObject()
|
self.reindexObject()
|
||||||
return appyType.result, actionRes
|
return appyType.result, actionRes
|
||||||
|
|
||||||
|
@ -869,7 +892,17 @@ class BaseMixin:
|
||||||
blank value is prepended to the list. If no p_className is defined,
|
blank value is prepended to the list. If no p_className is defined,
|
||||||
the field is supposed to belong to self's class'''
|
the field is supposed to belong to self's class'''
|
||||||
appyType = self.getAppyType(name, className=className)
|
appyType = self.getAppyType(name, className=className)
|
||||||
return appyType.getPossibleValues(self,withTranslations,withBlankValue)
|
if className:
|
||||||
|
# We need an instance of className, but self can be an instance of
|
||||||
|
# another class. So here we will search such an instance.
|
||||||
|
brains = self.executeQuery(className, maxResults=1, brainsOnly=True)
|
||||||
|
if brains:
|
||||||
|
obj = brains[0].getObject()
|
||||||
|
else:
|
||||||
|
obj = self
|
||||||
|
else:
|
||||||
|
obj = self
|
||||||
|
return appyType.getPossibleValues(obj, withTranslations, withBlankValue)
|
||||||
|
|
||||||
def appy(self):
|
def appy(self):
|
||||||
'''Returns a wrapper object allowing to manipulate p_self the Appy
|
'''Returns a wrapper object allowing to manipulate p_self the Appy
|
||||||
|
|
|
@ -84,7 +84,7 @@ class User(ModelClass):
|
||||||
validator=validatePassword, **gm)
|
validator=validatePassword, **gm)
|
||||||
password2 = String(format=String.PASSWORD, show=showPassword, **gm)
|
password2 = String(format=String.PASSWORD, show=showPassword, **gm)
|
||||||
gm['multiplicity'] = (0, None)
|
gm['multiplicity'] = (0, None)
|
||||||
roles = String(validator=Selection('getGrantableRoles'), **gm)
|
roles = String(validator=Selection('getGrantableRoles'), indexed=True, **gm)
|
||||||
|
|
||||||
# The Tool class ---------------------------------------------------------------
|
# The Tool class ---------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,10 @@
|
||||||
tool contextObj/getTool;
|
tool contextObj/getTool;
|
||||||
appFolder tool/getAppFolder;
|
appFolder tool/getAppFolder;
|
||||||
appName appFolder/getId;
|
appName appFolder/getId;
|
||||||
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType='edit');
|
phaseInfo python: contextObj.getAppyPhases(currentOnly=True, layoutType=layoutType);
|
||||||
phase phaseInfo/name;
|
phase phaseInfo/name;
|
||||||
page request/page|python:'main';
|
page request/page|python:'main';
|
||||||
cssAndJs python: contextObj.getCssAndJs(layoutType, page);
|
cssJs python: contextObj.getCssAndJs(contextObj.getAppyTypes(layoutType, page), layoutType);
|
||||||
css python: cssAndJs[0];
|
|
||||||
js python: cssAndJs[1];
|
|
||||||
confirmMsg request/confirmMsg | nothing;">
|
confirmMsg request/confirmMsg | nothing;">
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
|
||||||
|
@ -28,7 +26,7 @@
|
||||||
|
|
||||||
<tal:comment replace="nothing">Include type-specific CSS and JS.</tal:comment>
|
<tal:comment replace="nothing">Include type-specific CSS and JS.</tal:comment>
|
||||||
<metal:js fill-slot="javascript_head_slot">
|
<metal:js fill-slot="javascript_head_slot">
|
||||||
<tal:js condition="js" repeat="jsFile js">
|
<tal:js condition="cssJs/js" repeat="jsFile cssJs/js">
|
||||||
<script type="text/javascript" charset="iso-8859-1"
|
<script type="text/javascript" charset="iso-8859-1"
|
||||||
tal:condition="python:exists('portal/%s' % jsFile)"
|
tal:condition="python:exists('portal/%s' % jsFile)"
|
||||||
tal:attributes="src string:$portal_url/$jsFile">
|
tal:attributes="src string:$portal_url/$jsFile">
|
||||||
|
@ -36,7 +34,7 @@
|
||||||
</tal:js>
|
</tal:js>
|
||||||
</metal:js>
|
</metal:js>
|
||||||
<metal:css fill-slot="css_slot">
|
<metal:css fill-slot="css_slot">
|
||||||
<tal:css condition="css" repeat="cssFile css">
|
<tal:css condition="cssJs/css" repeat="cssFile cssJs/css">
|
||||||
<style type="text/css" media="all"
|
<style type="text/css" media="all"
|
||||||
tal:condition="python:exists('portal/%s' % cssFile)"
|
tal:condition="python:exists('portal/%s' % cssFile)"
|
||||||
tal:content="structure string:<!-- @import url($portal_url/$cssFile); -->">
|
tal:content="structure string:<!-- @import url($portal_url/$cssFile); -->">
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
</a></td>
|
</a></td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td class="noPadding">
|
<td class="noPadding">
|
||||||
<img tal:condition="python: member.has_permission('Delete objects', obj)"
|
<img tal:condition="python: member.has_permission('Delete objects', obj) and obj.mayDelete()"
|
||||||
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
||||||
tal:attributes="src string: $portal_url/skyn/delete.png;
|
tal:attributes="src string: $portal_url/skyn/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
</td>
|
</td>
|
||||||
</dt>
|
</dt>
|
||||||
|
|
||||||
<tal:publishedObject condition="python: contextObj and tool.userMayNavigate(contextObj)">
|
<tal:publishedObject condition="python: contextObj and contextObj.mayNavigate()">
|
||||||
<dt class="portletAppyItem portletCurrent"><b tal:content="contextObj/Title"></b></dt>
|
<dt class="portletAppyItem portletCurrent"><b tal:content="contextObj/Title"></b></dt>
|
||||||
<dt class="portletAppyItem"><metal:phases use-macro="here/skyn/portlet/macros/phases"/></dt>
|
<dt class="portletAppyItem"><metal:phases use-macro="here/skyn/portlet/macros/phases"/></dt>
|
||||||
</tal:publishedObject>
|
</tal:publishedObject>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
appName appFolder/id;
|
appName appFolder/id;
|
||||||
tool python: portal.get('portal_%s' % appName.lower());
|
tool python: portal.get('portal_%s' % appName.lower());
|
||||||
contentType request/type_name;
|
contentType request/type_name;
|
||||||
searchName request/search|python:'';">
|
searchName request/search|python:''">
|
||||||
|
|
||||||
<div metal:use-macro="here/skyn/page/macros/prologue"/>
|
<div metal:use-macro="here/skyn/page/macros/prologue"/>
|
||||||
<tal:comment replace="nothing">Query result</tal:comment>
|
<tal:comment replace="nothing">Query result</tal:comment>
|
||||||
|
|
|
@ -1,25 +1,43 @@
|
||||||
|
<tal:search metal:define-macro="master"
|
||||||
|
define="appFolder context/getParentNode;
|
||||||
|
contentType request/type_name;
|
||||||
|
refInfo request/ref|nothing;
|
||||||
|
tool python: here.portal_url.getPortalObject().get('portal_%s' % appFolder.id.lower());
|
||||||
|
searchInfo python: tool.getSearchInfo(contentType, refInfo);
|
||||||
|
cssJs python: tool.getCssAndJs(searchInfo['fields'], 'edit')">
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
|
||||||
lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"
|
lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"
|
||||||
xmlns:metal="http://xml.zope.org/namespaces/metal"
|
xmlns:metal="http://xml.zope.org/namespaces/metal"
|
||||||
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
xmlns:i18n="http://xml.zope.org/namespaces/i18n"
|
||||||
metal:use-macro="here/main_template/macros/master">
|
metal:use-macro="here/main_template/macros/master">
|
||||||
|
|
||||||
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
<tal:comment replace="nothing">Disable standard Plone green tabs</tal:comment>
|
||||||
<div metal:fill-slot="top_slot">
|
<div metal:fill-slot="top_slot">
|
||||||
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
<metal:block metal:use-macro="here/global_defines/macros/defines" />
|
||||||
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
<div tal:define="dummy python:request.set('disable_border', 1)" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Fill main slot of Plone main_template</tal:comment>
|
<tal:comment replace="nothing">Include type-specific CSS and JS.</tal:comment>
|
||||||
<body>
|
<metal:js fill-slot="javascript_head_slot">
|
||||||
<metal:fill fill-slot="main"
|
<tal:js condition="cssJs/js" repeat="jsFile cssJs/js">
|
||||||
tal:define="appFolder context/getParentNode;
|
<script type="text/javascript" charset="iso-8859-1"
|
||||||
contentType request/type_name;
|
tal:condition="python:exists('portal/%s' % jsFile)"
|
||||||
refInfo request/ref|nothing;
|
tal:attributes="src string:$portal_url/$jsFile">
|
||||||
tool python: portal.get('portal_%s' % appFolder.id.lower());
|
</script>
|
||||||
searchInfo python: tool.getSearchInfo(contentType, refInfo);
|
</tal:js>
|
||||||
searchableFields python: searchInfo[0];
|
</metal:js>
|
||||||
numberOfColumns python: searchInfo[1]">
|
<metal:css fill-slot="css_slot">
|
||||||
|
<tal:css condition="cssJs/css" repeat="cssFile cssJs/css">
|
||||||
|
<style type="text/css" media="all"
|
||||||
|
tal:condition="python:exists('portal/%s' % cssFile)"
|
||||||
|
tal:content="structure string:<!-- @import url($portal_url/$cssFile); -->">
|
||||||
|
</style>
|
||||||
|
</tal:css>
|
||||||
|
</metal:css>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<metal:fill fill-slot="main">
|
||||||
|
|
||||||
<tal:comment replace="nothing">Search title</tal:comment>
|
<tal:comment replace="nothing">Search title</tal:comment>
|
||||||
<h1><span tal:replace="python: tool.translate('%s_plural' % contentType)"/> —
|
<h1><span tal:replace="python: tool.translate('%s_plural' % contentType)"/> —
|
||||||
|
@ -32,8 +50,9 @@
|
||||||
<input tal:condition="refInfo" type="hidden" name="ref" tal:attributes="value refInfo"/>
|
<input tal:condition="refInfo" type="hidden" name="ref" tal:attributes="value refInfo"/>
|
||||||
|
|
||||||
<table class="no-style-table" cellpadding="0" cellspacing="0" width="100%">
|
<table class="no-style-table" cellpadding="0" cellspacing="0" width="100%">
|
||||||
<tr tal:repeat="searchRow python: tool.tabularize(searchableFields, numberOfColumns)" valign="top">
|
<tr tal:repeat="searchRow python: tool.tabularize(searchInfo['fieldDicts'], searchInfo['nbOfColumns'])"
|
||||||
<td tal:repeat="widget searchRow" tal:attributes="width python:'%d%%' % (100/numberOfColumns)">
|
valign="top">
|
||||||
|
<td tal:repeat="widget searchRow" tal:attributes="width python:'%d%%' % (100/searchInfo['nbOfColumns'])">
|
||||||
<tal:field condition="widget">
|
<tal:field condition="widget">
|
||||||
<tal:show define="name widget/name;
|
<tal:show define="name widget/name;
|
||||||
widgetName python: 'w_%s' % name;
|
widgetName python: 'w_%s' % name;
|
||||||
|
@ -53,3 +72,4 @@
|
||||||
</metal:fill>
|
</metal:fill>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
</tal:search>
|
||||||
|
|
|
@ -78,55 +78,71 @@
|
||||||
</metal:cell>
|
</metal:cell>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Search macro for an Date.</tal:comment>
|
<tal:comment replace="nothing">Search macro for an Date.</tal:comment>
|
||||||
<metal:search define-macro="search">
|
<metal:search define-macro="search"
|
||||||
|
tal:define="years python:range(widget['startYear'], widget['endYear']+1)">
|
||||||
<label tal:content="python: tool.translate(widget['labelId'])"></label>
|
<label tal:content="python: tool.translate(widget['labelId'])"></label>
|
||||||
<table cellpadding="0" cellspacing="0">
|
<table cellpadding="0" cellspacing="0">
|
||||||
<tal:comment replace="nothing">From</tal:comment>
|
<tal:comment replace="nothing">From</tal:comment>
|
||||||
<tr tal:define="fromName python: '%s*date' % widgetName">
|
<tr tal:define="yearFromName python: '%s*date' % widgetName;
|
||||||
|
monthFromName python: '%s_from_month' % name;
|
||||||
|
dayFromName python: '%s_from_day' % name;
|
||||||
|
dummyFromName python: '_d_ummy_from_%s' % name;">
|
||||||
<td width="10px"> </td>
|
<td width="10px"> </td>
|
||||||
<td>
|
<td>
|
||||||
<label tal:content="python: tool.translate('search_from')"></label>
|
<label tal:content="python: tool.translate('search_from')"></label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<select tal:attributes="name fromName">
|
<input type="hidden" tal:attributes="id dummyFromName; name dummyFromName" originalvalue=""/>
|
||||||
|
<select tal:attributes="id dayFromName; name dayFromName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python:range(widget['startYear'], widget['endYear']+1)"
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
||||||
tal:content="value" tal:attributes="value value"></option>
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
</select> /
|
</select> /
|
||||||
<select tal:attributes="name python: '%s_from_month' % name">
|
<select tal:attributes="id monthFromName; name monthFromName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
||||||
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
||||||
</select> /
|
</select> /
|
||||||
<select tal:attributes="name python: '%s_from_day' % name">
|
<select tal:attributes="id yearFromName; name yearFromName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
<option tal:repeat="value python:range(widget['startYear'], widget['endYear']+1)"
|
||||||
tal:content="value" tal:attributes="value value"></option>
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
</select>
|
</select>
|
||||||
|
<tal:comment replace="nothing">The icon for displaying the calendar (=date chooser)</tal:comment>
|
||||||
|
<a tal:condition="widget/calendar"
|
||||||
|
tal:attributes="onclick python: 'return showJsCalendar(\'%s\', \'%s\', \'%s\', \'%s\', \'%s\', null, null, %d, %d)' % (monthFromName, dummyFromName, yearFromName, monthFromName, dayFromName, years[0], years[-1])">
|
||||||
|
<img tal:attributes="src string: $portal_url/popup_calendar.gif"/></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tal:comment replace="nothing">To</tal:comment>
|
<tal:comment replace="nothing">To</tal:comment>
|
||||||
<tr>
|
<tr tal:define="dummyToName python: '_d_ummy_to_%s' % name;
|
||||||
|
yearToName python: '%s_to_year' % name;
|
||||||
|
monthToName python: '%s_to_month' % name;
|
||||||
|
dayToName python: '%s_to_day' % name">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>
|
<td>
|
||||||
<label tal:content="python: tool.translate('search_to')"></label>
|
<label tal:content="python: tool.translate('search_to')"></label>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td height="20px;">
|
||||||
<select tal:attributes="name python: '%s_to_year' % name">
|
<input type="hidden" tal:attributes="id dummyToName; name dummyToName" originalvalue=""/>
|
||||||
|
<select tal:attributes="id dayToName; name dayToName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python:range(widget['startYear'], widget['endYear']+1)"
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
||||||
tal:content="value" tal:attributes="value value"></option>
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
</select> /
|
</select> /
|
||||||
<select tal:attributes="name python: '%s_to_month' % name">
|
<select tal:attributes="id monthToName; name monthToName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 13)]"
|
||||||
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
tal:content="python:tool.getMonthName(value)" tal:attributes="value value"></option>
|
||||||
</select> /
|
</select> /
|
||||||
<select tal:attributes="name python: '%s_to_day' % name">
|
<select tal:attributes="id yearToName; name yearToName">
|
||||||
<option value="">--</option>
|
<option value="">--</option>
|
||||||
<option tal:repeat="value python: [str(v).zfill(2) for v in range(1, 32)]"
|
<option tal:repeat="value python:range(widget['startYear'], widget['endYear']+1)"
|
||||||
tal:content="value" tal:attributes="value value"></option>
|
tal:content="value" tal:attributes="value value"></option>
|
||||||
</select>
|
</select>
|
||||||
|
<a tal:condition="widget/calendar"
|
||||||
|
tal:attributes="onclick python: 'return showJsCalendar(\'%s\', \'%s\', \'%s\', \'%s\', \'%s\', null, null, %d, %d)' % (monthToName, dummyToName, yearToName, monthToName, dayToName, years[0], years[-1])">
|
||||||
|
<img tal:attributes="src string: $portal_url/popup_calendar.gif"/></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
<table class="no-style-table" cellpadding="0" cellspacing="0">
|
<table class="no-style-table" cellpadding="0" cellspacing="0">
|
||||||
<tr>
|
<tr>
|
||||||
<tal:comment replace="nothing">Arrows for moving objects up or down</tal:comment>
|
<tal:comment replace="nothing">Arrows for moving objects up or down</tal:comment>
|
||||||
<td class="noPadding" tal:condition="python: (len(objs)>1) and member.has_permission('Modify portal content', contextObj)">
|
<td class="noPadding" tal:condition="python: not appyType['isBack'] and (len(objs)>1) and member.has_permission('Modify portal content', contextObj)">
|
||||||
<tal:moveRef define="objectIndex python: contextObj.getAppyRefIndex(fieldName, obj);
|
<tal:moveRef define="objectIndex python: contextObj.getAppyRefIndex(fieldName, obj);
|
||||||
ajaxBaseCall python: navBaseCall.replace('**v**', '\'%s\',\'ChangeRefOrder\', {\'refObjectUid\':\'%s\', \'move\':\'**v**\'}' % (startNumber, obj.UID()))">
|
ajaxBaseCall python: navBaseCall.replace('**v**', '\'%s\',\'ChangeRefOrder\', {\'refObjectUid\':\'%s\', \'move\':\'**v**\'}' % (startNumber, obj.UID()))">
|
||||||
<tal:comment replace="nothing">Move up</tal:comment>
|
<tal:comment replace="nothing">Move up</tal:comment>
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</td>
|
</td>
|
||||||
<tal:comment replace="nothing">Delete the element</tal:comment>
|
<tal:comment replace="nothing">Delete the element</tal:comment>
|
||||||
<td class="noPadding">
|
<td class="noPadding">
|
||||||
<img tal:condition="python: member.has_permission('Delete objects', obj)"
|
<img tal:condition="python: not appyType['isBack'] and member.has_permission('Delete objects', obj) and obj.mayDelete()"
|
||||||
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
title="Delete" i18n:domain="plone" i18n:attributes="title" style="cursor:pointer"
|
||||||
tal:attributes="src string: $portal_url/skyn/delete.png;
|
tal:attributes="src string: $portal_url/skyn/delete.png;
|
||||||
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
onClick python:'onDeleteObject(\'%s\')' % obj.UID()"/>
|
||||||
|
@ -179,19 +179,10 @@
|
||||||
tal:attributes="class python:test(innerRef, 'innerAppyTable', '')">
|
tal:attributes="class python:test(innerRef, 'innerAppyTable', '')">
|
||||||
<tr valign="bottom"><td>
|
<tr valign="bottom"><td>
|
||||||
|
|
||||||
<tal:comment replace="nothing">Show backward reference(s)</tal:comment>
|
<tal:comment replace="nothing">Show forward or backward reference(s)</tal:comment>
|
||||||
<table class="no-style-table" cellspacing="0" cellpadding="0"
|
|
||||||
tal:condition="python: appyType['isBack'] and objs">
|
|
||||||
<tr tal:repeat="obj objs">
|
|
||||||
<td><metal:showObjectTitle use-macro="portal/skyn/widgets/ref/macros/objectTitle" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<tal:comment replace="nothing">Show forward reference(s)</tal:comment>
|
|
||||||
<table tal:attributes="class python:test(innerRef, '', 'listing nosort');
|
<table tal:attributes="class python:test(innerRef, '', 'listing nosort');
|
||||||
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);"
|
width python:test(innerRef, '100%', appyType['layouts']['view']['width']);"
|
||||||
align="right" tal:condition="python: not appyType['isBack'] and objs" cellpadding="0" cellspacing="0">
|
align="right" tal:condition="python: objs" cellpadding="0" cellspacing="0">
|
||||||
<tal:widgets define="widgets python: objs[0].getAppyTypesFromNames(appyType['shownInfo'])">
|
<tal:widgets define="widgets python: objs[0].getAppyTypesFromNames(appyType['shownInfo'])">
|
||||||
<tr tal:condition="appyType/showHeaders">
|
<tr tal:condition="appyType/showHeaders">
|
||||||
<th tal:repeat="widget widgets">
|
<th tal:repeat="widget widgets">
|
||||||
|
|
|
@ -101,7 +101,7 @@
|
||||||
<label tal:attributes="for andName" tal:content="python: tool.translate('search_and')"></label><br/>
|
<label tal:attributes="for andName" tal:content="python: tool.translate('search_and')"></label><br/>
|
||||||
</tal:operator>
|
</tal:operator>
|
||||||
<tal:comment replace="nothing">The list of values</tal:comment>
|
<tal:comment replace="nothing">The list of values</tal:comment>
|
||||||
<select tal:attributes="name widgetName" multiple="multiple" size="5">
|
<select tal:attributes="name widgetName; size widget/height" multiple="multiple">
|
||||||
<option tal:repeat="v python:tool.getPossibleValues(name, withTranslations=True, withBlankValue=False, className=contentType)"
|
<option tal:repeat="v python:tool.getPossibleValues(name, withTranslations=True, withBlankValue=False, className=contentType)"
|
||||||
tal:attributes="value python:v[0]; title python: v[1]"
|
tal:attributes="value python:v[0]; title python: v[1]"
|
||||||
tal:content="python: tool.truncateValue(v[1], widget)">
|
tal:content="python: tool.truncateValue(v[1], widget)">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
import os, re, httplib, sys, stat, urlparse, time
|
import os, re, httplib, sys, stat, urlparse, time, socket
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
|
@ -9,6 +9,9 @@ from appy.shared.utils import copyData
|
||||||
from appy.gen.utils import sequenceTypes
|
from appy.gen.utils import sequenceTypes
|
||||||
from appy.shared.xml_parser import XmlUnmarshaller, XmlMarshaller
|
from appy.shared.xml_parser import XmlUnmarshaller, XmlMarshaller
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
class ResourceError(Exception): pass
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class FormDataEncoder:
|
class FormDataEncoder:
|
||||||
'''Allows to encode form data for sending it through a HTTP request.'''
|
'''Allows to encode form data for sending it through a HTTP request.'''
|
||||||
|
@ -84,6 +87,7 @@ class HttpResponse:
|
||||||
if self.duration: duration = ', got in %.4f seconds' % self.duration
|
if self.duration: duration = ', got in %.4f seconds' % self.duration
|
||||||
return '<HttpResponse %s (%s)%s>' % (self.code, self.text, duration)
|
return '<HttpResponse %s (%s)%s>' % (self.code, self.text, duration)
|
||||||
|
|
||||||
|
xmlHeaders = ('text/xml', 'application/xml')
|
||||||
def extractData(self):
|
def extractData(self):
|
||||||
'''This method extracts, from the various parts of the HTTP response,
|
'''This method extracts, from the various parts of the HTTP response,
|
||||||
some useful information. For example, it will find the URI where to
|
some useful information. For example, it will find the URI where to
|
||||||
|
@ -91,11 +95,13 @@ class HttpResponse:
|
||||||
data into Python objects.'''
|
data into Python objects.'''
|
||||||
if self.code == 302:
|
if self.code == 302:
|
||||||
return urlparse.urlparse(self.headers['location'])[2]
|
return urlparse.urlparse(self.headers['location'])[2]
|
||||||
elif self.headers.has_key('content-type') and \
|
elif self.headers.has_key('content-type'):
|
||||||
self.headers['content-type'].startswith('text/xml'):
|
contentType = self.headers['content-type']
|
||||||
# Return an unmarshalled version of the XML content, for easy use
|
for xmlHeader in self.xmlHeaders:
|
||||||
# in Python.
|
if contentType.startswith(xmlHeader):
|
||||||
return XmlUnmarshaller().parse(self.body)
|
# Return an unmarshalled version of the XML content, for
|
||||||
|
# easy use in Python.
|
||||||
|
return XmlUnmarshaller().parse(self.body)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
|
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
|
||||||
|
@ -109,9 +115,6 @@ class Resource:
|
||||||
self.username = username
|
self.username = username
|
||||||
self.password = password
|
self.password = password
|
||||||
self.url = url
|
self.url = url
|
||||||
# If some headers must be sent with any request sent through this
|
|
||||||
# resource (like a cookie), you can store them in the following dict.
|
|
||||||
self.headers = {}
|
|
||||||
# If p_measure is True, we will measure, for every request sent, the
|
# If p_measure is True, we will measure, for every request sent, the
|
||||||
# time we wait until we receive the response.
|
# time we wait until we receive the response.
|
||||||
self.measure = measure
|
self.measure = measure
|
||||||
|
@ -127,6 +130,9 @@ class Resource:
|
||||||
self.port = port and int(port[1:]) or 80
|
self.port = port and int(port[1:]) or 80
|
||||||
self.uri = uri or '/'
|
self.uri = uri or '/'
|
||||||
else: raise 'Wrong URL: %s' % str(url)
|
else: raise 'Wrong URL: %s' % str(url)
|
||||||
|
# If some headers must be sent with any request sent through this
|
||||||
|
# resource (like a cookie), you can store them in the following dict.
|
||||||
|
self.headers = {'Host': self.host}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<Dav resource at %s>' % self.url
|
return '<Dav resource at %s>' % self.url
|
||||||
|
@ -147,7 +153,12 @@ class Resource:
|
||||||
def send(self, method, uri, body=None, headers={}, bodyType=None):
|
def send(self, method, uri, body=None, headers={}, bodyType=None):
|
||||||
'''Sends a HTTP request with p_method, for p_uri.'''
|
'''Sends a HTTP request with p_method, for p_uri.'''
|
||||||
conn = httplib.HTTP()
|
conn = httplib.HTTP()
|
||||||
conn.connect(self.host, self.port)
|
try:
|
||||||
|
conn.connect(self.host, self.port)
|
||||||
|
except socket.gaierror, sge:
|
||||||
|
raise ResourceError('Check your Internet connection (%s)'% str(sge))
|
||||||
|
except socket.error, se:
|
||||||
|
raise ResourceError('Connection error (%s)'% str(sge))
|
||||||
# Tell what kind of HTTP request it will be.
|
# Tell what kind of HTTP request it will be.
|
||||||
conn.putrequest(method, uri)
|
conn.putrequest(method, uri)
|
||||||
# Add HTTP headers
|
# Add HTTP headers
|
||||||
|
@ -222,6 +233,7 @@ class Resource:
|
||||||
'''Perform a HTTP GET on the server.'''
|
'''Perform a HTTP GET on the server.'''
|
||||||
if not uri: uri = self.uri
|
if not uri: uri = self.uri
|
||||||
return self.send('GET', uri, headers=headers)
|
return self.send('GET', uri, headers=headers)
|
||||||
|
rss = get
|
||||||
|
|
||||||
def post(self, data=None, uri=None, headers={}, encode='form'):
|
def post(self, data=None, uri=None, headers={}, encode='form'):
|
||||||
'''Perform a HTTP POST on the server. If p_encode is "form", p_data is
|
'''Perform a HTTP POST on the server. If p_encode is "form", p_data is
|
||||||
|
@ -230,7 +242,6 @@ class Resource:
|
||||||
body of the HTTP request.'''
|
body of the HTTP request.'''
|
||||||
if not uri: uri = self.uri
|
if not uri: uri = self.uri
|
||||||
# Prepare the data to send
|
# Prepare the data to send
|
||||||
headers['Host'] = self.host
|
|
||||||
if encode == 'form':
|
if encode == 'form':
|
||||||
# Format the form data and prepare headers
|
# Format the form data and prepare headers
|
||||||
body = FormDataEncoder(data).encode()
|
body = FormDataEncoder(data).encode()
|
||||||
|
|
|
@ -342,12 +342,6 @@ class XmlUnmarshaller(XmlParser):
|
||||||
# Alias: "unmarshall" -> "parse"
|
# Alias: "unmarshall" -> "parse"
|
||||||
unmarshall = XmlParser.parse
|
unmarshall = XmlParser.parse
|
||||||
|
|
||||||
class CssParser(XmlUnmarshaller):
|
|
||||||
cssTags = {'rss': 'object', 'channel': 'object', 'item': 'object'}
|
|
||||||
def startDocument(self):
|
|
||||||
XmlUnmarshaller.startDocument(self)
|
|
||||||
self.tagTypes.update(self.cssTags)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
class XmlMarshaller:
|
class XmlMarshaller:
|
||||||
'''This class allows to produce a XML version of a Python object, which
|
'''This class allows to produce a XML version of a Python object, which
|
||||||
|
|
Loading…
Reference in a new issue