summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: a77eaaa)
raw | patch | inline | side by side (parent: a77eaaa)
author | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 29 May 2002 01:16:17 +0000 (01:16 +0000) | ||
committer | richard <richard@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Wed, 29 May 2002 01:16:17 +0000 (01:16 +0000) |
though.
. #541941 ] changing multilink properties by mail
. #526730 ] search for messages capability
. #505180 ] split MailGW.handle_Message
- also changed cgi client since it was duplicating the functionality
. build htmlbase if tests are run using CVS checkout (removed note from
installation.txt)
. don't create an empty message on email issue creation if the email is empty
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@766 57a73879-2fb5-44c3-a270-3262357dd7e2
. #541941 ] changing multilink properties by mail
. #526730 ] search for messages capability
. #505180 ] split MailGW.handle_Message
- also changed cgi client since it was duplicating the functionality
. build htmlbase if tests are run using CVS checkout (removed note from
installation.txt)
. don't create an empty message on email issue creation if the email is empty
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@766 57a73879-2fb5-44c3-a270-3262357dd7e2
17 files changed:
CHANGES.txt | patch | blob | history | |
MANIFEST.in | patch | blob | history | |
MIGRATION.txt | patch | blob | history | |
doc/installation.txt | patch | blob | history | |
doc/user_guide.txt | patch | blob | history | |
roundup/cgi_client.py | patch | blob | history | |
roundup/mailgw.py | patch | blob | history | |
roundup/roundupdb.py | patch | blob | history | |
roundup/templates/classic/detectors/nosyreaction.py | patch | blob | history | |
roundup/templates/classic/detectors/statusauditor.py | [new file with mode: 0644] | patch | blob |
roundup/templates/extended/detectors/__init__.py | patch | blob | history | |
roundup/templates/extended/detectors/nosyreaction.py | patch | blob | history | |
roundup/templates/extended/detectors/statusauditor.py | [new file with mode: 0644] | patch | blob |
run_tests | patch | blob | history | |
setup.py | patch | blob | history | |
test/__init__.py | patch | blob | history | |
test/test_mailgw.py | patch | blob | history |
diff --git a/CHANGES.txt b/CHANGES.txt
index ec62d75cef6a1d634dd4f36e3507c6d2a237d456..e781a45b3b08d68dc124e1db7b217791a6a974fb 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
. applied patch #558876 ] cgi client customization
. split instance initialisation into two steps, allowing config changes
before the database is initialised.
+ . don't create an empty message on email issue creation if the email is empty
+ . #541941 ] changing multilink properties by mail
. #526730 ] search for messages capability
+ . #505180 ] split MailGW.handle_Message
+ - also changed cgi client since it was duplicating the functionality
Fixed:
. stop sending blank (whitespace-only) notes
. cleaned out the template stylesheets, removing a bunch of junk that really
wasn't necessary (font specs, styles never used) and added a style for
message content
+ . build htmlbase if tests are run using CVS checkout
2002-03-25 - 0.4.1
Feature:
diff --git a/MANIFEST.in b/MANIFEST.in
index 05ca55e06658b4a191f46873cec3aebda68a3fe3..0fc4e8f4f211837c205656eade00342496325421 100644 (file)
--- a/MANIFEST.in
+++ b/MANIFEST.in
recursive-include doc *.html *.png *.txt
exclude doc/announcement.txt
include roundup-*
-include *.txt
+include run_tests *.txt
diff --git a/MIGRATION.txt b/MIGRATION.txt
index 7730c360e46b72e95a5322541805f397ed4cfba4..ca793cd598b3bc45d57c79337c416d44b9736fbb 100644 (file)
--- a/MIGRATION.txt
+++ b/MIGRATION.txt
0.3.x -> 0.4.x
0.2.x -> 0.3.x
-From CVS
-========
-
-Files storage
--------------
-
-Messages and files from newly created issues will be put into subdierectories
-in thousands e.g. msg123 will be put into files/msg/0/msg123, file2003
-will go into files/file/2/file2003. Previous messages are still found, but
-could be put into this structure.
-
-Migrating from 0.4.0 to 0.4.1
+Migrating from 0.4.1 to 0.4.2
=============================
Configuration
- ADD_RECIPIENTS_TO_NOSY
+Mail Gateway
+------------
+
+You will need to copy the detectors from the distribution into your instance
+home detectors directory. The schema-specific code has been removed from the
+mail gateway and made into auditors:
+
+- nosyreactor.py has now got an updatenosy auditor which updates the nosy
+ list with author, recipient and assignedto information.
+- statusauditor.py makes the unread or resolved -> chatting changes and
+ presets the status of an issue to unread.
+
+
Migrating from 0.4.0 to 0.4.1
=============================
+Files storage
+-------------
+
+Messages and files from newly created issues will be put into subdierectories
+in thousands e.g. msg123 will be put into files/msg/0/msg123, file2003
+will go into files/file/2/file2003. Previous messages are still found, but
+could be put into this structure.
+
Configuration
-------------
The extended schema has similar variables added too - see the source for more
info.
-
-
Alternate E-Mail Addresses
--------------------------
diff --git a/doc/installation.txt b/doc/installation.txt
index 908cd59fb8ad2ce0d8964eaa8bc0f83411676004..e61279a3187233daedad9f183adb9af5e403a88b 100644 (file)
--- a/doc/installation.txt
+++ b/doc/installation.txt
Installing Roundup
==================
-:Version: $Revision: 1.10 $
+:Version: $Revision: 1.11 $
.. contents::
Run ``"python ./run_tests"`` and make sure there
are no errors. If there are errors, please let us know!
-Note: if you're installing from the CVS, you will need to run "python setup.py
-build" before the tests will work, as there are modules that must be
-generated that are not stored in the CVS.
-
Getting Roundup
===============
diff --git a/doc/user_guide.txt b/doc/user_guide.txt
index 1c1cc3a00f4050695bf209c489a2384c3c009772..87c73739d7568fcf94cc48474add45f6fb4297f7 100644 (file)
--- a/doc/user_guide.txt
+++ b/doc/user_guide.txt
User Guide
==========
-:Version: $Revision: 1.3 $
+:Version: $Revision: 1.4 $
.. contents::
The e-mail interface also provides a simple way to set properties on items. At
the end of the subject line, propname=value pairs can be specified in square
brackets, using the same conventions as for the roundup set shell command.
-explanatory message given in the exception.
+
+For example,
+
+- setting the priority of an issue::
+
+ Subject: Re: [issue1] the coffee machine is broken! [priority=urgent]
+
+- adding yourself to a nosy list (both of these are equivalent)::
+
+ Subject: Re: [issue2] we're out of widgets [nosy=+richard]
+ Subject: Re: [issue2] we're out of widgets [nosy=richard]
+
+- removing yourself from a nosy list::
+
+ Subject: Re: [issue2] we're out of widgets [nosy=-richard]
+
Message content
~~~~~~~~~~~~~~~
diff --git a/roundup/cgi_client.py b/roundup/cgi_client.py
index 8fb4139db0fef69117a94675c1dda625d705a0a5..ae013fcb2915fb00848b3000e0e5808117c55ab5 100644 (file)
--- a/roundup/cgi_client.py
+++ b/roundup/cgi_client.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: cgi_client.py,v 1.125 2002-05-25 07:16:24 rochecompaan Exp $
+# $Id: cgi_client.py,v 1.126 2002-05-29 01:16:17 richard Exp $
__doc__ = """
WWW request handler (also used in the stand-alone server).
showmsg = shownode
searchissue = searchnode
- def _add_author_to_nosy(self, props):
- ''' add the author value from the props to the nosy list
- '''
- if not props.has_key('author'):
- return
- author_id = props['author']
- if not props.has_key('nosy'):
- # load current nosy
- if self.nodeid:
- cl = self.db.classes[self.classname]
- l = cl.get(self.nodeid, 'nosy')
- if author_id in l:
- return
- props['nosy'] = l
- else:
- props['nosy'] = []
- if author_id not in props['nosy']:
- props['nosy'].append(author_id)
-
- def _add_assignedto_to_nosy(self, props):
- ''' add the assignedto value from the props to the nosy list
- '''
- # get the properties definition and make some checks
- if not props.has_key('assignedto'):
- return
- cl = self.db.classes[self.classname]
- propdef = cl.getprops()
- if not propdef.has_key('assignedto') or not propdef.has_key('nosy'):
- return
-
- # get the assignedto(s)
- if isinstance(propdef['assignedto'], hyperdb.Link):
- assignedto_ids = [props['assignedto']]
- elif isinstance(propdef['assignedto'], hyperdb.Multilink):
- assignedto_ids = props['assignedto']
- else:
- return
-
- # ok, now get the nosy list value
- if not props.has_key('nosy'):
- # load current nosy
- if self.nodeid:
- props['nosy'] = cl.get(self.nodeid, 'nosy')
- else:
- props['nosy'] = []
-
- # and update for assignedto id(s)
- for assignedto_id in assignedto_ids:
- if assignedto_id not in props['nosy']:
- props['nosy'].append(assignedto_id)
-
def _changenode(self, props):
''' change the node based on the contents of the form
'''
cl = self.db.classes[self.classname]
- # set status to chatting if 'unread' or 'resolved'
- try:
- # determine the id of 'unread','resolved' and 'chatting'
- unread_id = self.db.status.lookup('unread')
- resolved_id = self.db.status.lookup('resolved')
- chatting_id = self.db.status.lookup('chatting')
- current_status = cl.get(self.nodeid, 'status')
- if props.has_key('status'):
- new_status = props['status']
- else:
- # apparently there's a chance that some browsers don't
- # send status...
- new_status = current_status
- except KeyError:
- pass
- else:
- if new_status == unread_id or (new_status == resolved_id
- and current_status == resolved_id):
- props['status'] = chatting_id
-
- self._add_assignedto_to_nosy(props)
-
- # possibly add the author of the change to the nosy list
- if self.db.config.ADD_AUTHOR_TO_NOSY == 'yes':
- self._add_author_to_nosy(props)
# create the message
message, files = self._handle_message()
cl = self.db.classes[self.classname]
props = parsePropsFromForm(self.db, cl, self.form)
- # set status to 'unread' if not specified - a status of '- no
- # selection -' doesn't make sense
- if not props.has_key('status') and cl.getprops().has_key('status'):
- try:
- unread_id = self.db.status.lookup('unread')
- except KeyError:
- pass
- else:
- props['status'] = unread_id
-
- self._add_assignedto_to_nosy(props)
-
- # possibly add the author of the new node to the nosy list
- if self.db.config.ADD_AUTHOR_TO_NOSY in ('new', 'yes'):
- self._add_author_to_nosy(props)
-
# check for messages and files
message, files = self._handle_message()
if message:
#
# $Log: not supported by cvs2svn $
+# Revision 1.125 2002/05/25 07:16:24 rochecompaan
+# Merged search_indexing-branch with HEAD
+#
# Revision 1.124 2002/05/24 02:09:24 richard
# Nothing like a live demo to show up the bugs ;)
#
diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index 4c99afb0a22de18a65b7283dbb4032c2a92a10e6..53dc401d20dea4d9c4ba0613aa34621586ec9f67 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
an exception, the original message is bounced back to the sender with the
explanatory message given in the exception.
-$Id: mailgw.py,v 1.73 2002-05-22 04:12:05 richard Exp $
+$Id: mailgw.py,v 1.74 2002-05-29 01:16:17 richard Exp $
'''
title = ''
# but we do need either a title or a nodeid...
- if not nodeid and not title:
+ if nodeid is None and not title:
raise MailUsageError, '''
I cannot match your message to a node in the database - you need to either
supply a full node identifier (with number, eg "[issue123]" or keep the
Subject was: "%s"
'''%subject
- # extract the args
- subject_args = m.group('args')
-
# If there's no nodeid, check to see if this is a followup and
# maybe someone's responded to the initial mail that created an
# entry. Try to find the matching nodes with the same title, and
# use the _last_ one matched (since that'll _usually_ be the most
# recent...)
- if not nodeid and m.group('refwd'):
+ if nodeid is None and m.group('refwd'):
l = cl.stringFind(title=title)
if l:
nodeid = l[-1]
- # start of the props
+ # if a nodeid was specified, make sure it's valid
+ if nodeid is not None and not cl.hasnode(nodeid):
+ raise MailUsageError, '''
+The node specified by the designator in the subject of your message ("%s")
+does not exist.
+
+Subject was: "%s"
+'''%(nodeid, subject)
+
+ #
+ # extract the args
+ #
+ subject_args = m.group('args')
+
+ #
+ # handle the subject argument list
+ #
+ # figure what the properties of this Class are
properties = cl.getprops()
props = {}
-
- # handle the args
args = m.group('args')
if args:
+ errors = []
for prop in string.split(args, ';'):
# extract the property name and value
try:
- key, value = prop.split('=')
+ propname, value = prop.split('=')
except ValueError, message:
- raise MailUsageError, '''
-Subject argument list not of form [arg=value,value,...;arg=value,value...]
- (specific exception message was "%s")
-
-Subject was: "%s"
-'''%(message, subject)
+ errors.append('not of form [arg=value,'
+ 'value,...;arg=value,value...]')
+ break
# ensure it's a valid property name
- key = key.strip()
+ propname = propname.strip()
try:
- proptype = properties[key]
+ proptype = properties[propname]
except KeyError:
- raise MailUsageError, '''
-Subject argument list refers to an invalid property: "%s"
-
-Subject was: "%s"
-'''%(key, subject)
+ errors.append('refers to an invalid property: '
+ '"%s"'%propname)
+ continue
# convert the string value to a real property value
if isinstance(proptype, hyperdb.String):
- props[key] = value.strip()
+ props[propname] = value.strip()
if isinstance(proptype, hyperdb.Password):
- props[key] = password.Password(value.strip())
+ props[propname] = password.Password(value.strip())
elif isinstance(proptype, hyperdb.Date):
try:
- props[key] = date.Date(value.strip())
+ props[propname] = date.Date(value.strip())
except ValueError, message:
- raise UsageError, '''
-Subject argument list contains an invalid date for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('contains an invalid date for '
+ '%s.'%propname)
elif isinstance(proptype, hyperdb.Interval):
try:
- props[key] = date.Interval(value) # no strip needed
+ props[propname] = date.Interval(value)
except ValueError, message:
- raise UsageError, '''
-Subject argument list contains an invalid date interval for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('contains an invalid date interval'
+ 'for %s.'%propname)
elif isinstance(proptype, hyperdb.Link):
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
try:
- props[key] = linkcl.lookup(value)
+ props[propname] = linkcl.lookup(value)
except KeyError, message:
- raise MailUsageError, '''
-Subject argument list contains an invalid value for %s.
-
-Error was: %s
-Subject was: "%s"
-'''%(key, message, subject)
+ errors.append('"%s" is not a value for %s.'%(value,
+ propname))
elif isinstance(proptype, hyperdb.Multilink):
# get the linked class
linkcl = self.db.classes[proptype.classname]
propkey = linkcl.labelprop(default_to_id=1)
+ if nodeid:
+ curvalue = cl.get(nodeid, propname)
+ else:
+ curvalue = []
+
+ # handle each add/remove in turn
for item in value.split(','):
item = item.strip()
+
+ # handle +/-
+ remove = 0
+ if item.startswith('-'):
+ remove = 1
+ item = item[1:]
+ elif item.startswith('+'):
+ item = item[1:]
+
+ # look up the value
try:
item = linkcl.lookup(item)
except KeyError, message:
- raise MailUsageError, '''
-Subject argument list contains an invalid value for %s.
+ errors.append('"%s" is not a value for %s.'%(item,
+ propname))
+ continue
+
+ # perform the add/remove
+ if remove:
+ try:
+ curvalue.remove(item)
+ except ValueError:
+ errors.append('"%s" is not currently in '
+ 'for %s.'%(item, propname))
+ continue
+ else:
+ if item not in curvalue:
+ curvalue.append(item)
+
+ # that's it, set the new Multilink property value
+ props[propname] = curvalue
+
+ # handle any errors parsing the argument list
+ if errors:
+ errors = '\n- '.join(errors)
+ raise MailUsageError, '''
+There were problems handling your subject line argument list:
+- %s
-Error was: %s
Subject was: "%s"
-'''%(key, message, subject)
- if props.has_key(key):
- props[key].append(item)
- else:
- props[key] = [item]
+'''%(errors, subject)
#
# handle the users
files.append(self.db.file.create(type=mime_type, name=name,
content=data))
+ #
+ # create the message if there's a message body (content)
#
- # now handle the db stuff
- #
- if nodeid:
- # If an item designator (class name and id number) is found there,
- # the newly created "msg" node is added to the "messages" property
- # for that item, and any new "file" nodes are added to the "files"
- # property for the item.
-
- # if the message is currently 'unread' or 'resolved', then set
- # it to 'chatting'
- if properties.has_key('status'):
- try:
- # determine the id of 'unread', 'resolved' and 'chatting'
- unread_id = self.db.status.lookup('unread')
- resolved_id = self.db.status.lookup('resolved')
- chatting_id = self.db.status.lookup('chatting')
- except KeyError:
- pass
- else:
- current_status = cl.get(nodeid, 'status')
- if (not props.has_key('status') and
- current_status == unread_id or
- current_status == resolved_id):
- props['status'] = chatting_id
-
- # update the nosy list
- current = {}
- for nid in cl.get(nodeid, 'nosy'):
- current[nid] = 1
- self.updateNosy(cl, props, author, recipients, current)
-
- # create the message
+ if content:
message_id = self.db.msg.create(author=author,
recipients=recipients, date=date.Date('.'), summary=summary,
content=content, files=files, messageid=messageid,
inreplyto=inreplyto)
- try:
+
+ # attach the message to the node
+ if nodeid:
+ # add the message to the node's list
messages = cl.get(nodeid, 'messages')
- except IndexError:
- raise MailUsageError, '''
-The node specified by the designator in the subject of your message ("%s")
-does not exist.
+ messages.append(message_id)
+ props['messages'] = messages
+ else:
+ # pre-load the messages list
+ props['messages'] = [message_id]
-Subject was: "%s"
-'''%(nodeid, subject)
- messages.append(message_id)
- props['messages'] = messages
+ # set the title to the subject
+ if properties.has_key('title') and not props.has_key('title'):
+ props['title'] = title
- # now apply the changes
- try:
+ #
+ # perform the node change / create
+ #
+ try:
+ if nodeid:
cl.set(nodeid, **props)
- except (TypeError, IndexError, ValueError), message:
- raise MailUsageError, '''
-There was a problem with the message you sent:
- %s
-'''%message
- # commit the changes to the DB
- self.db.commit()
- else:
- # If just an item class name is found there, we attempt to create a
- # new item of that class with its "messages" property initialized to
- # contain the new "msg" node and its "files" property initialized to
- # contain any new "file" nodes.
- message_id = self.db.msg.create(author=author,
- recipients=recipients, date=date.Date('.'), summary=summary,
- content=content, files=files, messageid=messageid,
- inreplyto=inreplyto)
-
- # pre-set the issue to unread
- if properties.has_key('status') and not props.has_key('status'):
- try:
- # determine the id of 'unread'
- unread_id = self.db.status.lookup('unread')
- except KeyError:
- pass
- else:
- props['status'] = '1'
-
- # set the title to the subject
- if properties.has_key('title') and not props.has_key('title'):
- props['title'] = title
-
- # pre-load the messages list
- props['messages'] = [message_id]
-
- # set up (clean) the nosy list
- self.updateNosy(cl, props, author, recipients)
-
- # and attempt to create the new node
- try:
+ else:
nodeid = cl.create(**props)
- except (TypeError, IndexError, ValueError), message:
- raise MailUsageError, '''
+ except (TypeError, IndexError, ValueError), message:
+ raise MailUsageError, '''
There was a problem with the message you sent:
%s
'''%message
- # commit the new node(s) to the DB
- self.db.commit()
+ # commit the changes to the DB
+ self.db.commit()
return nodeid
- def updateNosy(self, cl, props, author, recipients, current=None):
- '''Determine what the nosy list should be given:
-
- props: properties specified on the subject line of the message
- author: the sender of the message
- recipients: the recipients (to, cc) of the message
- current: if the issue already exists, this is the current nosy
- list, as a dictionary.
- '''
- if current is None:
- current = {}
- ok = ('new', 'yes')
- else:
- ok = ('yes',)
-
- # add nosy in arguments to issue's nosy list
- nosy = props.get('nosy', [])
- for value in nosy:
- if not self.db.hasnode('user', value):
- continue
- if not current.has_key(value):
- current[value] = 1
-
- # add the author to the nosy list
- if getattr(self.instance, 'ADD_AUTHOR_TO_NOSY', 'new') in ok:
- if not current.has_key(author):
- current[author] = 1
-
- # add on the recipients of the message
- if getattr(self.instance, 'ADD_RECIPIENTS_TO_NOSY', 'new') in ok:
- for recipient in recipients:
- if not current.has_key(recipient):
- current[recipient] = 1
-
- # add assignedto(s) to the nosy list
- if props.has_key('assignedto'):
- propdef = cl.getprops()
- if isinstance(propdef['assignedto'], hyperdb.Link):
- assignedto_ids = [props['assignedto']]
- elif isinstance(propdef['assignedto'], hyperdb.Multilink):
- assignedto_ids = props['assignedto']
- for assignedto_id in assignedto_ids:
- if not current.has_key(assignedto_id):
- current[assignedto_id] = 1
-
- props['nosy'] = current.keys()
-
def parseContent(content, keep_citations, keep_body,
blank_line=re.compile(r'[\r\n]+\s*[\r\n]+'),
eol=re.compile(r'[\r\n]+'),
#
# $Log: not supported by cvs2svn $
+# Revision 1.73 2002/05/22 04:12:05 richard
+# . applied patch #558876 ] cgi client customization
+# ... with significant additions and modifications ;)
+# - extended handling of ML assignedto to all places it's handled
+# - added more NotFound info
+#
# Revision 1.72 2002/05/22 01:24:51 richard
# Added note to MIGRATION about new config vars. Also made us more resilient
# for upgraders. Reinstated list header style (oops)
diff --git a/roundup/roundupdb.py b/roundup/roundupdb.py
index f874db8cbc6336b89e84608d66201f10de4eafe6..9f1d81db9b171117a2c685180c6a41292426ef82 100644 (file)
--- a/roundup/roundupdb.py
+++ b/roundup/roundupdb.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: roundupdb.py,v 1.53 2002-05-25 07:16:24 rochecompaan Exp $
+# $Id: roundupdb.py,v 1.54 2002-05-29 01:16:17 richard Exp $
__doc__ = """
Extending hyperdb with types specific to issue-tracking.
"""
if propvalues.has_key('creation') or propvalues.has_key('activity'):
raise KeyError, '"creation" and "activity" are reserved'
- for audit in self.auditors['create']:
- audit(self.db, self, None, propvalues)
+ self.fireAuditors('create', None, propvalues)
nodeid = hyperdb.Class.create(self, **propvalues)
- for react in self.reactors['create']:
- react(self.db, self, nodeid, None)
+ self.fireReactors('create', nodeid, None)
return nodeid
def set(self, nodeid, **propvalues):
"""
if propvalues.has_key('creation') or propvalues.has_key('activity'):
raise KeyError, '"creation" and "activity" are reserved'
- for audit in self.auditors['set']:
- audit(self.db, self, nodeid, propvalues)
+ self.fireAuditors('set', nodeid, propvalues)
# Take a copy of the node dict so that the subsequent set
# operation doesn't modify the oldvalues structure.
try:
# with no intervening commit()
oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid))
hyperdb.Class.set(self, nodeid, **propvalues)
- for react in self.reactors['set']:
- react(self.db, self, nodeid, oldvalues)
+ self.fireReactors('set', nodeid, oldvalues)
def retire(self, nodeid):
"""These operations trigger detectors and can be vetoed. Attempts
to modify the "creation" or "activity" properties cause a KeyError.
"""
- for audit in self.auditors['retire']:
- audit(self.db, self, nodeid, None)
+ self.fireAuditors('retire', nodeid, None)
hyperdb.Class.retire(self, nodeid)
- for react in self.reactors['retire']:
- react(self.db, self, nodeid, None)
+ self.fireReactors('retire', nodeid, None)
def get(self, nodeid, propname, default=_marker, cache=1):
"""Attempts to get the "creation" or "activity" properties should
if detector not in l:
self.auditors[event].append(detector)
+ def fireAuditors(self, action, nodeid, newvalues):
+ """Fire all registered auditors.
+ """
+ for audit in self.auditors[action]:
+ audit(self.db, self, nodeid, newvalues)
+
def react(self, event, detector):
"""Register a detector
"""
if detector not in l:
self.reactors[event].append(detector)
+ def fireReactors(self, action, nodeid, oldvalues):
+ """Fire all registered reactors.
+ """
+ for react in self.reactors[action]:
+ react(self.db, self, nodeid, oldvalues)
class FileClass(Class):
def create(self, **propvalues):
appended to the "messages" field of the specified issue.
"""
- def nosymessage(self, nodeid, msgid, change_note):
+ def nosymessage(self, nodeid, msgid, oldvalues):
"""Send a message to the members of an issue's nosy list.
The message is sent only to users on the nosy list who are not
# figure the author's id, and indicate they've received the message
authid = messages.get(msgid, 'author')
- # get the current nosy list, we'll need it
- nosy = self.get(nodeid, 'nosy')
-
# possibly send the message to the author, as long as they aren't
# anonymous
if (self.db.config.MESSAGES_TO_AUTHOR == 'yes' and
r[authid] = 1
# now figure the nosy people who weren't recipients
+ nosy = self.get(nodeid, 'nosy')
for nosyid in nosy:
# Don't send nosy mail to the anonymous user (that user
# shouldn't appear in the nosy list, but just in case they
sendto.append(nosyid)
recipients.append(nosyid)
+ # generate a change note
+ if oldvalues:
+ note = self.generateChangeNote(nodeid, oldvalues)
+ else:
+ note = self.generateCreateNote(nodeid)
+
# we have new recipients
if sendto:
# map userids to addresses
messages.set(msgid, recipients=recipients)
# send the message
- self.send_message(nodeid, msgid, change_note, sendto)
+ self.send_message(nodeid, msgid, note, sendto)
# XXX backwards compatibility - don't remove
sendmessage = nosymessage
#
# $Log: not supported by cvs2svn $
+# Revision 1.53 2002/05/25 07:16:24 rochecompaan
+# Merged search_indexing-branch with HEAD
+#
# Revision 1.52 2002/05/15 03:27:16 richard
# . fixed SCRIPT_NAME in ZRoundup for instances not at top level of Zope
# (thanks dman)
diff --git a/roundup/templates/classic/detectors/nosyreaction.py b/roundup/templates/classic/detectors/nosyreaction.py
index c5c818ebb47f671dd6e49be3edd2dba79214c02d..4490b49a3df850ffc20818c504a25ae5580e32de 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: nosyreaction.py,v 1.11 2002-01-14 22:21:38 richard Exp $
+#$Id: nosyreaction.py,v 1.12 2002-05-29 01:16:17 richard Exp $
-from roundup import roundupdb
+from roundup import roundupdb, hyperdb
def nosyreaction(db, cl, nodeid, oldvalues):
''' A standard detector is provided that watches for additions to the
The journal recorded by the hyperdatabase on the "recipients" property
then provides a log of when the message was sent to whom.
'''
+ # send a copy of all new messages to the nosy list
+ for msgid in determineNewMessages(cl, nodeid, oldvalues):
+ try:
+ cl.nosymessage(nodeid, msgid, oldvalues)
+ except roundupdb.MessageSendError, message:
+ raise roundupdb.DetectorError, message
+
+def determineNewMessages(cl, nodeid, oldvalues):
+ ''' Figure a list of the messages that are being added to the given
+ node in this transaction.
+ '''
messages = []
- change_note = ''
if oldvalues is None:
# the action was a create, so use all the messages in the create
messages = cl.get(nodeid, 'messages')
- change_note = cl.generateCreateNote(nodeid)
elif oldvalues.has_key('messages'):
# the action was a set (so adding new messages to an existing issue)
m = {}
for msgid in cl.get(nodeid, 'messages'):
if not m.has_key(msgid):
messages.append(msgid)
- if messages:
- change_note = cl.generateChangeNote(nodeid, oldvalues)
- if not messages:
- return
+ return messages
- # send a copy to the nosy list
- for msgid in messages:
- try:
- cl.sendmessage(nodeid, msgid, change_note)
- except roundupdb.MessageSendError, message:
- raise roundupdb.DetectorError, message
+def updatenosy(db, cl, nodeid, newvalues):
+ '''Update the nosy list for changes to the assignedto
+ '''
+ # nodeid will be None if this is a new node
+ current = {}
+ if nodeid is None:
+ ok = ('new', 'yes')
+ else:
+ ok = ('yes',)
+ # old node, get the current values from the node if they haven't
+ # changed
+ if not newvalues.has_key('nosy'):
+ nosy = cl.get(nodeid, 'nosy')
+ for value in nosy:
+ if not current.has_key(value):
+ current[value] = 1
+
+ # if the nosy list changed in this transaction, init from the new value
+ if newvalues.has_key('nosy'):
+ nosy = newvalues.get('nosy', [])
+ for value in nosy:
+ if not db.hasnode('user', value):
+ continue
+ if not current.has_key(value):
+ current[value] = 1
+
+ # add assignedto(s) to the nosy list
+ if newvalues.has_key('assignedto'):
+ propdef = cl.getprops()
+ if isinstance(propdef['assignedto'], hyperdb.Link):
+ assignedto_ids = [newvalues['assignedto']]
+ elif isinstance(propdef['assignedto'], hyperdb.Multilink):
+ assignedto_ids = newvalues['assignedto']
+ for assignedto_id in assignedto_ids:
+ if not current.has_key(assignedto_id):
+ current[assignedto_id] = 1
+
+ # see if there's any new messages - if so, possibly add the author and
+ # recipient to the nosy
+ if newvalues.has_key('messages'):
+ if nodeid is None:
+ ok = ('new', 'yes')
+ messages = newvalues['messages']
+ else:
+ ok = ('yes',)
+ # figure which of the messages now on the issue weren't
+ # there before - make sure we don't get a cached version!
+ oldmessages = cl.get(nodeid, 'messages', cache=0)
+ messages = []
+ for msgid in newvalues['messages']:
+ if msgid not in oldmessages:
+ messages.append(msgid)
+
+ # configs for nosy modifications
+ add_author = getattr(db.config, 'ADD_AUTHOR_TO_NOSY', 'new')
+ add_recips = getattr(db.config, 'ADD_RECIPIENTS_TO_NOSY', 'new')
+
+ # now for each new message:
+ msg = db.msg
+ for msgid in messages:
+ if add_author in ok:
+ authid = msg.get(msgid, 'author')
+ current[authid] = 1
+
+ # add on the recipients of the message
+ if add_recips in ok:
+ for recipient in msg.get(msgid, 'recipients'):
+ current[recipient] = 1
+
+ # that's it, save off the new nosy list
+ newvalues['nosy'] = current.keys()
def init(db):
db.issue.react('create', nosyreaction)
db.issue.react('set', nosyreaction)
+ db.issue.audit('create', updatenosy)
+ db.issue.audit('set', updatenosy)
#
#$Log: not supported by cvs2svn $
+#Revision 1.11 2002/01/14 22:21:38 richard
+##503353 ] setting properties in initial email
+#
#Revision 1.10 2002/01/11 23:22:29 richard
# . #502437 ] rogue reactor and unittest
# in short, the nosy reactor was modifying the nosy list. That code had
diff --git a/roundup/templates/classic/detectors/statusauditor.py b/roundup/templates/classic/detectors/statusauditor.py
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (c) 2002 ekit.com Inc (http://www.ekit-inc.com/)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: statusauditor.py,v 1.1 2002-05-29 01:16:17 richard Exp $
+
+def chatty(db, cl, nodeid, newvalues):
+ ''' If the issue is currently 'unread' or 'resolved', then set
+ it to 'chatting'
+ '''
+ # don't fire if there's no new message (ie. chat)
+ if not newvalues.has_key('messages'):
+ return
+ if newvalues['messages'] == cl.get(nodeid, 'messages', cache=0):
+ return
+
+ # determine the id of 'unread', 'resolved' and 'chatting'
+ unread_id = db.status.lookup('unread')
+ resolved_id = db.status.lookup('resolved')
+ chatting_id = db.status.lookup('chatting')
+
+ # get the current value
+ current_status = cl.get(nodeid, 'status')
+
+ # see if there's an explicit change in this transaction
+ if newvalues.has_key('status') and newvalues['status'] != current_status:
+ # yep, skip
+ return
+
+ # ok, there's no explicit change, so do it manually
+ if current_status in (unread_id, resolved_id):
+ newvalues['status'] = chatting_id
+
+
+def presetunread(db, cl, nodeid, newvalues):
+ ''' Make sure the status is set on new issues
+ '''
+ if newvalues.has_key('status'):
+ return
+
+ # ok, do it
+ newvalues['status'] = db.status.lookup('unread')
+
+
+def init(db):
+ # fire before changes are made
+ db.issue.audit('set', chatty)
+ db.issue.audit('create', presetunread)
+
+#
+#$Log: not supported by cvs2svn $
+#
diff --git a/roundup/templates/extended/detectors/__init__.py b/roundup/templates/extended/detectors/__init__.py
index 2a705cf39b12a12d7e646cbbf186ec22759f65a1..78f532fd0d7c5b36a07214c8a2d07825e4c8eb71 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: __init__.py,v 1.3 2001-08-07 00:24:43 richard Exp $
+#$Id: __init__.py,v 1.4 2002-05-29 01:16:17 richard Exp $
def init(db):
''' execute the init functions of all the modules in this directory
#
#$Log: not supported by cvs2svn $
+#Revision 1.3 2001/08/07 00:24:43 richard
+#stupid typo
+#
#Revision 1.2 2001/08/07 00:15:51 richard
#Added the copyright/license notice to (nearly) all files at request of
#Bizar Software.
#
+#Revision 1.1 2001/07/23 23:29:10 richard
+#Adding the classic template
+#
#Revision 1.1 2001/07/23 03:50:47 anthonybaxter
#moved templates to proper location
#
diff --git a/roundup/templates/extended/detectors/nosyreaction.py b/roundup/templates/extended/detectors/nosyreaction.py
index b3030435a491db0a27eb71acf67e13cbbc37da05..4490b49a3df850ffc20818c504a25ae5580e32de 100644 (file)
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-#$Id: nosyreaction.py,v 1.11 2002-01-14 22:21:38 richard Exp $
+#$Id: nosyreaction.py,v 1.12 2002-05-29 01:16:17 richard Exp $
-from roundup import roundupdb
+from roundup import roundupdb, hyperdb
def nosyreaction(db, cl, nodeid, oldvalues):
''' A standard detector is provided that watches for additions to the
The journal recorded by the hyperdatabase on the "recipients" property
then provides a log of when the message was sent to whom.
'''
+ # send a copy of all new messages to the nosy list
+ for msgid in determineNewMessages(cl, nodeid, oldvalues):
+ try:
+ cl.nosymessage(nodeid, msgid, oldvalues)
+ except roundupdb.MessageSendError, message:
+ raise roundupdb.DetectorError, message
+
+def determineNewMessages(cl, nodeid, oldvalues):
+ ''' Figure a list of the messages that are being added to the given
+ node in this transaction.
+ '''
messages = []
- change_note = ''
if oldvalues is None:
# the action was a create, so use all the messages in the create
messages = cl.get(nodeid, 'messages')
- change_note = cl.generateCreateNote(nodeid)
elif oldvalues.has_key('messages'):
# the action was a set (so adding new messages to an existing issue)
m = {}
for msgid in cl.get(nodeid, 'messages'):
if not m.has_key(msgid):
messages.append(msgid)
- if messages:
- change_note = cl.generateChangeNote(nodeid, oldvalues)
- if not messages:
- return
+ return messages
- # send a copy to the nosy list
- for msgid in messages:
- try:
- cl.sendmessage(nodeid, msgid, change_note)
- except roundupdb.MessageSendError, message:
- raise roundupdb.DetectorError, message
+def updatenosy(db, cl, nodeid, newvalues):
+ '''Update the nosy list for changes to the assignedto
+ '''
+ # nodeid will be None if this is a new node
+ current = {}
+ if nodeid is None:
+ ok = ('new', 'yes')
+ else:
+ ok = ('yes',)
+ # old node, get the current values from the node if they haven't
+ # changed
+ if not newvalues.has_key('nosy'):
+ nosy = cl.get(nodeid, 'nosy')
+ for value in nosy:
+ if not current.has_key(value):
+ current[value] = 1
+ # if the nosy list changed in this transaction, init from the new value
+ if newvalues.has_key('nosy'):
+ nosy = newvalues.get('nosy', [])
+ for value in nosy:
+ if not db.hasnode('user', value):
+ continue
+ if not current.has_key(value):
+ current[value] = 1
+
+ # add assignedto(s) to the nosy list
+ if newvalues.has_key('assignedto'):
+ propdef = cl.getprops()
+ if isinstance(propdef['assignedto'], hyperdb.Link):
+ assignedto_ids = [newvalues['assignedto']]
+ elif isinstance(propdef['assignedto'], hyperdb.Multilink):
+ assignedto_ids = newvalues['assignedto']
+ for assignedto_id in assignedto_ids:
+ if not current.has_key(assignedto_id):
+ current[assignedto_id] = 1
+
+ # see if there's any new messages - if so, possibly add the author and
+ # recipient to the nosy
+ if newvalues.has_key('messages'):
+ if nodeid is None:
+ ok = ('new', 'yes')
+ messages = newvalues['messages']
+ else:
+ ok = ('yes',)
+ # figure which of the messages now on the issue weren't
+ # there before - make sure we don't get a cached version!
+ oldmessages = cl.get(nodeid, 'messages', cache=0)
+ messages = []
+ for msgid in newvalues['messages']:
+ if msgid not in oldmessages:
+ messages.append(msgid)
+
+ # configs for nosy modifications
+ add_author = getattr(db.config, 'ADD_AUTHOR_TO_NOSY', 'new')
+ add_recips = getattr(db.config, 'ADD_RECIPIENTS_TO_NOSY', 'new')
+
+ # now for each new message:
+ msg = db.msg
+ for msgid in messages:
+ if add_author in ok:
+ authid = msg.get(msgid, 'author')
+ current[authid] = 1
+
+ # add on the recipients of the message
+ if add_recips in ok:
+ for recipient in msg.get(msgid, 'recipients'):
+ current[recipient] = 1
+
+ # that's it, save off the new nosy list
+ newvalues['nosy'] = current.keys()
def init(db):
db.issue.react('create', nosyreaction)
db.issue.react('set', nosyreaction)
+ db.issue.audit('create', updatenosy)
+ db.issue.audit('set', updatenosy)
#
#$Log: not supported by cvs2svn $
+#Revision 1.11 2002/01/14 22:21:38 richard
+##503353 ] setting properties in initial email
+#
#Revision 1.10 2002/01/11 23:22:29 richard
# . #502437 ] rogue reactor and unittest
# in short, the nosy reactor was modifying the nosy list. That code had
#Added the copyright/license notice to (nearly) all files at request of
#Bizar Software.
#
+#Revision 1.1 2001/07/23 23:29:10 richard
+#Adding the classic template
+#
#Revision 1.1 2001/07/23 03:50:47 anthonybaxter
#moved templates to proper location
#
diff --git a/roundup/templates/extended/detectors/statusauditor.py b/roundup/templates/extended/detectors/statusauditor.py
--- /dev/null
@@ -0,0 +1,68 @@
+# Copyright (c) 2002 ekit.com Inc (http://www.ekit-inc.com/)
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+#$Id: statusauditor.py,v 1.1 2002-05-29 01:16:17 richard Exp $
+
+def chatty(db, cl, nodeid, newvalues):
+ ''' If the issue is currently 'unread' or 'resolved', then set
+ it to 'chatting'
+ '''
+ # don't fire if there's no new message (ie. chat)
+ if not newvalues.has_key('messages'):
+ return
+ if newvalues['messages'] == cl.get(nodeid, 'messages', cache=0):
+ return
+
+ # determine the id of 'unread', 'resolved' and 'chatting'
+ unread_id = db.status.lookup('unread')
+ resolved_id = db.status.lookup('resolved')
+ chatting_id = db.status.lookup('chatting')
+
+ # get the current value
+ current_status = cl.get(nodeid, 'status')
+
+ # see if there's an explicit change in this transaction
+ if newvalues.has_key('status') and newvalues['status'] != current_status:
+ # yep, skip
+ return
+
+ # ok, there's no explicit change, so do it manually
+ if current_status in (unread_id, resolved_id):
+ newvalues['status'] = chatting_id
+
+
+def presetunread(db, cl, nodeid, newvalues):
+ ''' Make sure the status is set on new issues
+ '''
+ if newvalues.has_key('status'):
+ return
+
+ # ok, do it
+ newvalues['status'] = db.status.lookup('unread')
+
+
+def init(db):
+ # fire before changes are made
+ db.issue.audit('set', chatty)
+ db.issue.audit('create', presetunread)
+
+#
+#$Log: not supported by cvs2svn $
+#
diff --git a/run_tests b/run_tests
index b9976d983668e546558ac49d8087b3ed2de93ff0..ee73ce7cee3b0b9b5df98d441323865785dce6ef 100755 (executable)
--- a/run_tests
+++ b/run_tests
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
-# $Id: run_tests,v 1.6 2002-05-25 07:24:29 rochecompaan Exp $
+# $Id: run_tests,v 1.7 2002-05-29 01:16:16 richard Exp $
+
+# make sure we have the htmlbase
+try:
+ from roundup.templates.classic import htmlbase
+except ImportError:
+ import setup
+ setup.buildTemplates()
from test import go
import sys
#
# $Log: not supported by cvs2svn $
+# Revision 1.6 2002/05/25 07:24:29 rochecompaan
+# oops
+#
# Revision 1.4 2002/02/14 23:38:12 richard
# Fixed the unit tests for the mailgw re: the x-roundup-name header.
# Also made the test runner more user-friendly:
diff --git a/setup.py b/setup.py
index ca2832c9ad08bd270d610fd2a8746c8f780148fc..7c64ffe210a57f2475b7fa46387f3a61cede159a 100644 (file)
--- a/setup.py
+++ b/setup.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: setup.py,v 1.33 2002-04-03 05:53:03 richard Exp $
+# $Id: setup.py,v 1.34 2002-05-29 01:16:16 richard Exp $
from distutils.core import setup, Extension
from distutils.util import get_platform
script = script + ".bat"
return script
-# build list of scripts from their implementation modules
-roundup_scripts = map(scriptname, glob('roundup/scripts/[!_]*.py'))
#############################################################################
return dir[0] != '.' and dir != 'CVS' and os.path.isdir(dir) \
and os.path.isfile(os.path.join(dir, '__init__.py'))
+# use that function to list all the templates
templates = map(os.path.basename, filter(isTemplateDir,
glob(os.path.join('roundup', 'templates', '*'))))
-packagelist = [
- 'roundup',
- 'roundup.backends',
- 'roundup.scripts',
- 'roundup.templates'
-]
-installdatafiles = [
- ('share/roundup/cgi-bin', ['cgi-bin/roundup.cgi']),
-]
-
-for template in templates:
- tdir = os.path.join('roundup', 'templates', template)
- makeHtmlBase(tdir)
-
- # add the template package and subpackage
- packagelist.append('roundup.templates.%s' % template)
- packagelist.append('roundup.templates.%s.detectors' % template)
-
- # scan for data files
- tfiles = glob(os.path.join(tdir, 'html', '*'))
- tfiles = filter(os.path.isfile, tfiles)
- installdatafiles.append(
- ('share/roundup/templates/%s/html' % template, tfiles)
- )
-
-
-setup(
- name = "roundup",
- version = "0.4.1",
- description = "Roundup issue tracking system.",
- author = "Richard Jones",
- author_email = "richard@users.sourceforge.net",
- url = 'http://sourceforge.net/projects/roundup/',
- packages = packagelist,
- # Override certain command classes with our own ones
- cmdclass = {
- 'build_scripts': build_scripts_roundup,
- },
- scripts = roundup_scripts,
-
- data_files = installdatafiles
-)
+def buildTemplates():
+ for template in templates:
+ tdir = os.path.join('roundup', 'templates', template)
+ makeHtmlBase(tdir)
+
+if __name__ == '__main__':
+ # build list of scripts from their implementation modules
+ roundup_scripts = map(scriptname, glob('roundup/scripts/[!_]*.py'))
+
+ # template munching
+ templates = map(os.path.basename, filter(isTemplateDir,
+ glob(os.path.join('roundup', 'templates', '*'))))
+ packagelist = [
+ 'roundup',
+ 'roundup.backends',
+ 'roundup.scripts',
+ 'roundup.templates'
+ ]
+ installdatafiles = [
+ ('share/roundup/cgi-bin', ['cgi-bin/roundup.cgi']),
+ ]
+
+ # munge the template HTML into the htmlbase module
+ buildTemplates()
+
+ # add the templates to the setup packages and data files lists
+ for template in templates:
+ tdir = os.path.join('roundup', 'templates', template)
+
+ # add the template package and subpackage
+ packagelist.append('roundup.templates.%s' % template)
+ packagelist.append('roundup.templates.%s.detectors' % template)
+
+ # scan for data files
+ tfiles = glob(os.path.join(tdir, 'html', '*'))
+ tfiles = filter(os.path.isfile, tfiles)
+ installdatafiles.append(
+ ('share/roundup/templates/%s/html' % template, tfiles)
+ )
+
+ # perform the setup action
+ setup(
+ name = "roundup",
+ version = "0.4.1",
+ description = "Roundup issue tracking system.",
+ author = "Richard Jones",
+ author_email = "richard@users.sourceforge.net",
+ url = 'http://sourceforge.net/projects/roundup/',
+ packages = packagelist,
+
+ # Override certain command classes with our own ones
+ cmdclass = {
+ 'build_scripts': build_scripts_roundup,
+ },
+ scripts = roundup_scripts,
+
+ data_files = installdatafiles
+ )
#
# $Log: not supported by cvs2svn $
+# Revision 1.33 2002/04/03 05:53:03 richard
+# Didn't get around to committing these after the last release.
+#
# Revision 1.32 2002/03/27 23:47:58 jhermann
# Fix for scripts running under CMD.EXE
#
diff --git a/test/__init__.py b/test/__init__.py
index 749ba6777c10323966c23165d9e22aaf38406b1a..92d9f6fcf44f22c73f96c20755393e5ea821e5fc 100644 (file)
--- a/test/__init__.py
+++ b/test/__init__.py
# BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
# SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
#
-# $Id: __init__.py,v 1.16 2002-02-14 23:38:12 richard Exp $
+# $Id: __init__.py,v 1.17 2002-05-29 01:16:17 richard Exp $
import os, tempfile, unittest, shutil
-os.environ['SENDMAILDEBUG'] = tempfile.mktemp()
+import roundup.roundupdb
+roundup.roundupdb.SENDMAILDEBUG=os.environ['SENDMAILDEBUG']=tempfile.mktemp()
# figure all the modules available
dir = os.path.split(__file__)[0]
#
# $Log: not supported by cvs2svn $
+# Revision 1.16 2002/02/14 23:38:12 richard
+# Fixed the unit tests for the mailgw re: the x-roundup-name header.
+# Also made the test runner more user-friendly:
+# ./run_tests - detect all tests in test/test_<name>.py and run them
+# ./run_tests <name> - run only test/test_<name>.py
+# eg ./run_tests mailgw - run the mailgw test from test/test_mailgw.py
+#
# Revision 1.15 2002/01/22 00:12:20 richard
# oops
#
diff --git a/test/test_mailgw.py b/test/test_mailgw.py
index 5431488765fb7de430c863ecbb43a3d9045a1522..93a1f8fb5b955affa47a0ed2c928b764fa0defb6 100644 (file)
--- a/test/test_mailgw.py
+++ b/test/test_mailgw.py
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#
-# $Id: test_mailgw.py,v 1.19 2002-05-23 04:26:05 richard Exp $
+# $Id: test_mailgw.py,v 1.20 2002-05-29 01:16:17 richard Exp $
import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib
except OSError, error:
if error.errno not in (errno.ENOENT, errno.ESRCH): raise
- def testNewIssue(self):
+ def doNewIssue(self):
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: Chef <chef@bork.bork.bork
l.sort()
self.assertEqual(l, ['2', '3'])
+ def testNewIssue(self):
+ self.doNewIssue()
+
def testNewIssueNosy(self):
self.instance.ADD_AUTHOR_TO_NOSY = 'yes'
message = cStringIO.StringIO('''Content-Type: text/plain;
# BUG should test some binary attamchent too.
- def testFollowup(self):
- self.testNewIssue()
+ def testSimpleFollowup(self):
+ self.doNewIssue()
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
-From: richard <richard@test>
+From: mary <mary@test>
To: issue_tracker@your.tracker.email.domain.example
Message-Id: <followup_dummy_id>
In-Reply-To: <dummy_test_message_id>
-Subject: [issue1] Testing... [assignedto=mary; nosy=john]
+Subject: [issue1] Testing...
-This is a followup
+This is a second followup
''')
handler = self.instance.MailGW(self.instance, self.db)
handler.main(message)
-
self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
'''FROM: roundup-admin@your.tracker.email.domain.example
-TO: chef@bork.bork.bork, john@test, mary@test
+TO: chef@bork.bork.bork, richard@test
Content-Type: text/plain
Subject: [issue1] Testing...
-To: chef@bork.bork.bork, john@test, mary@test
-From: richard <issue_tracker@your.tracker.email.domain.example>
+To: chef@bork.bork.bork, richard@test
+From: mary <issue_tracker@your.tracker.email.domain.example>
Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
MIME-Version: 1.0
Message-Id: <followup_dummy_id>
Content-Transfer-Encoding: quoted-printable
-richard <richard@test> added the comment:
+mary <mary@test> added the comment:
-This is a followup
+This is a second followup
----------
-assignedto: -> mary
-nosy: +mary, john
status: unread -> chatting
_________________________________________________________________________
"Roundup issue tracker" <issue_tracker@your.tracker.email.domain.example>
_________________________________________________________________________
''')
- def testFollowup2(self):
- self.testNewIssue()
+ def testFollowup(self):
+ self.doNewIssue()
+
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
-From: mary <mary@test>
+From: richard <richard@test>
To: issue_tracker@your.tracker.email.domain.example
Message-Id: <followup_dummy_id>
In-Reply-To: <dummy_test_message_id>
-Subject: [issue1] Testing...
+Subject: [issue1] Testing... [assignedto=mary; nosy=john]
-This is a second followup
+This is a followup
''')
handler = self.instance.MailGW(self.instance, self.db)
handler.main(message)
+ l = self.db.issue.get('1', 'nosy')
+ l.sort()
+ self.assertEqual(l, ['2', '3', '4', '5'])
+
self.compareStrings(open(os.environ['SENDMAILDEBUG']).read(),
'''FROM: roundup-admin@your.tracker.email.domain.example
-TO: chef@bork.bork.bork, richard@test
+TO: chef@bork.bork.bork, john@test, mary@test
Content-Type: text/plain
Subject: [issue1] Testing...
-To: chef@bork.bork.bork, richard@test
-From: mary <issue_tracker@your.tracker.email.domain.example>
+To: chef@bork.bork.bork, john@test, mary@test
+From: richard <issue_tracker@your.tracker.email.domain.example>
Reply-To: Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
MIME-Version: 1.0
Message-Id: <followup_dummy_id>
Content-Transfer-Encoding: quoted-printable
-mary <mary@test> added the comment:
+richard <richard@test> added the comment:
-This is a second followup
+This is a followup
----------
+assignedto: -> mary
+nosy: +mary, john
status: unread -> chatting
_________________________________________________________________________
"Roundup issue tracker" <issue_tracker@your.tracker.email.domain.example>
''')
def testFollowupTitleMatch(self):
- self.testNewIssue()
+ self.doNewIssue()
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: richard <richard@test>
''')
def testFollowupNosyAuthor(self):
- self.testNewIssue()
- self.instance.ADD_AUTHOR_TO_NOSY = 'yes'
+ self.doNewIssue()
+ self.db.config.ADD_AUTHOR_TO_NOSY = self.instance.ADD_AUTHOR_TO_NOSY = 'yes'
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: john@test
''')
def testFollowupNosyRecipients(self):
- self.testNewIssue()
- self.instance.ADD_RECIPIENTS_TO_NOSY = 'yes'
+ self.doNewIssue()
+ self.db.config.ADD_RECIPIENTS_TO_NOSY = self.instance.ADD_RECIPIENTS_TO_NOSY = 'yes'
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: richard@test
''')
def testFollowupNosyAuthorAndCopy(self):
- self.testNewIssue()
- self.instance.ADD_AUTHOR_TO_NOSY = 'yes'
+ self.doNewIssue()
+ self.db.config.ADD_AUTHOR_TO_NOSY = self.instance.ADD_AUTHOR_TO_NOSY = 'yes'
self.db.config.MESSAGES_TO_AUTHOR = 'yes'
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
''')
def testFollowupNoNosyAuthor(self):
- self.testNewIssue()
+ self.doNewIssue()
self.instance.ADD_AUTHOR_TO_NOSY = 'no'
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
''')
def testFollowupNoNosyRecipients(self):
- self.testNewIssue()
+ self.doNewIssue()
self.instance.ADD_RECIPIENTS_TO_NOSY = 'no'
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
''')
+ def testNosyRemove(self):
+ self.doNewIssue()
+
+ message = cStringIO.StringIO('''Content-Type: text/plain;
+ charset="iso-8859-1"
+From: richard <richard@test>
+To: issue_tracker@your.tracker.email.domain.example
+Message-Id: <followup_dummy_id>
+In-Reply-To: <dummy_test_message_id>
+Subject: [issue1] Testing... [nosy=-richard]
+
+''')
+ handler = self.instance.MailGW(self.instance, self.db)
+ handler.main(message)
+ l = self.db.issue.get('1', 'nosy')
+ l.sort()
+ self.assertEqual(l, ['2'])
+
+ # NO NOSY MESSAGE SHOULD BE SENT!
+ self.assert_(not os.path.exists(os.environ['SENDMAILDEBUG']))
+
def testEnc01(self):
- self.testNewIssue()
+ self.doNewIssue()
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: mary <mary@test>
def testMultipartEnc01(self):
- self.testNewIssue()
+ self.doNewIssue()
message = cStringIO.StringIO('''Content-Type: text/plain;
charset="iso-8859-1"
From: mary <mary@test>
#
# $Log: not supported by cvs2svn $
+# Revision 1.19 2002/05/23 04:26:05 richard
+# 'I must run unit tests before committing\n' * 100
+#
# Revision 1.18 2002/05/15 03:27:16 richard
# . fixed SCRIPT_NAME in ZRoundup for instances not at top level of Zope
# (thanks dman)