2009-06-29 07:06:01 -05:00
|
|
|
'''This package contains base classes for wrappers that hide to the Appy
|
2009-09-18 07:42:31 -05:00
|
|
|
developer the real classes used by the underlying web framework.'''
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2010-03-25 10:34:37 -05:00
|
|
|
import os, os.path, time, mimetypes, random
|
2009-12-17 14:14:52 -06:00
|
|
|
import appy.pod
|
2011-09-14 14:01:58 -05:00
|
|
|
from appy.gen import Type, Search, Ref, String
|
2010-11-26 10:30:46 -06:00
|
|
|
from appy.gen.utils import sequenceTypes
|
2010-03-25 10:34:37 -05:00
|
|
|
from appy.shared.utils import getOsTempFolder, executeCommand, normalizeString
|
2009-11-11 13:22:13 -06:00
|
|
|
from appy.shared.xml_parser import XmlMarshaller
|
2011-02-12 10:09:11 -06:00
|
|
|
from appy.shared.csv_parser import CsvMarshaller
|
2009-07-10 08:01:50 -05:00
|
|
|
|
|
|
|
# Some error messages ----------------------------------------------------------
|
|
|
|
WRONG_FILE_TUPLE = 'This is not the way to set a file. You can specify a ' \
|
|
|
|
'2-tuple (fileName, fileContent) or a 3-tuple (fileName, fileContent, ' \
|
|
|
|
'mimeType).'
|
2011-02-16 06:43:58 -06:00
|
|
|
FREEZE_ERROR = 'Error while trying to freeze a "%s" file in POD field ' \
|
|
|
|
'"%s" (%s).'
|
|
|
|
FREEZE_FATAL_ERROR = 'A server error occurred. Please contact the system ' \
|
|
|
|
'administrator.'
|
2009-06-29 07:06:01 -05:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2011-09-06 18:33:09 -05:00
|
|
|
class AbstractWrapper(object):
|
2011-01-28 07:36:30 -06:00
|
|
|
'''Any real Zope object has a companion object that is an instance of this
|
|
|
|
class.'''
|
2010-11-26 10:30:46 -06:00
|
|
|
def __init__(self, o): self.__dict__['o'] = o
|
2011-02-12 10:09:11 -06:00
|
|
|
def appy(self): return self
|
2010-11-26 10:30:46 -06:00
|
|
|
|
|
|
|
def __setattr__(self, name, value):
|
2009-07-10 08:01:50 -05:00
|
|
|
appyType = self.o.getAppyType(name)
|
2010-08-05 11:23:17 -05:00
|
|
|
if not appyType:
|
2009-07-10 08:01:50 -05:00
|
|
|
raise 'Attribute "%s" does not exist.' % name
|
2010-11-26 10:30:46 -06:00
|
|
|
appyType.store(self.o, value)
|
|
|
|
|
2011-09-06 18:33:09 -05:00
|
|
|
def __getattribute__(self, name):
|
|
|
|
'''Gets the attribute named p_name. Lot of cheating here.'''
|
|
|
|
if name == 'o': return object.__getattribute__(self, name)
|
|
|
|
elif name == 'tool': return self.o.getTool().appy()
|
|
|
|
elif name == 'request': return self.o.REQUEST
|
|
|
|
elif name == 'session': return self.o.REQUEST.SESSION
|
|
|
|
elif name == 'typeName': return self.__class__.__bases__[-1].__name__
|
|
|
|
elif name == 'id': return self.o.id
|
|
|
|
elif name == 'uid': return self.o.UID()
|
|
|
|
elif name == 'klass': return self.__class__.__bases__[-1]
|
|
|
|
elif name == 'url': return self.o.absolute_url()
|
|
|
|
elif name == 'state': return self.o.getState()
|
|
|
|
elif name == 'stateLabel':
|
|
|
|
o = self.o
|
|
|
|
appName = o.getProductConfig().PROJECTNAME
|
|
|
|
return o.translate(o.getWorkflowLabel(), domain=appName)
|
|
|
|
elif name == 'history':
|
|
|
|
o = self.o
|
|
|
|
key = o.workflow_history.keys()[0]
|
|
|
|
return o.workflow_history[key]
|
|
|
|
elif name == 'user':
|
|
|
|
return self.o.portal_membership.getAuthenticatedMember()
|
|
|
|
elif name == 'fields': return self.o.getAllAppyTypes()
|
|
|
|
# Now, let's try to return a real attribute.
|
2011-09-14 14:01:58 -05:00
|
|
|
res = object.__getattribute__(self, name)
|
2011-09-06 18:33:09 -05:00
|
|
|
# If we got an Appy type, return the value of this type for this object
|
|
|
|
if isinstance(res, Type):
|
|
|
|
o = self.o
|
|
|
|
if isinstance(res, Ref):
|
|
|
|
return res.getValue(o, noListIfSingleObj=True)
|
|
|
|
else:
|
|
|
|
return res.getValue(o)
|
|
|
|
return res
|
|
|
|
|
2009-11-11 13:22:13 -06:00
|
|
|
def __repr__(self):
|
2010-11-26 10:30:46 -06:00
|
|
|
return '<%s appyobj at %s>' % (self.klass.__name__, id(self))
|
|
|
|
|
2009-06-29 07:06:01 -05:00
|
|
|
def __cmp__(self, other):
|
2009-10-27 08:48:04 -05:00
|
|
|
if other: return cmp(self.o, other.o)
|
2011-07-26 15:15:04 -05:00
|
|
|
return 1
|
2010-11-26 10:30:46 -06:00
|
|
|
|
2011-01-14 02:06:25 -06:00
|
|
|
def _callCustom(self, methodName, *args, **kwargs):
|
|
|
|
'''This wrapper implements some methods like "validate" and "onEdit".
|
|
|
|
If the user has defined its own wrapper, its methods will not be
|
|
|
|
called. So this method allows, from the methods here, to call the
|
|
|
|
user versions.'''
|
|
|
|
if len(self.__class__.__bases__) > 1:
|
|
|
|
# There is a custom user class
|
|
|
|
customUser = self.__class__.__bases__[-1]
|
|
|
|
if customUser.__dict__.has_key(methodName):
|
2011-02-06 10:39:36 -06:00
|
|
|
return customUser.__dict__[methodName](self, *args, **kwargs)
|
2011-01-14 02:06:25 -06:00
|
|
|
|
2011-02-17 11:13:42 -06:00
|
|
|
def getField(self, name): return self.o.getAppyType(name)
|
|
|
|
|
2011-09-26 14:19:34 -05:00
|
|
|
def link(self, fieldName, obj, back=False):
|
2011-01-19 13:51:43 -06:00
|
|
|
'''This method links p_obj (which can be a list of objects) to this one
|
2011-09-26 14:19:34 -05:00
|
|
|
through reference field p_fieldName. As this method is recursive and
|
|
|
|
can be called to update the corresponding back reference, param
|
|
|
|
p_back is there, but, you, as Appy developer, should never set it
|
|
|
|
to True.'''
|
|
|
|
# p_objs can be a list of objects
|
2010-10-29 07:36:36 -05:00
|
|
|
if type(obj) in sequenceTypes:
|
2011-09-26 14:19:34 -05:00
|
|
|
for o in obj: self.link(fieldName, o, back=back)
|
|
|
|
return
|
|
|
|
# Gets the list of referred objects (=list of uids), or create it.
|
|
|
|
selfO = self.o
|
|
|
|
refs = getattr(selfO, fieldName, None)
|
|
|
|
if refs == None:
|
|
|
|
refs = selfO.getProductConfig().PersistentList()
|
|
|
|
setattr(selfO, fieldName, refs)
|
|
|
|
# Insert p_obj into it.
|
|
|
|
uid = obj.o.UID()
|
|
|
|
if uid not in refs:
|
|
|
|
refs.append(uid)
|
|
|
|
# Update the back reference
|
|
|
|
if not back:
|
|
|
|
backName = selfO.getAppyType(fieldName).back.attribute
|
|
|
|
obj.appy().link(backName, self, back=True)
|
2011-01-19 13:51:43 -06:00
|
|
|
|
2011-09-26 14:19:34 -05:00
|
|
|
def unlink(self, fieldName, obj, back=False):
|
2011-01-19 13:51:43 -06:00
|
|
|
'''This method unlinks p_obj (which can be a list of objects) from this
|
2011-09-26 14:19:34 -05:00
|
|
|
one through reference field p_fieldName. As this method is recursive
|
|
|
|
and can be called to update the corresponding back reference, param
|
|
|
|
p_back is there, but, you, as Appy developer, should never set it
|
|
|
|
to True.'''
|
|
|
|
# p_objs can be a list of objects
|
2011-01-19 13:51:43 -06:00
|
|
|
if type(obj) in sequenceTypes:
|
2011-09-26 14:19:34 -05:00
|
|
|
for o in obj: self.unlink(fieldName, o, back=back)
|
|
|
|
return
|
|
|
|
# Get the list of referred objects
|
|
|
|
selfO = self.o
|
|
|
|
refs = getattr(selfO, fieldName, None)
|
|
|
|
if not refs: return
|
|
|
|
# Unlink the object
|
|
|
|
uid = obj.o.UID()
|
|
|
|
if uid in refs:
|
|
|
|
refs.remove(uid)
|
|
|
|
# Update the back reference
|
|
|
|
if not back:
|
|
|
|
backName = selfO.getAppyType(fieldName).back.attribute
|
|
|
|
obj.appy().unlink(backName, self, back=True)
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2010-04-30 05:05:29 -05:00
|
|
|
def sort(self, fieldName, sortKey='title', reverse=False):
|
|
|
|
'''Sorts referred elements linked to p_self via p_fieldName according
|
|
|
|
to a given p_sortKey which must be an attribute set on referred
|
|
|
|
objects ("title", by default).'''
|
2011-09-26 14:19:34 -05:00
|
|
|
selfO = self.o
|
|
|
|
refs = getattr(selfO, fieldName, None)
|
|
|
|
if not refs: return
|
|
|
|
c = selfO.portal_catalog
|
|
|
|
refs.sort(lambda x,y: \
|
2010-04-30 05:05:29 -05:00
|
|
|
cmp(getattr(c(UID=x)[0].getObject().appy(), sortKey),
|
|
|
|
getattr(c(UID=y)[0].getObject().appy(), sortKey)))
|
|
|
|
if reverse:
|
2011-09-26 14:19:34 -05:00
|
|
|
refs.reverse()
|
2009-10-27 08:48:04 -05:00
|
|
|
|
2009-07-10 08:01:50 -05:00
|
|
|
def create(self, fieldNameOrClass, **kwargs):
|
|
|
|
'''If p_fieldNameOfClass is the name of a field, this method allows to
|
|
|
|
create an object and link it to the current one (self) through
|
|
|
|
reference field named p_fieldName.
|
|
|
|
If p_fieldNameOrClass is a class from the gen-application, it must
|
|
|
|
correspond to a root class and this method allows to create a
|
|
|
|
root object in the application folder.'''
|
|
|
|
isField = isinstance(fieldNameOrClass, basestring)
|
|
|
|
# Determine the portal type of the object to create
|
|
|
|
if isField:
|
2010-09-02 09:16:08 -05:00
|
|
|
fieldName = idPrefix = fieldNameOrClass
|
|
|
|
appyType = self.o.getAppyType(fieldName)
|
|
|
|
portalType = self.tool.o.getPortalType(appyType.klass)
|
2009-07-10 08:01:50 -05:00
|
|
|
else:
|
2010-09-02 09:16:08 -05:00
|
|
|
klass = fieldNameOrClass
|
|
|
|
idPrefix = klass.__name__
|
|
|
|
portalType = self.tool.o.getPortalType(klass)
|
2009-07-10 08:01:50 -05:00
|
|
|
# Determine object id
|
2009-06-29 07:06:01 -05:00
|
|
|
if kwargs.has_key('id'):
|
|
|
|
objId = kwargs['id']
|
|
|
|
del kwargs['id']
|
|
|
|
else:
|
2009-12-14 13:22:55 -06:00
|
|
|
objId = '%s.%f.%s' % (idPrefix, time.time(),
|
|
|
|
str(random.random()).split('.')[1])
|
2009-10-25 15:42:08 -05:00
|
|
|
# Determine if object must be created from external data
|
|
|
|
externalData = None
|
|
|
|
if kwargs.has_key('_data'):
|
|
|
|
externalData = kwargs['_data']
|
|
|
|
del kwargs['_data']
|
2009-07-10 08:01:50 -05:00
|
|
|
# Where must I create the object?
|
|
|
|
if not isField:
|
|
|
|
folder = self.o.getTool().getAppFolder()
|
2009-06-29 07:06:01 -05:00
|
|
|
else:
|
2009-07-10 08:01:50 -05:00
|
|
|
if hasattr(self, 'folder') and self.folder:
|
|
|
|
folder = self.o
|
|
|
|
else:
|
|
|
|
folder = self.o.getParentNode()
|
2009-06-29 07:06:01 -05:00
|
|
|
# Create the object
|
|
|
|
folder.invokeFactory(portalType, objId)
|
|
|
|
ploneObj = getattr(folder, objId)
|
2010-02-08 01:53:30 -06:00
|
|
|
appyObj = ploneObj.appy()
|
2009-06-29 07:06:01 -05:00
|
|
|
# Set object attributes
|
|
|
|
for attrName, attrValue in kwargs.iteritems():
|
2011-01-28 07:36:30 -06:00
|
|
|
setattr(appyObj, attrName, attrValue)
|
2009-07-10 08:01:50 -05:00
|
|
|
if isField:
|
|
|
|
# Link the object to this one
|
|
|
|
self.link(fieldName, ploneObj)
|
|
|
|
self.o.reindexObject()
|
|
|
|
# Call custom initialization
|
2009-11-20 13:17:06 -06:00
|
|
|
if externalData: param = externalData
|
|
|
|
else: param = True
|
|
|
|
if hasattr(appyObj, 'onEdit'): appyObj.onEdit(param)
|
2009-06-29 07:06:01 -05:00
|
|
|
ploneObj.reindexObject()
|
|
|
|
return appyObj
|
|
|
|
|
2011-02-16 06:43:58 -06:00
|
|
|
def freeze(self, fieldName, doAction=False):
|
|
|
|
'''This method freezes a POD document. TODO: allow to freeze Computed
|
|
|
|
fields.'''
|
|
|
|
rq = self.request
|
|
|
|
field = self.o.getAppyType(fieldName)
|
|
|
|
if field.type != 'Pod': raise 'Cannot freeze non-Pod field.'
|
|
|
|
# Perform the related action if required.
|
|
|
|
if doAction: self.request.set('askAction', True)
|
|
|
|
# Set the freeze format
|
|
|
|
rq.set('podFormat', field.freezeFormat)
|
|
|
|
# Generate the document.
|
|
|
|
doc = field.getValue(self.o)
|
|
|
|
if isinstance(doc, basestring):
|
|
|
|
self.log(FREEZE_ERROR % (field.freezeFormat, field.name, doc),
|
|
|
|
type='error')
|
|
|
|
if field.freezeFormat == 'odt': raise FREEZE_FATAL_ERROR
|
|
|
|
self.log('Trying to freeze the ODT version...')
|
|
|
|
# Try to freeze the ODT version of the document, which does not
|
|
|
|
# require to call OpenOffice/LibreOffice, so the risk of error is
|
|
|
|
# smaller.
|
|
|
|
self.request.set('podFormat', 'odt')
|
|
|
|
doc = field.getValue(self.o)
|
|
|
|
if isinstance(doc, basestring):
|
|
|
|
self.log(FREEZE_ERROR % ('odt', field.name, doc), type='error')
|
|
|
|
raise FREEZE_FATAL_ERROR
|
|
|
|
field.store(self.o, doc)
|
|
|
|
|
|
|
|
def unFreeze(self, fieldName):
|
|
|
|
'''This method un freezes a POD document. TODO: allow to unfreeze
|
|
|
|
Computed fields.'''
|
|
|
|
rq = self.request
|
|
|
|
field = self.o.getAppyType(fieldName)
|
|
|
|
if field.type != 'Pod': raise 'Cannot unFreeze non-Pod field.'
|
|
|
|
field.store(self.o, None)
|
|
|
|
|
2010-11-26 10:30:46 -06:00
|
|
|
def delete(self):
|
|
|
|
'''Deletes myself.'''
|
|
|
|
self.o.delete()
|
|
|
|
|
2011-06-28 02:12:20 -05:00
|
|
|
def translate(self, label, mapping={}, domain=None, language=None,
|
|
|
|
format='html'):
|
2009-10-18 07:52:27 -05:00
|
|
|
'''Check documentation of self.o.translate.'''
|
2011-06-28 02:12:20 -05:00
|
|
|
return self.o.translate(label, mapping, domain, language=language,
|
|
|
|
format=format)
|
2009-06-29 07:06:01 -05:00
|
|
|
|
2011-07-26 15:15:04 -05:00
|
|
|
def do(self, transition, comment='', doAction=True, doNotify=True,
|
2009-11-20 13:17:06 -06:00
|
|
|
doHistory=True):
|
2009-06-29 07:06:01 -05:00
|
|
|
'''This method allows to trigger on p_self a workflow p_transition
|
2011-07-26 15:15:04 -05:00
|
|
|
programmatically. See doc in self.o.do.'''
|
|
|
|
return self.o.do(transition, comment, doAction=doAction,
|
|
|
|
doNotify=doNotify, doHistory=doHistory, doSay=False)
|
2009-07-10 08:01:50 -05:00
|
|
|
|
2011-02-12 10:09:11 -06:00
|
|
|
def log(self, message, type='info'): return self.o.log(message, type)
|
|
|
|
def say(self, message, type='info'): return self.o.say(message, type)
|
2009-12-17 14:14:52 -06:00
|
|
|
|
2010-02-12 03:59:42 -06:00
|
|
|
def normalize(self, s, usage='fileName'):
|
2009-11-06 04:33:56 -06:00
|
|
|
'''Returns a version of string p_s whose special chars have been
|
|
|
|
replaced with normal chars.'''
|
2010-03-25 10:34:37 -05:00
|
|
|
return normalizeString(s, usage)
|
2009-11-06 04:33:56 -06:00
|
|
|
|
2010-02-22 08:28:20 -06:00
|
|
|
def search(self, klass, sortBy='', maxResults=None, noSecurity=False,
|
|
|
|
**fields):
|
2009-11-06 04:33:56 -06:00
|
|
|
'''Searches objects of p_klass. p_sortBy must be the name of an indexed
|
|
|
|
field (declared with indexed=True); every param in p_fields must
|
|
|
|
take the name of an indexed field and take a possible value of this
|
2009-11-24 15:41:42 -06:00
|
|
|
field. You can optionally specify a maximum number of results in
|
2010-01-12 14:15:14 -06:00
|
|
|
p_maxResults. If p_noSecurity is specified, you get all objects,
|
|
|
|
even if the logged user does not have the permission to view it.'''
|
2009-11-06 04:33:56 -06:00
|
|
|
# Find the content type corresponding to p_klass
|
2010-10-14 07:43:56 -05:00
|
|
|
contentType = self.tool.o.getPortalType(klass)
|
2009-11-06 04:33:56 -06:00
|
|
|
# Create the Search object
|
|
|
|
search = Search('customSearch', sortBy=sortBy, **fields)
|
2009-12-21 13:45:29 -06:00
|
|
|
if not maxResults:
|
|
|
|
maxResults = 'NO_LIMIT'
|
|
|
|
# If I let maxResults=None, only a subset of the results will be
|
|
|
|
# returned by method executeResult.
|
2010-10-14 07:43:56 -05:00
|
|
|
res = self.tool.o.executeQuery(contentType, search=search,
|
|
|
|
maxResults=maxResults, noSecurity=noSecurity)
|
2009-11-06 04:33:56 -06:00
|
|
|
return [o.appy() for o in res['objects']]
|
|
|
|
|
2010-01-14 10:54:18 -06:00
|
|
|
def count(self, klass, noSecurity=False, **fields):
|
2009-11-24 15:41:42 -06:00
|
|
|
'''Identical to m_search above, but returns the number of objects that
|
|
|
|
match the search instead of returning the objects themselves. Use
|
|
|
|
this method instead of writing len(self.search(...)).'''
|
2010-10-14 07:43:56 -05:00
|
|
|
contentType = self.tool.o.getPortalType(klass)
|
2009-11-24 15:41:42 -06:00
|
|
|
search = Search('customSearch', **fields)
|
2010-10-14 07:43:56 -05:00
|
|
|
res = self.tool.o.executeQuery(contentType, search=search,
|
|
|
|
brainsOnly=True, noSecurity=noSecurity)
|
2009-11-24 15:41:42 -06:00
|
|
|
if res: return res._len # It is a LazyMap instance
|
|
|
|
else: return 0
|
|
|
|
|
2010-02-22 08:28:20 -06:00
|
|
|
def compute(self, klass, sortBy='', maxResults=None, context=None,
|
|
|
|
expression=None, noSecurity=False, **fields):
|
2010-01-18 08:12:22 -06:00
|
|
|
'''This method, like m_search and m_count above, performs a query on
|
|
|
|
objects of p_klass. But in this case, instead of returning a list of
|
|
|
|
matching objects (like m_search) or counting elements (like p_count),
|
|
|
|
it evaluates, on every matching object, a Python p_expression (which
|
|
|
|
may be an expression or a statement), and returns, if needed, a
|
|
|
|
result. The result may be initialized through parameter p_context.
|
|
|
|
p_expression is evaluated with 2 variables in its context: "obj"
|
|
|
|
which is the currently walked object, instance of p_klass, and "ctx",
|
|
|
|
which is the context as initialized (or not) by p_context. p_context
|
|
|
|
may be used as
|
2010-02-22 08:28:20 -06:00
|
|
|
(1) a variable or instance that is updated on every call to
|
|
|
|
produce a result;
|
|
|
|
(2) an input variable or instance;
|
2010-01-18 08:12:22 -06:00
|
|
|
(3) both.
|
|
|
|
|
|
|
|
The method returns p_context, modified or not by evaluation of
|
|
|
|
p_expression on every matching object.
|
|
|
|
|
|
|
|
When you need to perform an action or computation on a lot of
|
|
|
|
objects, use this method instead of doing things like
|
|
|
|
|
|
|
|
"for obj in self.search(MyClass,...)"
|
|
|
|
'''
|
2010-10-14 07:43:56 -05:00
|
|
|
contentType = self.tool.o.getPortalType(klass)
|
2010-02-22 08:28:20 -06:00
|
|
|
search = Search('customSearch', sortBy=sortBy, **fields)
|
2010-01-18 08:12:22 -06:00
|
|
|
# Initialize the context variable "ctx"
|
|
|
|
ctx = context
|
2010-10-14 07:43:56 -05:00
|
|
|
for brain in self.tool.o.executeQuery(contentType, search=search, \
|
|
|
|
brainsOnly=True, maxResults=maxResults, noSecurity=noSecurity):
|
2010-01-18 08:12:22 -06:00
|
|
|
# Get the Appy object from the brain
|
2011-02-22 07:16:42 -06:00
|
|
|
if noSecurity: method = '_unrestrictedGetObject'
|
|
|
|
else: method = 'getObject'
|
|
|
|
exec 'obj = brain.%s().appy()' % method
|
2010-01-18 08:12:22 -06:00
|
|
|
exec expression
|
|
|
|
return ctx
|
|
|
|
|
2009-11-06 04:33:56 -06:00
|
|
|
def reindex(self):
|
|
|
|
'''Asks a direct object reindexing. In most cases you don't have to
|
|
|
|
reindex objects "manually" with this method. When an object is
|
|
|
|
modified after some user action has been performed, Appy reindexes
|
|
|
|
this object automatically. But if your code modifies other objects,
|
|
|
|
Appy may not know that they must be reindexed, too. So use this
|
|
|
|
method in those cases.'''
|
|
|
|
self.o.reindexObject()
|
|
|
|
|
2011-02-12 10:09:11 -06:00
|
|
|
def export(self, at='string', format='xml', include=None, exclude=None):
|
2011-02-23 04:30:44 -06:00
|
|
|
'''Creates an "exportable" version of this object. p_format is "xml" by
|
2011-02-12 10:09:11 -06:00
|
|
|
default, but can also be "csv". If p_format is:
|
|
|
|
* "xml", if p_at is "string", this method returns the XML version,
|
|
|
|
without the XML prologue. Else, (a) if not p_at, the XML
|
|
|
|
will be exported on disk, in the OS temp folder, with an
|
|
|
|
ugly name; (b) else, it will be exported at path p_at.
|
|
|
|
* "csv", if p_at is "string", this method returns the CSV data as a
|
|
|
|
string. If p_at is an opened file handler, the CSV line will
|
|
|
|
be appended in it.
|
|
|
|
If p_include is given, only fields whose names are in it will be
|
|
|
|
included. p_exclude, if given, contains names of fields that will
|
|
|
|
not be included in the result.
|
|
|
|
'''
|
|
|
|
if format == 'xml':
|
|
|
|
# Todo: take p_include and p_exclude into account.
|
|
|
|
# Determine where to put the result
|
|
|
|
toDisk = (at != 'string')
|
|
|
|
if toDisk and not at:
|
|
|
|
at = getOsTempFolder() + '/' + self.o.UID() + '.xml'
|
|
|
|
# Create the XML version of the object
|
|
|
|
marshaller = XmlMarshaller(cdata=True, dumpUnicode=True,
|
|
|
|
dumpXmlPrologue=toDisk,
|
|
|
|
rootTag=self.klass.__name__)
|
|
|
|
xml = marshaller.marshall(self.o, objectType='appy')
|
|
|
|
# Produce the desired result
|
|
|
|
if toDisk:
|
|
|
|
f = file(at, 'w')
|
|
|
|
f.write(xml.encode('utf-8'))
|
|
|
|
f.close()
|
|
|
|
return at
|
|
|
|
else:
|
|
|
|
return xml
|
|
|
|
elif format == 'csv':
|
|
|
|
if isinstance(at, basestring):
|
|
|
|
marshaller = CsvMarshaller(include=include, exclude=exclude)
|
|
|
|
return marshaller.marshall(self)
|
|
|
|
else:
|
|
|
|
marshaller = CsvMarshaller(at, include=include, exclude=exclude)
|
|
|
|
marshaller.marshall(self)
|
2009-11-11 13:22:13 -06:00
|
|
|
|
2010-01-14 01:56:04 -06:00
|
|
|
def historize(self, data):
|
|
|
|
'''This method allows to add "manually" a "data-change" event into the
|
|
|
|
object's history. Indeed, data changes are "automatically" recorded
|
|
|
|
only when an object is edited through the edit form, not when a
|
|
|
|
setter is called from the code.
|
|
|
|
|
|
|
|
p_data must be a dictionary whose keys are field names (strings) and
|
|
|
|
whose values are the previous field values.'''
|
2010-10-14 07:43:56 -05:00
|
|
|
self.o.addDataChange(data)
|
2011-02-23 04:30:44 -06:00
|
|
|
|
|
|
|
def formatText(self, text, format='html'):
|
|
|
|
'''Produces a representation of p_text into the desired p_format, which
|
|
|
|
is 'html' by default.'''
|
|
|
|
return self.o.formatText(text, format)
|
2009-06-29 07:06:01 -05:00
|
|
|
# ------------------------------------------------------------------------------
|