Added script eggify.py for wrapping a Python module into an egg, and plenty of minor improvements and refactorings.
This commit is contained in:
parent
aea19a819e
commit
52816ec343
23 changed files with 2048 additions and 1760 deletions
|
@ -452,6 +452,16 @@ class ToolMixin(BaseMixin):
|
|||
return pythonClass.maySearch(self.appy())
|
||||
return True
|
||||
|
||||
def userMayNavigate(self, someClass):
|
||||
'''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 class whose currently shown object is an
|
||||
instance of. If no such method exists, we return True.'''
|
||||
pythonClass = self.getAppyClass(someClass)
|
||||
if 'mayNavigate' in pythonClass.__dict__:
|
||||
return pythonClass.mayNavigate(self.appy())
|
||||
return True
|
||||
|
||||
def onImportObjects(self):
|
||||
'''This method is called when the user wants to create objects from
|
||||
external data.'''
|
||||
|
|
|
@ -77,6 +77,10 @@ class BaseMixin:
|
|||
|
||||
def delete(self):
|
||||
'''This methods is self's suicide.'''
|
||||
# Call a custom "onDelete" if it exists
|
||||
appyObj = self.appy()
|
||||
if hasattr(appyObj, 'onDelete'): appyObj.onDelete()
|
||||
# Delete the object
|
||||
self.getParentNode().manage_delObjects([self.id])
|
||||
|
||||
def onDelete(self):
|
||||
|
@ -245,8 +249,11 @@ class BaseMixin:
|
|||
return self.goto(obj.getUrl())
|
||||
if rq.get('buttonNext.x', None):
|
||||
# Go to the next page for this object
|
||||
# We remember page name, because the next method may set a new
|
||||
# current page if the current one is not visible anymore.
|
||||
pageName = rq['page']
|
||||
phaseInfo = self.getAppyPhases(currentOnly=True, layoutType='edit')
|
||||
pageName, pageInfo = self.getNextPage(phaseInfo, rq['page'])
|
||||
pageName, pageInfo = self.getNextPage(phaseInfo, pageName)
|
||||
if pageName:
|
||||
# Return to the edit or view page?
|
||||
if pageInfo['showOnEdit']:
|
||||
|
@ -576,7 +583,13 @@ class BaseMixin:
|
|||
|
||||
def getPreviousPage(self, phase, page):
|
||||
'''Returns the page that precedes p_page which is in p_phase.'''
|
||||
pageIndex = phase['pages'].index(page)
|
||||
try:
|
||||
pageIndex = phase['pages'].index(page)
|
||||
except ValueError:
|
||||
# The current page is probably not visible anymore. Return the
|
||||
# first available page in current phase.
|
||||
res = phase['pages'][0]
|
||||
return res, phase['pagesInfo'][res]
|
||||
if pageIndex > 0:
|
||||
# We stay on the same phase, previous page
|
||||
res = phase['pages'][pageIndex-1]
|
||||
|
@ -594,7 +607,13 @@ class BaseMixin:
|
|||
|
||||
def getNextPage(self, phase, page):
|
||||
'''Returns the page that follows p_page which is in p_phase.'''
|
||||
pageIndex = phase['pages'].index(page)
|
||||
try:
|
||||
pageIndex = phase['pages'].index(page)
|
||||
except ValueError:
|
||||
# The current page is probably not visible anymore. Return the
|
||||
# first available page in current phase.
|
||||
res = phase['pages'][0]
|
||||
return res, phase['pagesInfo'][res]
|
||||
if pageIndex < len(phase['pages'])-1:
|
||||
# We stay on the same phase, next page
|
||||
res = phase['pages'][pageIndex+1]
|
||||
|
@ -735,7 +754,9 @@ class BaseMixin:
|
|||
'''Executes action with p_fieldName on this object.'''
|
||||
appyType = self.getAppyType(actionName)
|
||||
actionRes = appyType(self.appy())
|
||||
self.reindexObject()
|
||||
if self.getParentNode().get(self.id):
|
||||
# Else, it means that the action has led to self's removal.
|
||||
self.reindexObject()
|
||||
return appyType.result, actionRes
|
||||
|
||||
def onExecuteAppyAction(self):
|
||||
|
@ -755,13 +776,17 @@ class BaseMixin:
|
|||
if (resultType == 'computation') or not successfull:
|
||||
self.plone_utils.addPortalMessage(msg)
|
||||
return self.goto(self.getUrl(rq['HTTP_REFERER']))
|
||||
else:
|
||||
elif resultType == 'file':
|
||||
# msg does not contain a message, but a complete file to show as is.
|
||||
# (or, if your prefer, the message must be shown directly to the
|
||||
# user, not encapsulated in a Plone page).
|
||||
res = self.getProductConfig().File(msg.name, msg.name, msg,
|
||||
content_type=mimetypes.guess_type(msg.name)[0])
|
||||
return res.index_html(rq, rq.RESPONSE)
|
||||
elif resultType == 'redirect':
|
||||
# msg does not contain a message, but the URL where to redirect
|
||||
# the user.
|
||||
return self.goto(msg)
|
||||
|
||||
def onTriggerTransition(self):
|
||||
'''This method is called whenever a user wants to trigger a workflow
|
||||
|
|
|
@ -28,11 +28,8 @@
|
|||
</td>
|
||||
</dt>
|
||||
|
||||
<tal:publishedObject condition="python: contextObj">
|
||||
<dt class="portletAppyItem portletCurrent">
|
||||
<a tal:attributes="href python: contextObj.getUrl(page='main')"
|
||||
tal:content="contextObj/Title"></a>
|
||||
</dt>
|
||||
<tal:publishedObject condition="python: contextObj and tool.userMayNavigate(contextObj.meta_type)">
|
||||
<dt class="portletAppyItem portletCurrent"><b tal:content="contextObj/Title"></b></dt>
|
||||
<dt class="portletAppyItem"><metal:phases use-macro="here/skyn/portlet/macros/phases"/></dt>
|
||||
</tal:publishedObject>
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ try:
|
|||
except ImportError:
|
||||
CustomizationPolicy = None
|
||||
from OFS.Image import File
|
||||
from ZPublisher.HTTPRequest import FileUpload
|
||||
from DateTime import DateTime
|
||||
from Products.CMFCore import utils as cmfutils
|
||||
from Products.CMFCore.utils import getToolByName
|
||||
|
|
|
@ -100,4 +100,13 @@ class UserWrapper(AbstractWrapper):
|
|||
if groupName in userGroups:
|
||||
group.removeMember(self.login)
|
||||
self._callCustom('onEdit', created)
|
||||
|
||||
def onDelete(self):
|
||||
'''Before deleting myself, I must delete the corresponding Plone
|
||||
user.'''
|
||||
# Delete the corresponding Plone user
|
||||
self.o.acl_users._doDelUser(self.login)
|
||||
self.log('Plone user "%s" deleted.' % self.login)
|
||||
# Call a custom "onDelete" if any.
|
||||
self._callCustom('onDelete')
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import os, os.path, time, mimetypes, random
|
||||
import appy.pod
|
||||
from appy.gen import Search
|
||||
from appy.gen.utils import sequenceTypes, FileWrapper
|
||||
from appy.gen.utils import sequenceTypes
|
||||
from appy.shared.utils import getOsTempFolder, executeCommand, normalizeString
|
||||
from appy.shared.xml_parser import XmlMarshaller
|
||||
|
||||
|
@ -18,97 +18,62 @@ WRONG_FILE_TUPLE = 'This is not the way to set a file. You can specify a ' \
|
|||
class AbstractWrapper:
|
||||
'''Any real web framework object has a companion object that is an instance
|
||||
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):
|
||||
setattr(self.o, name, v)
|
||||
elif isinstance(v, FileWrapper):
|
||||
setattr(self.o, 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.o, 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.o, name, ploneFile)
|
||||
def __setattr__(self, name, v):
|
||||
if name == 'title':
|
||||
self.o.setTitle(v)
|
||||
return
|
||||
def __init__(self, o): self.__dict__['o'] = o
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
appyType = self.o.getAppyType(name)
|
||||
if not appyType:
|
||||
raise 'Attribute "%s" does not exist.' % name
|
||||
if appyType.type == 'File':
|
||||
self._set_file_attribute(name, v)
|
||||
elif appyType.type == 'Ref':
|
||||
raise "Use methods 'link' or 'create' to modify references."
|
||||
else:
|
||||
setattr(self.o, name, v)
|
||||
appyType.store(self.o, value)
|
||||
|
||||
def __repr__(self):
|
||||
return '<%s wrapper at %s>' % (self.klass.__name__, id(self))
|
||||
return '<%s appyobj at %s>' % (self.klass.__name__, id(self))
|
||||
|
||||
def __cmp__(self, other):
|
||||
if other: return cmp(self.o, other.o)
|
||||
else: return 1
|
||||
else: return 1
|
||||
|
||||
def get_tool(self): return self.o.getTool().appy()
|
||||
tool = property(get_tool)
|
||||
|
||||
def get_request(self): return self.o.REQUEST
|
||||
request = property(get_request)
|
||||
|
||||
def get_session(self): return self.o.REQUEST.SESSION
|
||||
session = property(get_session)
|
||||
|
||||
def get_typeName(self): return self.__class__.__bases__[-1].__name__
|
||||
typeName = property(get_typeName)
|
||||
|
||||
def get_id(self): return self.o.id
|
||||
id = property(get_id)
|
||||
|
||||
def get_uid(self): return self.o.UID()
|
||||
uid = property(get_uid)
|
||||
|
||||
def get_state(self):
|
||||
return self.o.portal_workflow.getInfoFor(self.o, 'review_state')
|
||||
state = property(get_state)
|
||||
|
||||
def get_stateLabel(self):
|
||||
appName = self.o.getProductConfig().PROJECTNAME
|
||||
return self.o.utranslate(self.o.getWorkflowLabel(), domain=appName)
|
||||
return self.o.translate(self.o.getWorkflowLabel(), domain=appName)
|
||||
stateLabel = property(get_stateLabel)
|
||||
|
||||
def get_klass(self): return self.__class__.__bases__[-1]
|
||||
klass = property(get_klass)
|
||||
|
||||
def get_url(self): return self.o.absolute_url()
|
||||
url = property(get_url)
|
||||
|
||||
def get_history(self):
|
||||
key = self.o.workflow_history.keys()[0]
|
||||
return self.o.workflow_history[key]
|
||||
history = property(get_history)
|
||||
|
||||
def get_user(self): return self.o.portal_membership.getAuthenticatedMember()
|
||||
user = property(get_user)
|
||||
|
||||
def get_fields(self): return self.o.getAllAppyTypes()
|
||||
fields = property(get_fields)
|
||||
|
||||
|
@ -210,6 +175,10 @@ class AbstractWrapper:
|
|||
ploneObj.reindexObject()
|
||||
return appyObj
|
||||
|
||||
def delete(self):
|
||||
'''Deletes myself.'''
|
||||
self.o.delete()
|
||||
|
||||
def translate(self, label, mapping={}, domain=None, language=None):
|
||||
'''Check documentation of self.o.translate.'''
|
||||
return self.o.translate(label, mapping, domain, language=language)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue