[gen] Added a mechanism for caching method calls that are performed several times for displaying a single ui page (ie: field.show methods).
This commit is contained in:
parent
b12ea0a64d
commit
244826194b
|
@ -7,14 +7,13 @@ from appy.gen.layout import Table
|
||||||
from appy.gen.layout import defaultFieldLayouts
|
from appy.gen.layout import defaultFieldLayouts
|
||||||
from appy.gen.mail import sendNotification
|
from appy.gen.mail import sendNotification
|
||||||
from appy.gen.indexer import defaultIndexes, XhtmlTextExtractor
|
from appy.gen.indexer import defaultIndexes, XhtmlTextExtractor
|
||||||
from appy.gen.utils import GroupDescr, Keywords, getClassName, SomeObjects
|
from appy.gen import utils as gutils
|
||||||
import appy.pod
|
import appy.pod
|
||||||
from appy.pod.renderer import Renderer
|
from appy.pod.renderer import Renderer
|
||||||
from appy.shared.data import countries
|
from appy.shared.data import countries
|
||||||
from appy.shared.xml_parser import XhtmlCleaner
|
from appy.shared.xml_parser import XhtmlCleaner
|
||||||
from appy.shared.diff import HtmlDiff
|
from appy.shared.diff import HtmlDiff
|
||||||
from appy.shared.utils import Traceback, getOsTempFolder, formatNumber, \
|
from appy.shared import utils as sutils
|
||||||
FileWrapper, sequenceTypes
|
|
||||||
|
|
||||||
# Default Appy permissions -----------------------------------------------------
|
# Default Appy permissions -----------------------------------------------------
|
||||||
r, w, d = ('read', 'write', 'delete')
|
r, w, d = ('read', 'write', 'delete')
|
||||||
|
@ -30,7 +29,7 @@ labelTypes = ('label', 'descr', 'help')
|
||||||
def initMasterValue(v):
|
def initMasterValue(v):
|
||||||
'''Standardizes p_v as a list of strings.'''
|
'''Standardizes p_v as a list of strings.'''
|
||||||
if not isinstance(v, bool) and not v: res = []
|
if not isinstance(v, bool) and not v: res = []
|
||||||
elif type(v) not in sequenceTypes: res = [v]
|
elif type(v) not in sutils.sequenceTypes: res = [v]
|
||||||
else: res = v
|
else: res = v
|
||||||
return [str(v) for v in res]
|
return [str(v) for v in res]
|
||||||
|
|
||||||
|
@ -249,8 +248,8 @@ class Group:
|
||||||
# First, create the corresponding GroupDescr if not already in
|
# First, create the corresponding GroupDescr if not already in
|
||||||
# p_groupDescrs.
|
# p_groupDescrs.
|
||||||
if self.name not in groupDescrs:
|
if self.name not in groupDescrs:
|
||||||
groupDescr = groupDescrs[self.name] = \
|
groupDescr = groupDescrs[self.name] = gutils.GroupDescr(\
|
||||||
GroupDescr(self, page, metaType, forSearch=forSearch).get()
|
self, page, metaType, forSearch=forSearch).get()
|
||||||
# Insert the group at the higher level (ie, directly in p_widgets)
|
# Insert the group at the higher level (ie, directly in p_widgets)
|
||||||
# if the group is not itself in a group.
|
# if the group is not itself in a group.
|
||||||
if not self.group:
|
if not self.group:
|
||||||
|
@ -258,7 +257,7 @@ class Group:
|
||||||
else:
|
else:
|
||||||
outerGroupDescr = self.group.insertInto(widgets, groupDescrs,
|
outerGroupDescr = self.group.insertInto(widgets, groupDescrs,
|
||||||
page, metaType, forSearch=forSearch)
|
page, metaType, forSearch=forSearch)
|
||||||
GroupDescr.addWidget(outerGroupDescr, groupDescr)
|
gutils.GroupDescr.addWidget(outerGroupDescr, groupDescr)
|
||||||
else:
|
else:
|
||||||
groupDescr = groupDescrs[self.name]
|
groupDescr = groupDescrs[self.name]
|
||||||
return groupDescr
|
return groupDescr
|
||||||
|
@ -348,12 +347,12 @@ class Search:
|
||||||
if (field and (field.getIndexType() == 'TextIndex')) or \
|
if (field and (field.getIndexType() == 'TextIndex')) or \
|
||||||
(fieldName == 'SearchableText'):
|
(fieldName == 'SearchableText'):
|
||||||
# For TextIndex indexes. We must split p_fieldValue into keywords.
|
# For TextIndex indexes. We must split p_fieldValue into keywords.
|
||||||
res = Keywords(fieldValue).get()
|
res = gutils.Keywords(fieldValue).get()
|
||||||
elif isinstance(fieldValue, basestring) and fieldValue.endswith('*'):
|
elif isinstance(fieldValue, basestring) and fieldValue.endswith('*'):
|
||||||
v = fieldValue[:-1]
|
v = fieldValue[:-1]
|
||||||
# Warning: 'z' is higher than 'Z'!
|
# Warning: 'z' is higher than 'Z'!
|
||||||
res = {'query':(v,v+'z'), 'range':'min:max'}
|
res = {'query':(v,v+'z'), 'range':'min:max'}
|
||||||
elif type(fieldValue) in sequenceTypes:
|
elif type(fieldValue) in sutils.sequenceTypes:
|
||||||
if fieldValue and isinstance(fieldValue[0], basestring):
|
if fieldValue and isinstance(fieldValue[0], basestring):
|
||||||
# We have a list of string values (ie: we need to
|
# We have a list of string values (ie: we need to
|
||||||
# search v1 or v2 or...)
|
# search v1 or v2 or...)
|
||||||
|
@ -410,7 +409,7 @@ class Search:
|
||||||
def isShowable(self, klass, tool):
|
def isShowable(self, klass, tool):
|
||||||
'''Is this Search instance (defined in p_klass) showable?'''
|
'''Is this Search instance (defined in p_klass) showable?'''
|
||||||
if self.show.__class__.__name__ == 'staticmethod':
|
if self.show.__class__.__name__ == 'staticmethod':
|
||||||
return self.show.__get__(klass)(tool)
|
return gutils.callMethod(tool, self.show, klass=klass)
|
||||||
return self.show
|
return self.show
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
@ -540,7 +539,7 @@ class Type:
|
||||||
self.name = name
|
self.name = name
|
||||||
# Determine prefix for this class
|
# Determine prefix for this class
|
||||||
if not klass: prefix = appName
|
if not klass: prefix = appName
|
||||||
else: prefix = getClassName(klass, appName)
|
else: prefix = gutils.getClassName(klass, appName)
|
||||||
# Recompute the ID (and derived attributes) that may have changed if
|
# Recompute the ID (and derived attributes) that may have changed if
|
||||||
# we are in debug mode (because we recreate new Type instances).
|
# we are in debug mode (because we recreate new Type instances).
|
||||||
self.id = id(self)
|
self.id = id(self)
|
||||||
|
@ -628,7 +627,7 @@ class Type:
|
||||||
else:
|
else:
|
||||||
res = self.show
|
res = self.show
|
||||||
# Take into account possible values 'view', 'edit', 'result'...
|
# Take into account possible values 'view', 'edit', 'result'...
|
||||||
if type(res) in sequenceTypes:
|
if type(res) in sutils.sequenceTypes:
|
||||||
for r in res:
|
for r in res:
|
||||||
if r == layoutType: return True
|
if r == layoutType: return True
|
||||||
return False
|
return False
|
||||||
|
@ -645,7 +644,7 @@ class Type:
|
||||||
master, masterValue = masterData
|
master, masterValue = masterData
|
||||||
reqValue = master.getRequestValue(obj.REQUEST)
|
reqValue = master.getRequestValue(obj.REQUEST)
|
||||||
# reqValue can be a list or not
|
# reqValue can be a list or not
|
||||||
if type(reqValue) not in sequenceTypes:
|
if type(reqValue) not in sutils.sequenceTypes:
|
||||||
return reqValue in masterValue
|
return reqValue in masterValue
|
||||||
else:
|
else:
|
||||||
for m in masterValue:
|
for m in masterValue:
|
||||||
|
@ -847,7 +846,7 @@ class Type:
|
||||||
if forSearch and (value != None):
|
if forSearch and (value != None):
|
||||||
if isinstance(value, unicode):
|
if isinstance(value, unicode):
|
||||||
res = value.encode('utf-8')
|
res = value.encode('utf-8')
|
||||||
elif type(value) in sequenceTypes:
|
elif type(value) in sutils.sequenceTypes:
|
||||||
res = []
|
res = []
|
||||||
for v in value:
|
for v in value:
|
||||||
if isinstance(v, unicode): res.append(v.encode('utf-8'))
|
if isinstance(v, unicode): res.append(v.encode('utf-8'))
|
||||||
|
@ -966,7 +965,7 @@ class Type:
|
||||||
p_self type definition on p_obj.'''
|
p_self type definition on p_obj.'''
|
||||||
setattr(obj, self.name, value)
|
setattr(obj, self.name, value)
|
||||||
|
|
||||||
def callMethod(self, obj, method, raiseOnError=True):
|
def callMethod(self, obj, method, cache=True):
|
||||||
'''This method is used to call a p_method on p_obj. p_method is part of
|
'''This method is used to call a p_method on p_obj. p_method is part of
|
||||||
this type definition (ie a default method, the method of a Computed
|
this type definition (ie a default method, the method of a Computed
|
||||||
field, a method used for showing or not a field...). Normally, those
|
field, a method used for showing or not a field...). Normally, those
|
||||||
|
@ -975,26 +974,22 @@ class Type:
|
||||||
p_method with no arg *or* with the field arg.'''
|
p_method with no arg *or* with the field arg.'''
|
||||||
obj = obj.appy()
|
obj = obj.appy()
|
||||||
try:
|
try:
|
||||||
return method(obj)
|
return gutils.callMethod(obj, method, cache=cache)
|
||||||
except TypeError, te:
|
except TypeError, te:
|
||||||
# Try a version of the method that would accept self as an
|
# Try a version of the method that would accept self as an
|
||||||
# additional parameter.
|
# additional parameter. In this case, we do not try to cache the
|
||||||
tb = Traceback.get()
|
# value (we do not call gutils.callMethod), because the value may
|
||||||
|
# be different depending on the parameter.
|
||||||
|
tb = sutils.Traceback.get()
|
||||||
try:
|
try:
|
||||||
return method(obj, self)
|
return method(obj, self)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
obj.log(tb, type='error')
|
obj.log(tb, type='error')
|
||||||
if raiseOnError:
|
# Raise the initial error.
|
||||||
# Raise the initial error.
|
raise te
|
||||||
raise te
|
|
||||||
else:
|
|
||||||
return str(te)
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
obj.log(Traceback.get(), type='error')
|
obj.log(sutils.Traceback.get(), type='error')
|
||||||
if raiseOnError:
|
raise e
|
||||||
raise e
|
|
||||||
else:
|
|
||||||
return str(e)
|
|
||||||
|
|
||||||
def process(self, obj):
|
def process(self, obj):
|
||||||
'''This method is a general hook allowing a field to perform some
|
'''This method is a general hook allowing a field to perform some
|
||||||
|
@ -1047,7 +1042,7 @@ class Float(Type):
|
||||||
self.precision = precision
|
self.precision = precision
|
||||||
# The decimal separator can be a tuple if several are allowed, ie
|
# The decimal separator can be a tuple if several are allowed, ie
|
||||||
# ('.', ',')
|
# ('.', ',')
|
||||||
if type(sep) not in sequenceTypes:
|
if type(sep) not in sutils.sequenceTypes:
|
||||||
self.sep = (sep,)
|
self.sep = (sep,)
|
||||||
else:
|
else:
|
||||||
self.sep = sep
|
self.sep = sep
|
||||||
|
@ -1065,8 +1060,8 @@ class Float(Type):
|
||||||
self.pythonType = float
|
self.pythonType = float
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value, showChanges=False):
|
def getFormattedValue(self, obj, value, showChanges=False):
|
||||||
return formatNumber(value, sep=self.sep[0], precision=self.precision,
|
return sutils.formatNumber(value, sep=self.sep[0],
|
||||||
tsep=self.tsep)
|
precision=self.precision, tsep=self.tsep)
|
||||||
|
|
||||||
def validateValue(self, obj, value):
|
def validateValue(self, obj, value):
|
||||||
# Replace used separator with the Python separator '.'
|
# Replace used separator with the Python separator '.'
|
||||||
|
@ -1487,7 +1482,7 @@ class String(Type):
|
||||||
value = value[:self.maxChars]
|
value = value[:self.maxChars]
|
||||||
# Get a multivalued value if required.
|
# Get a multivalued value if required.
|
||||||
if value and self.isMultiValued() and \
|
if value and self.isMultiValued() and \
|
||||||
(type(value) not in sequenceTypes):
|
(type(value) not in sutils.sequenceTypes):
|
||||||
value = [value]
|
value = [value]
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
@ -1701,12 +1696,12 @@ class File(Type):
|
||||||
res.filename = fileName
|
res.filename = fileName
|
||||||
res.content_type = mimetypes.guess_type(fileName)[0]
|
res.content_type = mimetypes.guess_type(fileName)[0]
|
||||||
f.close()
|
f.close()
|
||||||
if not zope: res = FileWrapper(res)
|
if not zope: res = sutils.FileWrapper(res)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def getValue(self, obj):
|
def getValue(self, obj):
|
||||||
value = Type.getValue(self, obj)
|
value = Type.getValue(self, obj)
|
||||||
if value: value = FileWrapper(value)
|
if value: value = sutils.FileWrapper(value)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value, showChanges=False):
|
def getFormattedValue(self, obj, value, showChanges=False):
|
||||||
|
@ -1786,11 +1781,11 @@ class File(Type):
|
||||||
setattr(obj, self.name, existingValue)
|
setattr(obj, self.name, existingValue)
|
||||||
elif isinstance(value, OFSImageFile):
|
elif isinstance(value, OFSImageFile):
|
||||||
setattr(obj, self.name, value)
|
setattr(obj, self.name, value)
|
||||||
elif isinstance(value, FileWrapper):
|
elif isinstance(value, sutils.FileWrapper):
|
||||||
setattr(obj, self.name, value._zopeFile)
|
setattr(obj, self.name, value._zopeFile)
|
||||||
elif isinstance(value, basestring):
|
elif isinstance(value, basestring):
|
||||||
setattr(obj, self.name, File.getFileObject(value, zope=True))
|
setattr(obj, self.name, File.getFileObject(value, zope=True))
|
||||||
elif type(value) in sequenceTypes:
|
elif type(value) in sutils.sequenceTypes:
|
||||||
# It should be a 2-tuple or 3-tuple
|
# It should be a 2-tuple or 3-tuple
|
||||||
fileName = None
|
fileName = None
|
||||||
mimeType = None
|
mimeType = None
|
||||||
|
@ -1956,13 +1951,13 @@ class Ref(Type):
|
||||||
if defValue:
|
if defValue:
|
||||||
# I must prefix call to function "type" with "__builtins__"
|
# I must prefix call to function "type" with "__builtins__"
|
||||||
# because this name was overridden by a method parameter.
|
# because this name was overridden by a method parameter.
|
||||||
if __builtins__['type'](defValue) in sequenceTypes:
|
if __builtins__['type'](defValue) in sutils.sequenceTypes:
|
||||||
uids = [o.o.UID() for o in defValue]
|
uids = [o.o.UID() for o in defValue]
|
||||||
else:
|
else:
|
||||||
uids = [defValue.o.UID()]
|
uids = [defValue.o.UID()]
|
||||||
# Prepare the result: an instance of SomeObjects, that will be unwrapped
|
# Prepare the result: an instance of SomeObjects, that will be unwrapped
|
||||||
# if not required.
|
# if not required.
|
||||||
res = SomeObjects()
|
res = gutils.SomeObjects()
|
||||||
res.totalNumber = res.batchSize = len(uids)
|
res.totalNumber = res.batchSize = len(uids)
|
||||||
batchNeeded = startNumber != None
|
batchNeeded = startNumber != None
|
||||||
if batchNeeded:
|
if batchNeeded:
|
||||||
|
@ -2040,7 +2035,7 @@ class Ref(Type):
|
||||||
'''This method links p_value (which can be a list of objects) to p_obj
|
'''This method links p_value (which can be a list of objects) to p_obj
|
||||||
through this Ref field.'''
|
through this Ref field.'''
|
||||||
# p_value can be a list of objects
|
# p_value can be a list of objects
|
||||||
if type(value) in sequenceTypes:
|
if type(value) in sutils.sequenceTypes:
|
||||||
for v in value: self.linkObject(obj, v, back=back)
|
for v in value: self.linkObject(obj, v, back=back)
|
||||||
return
|
return
|
||||||
# Gets the list of referred objects (=list of uids), or create it.
|
# Gets the list of referred objects (=list of uids), or create it.
|
||||||
|
@ -2068,7 +2063,7 @@ class Ref(Type):
|
||||||
'''This method unlinks p_value (which can be a list of objects) from
|
'''This method unlinks p_value (which can be a list of objects) from
|
||||||
p_obj through this Ref field.'''
|
p_obj through this Ref field.'''
|
||||||
# p_value can be a list of objects
|
# p_value can be a list of objects
|
||||||
if type(value) in sequenceTypes:
|
if type(value) in sutils.sequenceTypes:
|
||||||
for v in value: self.unlinkObject(obj, v, back=back)
|
for v in value: self.unlinkObject(obj, v, back=back)
|
||||||
return
|
return
|
||||||
obj = obj.o
|
obj = obj.o
|
||||||
|
@ -2093,7 +2088,7 @@ class Ref(Type):
|
||||||
# Standardize p_value into a list of Zope objects
|
# Standardize p_value into a list of Zope objects
|
||||||
objects = value
|
objects = value
|
||||||
if not objects: objects = []
|
if not objects: objects = []
|
||||||
if type(objects) not in sequenceTypes: objects = [objects]
|
if type(objects) not in sutils.sequenceTypes: objects = [objects]
|
||||||
tool = obj.getTool()
|
tool = obj.getTool()
|
||||||
for i in range(len(objects)):
|
for i in range(len(objects)):
|
||||||
if isinstance(objects[i], basestring):
|
if isinstance(objects[i], basestring):
|
||||||
|
@ -2234,7 +2229,7 @@ class Computed(Type):
|
||||||
return self.callMacro(obj, self.method)
|
return self.callMacro(obj, self.method)
|
||||||
else:
|
else:
|
||||||
# self.method is a method that will return the field value
|
# self.method is a method that will return the field value
|
||||||
return self.callMethod(obj, self.method, raiseOnError=True)
|
return self.callMethod(obj, self.method, cache=False)
|
||||||
|
|
||||||
def getFormattedValue(self, obj, value, showChanges=False):
|
def getFormattedValue(self, obj, value, showChanges=False):
|
||||||
if not isinstance(value, basestring): return str(value)
|
if not isinstance(value, basestring): return str(value)
|
||||||
|
@ -2278,12 +2273,12 @@ class Action(Type):
|
||||||
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
def getDefaultLayouts(self): return {'view': 'l-f', 'edit': 'lrv-f'}
|
||||||
def __call__(self, obj):
|
def __call__(self, obj):
|
||||||
'''Calls the action on p_obj.'''
|
'''Calls the action on p_obj.'''
|
||||||
if type(self.action) in sequenceTypes:
|
if type(self.action) in sutils.sequenceTypes:
|
||||||
# There are multiple Python methods
|
# There are multiple Python methods
|
||||||
res = [True, '']
|
res = [True, '']
|
||||||
for act in self.action:
|
for act in self.action:
|
||||||
actRes = act(obj)
|
actRes = act(obj)
|
||||||
if type(actRes) in sequenceTypes:
|
if type(actRes) in sutils.sequenceTypes:
|
||||||
res[0] = res[0] and actRes[0]
|
res[0] = res[0] and actRes[0]
|
||||||
if self.result.startswith('file'):
|
if self.result.startswith('file'):
|
||||||
res[1] = res[1] + actRes[1]
|
res[1] = res[1] + actRes[1]
|
||||||
|
@ -2294,7 +2289,7 @@ class Action(Type):
|
||||||
else:
|
else:
|
||||||
# There is only one Python method
|
# There is only one Python method
|
||||||
actRes = self.action(obj)
|
actRes = self.action(obj)
|
||||||
if type(actRes) in sequenceTypes:
|
if type(actRes) in sutils.sequenceTypes:
|
||||||
res = list(actRes)
|
res = list(actRes)
|
||||||
else:
|
else:
|
||||||
res = [actRes, '']
|
res = [actRes, '']
|
||||||
|
@ -2395,7 +2390,9 @@ class Pod(Type):
|
||||||
retrieved by calling pod to compute the result.'''
|
retrieved by calling pod to compute the result.'''
|
||||||
rq = getattr(obj, 'REQUEST', None)
|
rq = getattr(obj, 'REQUEST', None)
|
||||||
res = getattr(obj.aq_base, self.name, None)
|
res = getattr(obj.aq_base, self.name, None)
|
||||||
if res and res.size: return FileWrapper(res) # Return the frozen file.
|
if res and res.size:
|
||||||
|
# Return the frozen file.
|
||||||
|
return sutils.FileWrapper(res)
|
||||||
# If we are here, it means that we must call pod to compute the file.
|
# If we are here, it means that we must call pod to compute the file.
|
||||||
# A Pod field differs from other field types because there can be
|
# A Pod field differs from other field types because there can be
|
||||||
# several ways to produce the field value (ie: output file format can be
|
# several ways to produce the field value (ie: output file format can be
|
||||||
|
@ -2419,7 +2416,7 @@ class Pod(Type):
|
||||||
specificContext = self.context
|
specificContext = self.context
|
||||||
# Temporary file where to generate the result
|
# Temporary file where to generate the result
|
||||||
tempFileName = '%s/%s_%f.%s' % (
|
tempFileName = '%s/%s_%f.%s' % (
|
||||||
getOsTempFolder(), obj.uid, time.time(), outputFormat)
|
sutils.getOsTempFolder(), obj.uid, time.time(), outputFormat)
|
||||||
# Define parameters to give to the appy.pod renderer
|
# Define parameters to give to the appy.pod renderer
|
||||||
podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self,
|
podContext = {'tool': tool, 'user': obj.user, 'self': obj, 'field':self,
|
||||||
'now': obj.o.getProductConfig().DateTime(),
|
'now': obj.o.getProductConfig().DateTime(),
|
||||||
|
@ -2486,7 +2483,7 @@ class Pod(Type):
|
||||||
|
|
||||||
def store(self, obj, value):
|
def store(self, obj, value):
|
||||||
'''Stores (=freezes) a document (in p_value) in the field.'''
|
'''Stores (=freezes) a document (in p_value) in the field.'''
|
||||||
if isinstance(value, FileWrapper):
|
if isinstance(value, sutils.FileWrapper):
|
||||||
value = value._zopeFile
|
value = value._zopeFile
|
||||||
setattr(obj, self.name, value)
|
setattr(obj, self.name, value)
|
||||||
|
|
||||||
|
|
|
@ -164,7 +164,7 @@ class Calendar(Type):
|
||||||
'''Returns the list of other calendars whose events must also be shown
|
'''Returns the list of other calendars whose events must also be shown
|
||||||
on this calendar.'''
|
on this calendar.'''
|
||||||
if self.otherCalendars:
|
if self.otherCalendars:
|
||||||
res = self.callMethod(obj, self.otherCalendars, preComputed)
|
res = self.otherCalendars(obj.appy(), preComputed)
|
||||||
# Replace field names with field objects
|
# Replace field names with field objects
|
||||||
for i in range(len(res)):
|
for i in range(len(res)):
|
||||||
res[i][1] = res[i][0].getField(res[i][1])
|
res[i][1] = res[i][0].getField(res[i][1])
|
||||||
|
|
51
gen/utils.py
51
gen/utils.py
|
@ -306,4 +306,55 @@ def updateRolesForPermission(permission, roles, obj):
|
||||||
existingRoles = perm.getRoles()
|
existingRoles = perm.getRoles()
|
||||||
allRoles = set(existingRoles).union(roles)
|
allRoles = set(existingRoles).union(roles)
|
||||||
obj.manage_permission(permission, tuple(allRoles), acquire=0)
|
obj.manage_permission(permission, tuple(allRoles), acquire=0)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
def callMethod(obj, method, klass=None, cache=True):
|
||||||
|
'''This function is used to call a p_method on some Appy p_obj. m_method
|
||||||
|
can be an instance method on p_obj; it can also be a static method. In
|
||||||
|
this latter case, p_obj is the tool and the static method, defined in
|
||||||
|
p_klass, will be called with the tool as unique arg.
|
||||||
|
|
||||||
|
A method cache is implemented on the request object (available at
|
||||||
|
p_obj.request). So while handling a single request from the ui, every
|
||||||
|
method is called only once. Some method calls must not be cached (ie,
|
||||||
|
values of Computed fields). In this case, p_cache will be False.'''
|
||||||
|
rq = obj.request
|
||||||
|
# Create the method cache if it does not exist on the request
|
||||||
|
if not hasattr(rq, 'methodCache'): rq.methodCache = {}
|
||||||
|
# If m_method is a static method or an instance method, unwrap the true
|
||||||
|
# Python function object behind it.
|
||||||
|
methodType = method.__class__.__name__
|
||||||
|
if methodType == 'staticmethod':
|
||||||
|
method = method.__get__(klass)
|
||||||
|
elif methodType == 'instancemethod':
|
||||||
|
method = method.im_func
|
||||||
|
# Call the method if cache is not needed.
|
||||||
|
if not cache: return method(obj)
|
||||||
|
# If first arg of method is named "tool" instead of the traditional "self",
|
||||||
|
# we cheat and will call the method with the tool as first arg. This will
|
||||||
|
# allow to consider this method as if it was a static method on the tool.
|
||||||
|
# Every method call, even on different instances, will be cached in a unique
|
||||||
|
# key.
|
||||||
|
cheat = False
|
||||||
|
if not klass and (method.func_code.co_varnames[0] == 'tool'):
|
||||||
|
prefix = obj.klass.__name__
|
||||||
|
obj = obj.tool
|
||||||
|
cheat = True
|
||||||
|
# Build the key of this method call in the cache.
|
||||||
|
# First part of the key: the p_obj's uid (if p_method is an instance method)
|
||||||
|
# or p_className (if p_method is a static method).
|
||||||
|
if not cheat:
|
||||||
|
if klass:
|
||||||
|
prefix = klass.__name__
|
||||||
|
else:
|
||||||
|
prefix = obj.uid
|
||||||
|
# Second part of the key: p_method name
|
||||||
|
key = '%s:%s' % (prefix, method.func_name)
|
||||||
|
# Return the cached value if present in the method cache.
|
||||||
|
if key in rq.methodCache:
|
||||||
|
return rq.methodCache[key]
|
||||||
|
# No cached value: call the method, cache the result and return it
|
||||||
|
res = method(obj)
|
||||||
|
rq.methodCache[key] = res
|
||||||
|
return res
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -13,7 +13,7 @@ class UserWrapper(AbstractWrapper):
|
||||||
if self.user.has_role('Manager'): return True
|
if self.user.has_role('Manager'): return True
|
||||||
return ('view', 'result')
|
return ('view', 'result')
|
||||||
|
|
||||||
def showName(self):
|
def showName(tool):
|
||||||
'''Name and first name, by default, are always shown.'''
|
'''Name and first name, by default, are always shown.'''
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ class UserWrapper(AbstractWrapper):
|
||||||
email = self.email
|
email = self.email
|
||||||
return email and (email != self.login)
|
return email and (email != self.login)
|
||||||
|
|
||||||
def showRoles(self):
|
def showRoles(tool):
|
||||||
'''Only the admin can view or edit roles.'''
|
'''Only the admin can view or edit roles.'''
|
||||||
return self.user.has_role('Manager')
|
return tool.user.has_role('Manager')
|
||||||
|
|
||||||
def validateLogin(self, login):
|
def validateLogin(self, login):
|
||||||
'''Is this p_login valid?'''
|
'''Is this p_login valid?'''
|
||||||
|
|
Loading…
Reference in a new issue