changeset 8492:166cb2632315

issue2551413 - Broken MultiLink columns in CSV export (take 2) Changed how I solved this. Restored the original line that cmeerw took out, but use the 'id' field rather than the 'name' field. The if statements folowing the line change it to the 'name' field (realname if it's a user object): if there is one. Updated the tests to test for this error and exercise the code. I had to change the test to create/add messages to an issue. This required that I suppress the sending of nosy messages using SENDMAILDEBUG env var.
author John Rouillard <rouilj@ieee.org>
date Mon, 15 Dec 2025 00:04:16 -0500
parents 520075b29474
children d0dfb4085e94
files roundup/cgi/actions.py test/test_cgi.py
diffstat 2 files changed, 30 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/roundup/cgi/actions.py	Sun Dec 14 22:40:46 2025 -0500
+++ b/roundup/cgi/actions.py	Mon Dec 15 00:04:16 2025 -0500
@@ -1688,6 +1688,9 @@
             if isinstance(props[col], hyperdb.Multilink):
                 cname = props[col].classname
                 cclass = self.db.getclass(cname)
+                # Use id by default to handle cases like messages
+                # which have no useful label field issue2551413
+                represent[col] = repr_list(cclass, 'id')
                 if not self.hasPermission(self.permissionType, classname=cname):
                     represent[col] = repr_no_right(cclass, 'name')
                 else:
@@ -1695,10 +1698,6 @@
                         represent[col] = repr_list(cclass, 'name')
                     elif cname == 'user':
                         represent[col] = repr_list(cclass, 'realname')
-                    else:
-                        # handle cases like messages which have no
-                        # useful label field issue2551413 
-                        represent[col] = repr_list(cclass, 'id')
             if isinstance(props[col], hyperdb.Link):
                 cname = props[col].classname
                 cclass = self.db.getclass(cname)
--- a/test/test_cgi.py	Sun Dec 14 22:40:46 2025 -0500
+++ b/test/test_cgi.py	Mon Dec 15 00:04:16 2025 -0500
@@ -108,8 +108,12 @@
 class testCsvExport(object):
 
     def testCSVExportBase(self):
+        if 'SENDMAILDEBUG' not in os.environ:
+            os.environ['SENDMAILDEBUG'] = 'mail-test1.log'
+        SENDMAILDEBUG = os.environ['SENDMAILDEBUG']
+
         cl = self._make_client(
-            {'@columns': 'id,title,status,keyword,assignedto,nosy,creation'},
+            {'@columns': 'id,title,status,keyword,assignedto,nosy,creation,messages'},
             nodeid=None, userid='1')
         cl.classname = 'issue'
 
@@ -127,19 +131,26 @@
             return dummyClosure
         date.Date = dummyDate()
 
+        a_msg = self.db.msg.create(content="23a", author="4",
+                                   messageid="xyzzy@there",
+                                   recipients=['3'])
+        b_msg = self.db.msg.create(content="23b", author="3",
+                                   messageid="xyzzy@here",
+                                   recipients=['4'])
+        
         self.db.issue.create(title='foo1', status='2', assignedto='4', nosy=['3',demo_id])
         self.db.issue.create(title='bar2', status='1', assignedto='3', keyword=[key_id1,key_id2])
-        self.db.issue.create(title='baz32', status='4')
+        self.db.issue.create(title='baz32', status='4', messages=[a_msg, b_msg])
+        
         output = io.BytesIO()
         cl.request = MockNull()
         cl.request.wfile = output
         # call export version that outputs names
         actions.ExportCSVAction(cl).handle()
-        should_be=(s2b('"id","title","status","keyword","assignedto","nosy","creation"\r\n'
-                       '"1","foo1","deferred","","Contrary, Mary","Bork, Chef;Contrary, Mary;demo","2000-06-26 00:34"\r\n'
-                       '"2","bar2","unread","keyword1;keyword2","Bork, Chef","Bork, Chef","2000-06-26 00:34"\r\n'
-                       '"3","baz32","need-eg","","","","2000-06-26 00:34"\r\n'))
-
+        should_be=(s2b('"id","title","status","keyword","assignedto","nosy","creation","messages"\r\n'
+                       '"1","foo1","deferred","","Contrary, Mary","Bork, Chef;Contrary, Mary;demo","2000-06-26 00:34",""\r\n'
+                       '"2","bar2","unread","keyword1;keyword2","Bork, Chef","Bork, Chef","2000-06-26 00:34",""\r\n'
+                       '"3","baz32","need-eg","","","Bork, Chef;Contrary, Mary","2000-06-26 00:34","1;2"\r\n'))
 
         #print(should_be)
         #print(output.getvalue())
@@ -149,10 +160,10 @@
         cl.request.wfile = output
         # call export version that outputs id numbers
         actions.ExportCSVWithIdAction(cl).handle()
-        should_be = s2b('"id","title","status","keyword","assignedto","nosy","creation"\r\n'
-                        '''"1","foo1","2","[]","4","['3', '4', '5']","2000-06-26.00:34:02"\r\n'''
-                        '''"2","bar2","1","['1', '2']","3","['3']","2000-06-26.00:34:02"\r\n'''
-                        '''"3","baz32","4","[]","None","[]","2000-06-26.00:34:02"\r\n''')
+        should_be = s2b('"id","title","status","keyword","assignedto","nosy","creation","messages"\r\n'
+                        '''"1","foo1","2","[]","4","['3', '4', '5']","2000-06-26.00:34:02","[]"\r\n'''
+                        '''"2","bar2","1","['1', '2']","3","['3']","2000-06-26.00:34:02","[]"\r\n'''
+                        '''"3","baz32","4","[]","None","['3', '4']","2000-06-26.00:34:02","['1', '2']"\r\n''')
         #print(should_be)
         #print(output.getvalue())
         self.assertEqual(output.getvalue(), should_be)
@@ -184,6 +195,11 @@
                         "\"2\",\"bar2\",\"1\",\"['1', '2']\",\"3\",\"['3']\"\r\n")
         self.assertEqual(output.getvalue(), should_be)
 
+        # clean up from email log
+        if os.path.exists(SENDMAILDEBUG):
+            os.remove(SENDMAILDEBUG)
+
+
 class FormTestCase(FormTestParent, StringFragmentCmpHelper, testCsvExport, unittest.TestCase):
 
     def setUp(self):

Roundup Issue Tracker: http://roundup-tracker.org/