Ticket #197: discussion_plugin_support2.diff
File discussion_plugin_support2.diff, 12.7 KB (added by bas, 14 years ago) |
---|
-
email2trac.py.in
112 112 class TicketEmailParser(object): 113 113 env = None 114 114 comment = '> ' 115 115 116 116 def __init__(self, env, parameters, version): 117 117 self.env = env 118 118 119 119 120 # Database connection 120 121 # 121 122 self.db = None … … 283 284 self.SUBJECT_FIELD_SEPARATOR = '&' 284 285 285 286 self.trac_smtp_from = self.get_config('notification', 'smtp_from') 287 self.system = None 286 288 287 289 ########## Email Header Functions ########################################################### 288 290 … … 1102 1104 # Insert the attachment 1103 1105 # 1104 1106 fd = open(path, 'rb') 1105 att = attachment.Attachment(self.env, 'ticket', self.id) 1106 1107 if self.system == 'discussion': 1108 att = attachment.Attachment(self.env, 'discussion', 'topic/%s' 1109 % (self.id,)) 1110 else: 1111 att = attachment.Attachment(self.env, 'ticket', self.id) 1112 1107 1113 # This will break the ticket_update system, the body_text is vaporized 1108 1114 # ;-( 1109 1115 # … … 1125 1131 # 1126 1132 return status 1127 1133 1128 ########## Fullblog functions ################################################# ##########1134 ########## Fullblog functions ################################################# 1129 1135 1130 1136 def blog(self, id): 1131 1137 """ … … 1170 1176 blog.create_post(req, post, self.author, u'Created by email2trac', False) 1171 1177 1172 1178 1179 ########## Discussion functions ############################################## 1173 1180 1174 ########## MAIN function ########################################################### 1181 def discussion_topic(self, content, subject): 1175 1182 1183 # Import modules. 1184 from tracdiscussion.api import DiscussionApi 1185 from trac.util.datefmt import to_timestamp, utc 1186 1187 if self.DEBUG: 1188 print 'TD: Creating a new topic in forum:', self.id 1189 1190 # Get dissussion API component. 1191 api = self.env[DiscussionApi] 1192 context = self._create_context(content, subject) 1193 1194 # Get forum for new topic. 1195 forum = api.get_forum(context, self.id) 1196 1197 if not forum and self.DEBUG: 1198 print 'ERROR: Replied forum doesn\'t exist' 1199 1200 # Prepare topic. 1201 topic = {'forum' : forum['id'], 1202 'subject' : context.subject, 1203 'time': to_timestamp(datetime.now(utc)), 1204 'author' : self.author, 1205 'subscribers' : [self.email_addr], 1206 'body' : self.body_text(context.content_parts)} 1207 1208 # Add topic to DB and commit it. 1209 self._add_topic(api, context, topic) 1210 self.db.commit() 1211 1212 def discussion_topic_reply(self, content, subject): 1213 1214 # Import modules. 1215 from tracdiscussion.api import DiscussionApi 1216 from trac.util.datefmt import to_timestamp, utc 1217 1218 if self.DEBUG: 1219 print 'TD: Replying to discussion topic', self.id 1220 1221 # Get dissussion API component. 1222 api = self.env[DiscussionApi] 1223 context = self._create_context(content, subject) 1224 1225 # Get replied topic. 1226 topic = api.get_topic(context, self.id) 1227 1228 if not topic and self.DEBUG: 1229 print 'ERROR: Replied topic doesn\'t exist' 1230 1231 # Prepare message. 1232 message = {'forum' : topic['forum'], 1233 'topic' : topic['id'], 1234 'replyto' : -1, 1235 'time' : to_timestamp(datetime.now(utc)), 1236 'author' : self.author, 1237 'body' : self.body_text(context.content_parts)} 1238 1239 # Add message to DB and commit it. 1240 self._add_message(api, context, message) 1241 self.db.commit() 1242 1243 def discussion_message_reply(self, content, subject): 1244 1245 # Import modules. 1246 from tracdiscussion.api import DiscussionApi 1247 from trac.util.datefmt import to_timestamp, utc 1248 1249 if self.DEBUG: 1250 print 'TD: Replying to discussion message', self.id 1251 1252 # Get dissussion API component. 1253 api = self.env[DiscussionApi] 1254 context = self._create_context(content, subject) 1255 1256 # Get replied message. 1257 message = api.get_message(context, self.id) 1258 1259 if not message and self.DEBUG: 1260 print 'ERROR: Replied message doesn\'t exist' 1261 1262 # Prepare message. 1263 message = {'forum' : message['forum'], 1264 'topic' : message['topic'], 1265 'replyto' : message['id'], 1266 'time' : to_timestamp(datetime.now(utc)), 1267 'author' : self.author, 1268 'body' : self.body_text(context.content_parts)} 1269 1270 # Add message to DB and commit it. 1271 self._add_message(api, context, message) 1272 self.db.commit() 1273 1274 def _create_context(self, content, subject): 1275 1276 # Import modules. 1277 from trac.mimeview import Context 1278 from trac.web.api import Request 1279 from trac.perm import PermissionCache 1280 1281 # TODO: Read server base URL from config. 1282 # Create request object to mockup context creation. 1283 # 1284 environ = {'SERVER_PORT' : 80, 1285 'SERVER_NAME' : 'test', 1286 'REQUEST_METHOD' : 'POST', 1287 'wsgi.url_scheme' : 'http', 1288 'wsgi.input' : sys.stdin} 1289 chrome = {'links': {}, 1290 'scripts': [], 1291 'ctxtnav': [], 1292 'warnings': [], 1293 'notices': []} 1294 1295 if self.env.base_url_for_redirect: 1296 environ['trac.base_url'] = self.env.base_url 1297 1298 req = Request(environ, None) 1299 req.chrome = chrome 1300 req.tz = 'missing' 1301 req.authname = self.author 1302 req.perm = PermissionCache(self.env, self.author) 1303 1304 # Create and return context. 1305 context = Context.from_request(req) 1306 context.realm = 'discussion-email2trac' 1307 context.cursor = self.db.cursor() 1308 context.content = content 1309 context.subject = subject 1310 1311 # Read content parts from content. 1312 context.content_parts = self.get_message_parts(content) 1313 context.content_parts = self.unique_attachment_names( 1314 context.content_parts) 1315 1316 return context 1317 1318 def _add_topic(self, api, context, topic): 1319 context.req.perm.assert_permission('DISCUSSION_APPEND') 1320 1321 # Filter topic. 1322 for discussion_filter in api.discussion_filters: 1323 accept, topic_or_error = discussion_filter.filter_topic( 1324 context, topic) 1325 if accept: 1326 topic = topic_or_error 1327 else: 1328 raise TracError(topic_or_error) 1329 1330 # Add a new topic. 1331 api.add_topic(context, topic) 1332 1333 # Get inserted topic with new ID. 1334 topic = api.get_topic_by_time(context, topic['time']) 1335 1336 # Attach attachments. 1337 self.id = topic['id'] 1338 self.attach_attachments(context.content_parts, self.VERSION == 0.9) 1339 1340 # Notify change listeners. 1341 for listener in api.topic_change_listeners: 1342 listener.topic_created(context, topic) 1343 1344 def _add_message(self, api, context, message): 1345 context.req.perm.assert_permission('DISCUSSION_APPEND') 1346 1347 # Filter message. 1348 for discussion_filter in api.discussion_filters: 1349 accept, message_or_error = discussion_filter.filter_message( 1350 context, message) 1351 if accept: 1352 message = message_or_error 1353 else: 1354 raise TracError(message_or_error) 1355 1356 # Add message. 1357 api.add_message(context, message) 1358 1359 # Get inserted message with new ID. 1360 message = api.get_message_by_time(context, message['time']) 1361 1362 # Attach attachments. 1363 self.id = message['topic'] 1364 self.attach_attachments(context.content_parts, self.VERSION == 0.9) 1365 1366 # Notify change listeners. 1367 for listener in api.message_change_listeners: 1368 listener.message_created(context, message) 1369 1370 ########## MAIN function ###################################################### 1371 1176 1372 def parse(self, fp): 1177 1373 """ 1178 1374 """ … … 1236 1432 if self.get_config('components', 'tracfullblog.*') in ['enabled']: 1237 1433 blog_enabled = True 1238 1434 1435 # Check if DiscussionPlugin is installed 1436 # 1437 discussion_enabled = None 1438 if self.get_config('components', 'tracdiscussion.api.*') in ['enabled']: 1439 discussion_enabled = True 1440 1239 1441 if not m['Subject']: 1240 1442 subject = 'No Subject' 1241 1443 else: 1242 subject = self.email_to_unicode(m['Subject']) 1444 subject = self.email_to_unicode(m['Subject']) 1243 1445 1244 # 1446 if self.DEBUG: 1447 print "TD:", subject 1448 1449 # 1245 1450 # [hic] #1529: Re: LRZ 1246 1451 # [hic] #1529?owner=bas,priority=medium: Re: LRZ 1247 1452 # 1248 1453 TICKET_RE = re.compile(r""" 1249 1454 (?P<blog>blog:(?P<blog_id>\w*)) 1455 |(?P<forum>Forum[ ][#](?P<forum_id>\d+)[ ]-[ ]?) 1456 |(?P<topic>Topic[ ][#](?P<topic_id>\d+)[ ]-[ ]?) 1457 |(?P<message>Message[ ][#](?P<message_id>\d+)[ ]-[ ]?) 1250 1458 |(?P<new_fields>[#][?].*) 1251 1459 |(?P<reply>[#][\d]+:) 1252 1460 |(?P<reply_fields>[#][\d]+\?.*?:) 1253 1461 """, re.VERBOSE) 1254 1462 1255 # Find out if this is a ticket or a blog1463 # Find out if this is a ticket, a blog or a discussion 1256 1464 # 1257 1465 result = TICKET_RE.search(subject) 1258 1466 1259 1467 if result: 1260 1468 if result.group('blog'): 1261 1469 if blog_enabled: 1470 self.system = 'blog' 1262 1471 self.blog(result.group('blog_id')) 1263 1472 else: 1264 1473 if self.DEBUG: 1265 print " Fullblog plugin is not installed"1474 print "ERROR: Fullblog plugin is not installed" 1266 1475 return 1267 1476 1477 # New topic. 1478 # 1479 elif result.group('forum'): 1480 if discussion_enabled: 1481 self.id = int(result.group('forum_id')) 1482 self.system = 'discussion' 1483 self.discussion_topic(m, subject[result.end('forum'):]) 1484 else: 1485 if self.DEBUG: 1486 print "ERROR: Discussion plugin is not installed" 1487 return 1488 1489 # Reply to topic. 1490 # 1491 elif result.group('topic'): 1492 if discussion_enabled: 1493 self.id = int(result.group('topic_id')) 1494 self.system = 'discussion' 1495 self.discussion_topic_reply(m, subject[result.end( 1496 'topic'):]) 1497 else: 1498 if self.DEBUG: 1499 print "ERROR: Discussion plugin is not installed" 1500 return 1501 1502 # Reply to topic message. 1503 # 1504 elif result.group('message'): 1505 if discussion_enabled: 1506 self.id = int(result.group('message_id')) 1507 self.system = 'discussion' 1508 self.discussion_message_reply(m, subject[result.end( 1509 'message'):]) 1510 else: 1511 if self.DEBUG: 1512 print "ERROR: Discussion plugin is not installed" 1513 return 1514 1268 1515 # update ticket + fields 1269 1516 # 1270 if result.group('reply_fields') and self.TICKET_UPDATE: 1517 elif result.group('reply_fields') and self.TICKET_UPDATE: 1518 self.system = 'ticket' 1271 1519 self.ticket_update(m, result.group('reply_fields'), spam_msg) 1272 1520 1273 1521 # Update ticket 1274 1522 # 1275 1523 elif result.group('reply') and self.TICKET_UPDATE: 1524 self.system = 'ticket' 1276 1525 self.ticket_update(m, result.group('reply'), spam_msg) 1277 1526 1278 1527 # New ticket + fields 1279 1528 # 1280 1529 elif result.group('new_fields'): 1530 self.system = 'ticket' 1281 1531 self.new_ticket(m, subject[:result.start('new_fields')], spam_msg, result.group('new_fields')) 1282 1532 1283 1533 else: 1534 self.system = 'ticket' 1284 1535 result = self.ticket_update_by_subject(subject) 1285 1536 if result: 1286 1537 self.ticket_update(m, result, spam_msg) … … 1288 1539 # No update by subject, so just create a new ticket 1289 1540 self.new_ticket(m, subject, spam_msg) 1290 1541 1291 1292 1542 ########## BODY TEXT functions ########################################################### 1293 1543 1294 1544 def strip_signature(self, text): … … 1600 1850 def attachment_exists(self, filename): 1601 1851 1602 1852 if self.DEBUG: 1603 s = 'TD: attachment already exists: Ticket id : '1853 s = 'TD: attachment already exists: Id : ' 1604 1854 try: 1605 print "%s%s, Filename : %s" % (s, self.id, filename)1855 print "%s%s, Filename : %s" % (s, self.id, filename) 1606 1856 except UnicodeEncodeError, detail: 1607 1857 print "%s%s, Filename : Can not be printed due to non-ascii characters" %(s, self.id) 1608 1858 … … 1612 1862 return False 1613 1863 1614 1864 try: 1615 att = attachment.Attachment(self.env, 'ticket', self.id, filename) 1865 if self.system == 'discussion': 1866 att = attachment.Attachment(self.env, 'discussion', 'ticket/%s' 1867 % (self.id,), filename) 1868 else: 1869 att = attachment.Attachment(self.env, 'ticket', self.id, 1870 filename) 1616 1871 return True 1617 1872 except attachment.ResourceNotFound: 1618 1873 return False … … 1633 1888 inline = self.inline_part(part) 1634 1889 1635 1890 if part.get_content_maintype() == 'image' and inline: 1636 body_text.append('[[Image(%s)]]' % filename) 1891 if self.system != 'discussion': 1892 body_text.append('[[Image(%s)]]' % filename) 1637 1893 body_text.append("") 1638 1894 else: 1639 body_text.append('[attachment:"%s"]' % filename) 1895 if self.system != 'discussion': 1896 body_text.append('[attachment:"%s"]' % filename) 1640 1897 body_text.append("") 1641 1898 1642 1899 body_text = '\r\n'.join(body_text) … … 1749 2006 1750 2007 ENABLE_SYSLOG = 0 1751 2008 1752 1753 2009 SHORT_OPT = 'chf:np:t:v' 1754 2010 LONG_OPT = ['component=', 'dry-run', 'help', 'file=', 'project=', 'ticket_prefix=', 'verbose'] 1755 2011 … … 1822 2078 # see http://projects.edgewall.com/trac/changeset/2799 1823 2079 from trac.ticket.notification import TicketNotifyEmail 1824 2080 from trac import config as trac_config 2081 from trac.core import TracError 2082 1825 2083 elif version == '0.11': 1826 2084 from trac import attachment 1827 2085 from trac.env import Environment … … 1829 2087 from trac.web.href import Href 1830 2088 from trac import config as trac_config 1831 2089 from trac import util 2090 from trac.core import TracError 1832 2091 1833 2092 1834 2093 # … … 1851 2110 python_egg_cache = str(settings['python_egg_cache']) 1852 2111 os.environ['PYTHON_EGG_CACHE'] = python_egg_cache 1853 2112 2113 2114 if int(settings['debug']) > 0: 2115 print 'Loading environment', settings['project'] 2116 1854 2117 env = Environment(settings['project'], create=0) 1855 2118 1856 2119 tktparser = TicketEmailParser(env, settings, float(version))