Implemented blueprints https://blueprints.launchpad.net/appy/+spec/gen-create-root-objects, https://blueprints.launchpad.net/appy/+spec/gen-get-flavour and https://blueprints.launchpad.net/appy/+spec/pod-define-variables
This commit is contained in:
parent
ec17f900a6
commit
10eea7d735
19 changed files with 1770 additions and 1644 deletions
|
@ -408,9 +408,11 @@ class Generator(AbstractGenerator):
|
|||
self.copyFile('workflows.py', repls, destFolder='Extensions')
|
||||
|
||||
def generateWrapperProperty(self, attrName, appyType):
|
||||
# Generate getter
|
||||
'''Generates the getter for attribute p_attrName having type
|
||||
p_appyType.'''
|
||||
res = ' def get_%s(self):\n' % attrName
|
||||
blanks = ' '*8
|
||||
getterName = 'get%s%s' % (attrName[0].upper(), attrName[1:])
|
||||
if isinstance(appyType, Ref):
|
||||
res += blanks + 'return self.o._appy_getRefs("%s", ' \
|
||||
'noListIfSingleObj=True)\n' % attrName
|
||||
|
@ -418,8 +420,11 @@ class Generator(AbstractGenerator):
|
|||
res += blanks + 'appyType = getattr(self.klass, "%s")\n' % attrName
|
||||
res += blanks + 'return self.o.getComputedValue(' \
|
||||
'appyType.__dict__)\n'
|
||||
elif isinstance(appyType, File):
|
||||
res += blanks + 'v = self.o.%s()\n' % getterName
|
||||
res += blanks + 'if not v: return None\n'
|
||||
res += blanks + 'else: return FileWrapper(v)\n'
|
||||
else:
|
||||
getterName = 'get%s%s' % (attrName[0].upper(), attrName[1:])
|
||||
if attrName in ArchetypeFieldDescriptor.specialParams:
|
||||
getterName = attrName.capitalize()
|
||||
res += blanks + 'return self.o.%s()\n' % getterName
|
||||
|
|
|
@ -142,9 +142,14 @@ class ToolMixin(AbstractMixin):
|
|||
else:
|
||||
# Create a FieldDescr instance
|
||||
appyType = anObject.getAppyType(fieldName)
|
||||
atField = anObject.schema.get(fieldName)
|
||||
fieldDescr = FieldDescr(atField, appyType, None)
|
||||
res.append(fieldDescr.get())
|
||||
if not appyType:
|
||||
res.append({'atField': None, 'name': fieldName})
|
||||
# The field name is wrong.
|
||||
# We return it so we can show it in an error message.
|
||||
else:
|
||||
atField = anObject.schema.get(fieldName)
|
||||
fieldDescr = FieldDescr(atField, appyType, None)
|
||||
res.append(fieldDescr.get())
|
||||
return res
|
||||
|
||||
xhtmlToText = re.compile('<.*?>', re.S)
|
||||
|
|
|
@ -654,7 +654,8 @@
|
|||
<tal:comment replace="nothing">Columns corresponding to other fields</tal:comment>
|
||||
<tal:otherFields repeat="fieldDescr fieldDescrs">
|
||||
<tal:standardField condition="python: fieldDescr != 'workflowState'">
|
||||
<td tal:attributes="id python:'field_%s' % fieldDescr['atField'].getName()">
|
||||
<td tal:condition="fieldDescr/atField"
|
||||
tal:attributes="id python:'field_%s' % fieldDescr['atField'].getName()">
|
||||
<tal:field define="contextObj python:obj;
|
||||
isEdit python:False;
|
||||
showLabel python:False;
|
||||
|
@ -663,6 +664,9 @@
|
|||
<metal:field use-macro="here/<!macros!>/macros/showArchetypesField"/>
|
||||
</tal:field>
|
||||
</td>
|
||||
<td tal:condition="not: fieldDescr/atField" style="color:red">Field
|
||||
<span tal:replace="fieldDescr/name"/> not found.
|
||||
</td>
|
||||
</tal:standardField>
|
||||
<tal:workflowState condition="python: fieldDescr == 'workflowState'">
|
||||
<td id="field_workflow_state" i18n:translate="" tal:content="obj/getWorkflowLabel"></td>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# ------------------------------------------------------------------------------
|
||||
from appy.gen import *
|
||||
from appy.gen.plone25.wrappers import AbstractWrapper
|
||||
from appy.gen.plone25.wrappers import AbstractWrapper, FileWrapper
|
||||
from appy.gen.plone25.wrappers.ToolWrapper import ToolWrapper
|
||||
from appy.gen.plone25.wrappers.FlavourWrapper import FlavourWrapper
|
||||
from appy.gen.plone25.wrappers.PodTemplateWrapper import PodTemplateWrapper
|
||||
|
|
|
@ -18,6 +18,7 @@ from OFS.Image import File
|
|||
from DateTime import DateTime
|
||||
from Products.CMFCore.utils import getToolByName
|
||||
from Products.CMFPlone.PloneBatch import Batch
|
||||
from OFS.Image import File
|
||||
import logging
|
||||
logger = logging.getLogger('<!applicationName!>')
|
||||
|
||||
|
|
|
@ -2,7 +2,14 @@
|
|||
developer the real classes used by the undelrying web framework.'''
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
import time
|
||||
import time, os.path, mimetypes
|
||||
from appy.gen.utils import sequenceTypes
|
||||
from appy.shared.utils import getOsTempFolder
|
||||
|
||||
# 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).'
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class AbstractWrapper:
|
||||
|
@ -10,8 +17,56 @@ class AbstractWrapper:
|
|||
of this class.'''
|
||||
def __init__(self, o):
|
||||
self.__dict__['o'] = o
|
||||
def _set_file_attribute(self, name, v):
|
||||
'''Updates the value of a file attribute named p_name with value p_v.
|
||||
p_v may be:
|
||||
- a string value containing the path to a file on disk;
|
||||
- a 2-tuple (fileName, fileContent) where
|
||||
* fileName = the name of the file (ie "myFile.odt")
|
||||
* fileContent = the binary or textual content of the file or an
|
||||
open file handler.
|
||||
- a 3-tuple (fileName, fileContent, mimeType) where mimeType is the
|
||||
v MIME type of the file.'''
|
||||
ploneFileClass = self.o.getProductConfig().File
|
||||
if isinstance(v, ploneFileClass):
|
||||
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
|
||||
elif isinstance(v, FileWrapper):
|
||||
setattr(self, name, v._atFile)
|
||||
elif isinstance(v, basestring):
|
||||
f = file(v)
|
||||
fileName = os.path.basename(v)
|
||||
fileId = 'file.%f' % time.time()
|
||||
ploneFile = ploneFileClass(fileId, fileName, f)
|
||||
ploneFile.filename = fileName
|
||||
ploneFile.content_type = mimetypes.guess_type(fileName)[0]
|
||||
setattr(self, name, ploneFile)
|
||||
f.close()
|
||||
elif type(v) in sequenceTypes:
|
||||
# It should be a 2-tuple or 3-tuple
|
||||
fileName = None
|
||||
mimeType = None
|
||||
if len(v) == 2:
|
||||
fileName, fileContent = v
|
||||
elif len(v) == 3:
|
||||
fileName, fileContent, mimeType = v
|
||||
else:
|
||||
raise WRONG_FILE_TUPLE
|
||||
if fileName:
|
||||
fileId = 'file.%f' % time.time()
|
||||
ploneFile = ploneFileClass(fileId, fileName, fileContent)
|
||||
ploneFile.filename = fileName
|
||||
if not mimeType:
|
||||
mimeType = mimetypes.guess_type(fileName)[0]
|
||||
ploneFile.content_type = mimeType
|
||||
setattr(self, name, ploneFile)
|
||||
def __setattr__(self, name, v):
|
||||
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
|
||||
appyType = self.o.getAppyType(name)
|
||||
if not appyType and (name != 'title'):
|
||||
raise 'Attribute "%s" does not exist.' % name
|
||||
if appyType and (appyType['type'] == 'File'):
|
||||
self._set_file_attribute(name, v)
|
||||
else:
|
||||
exec "self.o.set%s%s(v)" % (name[0].upper(), name[1:])
|
||||
def __cmp__(self, other):
|
||||
if other:
|
||||
return cmp(self.o, other.o)
|
||||
|
@ -20,6 +75,9 @@ class AbstractWrapper:
|
|||
def get_tool(self):
|
||||
return self.o.getTool()._appy_getWrapper(force=True)
|
||||
tool = property(get_tool)
|
||||
def get_flavour(self):
|
||||
return self.o.getTool().getFlavour(self.o, appy=True)
|
||||
flavour = property(get_flavour)
|
||||
def get_session(self):
|
||||
return self.o.REQUEST.SESSION
|
||||
session = property(get_session)
|
||||
|
@ -61,21 +119,37 @@ class AbstractWrapper:
|
|||
sortedRefField
|
||||
getattr(self.o, sortedRefField).append(obj.UID())
|
||||
|
||||
def create(self, fieldName, **kwargs):
|
||||
'''This method allows to create an object and link it to the current
|
||||
one through reference field named p_fieldName.'''
|
||||
# Determine object id and portal type
|
||||
portalType = self.o.getAppyRefPortalType(fieldName)
|
||||
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:
|
||||
fieldName = fieldNameOrClass
|
||||
idPrefix = fieldName
|
||||
portalType = self.o.getAppyRefPortalType(fieldName)
|
||||
else:
|
||||
theClass = fieldNameOrClass
|
||||
idPrefix = theClass.__name__
|
||||
portalType = self.o._appy_getAtType(theClass, self.flavour.o)
|
||||
# Determine object id
|
||||
if kwargs.has_key('id'):
|
||||
objId = kwargs['id']
|
||||
del kwargs['id']
|
||||
else:
|
||||
objId = '%s.%f' % (fieldName, time.time())
|
||||
# Where must I create te object?
|
||||
if hasattr(self, 'folder') and self.folder:
|
||||
folder = self.o
|
||||
objId = '%s.%f' % (idPrefix, time.time())
|
||||
# Where must I create the object?
|
||||
if not isField:
|
||||
folder = self.o.getTool().getAppFolder()
|
||||
else:
|
||||
folder = self.o.getParentNode()
|
||||
if hasattr(self, 'folder') and self.folder:
|
||||
folder = self.o
|
||||
else:
|
||||
folder = self.o.getParentNode()
|
||||
# Create the object
|
||||
folder.invokeFactory(portalType, objId)
|
||||
ploneObj = getattr(folder, objId)
|
||||
|
@ -93,13 +167,15 @@ class AbstractWrapper:
|
|||
pass
|
||||
else:
|
||||
getattr(ploneObj, setterName)(attrValue)
|
||||
# Link the object to this one
|
||||
self.link(fieldName, ploneObj)
|
||||
if isField:
|
||||
# Link the object to this one
|
||||
self.link(fieldName, ploneObj)
|
||||
self.o.reindexObject()
|
||||
# Call custom initialization
|
||||
try:
|
||||
appyObj.onEdit(True) # Call custom initialization
|
||||
appyObj.onEdit(True)
|
||||
except AttributeError:
|
||||
pass
|
||||
self.o.reindexObject()
|
||||
ploneObj.reindexObject()
|
||||
return appyObj
|
||||
|
||||
|
@ -133,4 +209,49 @@ class AbstractWrapper:
|
|||
self.o._v_appy_do = {'doAction': doAction, 'doNotify': doNotify}
|
||||
wfTool.doActionFor(self.o, transitionName, comment=comment)
|
||||
del self.o._v_appy_do
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
class FileWrapper:
|
||||
'''When you get, from an appy object, the value of a File attribute, you
|
||||
get an instance of this class.'''
|
||||
def __init__(self, atFile):
|
||||
'''This constructor is only used by Appy to create a nice File instance
|
||||
from a Plone/Zope corresponding instance (p_atFile). If you need to
|
||||
create a new file and assign it to a File attribute, use the
|
||||
attribute setter, do not create yourself an instance of this
|
||||
class.'''
|
||||
d = self.__dict__
|
||||
d['_atFile'] = atFile # Not for you!
|
||||
d['name'] = atFile.filename
|
||||
d['content'] = atFile.data
|
||||
d['mimeType'] = atFile.content_type
|
||||
d['size'] = atFile.size # In bytes
|
||||
|
||||
def __setattr__(self, name, v):
|
||||
d = self.__dict__
|
||||
if name == 'name':
|
||||
self._atFile.filename = v
|
||||
d['name'] = v
|
||||
elif name == 'content':
|
||||
self._atFile.update_data(v, self.mimeType, len(v))
|
||||
d['content'] = v
|
||||
d['size'] = len(v)
|
||||
elif name == 'mimeType':
|
||||
self._atFile.content_type = self.mimeType = v
|
||||
else:
|
||||
raise 'Impossible to set attribute %s. "Settable" attributes ' \
|
||||
'are "name", "content" and "mimeType".' % name
|
||||
|
||||
def dump(self, filePath=None):
|
||||
'''Writes the file on disk. If p_filePath is specified, it is the
|
||||
path name where the file will be dumped; folders mentioned in it
|
||||
must exist. If not, the file will be dumped in the OS temp folder.
|
||||
The absoulte path name of the dumped file is returned.'''
|
||||
if not filePath:
|
||||
filePath = '%s/file%f.%s' % (getOsTempFolder(), time.time(),
|
||||
self.name)
|
||||
f = file(filePath, 'w')
|
||||
f.write(self.content)
|
||||
f.close()
|
||||
return filePath
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue