Bugfix in the search engine, implemented float fields in the advanced search, execute batchjobs as Zope admin.

This commit is contained in:
Gaetan Delannay 2010-01-14 17:54:18 +01:00
parent d192496c88
commit bdc7baf25a
5 changed files with 61 additions and 36 deletions

View file

@ -247,10 +247,10 @@ class ZodbBackupScript:
# Check command format # Check command format
if options.command: if options.command:
parts = options.command.split(':') parts = options.command.split(':')
if len(parts) not in (3,4): if len(parts) not in (4,5):
raise BackupError('Command format must be ' \ raise BackupError('Command format must be ' \
'<PloneInstancePath>:<ApplicationName>:<ToolMethodName>' \ '<ZopeAdmin><PloneInstancePath>:<ApplicationName>:' \
'[:<args>]') '<ToolMethodName>[:<args>]')
def run(self): def run(self):
optParser = OptionParser(usage=ZodbBackupScript.__doc__) optParser = OptionParser(usage=ZodbBackupScript.__doc__)
@ -313,10 +313,11 @@ class ZodbBackupScript:
type='string') type='string')
optParser.add_option("-c", "--command", dest="command", optParser.add_option("-c", "--command", dest="command",
help="Command to execute while Zope is running. It must have the " \ help="Command to execute while Zope is running. It must have the " \
"following format: <PloneInstancePath>:<ApplicationName>:" \ "following format: <ZopeAdmin>:<PloneInstancePath>:" \
"<ToolMethodName>[:<args>]. <PloneInstancePath> is the path, " \ "<ApplicationName>:<ToolMethodName>[:<args>]. <ZopeAdmin> is the " \
"within Zope, to the Plone Site object (if not at the root of " \ "user name of the Zope administrator; <PloneInstancePath> is the " \
"the Zope hierarchy, use '/' as folder separator); " \ "path, within Zope, to the Plone Site object (if not at the " \
"root of the Zope hierarchy, use '/' as folder separator); " \
"<ApplicationName> is the name of the Appy application; " \ "<ApplicationName> is the name of the Appy application; " \
"<ToolMethodName> is the name of the method to call on the tool " \ "<ToolMethodName> is the name of the method to call on the tool " \
"in this Appy application; (optional) <args> are the arguments " \ "in this Appy application; (optional) <args> are the arguments " \

View file

@ -1,8 +1,9 @@
'''job.py must be executed by a "zopectl run" command and, as single arg, '''job.py must be executed by a "zopectl run" command and, as single arg,
must get a string with the following format: must get a string with the following format:
<PloneInstancePath>:<ApplicationName>:<ToolMethodName>[:<args>]. <ZopeAdmin><PloneInstancePath>:<ApplicationName>:<ToolMethodName>[:<args>].
<ZopeAdmin> is the userName of the Zope administrator for this instance.
<PloneInstancePath> is the path, within Zope, to the Plone Site object (if <PloneInstancePath> is the path, within Zope, to the Plone Site object (if
not at the root of the Zope hierarchy, use '/' as not at the root of the Zope hierarchy, use '/' as
folder separator); folder separator);
@ -16,7 +17,8 @@
are supported). Several arguments must be separated by '*'.''' are supported). Several arguments must be separated by '*'.'''
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
import sys import sys, transaction
# Check that job.py is called with the right parameters. # Check that job.py is called with the right parameters.
if len(sys.argv) != 2: if len(sys.argv) != 2:
print 'job.py was called with wrong args.' print 'job.py was called with wrong args.'
@ -24,20 +26,26 @@ if len(sys.argv) != 2:
else: else:
command = sys.argv[1] command = sys.argv[1]
parts = command.split(':') parts = command.split(':')
if len(parts) not in (3,4): if len(parts) not in (4,5):
print 'job.py was called with wrong args.' print 'job.py was called with wrong args.'
print __doc__ print __doc__
else: else:
# Unwrap parameters # Unwrap parameters
if len(parts) == 3: if len(parts) == 4:
plonePath, appName, toolMethod = parts zopeUser, plonePath, appName, toolMethod = parts
args = () args = ()
else: else:
plonePath, appName, toolMethod, args = parts zopeUser, plonePath, appName, toolMethod, args = parts
# Zope was initialized in a minimal way. Complete Zope and Plone # Zope was initialized in a minimal way. Complete Zope install.
# installation.
from Testing import makerequest from Testing import makerequest
app = makerequest.makerequest(app) app = makerequest.makerequest(app)
# Log as Zope admin
from AccessControl.SecurityManagement import newSecurityManager
user = app.acl_users.getUserById(zopeUser)
if not hasattr(user, 'aq_base'):
user = user.__of__(uf)
newSecurityManager(None, user)
# Get the Plone site # Get the Plone site
ploneSite = app # Initialised with the Zope root object. ploneSite = app # Initialised with the Zope root object.
for elem in plonePath.split('/'): for elem in plonePath.split('/'):
@ -48,4 +56,5 @@ else:
# Execute the method on the tool # Execute the method on the tool
if args: args = args.split('*') if args: args = args.split('*')
exec 'tool.%s(*args)' % toolMethod exec 'tool.%s(*args)' % toolMethod
transaction.commit()
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -176,17 +176,22 @@ class ToolMixin(AbstractMixin):
v = fieldValue[:-1] v = fieldValue[:-1]
params[attrName] = {'query':(v,v+'Z'), 'range':'minmax'} params[attrName] = {'query':(v,v+'Z'), 'range':'minmax'}
elif type(fieldValue) in sequenceTypes: elif type(fieldValue) in sequenceTypes:
# We have a range of values instead of a single value if fieldValue and isinstance(fieldValue[0], basestring):
minv, maxv = fieldValue # We have a list of string values (ie: we need to
rangev = 'minmax' # search v1 or v2 or...)
queryv = fieldValue params[attrName] = fieldValue
if minv == None: else:
rangev = 'max' # We have a range of (int, float, DateTime...) values
queryv = maxv minv, maxv = fieldValue
elif maxv == None: rangev = 'minmax'
rangev = 'min' queryv = fieldValue
queryv = minv if minv == None:
params[attrName] = {'query':queryv, 'range':rangev} rangev = 'max'
queryv = maxv
elif maxv == None:
rangev = 'min'
queryv = minv
params[attrName] = {'query':queryv, 'range':rangev}
else: else:
params[attrName] = fieldValue params[attrName] = fieldValue
# Add a sort order if specified # Add a sort order if specified
@ -405,9 +410,9 @@ class ToolMixin(AbstractMixin):
'''Returns True if request value in key p_key can be considered as '''Returns True if request value in key p_key can be considered as
empty.''' empty.'''
rq = self.REQUEST.form rq = self.REQUEST.form
if key.endswith('*int'): if key.endswith('*int') or key.endswith('*float'):
# We return True if "from" AND "to" values are empty. # We return True if "from" AND "to" values are empty.
toKey = '%s_to' % key[2:-4] toKey = '%s_to' % key[2:key.find('*')]
return not rq[key].strip() and not rq[toKey].strip() return not rq[key].strip() and not rq[toKey].strip()
elif key.endswith('*date'): elif key.endswith('*date'):
# We return True if "from" AND "to" values are empty. A value is # We return True if "from" AND "to" values are empty. A value is
@ -459,14 +464,16 @@ class ToolMixin(AbstractMixin):
attrName, attrType = attrName.split('*') attrName, attrType = attrName.split('*')
if attrType == 'bool': if attrType == 'bool':
exec 'attrValue = %s' % attrValue exec 'attrValue = %s' % attrValue
elif attrType == 'int': elif attrType in ('int', 'float'):
# Get the "from" value # Get the "from" value
if not attrValue.strip(): attrValue = None if not attrValue.strip(): attrValue = None
else: attrValue = int(attrValue) else:
exec 'attrValue = %s(attrValue)' % attrType
# Get the "to" value # Get the "to" value
toValue = rq.form['%s_to' % attrName[2:]].strip() toValue = rq.form['%s_to' % attrName[2:]].strip()
if not toValue: toValue = None if not toValue: toValue = None
else: toValue = int(toValue) else:
exec 'toValue = %s(toValue)' % attrType
attrValue = (attrValue, toValue) attrValue = (attrValue, toValue)
elif attrType == 'date': elif attrType == 'date':
prefix = attrName[2:] prefix = attrName[2:]
@ -703,6 +710,6 @@ class ToolMixin(AbstractMixin):
res = [] res = []
for v in validator: for v in validator:
text = self.translate('%s_list_%s' % (appyType['label'], v)) text = self.translate('%s_list_%s' % (appyType['label'], v))
res.append((v, self.truncate(text, 50))) res.append((v, self.truncate(text, 30)))
return res return res
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View file

@ -11,7 +11,15 @@
</metal:searchInteger> </metal:searchInteger>
<metal:searchFloat define-macro="searchFloat"> <metal:searchFloat define-macro="searchFloat">
<p tal:content="fieldName">Hello</p> <label tal:content="python: tool.translate(appyType['label'])"></label><br>&nbsp;&nbsp;
<tal:from define="fromName python: '%s*float' % widgetName">
<label tal:attributes="for fromName" tal:content="python: tool.translate('search_from')"></label>
<input type="text" tal:attributes="name fromName" size="4"/>
</tal:from>
<tal:to define="toName python: '%s_to' % fieldName">
<label tal:attributes="for toName" tal:content="python: tool.translate('search_to')"></label>
<input type="text" tal:attributes="name toName" size="4"/>
</tal:to><br/>
</metal:searchFloat> </metal:searchFloat>
<metal:searchString define-macro="searchString"> <metal:searchString define-macro="searchString">

View file

@ -283,7 +283,7 @@ class AbstractWrapper:
maxResults=maxResults, noSecurity=noSecurity) maxResults=maxResults, noSecurity=noSecurity)
return [o.appy() for o in res['objects']] return [o.appy() for o in res['objects']]
def count(self, klass, **fields): def count(self, klass, noSecurity=False, **fields):
'''Identical to m_search above, but returns the number of objects that '''Identical to m_search above, but returns the number of objects that
match the search instead of returning the objects themselves. Use match the search instead of returning the objects themselves. Use
this method instead of writing len(self.search(...)).''' this method instead of writing len(self.search(...)).'''
@ -291,7 +291,7 @@ class AbstractWrapper:
contentType = flavour.o.getPortalType(klass) contentType = flavour.o.getPortalType(klass)
search = Search('customSearch', **fields) search = Search('customSearch', **fields)
res = self.tool.o.executeQuery(contentType,flavour.number,search=search, res = self.tool.o.executeQuery(contentType,flavour.number,search=search,
brainsOnly=True) brainsOnly=True, noSecurity=noSecurity)
if res: return res._len # It is a LazyMap instance if res: return res._len # It is a LazyMap instance
else: return 0 else: return 0