Mercurial > p > roundup > code
comparison roundup/roundupdb.py @ 753:938edfdeac6e
Sorry about this huge checkin!
It's fixing a lot of related stuff in one go though.
. [SF#541941] changing multilink properties by mail
. [SF#526730] search for messages capability
. [SF#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
| author | Richard Jones <richard@users.sourceforge.net> |
|---|---|
| date | Wed, 29 May 2002 01:16:17 +0000 |
| parents | 51c425129b35 |
| children | ae0ec3c15e0d |
comparison
equal
deleted
inserted
replaced
| 752:a721f4e7ebbc | 753:938edfdeac6e |
|---|---|
| 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
| 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" | 14 # FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" |
| 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, | 15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, |
| 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 17 # | 17 # |
| 18 # $Id: roundupdb.py,v 1.53 2002-05-25 07:16:24 rochecompaan Exp $ | 18 # $Id: roundupdb.py,v 1.54 2002-05-29 01:16:17 richard Exp $ |
| 19 | 19 |
| 20 __doc__ = """ | 20 __doc__ = """ |
| 21 Extending hyperdb with types specific to issue-tracking. | 21 Extending hyperdb with types specific to issue-tracking. |
| 22 """ | 22 """ |
| 23 | 23 |
| 113 """These operations trigger detectors and can be vetoed. Attempts | 113 """These operations trigger detectors and can be vetoed. Attempts |
| 114 to modify the "creation" or "activity" properties cause a KeyError. | 114 to modify the "creation" or "activity" properties cause a KeyError. |
| 115 """ | 115 """ |
| 116 if propvalues.has_key('creation') or propvalues.has_key('activity'): | 116 if propvalues.has_key('creation') or propvalues.has_key('activity'): |
| 117 raise KeyError, '"creation" and "activity" are reserved' | 117 raise KeyError, '"creation" and "activity" are reserved' |
| 118 for audit in self.auditors['create']: | 118 self.fireAuditors('create', None, propvalues) |
| 119 audit(self.db, self, None, propvalues) | |
| 120 nodeid = hyperdb.Class.create(self, **propvalues) | 119 nodeid = hyperdb.Class.create(self, **propvalues) |
| 121 for react in self.reactors['create']: | 120 self.fireReactors('create', nodeid, None) |
| 122 react(self.db, self, nodeid, None) | |
| 123 return nodeid | 121 return nodeid |
| 124 | 122 |
| 125 def set(self, nodeid, **propvalues): | 123 def set(self, nodeid, **propvalues): |
| 126 """These operations trigger detectors and can be vetoed. Attempts | 124 """These operations trigger detectors and can be vetoed. Attempts |
| 127 to modify the "creation" or "activity" properties cause a KeyError. | 125 to modify the "creation" or "activity" properties cause a KeyError. |
| 128 """ | 126 """ |
| 129 if propvalues.has_key('creation') or propvalues.has_key('activity'): | 127 if propvalues.has_key('creation') or propvalues.has_key('activity'): |
| 130 raise KeyError, '"creation" and "activity" are reserved' | 128 raise KeyError, '"creation" and "activity" are reserved' |
| 131 for audit in self.auditors['set']: | 129 self.fireAuditors('set', nodeid, propvalues) |
| 132 audit(self.db, self, nodeid, propvalues) | |
| 133 # Take a copy of the node dict so that the subsequent set | 130 # Take a copy of the node dict so that the subsequent set |
| 134 # operation doesn't modify the oldvalues structure. | 131 # operation doesn't modify the oldvalues structure. |
| 135 try: | 132 try: |
| 136 # try not using the cache initially | 133 # try not using the cache initially |
| 137 oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid, | 134 oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid, |
| 139 except IndexError: | 136 except IndexError: |
| 140 # this will be needed if somone does a create() and set() | 137 # this will be needed if somone does a create() and set() |
| 141 # with no intervening commit() | 138 # with no intervening commit() |
| 142 oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid)) | 139 oldvalues = copy.deepcopy(self.db.getnode(self.classname, nodeid)) |
| 143 hyperdb.Class.set(self, nodeid, **propvalues) | 140 hyperdb.Class.set(self, nodeid, **propvalues) |
| 144 for react in self.reactors['set']: | 141 self.fireReactors('set', nodeid, oldvalues) |
| 145 react(self.db, self, nodeid, oldvalues) | |
| 146 | 142 |
| 147 def retire(self, nodeid): | 143 def retire(self, nodeid): |
| 148 """These operations trigger detectors and can be vetoed. Attempts | 144 """These operations trigger detectors and can be vetoed. Attempts |
| 149 to modify the "creation" or "activity" properties cause a KeyError. | 145 to modify the "creation" or "activity" properties cause a KeyError. |
| 150 """ | 146 """ |
| 151 for audit in self.auditors['retire']: | 147 self.fireAuditors('retire', nodeid, None) |
| 152 audit(self.db, self, nodeid, None) | |
| 153 hyperdb.Class.retire(self, nodeid) | 148 hyperdb.Class.retire(self, nodeid) |
| 154 for react in self.reactors['retire']: | 149 self.fireReactors('retire', nodeid, None) |
| 155 react(self.db, self, nodeid, None) | |
| 156 | 150 |
| 157 def get(self, nodeid, propname, default=_marker, cache=1): | 151 def get(self, nodeid, propname, default=_marker, cache=1): |
| 158 """Attempts to get the "creation" or "activity" properties should | 152 """Attempts to get the "creation" or "activity" properties should |
| 159 do the right thing. | 153 do the right thing. |
| 160 """ | 154 """ |
| 206 """ | 200 """ |
| 207 l = self.auditors[event] | 201 l = self.auditors[event] |
| 208 if detector not in l: | 202 if detector not in l: |
| 209 self.auditors[event].append(detector) | 203 self.auditors[event].append(detector) |
| 210 | 204 |
| 205 def fireAuditors(self, action, nodeid, newvalues): | |
| 206 """Fire all registered auditors. | |
| 207 """ | |
| 208 for audit in self.auditors[action]: | |
| 209 audit(self.db, self, nodeid, newvalues) | |
| 210 | |
| 211 def react(self, event, detector): | 211 def react(self, event, detector): |
| 212 """Register a detector | 212 """Register a detector |
| 213 """ | 213 """ |
| 214 l = self.reactors[event] | 214 l = self.reactors[event] |
| 215 if detector not in l: | 215 if detector not in l: |
| 216 self.reactors[event].append(detector) | 216 self.reactors[event].append(detector) |
| 217 | 217 |
| 218 def fireReactors(self, action, nodeid, oldvalues): | |
| 219 """Fire all registered reactors. | |
| 220 """ | |
| 221 for react in self.reactors[action]: | |
| 222 react(self.db, self, nodeid, oldvalues) | |
| 218 | 223 |
| 219 class FileClass(Class): | 224 class FileClass(Class): |
| 220 def create(self, **propvalues): | 225 def create(self, **propvalues): |
| 221 ''' snaffle the file propvalue and store in a file | 226 ''' snaffle the file propvalue and store in a file |
| 222 ''' | 227 ''' |
| 295 | 300 |
| 296 The given text is saved as the body of the message and the node is | 301 The given text is saved as the body of the message and the node is |
| 297 appended to the "messages" field of the specified issue. | 302 appended to the "messages" field of the specified issue. |
| 298 """ | 303 """ |
| 299 | 304 |
| 300 def nosymessage(self, nodeid, msgid, change_note): | 305 def nosymessage(self, nodeid, msgid, oldvalues): |
| 301 """Send a message to the members of an issue's nosy list. | 306 """Send a message to the members of an issue's nosy list. |
| 302 | 307 |
| 303 The message is sent only to users on the nosy list who are not | 308 The message is sent only to users on the nosy list who are not |
| 304 already on the "recipients" list for the message. | 309 already on the "recipients" list for the message. |
| 305 | 310 |
| 316 r[recipid] = 1 | 321 r[recipid] = 1 |
| 317 | 322 |
| 318 # figure the author's id, and indicate they've received the message | 323 # figure the author's id, and indicate they've received the message |
| 319 authid = messages.get(msgid, 'author') | 324 authid = messages.get(msgid, 'author') |
| 320 | 325 |
| 321 # get the current nosy list, we'll need it | |
| 322 nosy = self.get(nodeid, 'nosy') | |
| 323 | |
| 324 # possibly send the message to the author, as long as they aren't | 326 # possibly send the message to the author, as long as they aren't |
| 325 # anonymous | 327 # anonymous |
| 326 if (self.db.config.MESSAGES_TO_AUTHOR == 'yes' and | 328 if (self.db.config.MESSAGES_TO_AUTHOR == 'yes' and |
| 327 users.get(authid, 'username') != 'anonymous'): | 329 users.get(authid, 'username') != 'anonymous'): |
| 328 sendto.append(authid) | 330 sendto.append(authid) |
| 329 r[authid] = 1 | 331 r[authid] = 1 |
| 330 | 332 |
| 331 # now figure the nosy people who weren't recipients | 333 # now figure the nosy people who weren't recipients |
| 334 nosy = self.get(nodeid, 'nosy') | |
| 332 for nosyid in nosy: | 335 for nosyid in nosy: |
| 333 # Don't send nosy mail to the anonymous user (that user | 336 # Don't send nosy mail to the anonymous user (that user |
| 334 # shouldn't appear in the nosy list, but just in case they | 337 # shouldn't appear in the nosy list, but just in case they |
| 335 # do...) | 338 # do...) |
| 336 if users.get(nosyid, 'username') == 'anonymous': | 339 if users.get(nosyid, 'username') == 'anonymous': |
| 339 if not r.has_key(nosyid): | 342 if not r.has_key(nosyid): |
| 340 # send it to them | 343 # send it to them |
| 341 sendto.append(nosyid) | 344 sendto.append(nosyid) |
| 342 recipients.append(nosyid) | 345 recipients.append(nosyid) |
| 343 | 346 |
| 347 # generate a change note | |
| 348 if oldvalues: | |
| 349 note = self.generateChangeNote(nodeid, oldvalues) | |
| 350 else: | |
| 351 note = self.generateCreateNote(nodeid) | |
| 352 | |
| 344 # we have new recipients | 353 # we have new recipients |
| 345 if sendto: | 354 if sendto: |
| 346 # map userids to addresses | 355 # map userids to addresses |
| 347 sendto = [users.get(i, 'address') for i in sendto] | 356 sendto = [users.get(i, 'address') for i in sendto] |
| 348 | 357 |
| 349 # update the message's recipients list | 358 # update the message's recipients list |
| 350 messages.set(msgid, recipients=recipients) | 359 messages.set(msgid, recipients=recipients) |
| 351 | 360 |
| 352 # send the message | 361 # send the message |
| 353 self.send_message(nodeid, msgid, change_note, sendto) | 362 self.send_message(nodeid, msgid, note, sendto) |
| 354 | 363 |
| 355 # XXX backwards compatibility - don't remove | 364 # XXX backwards compatibility - don't remove |
| 356 sendmessage = nosymessage | 365 sendmessage = nosymessage |
| 357 | 366 |
| 358 def send_message(self, nodeid, msgid, note, sendto): | 367 def send_message(self, nodeid, msgid, note, sendto): |
| 623 m.insert(0, '') | 632 m.insert(0, '') |
| 624 return '\n'.join(m) | 633 return '\n'.join(m) |
| 625 | 634 |
| 626 # | 635 # |
| 627 # $Log: not supported by cvs2svn $ | 636 # $Log: not supported by cvs2svn $ |
| 637 # Revision 1.53 2002/05/25 07:16:24 rochecompaan | |
| 638 # Merged search_indexing-branch with HEAD | |
| 639 # | |
| 628 # Revision 1.52 2002/05/15 03:27:16 richard | 640 # Revision 1.52 2002/05/15 03:27:16 richard |
| 629 # . fixed SCRIPT_NAME in ZRoundup for instances not at top level of Zope | 641 # . fixed SCRIPT_NAME in ZRoundup for instances not at top level of Zope |
| 630 # (thanks dman) | 642 # (thanks dman) |
| 631 # . fixed some sorting issues that were breaking some unit tests under py2.2 | 643 # . fixed some sorting issues that were breaking some unit tests under py2.2 |
| 632 # . mailgw test output dir was confusing the init test (but only on 2.2 *shrug*) | 644 # . mailgw test output dir was confusing the init test (but only on 2.2 *shrug*) |
