From 3026636ec44af3e8bfa4f231be5f20e2c450a2ab Mon Sep 17 00:00:00 2001 From: richard Date: Thu, 6 Feb 2003 05:43:49 +0000 Subject: [PATCH] Fixed a backlog of bug reports, and worked on python 2.3 compatibility: - fixed templating filter function arguments (sf bug 678911) - fixed multiselect in searching (sf bug 676874) - fixed parsing of content-disposition filenames (sf bug 675116) - added 'h' to roundup-server optarg list (sf bug 674070) - fixed doc for db.history in anydbm and rdbms_common (sf bug 679221) - fixed timelog example so it handles new issues (sf bug 678908) - handle missing os.fork() (sf bug 681046) - fixed roundup-reminder (sf bug 681042) - fixed int assumptions about Number values (sf bug 677762) - added warning filter for "FutureWarning: hex/oct constants > sys.maxint will return positive values..." (literal 0xffff0000 in portalocker.py) - fixed ZPT code generating SyntaxWarning for assignment to None git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/trunk@1487 57a73879-2fb5-44c3-a270-3262357dd7e2 --- CHANGES.txt | 25 +++++++++++++++----- COPYING.txt | 28 ++++++++++++++++++---- README.txt | 35 +--------------------------- doc/customizing.txt | 13 +++++++---- doc/index.txt | 1 + roundup/admin.py | 6 ++--- roundup/backends/back_anydbm.py | 4 ++-- roundup/backends/locking.py | 7 +++++- roundup/backends/rdbms_common.py | 4 ++-- roundup/cgi/PageTemplates/TALES.py | 4 ++-- roundup/cgi/TAL/TALInterpreter.py | 2 +- roundup/cgi/client.py | 14 ++++++++--- roundup/cgi/templating.py | 4 ++++ roundup/mailgw.py | 29 +++++++++++++++++++---- roundup/scripts/roundup_server.py | 8 +++++-- scripts/roundup-reminder | 6 ++--- test/test_mailgw.py | 37 +++++++++++++++++++++++++++++- 17 files changed, 155 insertions(+), 72 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 07990c7..937b7a5 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -30,12 +30,25 @@ are given with the most recent entry first. - more proper sorting/grouping on mulitilink properties. Sorting is performed not only by number of links, but also by links itself. This makes usable grouping e.g. by topic multilink - -2003-??-?? 0.5.5 -- fixed rdbms searching by ID (sf bug 666615) -- detect corrupted index and raise semi-useful exception (sf bug 666767) -- open server logfile unbuffered -- revert StringHTMLProperty to not hyperlink text by default +- fixed templating filter function arguments (sf bug 678911) +- fixed multiselect in searching (sf bug 676874) +- fixed parsing of content-disposition filenames (sf bug 675116) +- added 'h' to roundup-server optarg list (sf bug 674070) +- fixed doc for db.history in anydbm and rdbms_common (sf bug 679221) +- fixed timelog example so it handles new issues (sf bug 678908) +- handle missing os.fork() (sf bug 681046) +- fixed roundup-reminder (sf bug 681042) +- fixed int assumptions about Number values (sf bug 677762) +- added warning filter for "FutureWarning: hex/oct constants > sys.maxint will + return positive values..." (literal 0xffff0000 in portalocker.py) +- fixed ZPT code generating SyntaxWarning for assignment to None + + +2003-??-?? 0.5.6 +- changes appear in 0.6.0 + +2003-01-24 0.5.5 +- changes appear in 0.6.0 2003-01-10 0.5.4 diff --git a/COPYING.txt b/COPYING.txt index f518e8a..0f06922 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -1,3 +1,6 @@ +Roundup Licensing +----------------- + Copyright (c) 2002 eKit.com Inc (http://www.ekit.com/) Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,11 +21,12 @@ 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. + Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) This module is free software, and you may redistribute it and/or modify -under the same terms as Python, so long as this copyright message and -disclaimer are retained in their original form. +under the same terms as Python 2.1 (the PSF LICENSE AGREEMENT), so long +as this copyright message and disclaimer are retained in their original form. IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING @@ -35,6 +39,22 @@ FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. -The stylesheet included with this package has been copied from the Zope -management interface and presumably belongs to Digital Creations. + +PageTemplates Licensing +----------------------- + +Portions of this code (roundup.cgi.PageTemplates, roundup.cgi.TAL and +roundup.cgi.ZTUtils) have been copied from Zope. They have been modified in +the following manner: + +- removal of unit tests, Zope-specific code and support files from + PageTemplates: PageTemplateFile.py, ZPythonExpr.py, ZRPythonExpr.py, + ZopePageTemplate.py, examples, help, tests, CHANGES.txt, HISTORY.txt, + version.txt and www. From TAL: DummyEngine.py, HISTORY.txt, CHANGES.txt, + benchmark, driver.py, markbench.py, ndiff.py, runtest.py, setpath.py, + tests and timer.py. From ZTUtils: SimpleTree.py, Zope.py, CHANGES.txt and + HISTORY.txt. +- editing to remove dependencies on Zope modules (see files for change notes) + +The license for this code is in doc/ZPL.txt. diff --git a/README.txt b/README.txt index 808be60..3a4e833 100644 --- a/README.txt +++ b/README.txt @@ -16,38 +16,5 @@ See the index.txt file in the "doc" directory. License ======= -Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/) -This module is free software, and you may redistribute it and/or modify -under the same terms as Python, so long as this copyright message and -disclaimer are retained in their original form. - -IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR -DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OF THIS CODE, EVEN IF BIZAR SOFTWARE PTY LTD HAS BEEN ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. - -BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, -BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" -BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, -SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. - - -PageTemplates Licensing ------------------------ - -Portions of this code (roundup.cgi.PageTemplates, roundup.cgi.TAL and -roundup.cgi.ZTUtils) have been copied from Zope. They have been modified in -the following manner: - -- removal of unit tests, Zope-specific code and support files from - PageTemplates: PageTemplateFile.py, ZPythonExpr.py, ZRPythonExpr.py, - ZopePageTemplate.py, examples, help, tests, CHANGES.txt, HISTORY.txt, - version.txt and www. From TAL: DummyEngine.py, HISTORY.txt, CHANGES.txt, - benchmark, driver.py, markbench.py, ndiff.py, runtest.py, setpath.py, - tests and timer.py. From ZTUtils: SimpleTree.py, Zope.py, CHANGES.txt and - HISTORY.txt. -- editing to remove dependencies on Zope modules (see files for change notes) - -The license for this code is in doc/ZPL.txt. +See COPYING.txt diff --git a/doc/customizing.txt b/doc/customizing.txt index eb6b8ad..a1b46ac 100644 --- a/doc/customizing.txt +++ b/doc/customizing.txt @@ -2,7 +2,7 @@ Customising Roundup =================== -:Version: $Revision: 1.72 $ +:Version: $Revision: 1.73 $ .. This document borrows from the ZopeBook section on ZPT. The original is at: http://www.zope.org/Documentation/Books/ZopeBook/current/ZPT.stx @@ -2558,6 +2558,7 @@ able to give a summary of the total time spent on a particular issue. ''' actions = client.Client.actions + ( ('edit_with_timelog', 'timelogEditAction'), + ('new_with_timelog', 'timelogEditAction'), ) def timelogEditAction(self): @@ -2587,7 +2588,10 @@ able to give a summary of the total time spent on a particular issue. self.form.list.append(MiniFieldStorage('times', entry)) # punt to the normal edit action - return self.editItemAction() + if self.nodeid: + return self.editItemAction() + else: + return self.newItemAction() you add this code to your Client class in your tracker's ``interfaces.py`` file. Locate the section that looks like:: @@ -2621,14 +2625,15 @@ able to give a summary of the total time spent on a particular issue. - + The important change is setting the action to "edit_with_timelog" for - edit operations (where the item exists) + edit operations (where the item exists) and "new_with_timelog" for + creations operations. 6. We want to display a total of the time log times that have been accumulated for an issue. To do this, we'll need to actually write some Python code, diff --git a/doc/index.txt b/doc/index.txt index 4774962..37ad702 100644 --- a/doc/index.txt +++ b/doc/index.txt @@ -55,6 +55,7 @@ Duncan Booth, Seb Brezel, Titus Brown, Roch'e Compaan, +Hernan Martinez Foffani, Engelbert Gruber, Juergen Hermann, Tobias Hunger, diff --git a/roundup/admin.py b/roundup/admin.py index 88cbace..8dfde0d 100644 --- a/roundup/admin.py +++ b/roundup/admin.py @@ -16,7 +16,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -# $Id: admin.py,v 1.35 2002-10-03 06:56:28 richard Exp $ +# $Id: admin.py,v 1.36 2003-02-06 05:43:47 richard Exp $ '''Administration commands for maintaining Roundup trackers. ''' @@ -497,7 +497,7 @@ Command help: elif isinstance(proptype, hyperdb.Boolean): props[key] = value.lower() in ('yes', 'true', 'on', '1') elif isinstance(proptype, hyperdb.Number): - props[key] = int(value) + props[key] = float(value) # try the set try: @@ -682,7 +682,7 @@ Command help: elif isinstance(proptype, hyperdb.Boolean): props[propname] = value.lower() in ('yes', 'true', 'on', '1') elif isinstance(proptype, hyperdb.Number): - props[propname] = int(value) + props[propname] = float(value) # check for the key property propname = cl.getkey() diff --git a/roundup/backends/back_anydbm.py b/roundup/backends/back_anydbm.py index c106f2f..681698b 100644 --- a/roundup/backends/back_anydbm.py +++ b/roundup/backends/back_anydbm.py @@ -15,7 +15,7 @@ # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. # -#$Id: back_anydbm.py,v 1.99 2003-02-03 11:14:16 kedder Exp $ +#$Id: back_anydbm.py,v 1.100 2003-02-06 05:43:47 richard Exp $ ''' This module defines a backend that saves the hyperdatabase in a database chosen by anydbm. It is guaranteed to always be available in python @@ -1347,7 +1347,7 @@ class Class(hyperdb.Class): The returned list contains tuples of the form - (date, tag, action, params) + (nodeid, date, tag, action, params) 'date' is a Timestamp object specifying the time of the change and 'tag' is the journaltag specified when the database was opened. diff --git a/roundup/backends/locking.py b/roundup/backends/locking.py index 6c64618..634c14c 100644 --- a/roundup/backends/locking.py +++ b/roundup/backends/locking.py @@ -19,7 +19,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# $Id: locking.py,v 1.2 2002-09-10 00:11:50 richard Exp $ +# $Id: locking.py,v 1.3 2003-02-06 05:43:47 richard Exp $ '''This module provides a generic interface to acquire and release exclusive access to a file. @@ -27,6 +27,11 @@ exclusive access to a file. It should work on Unix and Windows. ''' +import warnings +warnings.filterwarnings("ignore", + r'hex/oct constants > sys\.maxint .*', FutureWarning, + 'portalocker', 0) + import portalocker def acquire_lock(path, block=1): diff --git a/roundup/backends/rdbms_common.py b/roundup/backends/rdbms_common.py index 30fd371..002f5a7 100644 --- a/roundup/backends/rdbms_common.py +++ b/roundup/backends/rdbms_common.py @@ -1,4 +1,4 @@ -# $Id: rdbms_common.py,v 1.29 2003-01-15 22:17:19 kedder Exp $ +# $Id: rdbms_common.py,v 1.30 2003-02-06 05:43:47 richard Exp $ ''' Relational database (SQL) backend common code. Basics: @@ -1540,7 +1540,7 @@ class Class(hyperdb.Class): The returned list contains tuples of the form - (date, tag, action, params) + (nodeid, date, tag, action, params) 'date' is a Timestamp object specifying the time of the change and 'tag' is the journaltag specified when the database was opened. diff --git a/roundup/cgi/PageTemplates/TALES.py b/roundup/cgi/PageTemplates/TALES.py index 229064c..ef13436 100644 --- a/roundup/cgi/PageTemplates/TALES.py +++ b/roundup/cgi/PageTemplates/TALES.py @@ -19,7 +19,7 @@ Modified for Roundup 0.5 release: - changed imports to import from roundup.cgi """ -__version__='$Revision: 1.4 $'[11:-2] +__version__='$Revision: 1.5 $'[11:-2] import re, sys from roundup.cgi import ZTUtils @@ -228,7 +228,7 @@ class Context: evaluateValue = evaluate evaluateBoolean = evaluate - def evaluateText(self, expr, None=None): + def evaluateText(self, expr): text = self.evaluate(expr) if text is Default or text is None: return text diff --git a/roundup/cgi/TAL/TALInterpreter.py b/roundup/cgi/TAL/TALInterpreter.py index be6d11e..341b861 100644 --- a/roundup/cgi/TAL/TALInterpreter.py +++ b/roundup/cgi/TAL/TALInterpreter.py @@ -168,7 +168,7 @@ class TALInterpreter: bytecode_handlers = {} - def interpret(self, program, None=None): + def interpret(self, program): oldlevel = self.level self.level = oldlevel + 1 handlers = self.dispatch diff --git a/roundup/cgi/client.py b/roundup/cgi/client.py index 108e52c..aa9a3a6 100644 --- a/roundup/cgi/client.py +++ b/roundup/cgi/client.py @@ -1,4 +1,4 @@ -# $Id: client.py,v 1.75 2003-02-03 00:01:44 richard Exp $ +# $Id: client.py,v 1.76 2003-02-06 05:43:47 richard Exp $ __doc__ = """ WWW request handler (also used in the stand-alone server). @@ -945,7 +945,15 @@ class Client: props = self.db.classes[self.classname].getprops() for key in self.form.keys(): if not props.has_key(key): continue - if not self.form[key].value: continue + if isinstance(self.form[key], type([])): + # search for at least one entry which is not empty + for minifield in self.form[key]: + if minifield.value: + break + else: + continue + else: + if not self.form[key].value: continue self.form.value.append(cgi.MiniFieldStorage(':filter', key)) # handle saving the query params @@ -1369,7 +1377,7 @@ def parsePropsFromForm(db, cl, form, nodeid=0, num_re=re.compile('^\d+$')): elif isinstance(proptype, hyperdb.Boolean): value = value.lower() in ('yes', 'true', 'on', '1') elif isinstance(proptype, hyperdb.Number): - value = int(value) + value = float(value) else: # if we're creating, just don't include this property if not nodeid: diff --git a/roundup/cgi/templating.py b/roundup/cgi/templating.py index 145b3b6..d69f68e 100644 --- a/roundup/cgi/templating.py +++ b/roundup/cgi/templating.py @@ -391,6 +391,10 @@ class HTMLClass(HTMLPermissions): filterspec = request.filterspec sort = request.sort group = request.group + else: + filterspec = {} + sort = (None,None) + group = (None,None) if self.classname == 'user': klass = HTMLUser else: diff --git a/roundup/mailgw.py b/roundup/mailgw.py index 962d9b8..83ccce0 100644 --- a/roundup/mailgw.py +++ b/roundup/mailgw.py @@ -73,12 +73,12 @@ are calling the create() method to create a new node). If an auditor raises an exception, the original message is bounced back to the sender with the explanatory message given in the exception. -$Id: mailgw.py,v 1.108 2003-01-27 16:32:46 kedder Exp $ +$Id: mailgw.py,v 1.109 2003-02-06 05:43:47 richard Exp $ ''' import string, re, os, mimetools, cStringIO, smtplib, socket, binascii, quopri import time, random, sys -import traceback, MimeWriter +import traceback, MimeWriter, rfc822 import hyperdb, date, password import rfc2822 @@ -113,6 +113,26 @@ def initialiseSecurity(security): description="User may use the email interface") security.addPermissionToRole('Admin', p) +def getparam(str, param): + ''' From the rfc822 "header" string, extract "param" if it appears. + ''' + if ';' not in str: + return None + str = str[str.index(';'):] + while str[:1] == ';': + str = str[1:] + if ';' in str: + # XXX Should parse quotes! + end = str.index(';') + else: + end = len(str) + f = str[:end] + if '=' in f: + i = f.index('=') + if f[:i].strip().lower() == param: + return rfc822.unquote(f[i+1:].strip()) + return None + class Message(mimetools.Message): ''' subclass mimetools.Message so we can retrieve the parts of the message... @@ -713,6 +733,7 @@ Subject was: "%s" elif subtype == 'multipart/alternative': # Search for text/plain in message with attachment and # alternative text representation + # skip over intro to first boundary part.getPart() while 1: # get the next part @@ -730,7 +751,7 @@ Subject was: "%s" if not name: disp = part.getheader('content-disposition', None) if disp: - name = disp.getparam('filename') + name = getparam(disp, 'filename') if name: name = name.strip() # this is just an attachment @@ -946,7 +967,7 @@ def setPropArrayFromString(self, cl, propString, nodeid = None): props[propname] = value.lower() in ('yes', 'true', 'on', '1') elif isinstance(proptype, hyperdb.Number): value = value.strip() - props[propname] = int(value) + props[propname] = float(value) return errors, props diff --git a/roundup/scripts/roundup_server.py b/roundup/scripts/roundup_server.py index 9f3a26a..afe9d5e 100644 --- a/roundup/scripts/roundup_server.py +++ b/roundup/scripts/roundup_server.py @@ -16,7 +16,7 @@ # """ HTTP Server that serves roundup. -$Id: roundup_server.py,v 1.17 2003-01-13 02:44:42 richard Exp $ +$Id: roundup_server.py,v 1.18 2003-02-06 05:43:49 richard Exp $ """ # python version check @@ -247,7 +247,7 @@ def run(): try: # handle the command-line args try: - optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:') + optlist, args = getopt.getopt(sys.argv[1:], 'n:p:u:d:l:h') except getopt.GetoptError, e: usage(str(e)) @@ -301,6 +301,10 @@ def run(): # fork? if pidfile: + if not hasattr(os, 'fork'): + print "Sorry, you can't run the server as a daemon on this" \ + 'Operating System' + sys.exit(0) daemonize(pidfile) # redirect stdout/stderr to our logfile diff --git a/scripts/roundup-reminder b/scripts/roundup-reminder index 8bc9576..733d233 100755 --- a/scripts/roundup-reminder +++ b/scripts/roundup-reminder @@ -19,7 +19,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# $Id: roundup-reminder,v 1.4 2002-09-10 03:01:19 richard Exp $ +# $Id: roundup-reminder,v 1.5 2003-02-06 05:43:49 richard Exp $ ''' Simple script that emails all users of a tracker with the issues that @@ -32,7 +32,7 @@ Note: The instance that this script was designed for has a modified schema! You will want to modify this script to customise it for your own schema! ''' -import cStringIO, MimeWriter, smtplib +import sys, cStringIO, MimeWriter, smtplib from roundup import instance, date # open the instance @@ -132,7 +132,7 @@ and click on "My Issues". Do NOT respond to this message. creation = creation_date.pretty() if not timeliness_id: timeliness_id = ' ' title = db.issue.get(issue_id, 'title') - issue_id = '%s'%(db.config.ISSUE_TRACKER_WEB, + issue_id = '%s'%(db.config.TRACKER_WEB, issue_id, issue_id) colour = colours.get(timeliness, '') print >>body, '''%s%s%s diff --git a/test/test_mailgw.py b/test/test_mailgw.py index 5fed002..a2785ea 100644 --- a/test/test_mailgw.py +++ b/test/test_mailgw.py @@ -8,7 +8,7 @@ # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # -# $Id: test_mailgw.py,v 1.38 2003-01-15 22:17:20 kedder Exp $ +# $Id: test_mailgw.py,v 1.39 2003-02-06 05:43:49 richard Exp $ import unittest, cStringIO, tempfile, os, shutil, errno, imp, sys, difflib @@ -780,6 +780,41 @@ http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1 _______________________________________________________________________ ''') + def testContentDisposition(self): + self.doNewIssue() + message = cStringIO.StringIO('''Content-Type: text/plain; + charset="iso-8859-1" +From: mary +To: issue_tracker@your.tracker.email.domain.example +Message-Id: +In-Reply-To: +Subject: [issue1] Testing... +Content-Type: multipart/mixed; boundary="bCsyhTFzCvuiizWE" +Content-Disposition: inline + + +--bCsyhTFzCvuiizWE +Content-Type: text/plain; charset=us-ascii +Content-Disposition: inline + +test attachment binary + +--bCsyhTFzCvuiizWE +Content-Type: application/octet-stream +Content-Disposition: attachment; filename="main.dvi" + +xxxxxx + +--bCsyhTFzCvuiizWE-- +''') + handler = self.instance.MailGW(self.instance, self.db) + handler.trapExceptions = 0 + handler.main(message) + messages = self.db.issue.get('1', 'messages') + messages.sort() + file = self.db.msg.get(messages[-1], 'files')[0] + self.assertEqual(self.db.file.get(file, 'name'), 'main.dvi') + def testFollowupStupidQuoting(self): self.doNewIssue() -- 2.30.2