Changeset 477
- Timestamp:
- 08/10/10 09:28:13 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/email2trac.py.in
r476 r477 319 319 320 320 ## Trac can not handle author's name that contains spaces 321 # and forbid the ticket email address as author field 322 321 # 323 322 if self.email_addr == self.trac_smtp_from: 324 323 if self.email_name: … … 640 639 return (self.check_permission_participants(tkt, action)) 641 640 642 # Default is to allow everybody ticket updates and ticket creation 641 ## Default is to allow everybody ticket updates and ticket creation 642 # 643 643 else: 644 644 return True … … 666 666 return False 667 667 668 # Build a system dictionary from the ticket fields669 # with field as index and option as value668 ## Build a system dictionary from the ticket fields 669 # with field as index and option as value 670 670 # 671 671 sys_dict = dict() … … 679 679 680 680 ## Check user supplied fields an compare them with the 681 # system one's681 # system one's 682 682 # 683 683 for field,value in user_dict.items(): … … 702 702 if sys_dict.has_key(field): 703 703 704 # Check if value is an allowed system option, if TypeError then705 # every value is allowed704 ## Check if value is an allowed system option, if TypeError then 705 # every value is allowed 706 706 # 707 707 try: … … 709 709 ticket[field] = value 710 710 else: 711 # Must we set a default if value is not allowed711 ## Must we set a default if value is not allowed 712 712 # 713 713 if new: … … 738 738 return False 739 739 740 # Must we update ticket fields740 ## Must we update ticket fields 741 741 # 742 742 update_fields = dict() … … 746 746 update_fields = self.str_to_dict(keywords) 747 747 748 # Strip '#'748 ## Strip '#' 749 749 # 750 750 self.id = int(id[1:]) … … 752 752 except ValueError: 753 753 754 # Strip '#'754 ## Strip '#' 755 755 # 756 756 self.id = int(id[1:]) … … 758 758 self.logger.debug("ticket_update id %s" %id) 759 759 760 # When is the change committed760 ## When is the change committed 761 761 # 762 762 if self.VERSION < 0.11: … … 770 770 except util.TracError, detail: 771 771 772 # Not a valid ticket773 772 ## Not a valid ticket 773 # 774 774 self.id = None 775 775 return False 776 776 777 # Check the permission of the reporter777 ## Check the permission of the reporter 778 778 # 779 779 if self.parameters.ticket_permission_system: … … 852 852 message_parts = self.unique_attachment_names(message_parts) 853 853 854 # Must we update some ticket fields properties via body_text854 ## Must we update some ticket fields properties via body_text 855 855 # 856 856 if self.properties: … … 954 954 955 955 if result: 956 # This is a reply 956 ## This is a reply 957 # 957 958 orig_subject = result.group(3) 958 959 … … 962 963 summaries = [orig_subject, '%%: %s' % orig_subject] 963 964 964 ## 965 # Convert days to seconds965 ## Convert days to seconds 966 # 966 967 lookback = int(time.mktime(time.gmtime())) - \ 967 968 self.parameters.ticket_update_by_subject_lookback * 24 * 3600 … … 1002 1003 self.set_ticket_fields(tkt) 1003 1004 1004 # Check the permission of the reporter1005 ## Check the permission of the reporter 1005 1006 # 1006 1007 if self.parameters.ticket_permission_system: … … 1009 1010 return False 1010 1011 1011 # Old style setting for component, will be removed1012 ## Old style setting for component, will be removed 1012 1013 # 1013 1014 if spam: … … 1033 1034 message_parts = self.get_message_parts(msg) 1034 1035 1035 # Must we update some ticket fields properties via body_text1036 ## Must we update some ticket fields properties via body_text 1036 1037 # 1037 1038 if self.properties: … … 1040 1041 message_parts = self.unique_attachment_names(message_parts) 1041 1042 1042 # produce e-mail like header1043 ## produce e-mail like header 1043 1044 # 1044 1045 head = '' … … 1051 1052 tkt['description'] = body_text 1052 1053 1053 # When is the change committed1054 ## When is the change committed 1054 1055 # 1055 1056 if self.VERSION < 0.11: … … 1066 1067 comment = '' 1067 1068 1068 # some routines in trac are dependend on ticket id1069 # like alternate notify template1069 ## some routines in trac are dependend on ticket id 1070 # like alternate notify template 1070 1071 # 1071 1072 if self.parameters.alternate_notify_template: … … 1114 1115 count = 0 1115 1116 1116 # Get Maxium attachment size1117 ## Get Maxium attachment size 1117 1118 # 1118 1119 max_size = int(self.get_config('attachment', 'max_size')) … … 1120 1121 1121 1122 for item in message_parts: 1122 # Skip body parts 1123 ## Skip body parts 1124 # 1123 1125 if not isinstance(item, tuple): 1124 1126 continue 1125 1127 1126 1128 (original, filename, part) = item 1127 # 1128 # We have to determine the size so we use this temporary solution. we must escape it1129 # else we get UnicodeErrors.1129 1130 ## We have to determine the size so we use this temporary solution. we must escape it 1131 # else we get UnicodeErrors. 1130 1132 # 1131 1133 path, fd = util.create_unique_file(os.path.join(self.parameters.tmpdir, util.text.unicode_quote(filename))) … … 1136 1138 fd.close() 1137 1139 1138 # get the file_size1140 ## get the file_size 1139 1141 # 1140 1142 stats = os.lstat(path) 1141 1143 file_size = stats[ST_SIZE] 1142 1144 1143 # Check if the attachment size is allowed1145 ## Check if the attachment size is allowed 1144 1146 # 1145 1147 if (max_size != -1) and (file_size > max_size): … … 1152 1154 count = count + 1 1153 1155 1154 # Insert the attachment1156 ## Insert the attachment 1155 1157 # 1156 1158 fd = open(path, 'rb') … … 1162 1164 att = attachment.Attachment(self.env, 'ticket', self.id) 1163 1165 1164 # This will break the ticket_update system, the body_text is vaporized1165 # ;-(1166 ## This will break the ticket_update system, the body_text is vaporized 1167 # ;-( 1166 1168 # 1167 1169 if not update: … … 1176 1178 status = '%s\nFilename %s could not be saved, problem: %s' %(status, filename, detail) 1177 1179 1178 # Remove the created temporary filename1180 ## Remove the created temporary filename 1179 1181 # 1180 1182 fd.close() … … 1191 1193 The blog create/update function 1192 1194 """ 1193 # import the modules1195 ## import the modules 1194 1196 # 1195 1197 from tracfullblog.core import FullBlogCore … … 1197 1199 from trac.test import Mock, MockPerm 1198 1200 1199 # instantiate blog core 1201 ## instantiate blog core 1202 # 1200 1203 blog = FullBlogCore(self.env) 1201 1204 req = Mock(authname='anonymous', perm=MockPerm(), args={}) … … 1203 1206 if id: 1204 1207 1205 # update blog1208 ## update blog 1206 1209 # 1207 1210 comment = BlogComment(self.env, id) … … 1214 1217 1215 1218 else: 1216 # create blog1219 ## create blog 1217 1220 # 1218 1221 import time … … 1234 1237 def discussion_topic(self, content, subject): 1235 1238 1236 # Import modules. 1239 ## Import modules. 1240 # 1237 1241 from tracdiscussion.api import DiscussionApi 1238 1242 from trac.util.datefmt import to_timestamp, utc … … 1240 1244 self.logger.debug('Creating a new topic in forum:', self.id) 1241 1245 1242 # Get dissussion API component. 1246 ## Get dissussion API component. 1247 # 1243 1248 api = self.env[DiscussionApi] 1244 1249 context = self._create_context(content, subject) 1245 1250 1246 # Get forum for new topic. 1251 ## Get forum for new topic. 1252 # 1247 1253 forum = api.get_forum(context, self.id) 1248 1254 … … 1250 1256 self.logger.error("ERROR: Replied forum doesn't exist") 1251 1257 1252 # Prepare topic. 1258 ## Prepare topic. 1259 # 1253 1260 topic = {'forum' : forum['id'], 1254 1261 'subject' : context.subject, … … 1258 1265 'body' : self.body_text(context.content_parts)} 1259 1266 1260 # Add topic to DB and commit it. 1267 ## Add topic to DB and commit it. 1268 # 1261 1269 self._add_topic(api, context, topic) 1262 1270 self.db.commit() … … 1264 1272 def discussion_topic_reply(self, content, subject): 1265 1273 1266 # Import modules. 1274 ## Import modules. 1275 # 1267 1276 from tracdiscussion.api import DiscussionApi 1268 1277 from trac.util.datefmt import to_timestamp, utc … … 1270 1279 self.logger.debug('Replying to discussion topic', self.id) 1271 1280 1272 # Get dissussion API component. 1281 ## Get dissussion API component. 1282 # 1273 1283 api = self.env[DiscussionApi] 1274 1284 context = self._create_context(content, subject) 1275 1285 1276 # Get replied topic. 1286 ## Get replied topic. 1287 # 1277 1288 topic = api.get_topic(context, self.id) 1278 1289 … … 1280 1291 self.logger.error("ERROR: Replied topic doesn't exist") 1281 1292 1282 # Prepare message. 1293 ## Prepare message. 1294 # 1283 1295 message = {'forum' : topic['forum'], 1284 1296 'topic' : topic['id'], … … 1288 1300 'body' : self.body_text(context.content_parts)} 1289 1301 1290 # Add message to DB and commit it. 1302 ## Add message to DB and commit it. 1303 # 1291 1304 self._add_message(api, context, message) 1292 1305 self.db.commit() … … 1294 1307 def discussion_message_reply(self, content, subject): 1295 1308 1296 # Import modules. 1309 ## Import modules. 1310 # 1297 1311 from tracdiscussion.api import DiscussionApi 1298 1312 from trac.util.datefmt import to_timestamp, utc … … 1300 1314 self.loggger.debug('Replying to discussion message', self.id) 1301 1315 1302 # Get dissussion API component. 1316 ## Get dissussion API component. 1317 # 1303 1318 api = self.env[DiscussionApi] 1304 1319 context = self._create_context(content, subject) 1305 1320 1306 # Get replied message. 1321 ## Get replied message. 1322 # 1307 1323 message = api.get_message(context, self.id) 1308 1324 … … 1310 1326 self.logger.error("ERROR: Replied message doesn't exist") 1311 1327 1312 # Prepare message. 1328 ## Prepare message. 1329 # 1313 1330 message = {'forum' : message['forum'], 1314 1331 'topic' : message['topic'], … … 1318 1335 'body' : self.body_text(context.content_parts)} 1319 1336 1320 # Add message to DB and commit it. 1337 ## Add message to DB and commit it. 1338 # 1321 1339 self._add_message(api, context, message) 1322 1340 self.db.commit() … … 1324 1342 def _create_context(self, content, subject): 1325 1343 1326 # Import modules. 1344 ## Import modules. 1345 # 1327 1346 from trac.mimeview import Context 1328 1347 from trac.web.api import Request 1329 1348 from trac.perm import PermissionCache 1330 1349 1331 # TODO: Read server base URL from config.1332 # Create request object to mockup context creation.1350 ## TODO: Read server base URL from config. 1351 # Create request object to mockup context creation. 1333 1352 # 1334 1353 environ = {'SERVER_PORT' : 80, … … 1352 1371 req.perm = PermissionCache(self.env, self.author) 1353 1372 1354 # Create and return context. 1373 ## Create and return context. 1374 # 1355 1375 context = Context.from_request(req) 1356 1376 context.realm = 'discussion-email2trac' … … 1359 1379 context.subject = subject 1360 1380 1361 # Read content parts from content. 1381 ## Read content parts from content. 1382 # 1362 1383 context.content_parts = self.get_message_parts(content) 1363 1384 context.content_parts = self.unique_attachment_names( … … 1369 1390 context.req.perm.assert_permission('DISCUSSION_APPEND') 1370 1391 1371 # Filter topic. 1392 ## Filter topic. 1393 # 1372 1394 for discussion_filter in api.discussion_filters: 1373 1395 accept, topic_or_error = discussion_filter.filter_topic( … … 1378 1400 raise TracError(topic_or_error) 1379 1401 1380 # Add a new topic. 1402 ## Add a new topic. 1403 # 1381 1404 api.add_topic(context, topic) 1382 1405 1383 # Get inserted topic with new ID. 1406 ## Get inserted topic with new ID. 1407 # 1384 1408 topic = api.get_topic_by_time(context, topic['time']) 1385 1409 1386 # Attach attachments. 1410 ## Attach attachments. 1411 # 1387 1412 self.id = topic['id'] 1388 1413 self.attach_attachments(context.content_parts, True) 1389 1414 1390 # Notify change listeners. 1415 ## Notify change listeners. 1416 # 1391 1417 for listener in api.topic_change_listeners: 1392 1418 listener.topic_created(context, topic) … … 1395 1421 context.req.perm.assert_permission('DISCUSSION_APPEND') 1396 1422 1397 # Filter message. 1423 ## Filter message. 1424 # 1398 1425 for discussion_filter in api.discussion_filters: 1399 1426 accept, message_or_error = discussion_filter.filter_message( … … 1404 1431 raise TracError(message_or_error) 1405 1432 1406 # Add message. 1433 ## Add message. 1434 # 1407 1435 api.add_message(context, message) 1408 1436 1409 # Get inserted message with new ID. 1437 ## Get inserted message with new ID. 1438 # 1410 1439 message = api.get_message_by_time(context, message['time']) 1411 1440 1412 # Attach attachments. 1441 ## Attach attachments. 1442 # 1413 1443 self.id = message['topic'] 1414 1444 self.attach_attachments(context.content_parts, True) 1415 1445 1416 # Notify change listeners. 1446 ## Notify change listeners. 1447 # 1417 1448 for listener in api.message_change_listeners: 1418 1449 listener.message_created(context, message) … … 1432 1463 return 1433 1464 1434 # Work around lack of header folding in Python; see http://bugs.python.org/issue4696 1465 ## Work around lack of header folding in Python; see http://bugs.python.org/issue4696 1466 # 1435 1467 try: 1436 1468 m.replace_header('Subject', m['Subject'].replace('\r', '').replace('\n', '')) … … 1456 1488 return False 1457 1489 1458 # If spam drop the message1490 ## If spam drop the message 1459 1491 # 1460 1492 if self.spam(m) == 'drop': … … 1473 1505 self.logger.debug('subject: %s' %subject) 1474 1506 1475 # 1476 # [hic] #1529: Re: LRZ 1477 # [hic] #1529?owner=bas,priority=medium: Re: LRZ 1507 ## [hic] #1529: Re: LRZ 1508 # [hic] #1529?owner=bas,priority=medium: Re: LRZ 1478 1509 # 1479 1510 ticket_regex = r''' … … 1481 1512 |(?P<reply>(?P<id>[#][\d]+)(?P<fields>\?.*)?:) 1482 1513 ''' 1483 # Check if FullBlogPlugin is installed1514 ## Check if FullBlogPlugin is installed 1484 1515 # 1485 1516 blog_enabled = None … … 1490 1521 1491 1522 1492 # Check if DiscussionPlugin is installed1523 ## Check if DiscussionPlugin is installed 1493 1524 # 1494 1525 discussion_enabled = None … … 1506 1537 SYSTEM_RE = re.compile(regex_str, re.VERBOSE) 1507 1538 1508 # Find out if this is a ticket, a blog or a discussion1539 ## Find out if this is a ticket, a blog or a discussion 1509 1540 # 1510 1541 result = SYSTEM_RE.search(subject) 1511 1542 1512 1543 if result: 1513 # update ticket + fields 1514 # 1515 #if result.group('reply') and self.parameters.ticket_update: 1544 ## update ticket + fields 1545 # 1516 1546 if result.group('reply'): 1517 1547 self.system = 'ticket' 1518 1548 1519 # Skip the last ':' character1549 ## Skip the last ':' character 1520 1550 # 1521 1551 if not self.ticket_update(m, result.group('reply')[:-1], spam_msg): 1522 1552 self.new_ticket(m, subject, spam_msg) 1523 1553 1524 # New ticket + fields1554 ## New ticket + fields 1525 1555 # 1526 1556 elif result.group('new_fields'): … … 1534 1564 1535 1565 if discussion_enabled: 1536 # New topic.1566 ## New topic. 1537 1567 # 1538 1568 if result.group('forum'): … … 1541 1571 self.discussion_topic(m, subject[result.end('forum'):]) 1542 1572 1543 # Reply to topic.1573 ## Reply to topic. 1544 1574 # 1545 1575 elif result.group('topic'): … … 1548 1578 self.discussion_topic_reply(m, subject[result.end('topic'):]) 1549 1579 1550 # Reply to topic message.1580 ## Reply to topic message. 1551 1581 # 1552 1582 elif result.group('message'): … … 1562 1592 self.new_ticket(m, subject, spam_msg) 1563 1593 else: 1564 # No update by subject, so just create a new ticket 1594 ## No update by subject, so just create a new ticket 1595 # 1565 1596 self.new_ticket(m, subject, spam_msg) 1566 1597 … … 1593 1624 from re import match 1594 1625 1595 # Figure out the quote level and the content of the current line 1626 ## Figure out the quote level and the content of the current line 1627 # 1596 1628 m = match('(>*)( ?)(.*)', line) 1597 1629 linequotelevel = len(m.group(1)) 1598 1630 line = m.group(3) 1599 1631 1600 # Determine whether this line is flowed 1632 ## Determine whether this line is flowed 1633 # 1601 1634 if line and line != '-- ' and line[-1] == ' ': 1602 1635 flowed = 1 … … 1607 1640 line = line[:-1] 1608 1641 1609 # If the previous line is flowed, append this line to it 1642 ## If the previous line is flowed, append this line to it 1643 # 1610 1644 if prevflowed and line != '-- ' and linequotelevel == quotelevel: 1611 1645 flowedlines[-1] += line 1612 # Otherwise, start a new line 1646 1647 ## Otherwise, start a new line 1648 # 1613 1649 else: 1614 1650 flowedlines.append('>' * linequotelevel + line) … … 1751 1787 self.logger.debug(' Inline body part') 1752 1788 1753 # Try to decode, if fails then do not decode1789 ## Try to decode, if fails then do not decode 1754 1790 # 1755 1791 body_text = part.get_payload(decode=1) … … 1799 1835 self.print_unicode(s) 1800 1836 1801 ## 1802 # First try to use email header function to convert filename. 1837 ## First try to use email header function to convert filename. 1803 1838 # If this fails the use the plan filename 1839 # 1804 1840 try: 1805 1841 filename = self.email_to_unicode(part.get_filename()) … … 1832 1868 filename = 'untitled-part' 1833 1869 1834 # Guess the extension from the content type, use non strict mode1835 # some additional non-standard but commonly used MIME types1836 # are also recognized1870 ## Guess the extension from the content type, use non strict mode 1871 # some additional non-standard but commonly used MIME types 1872 # are also recognized 1837 1873 # 1838 1874 ext = mimetypes.guess_extension(part.get_content_type(), False) … … 1853 1889 filename = filename.replace(forbidden_char,'') 1854 1890 1855 # 1856 # We try to normalize the filename to utf-8 NFC if we can. 1857 # Files uploaded from OS X might be in NFD. 1858 # Check python version and then try it 1891 ## We try to normalize the filename to utf-8 NFC if we can. 1892 # Files uploaded from OS X might be in NFD. 1893 # Check python version and then try it 1859 1894 # 1860 1895 #if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3): … … 1864 1899 # pass 1865 1900 1866 # Make the filename unique for this ticket 1901 ## Make the filename unique for this ticket 1902 # 1867 1903 num = 0 1868 1904 unique_filename = filename … … 1890 1926 self.print_unicode(s) 1891 1927 1892 # We have no valid ticket id1928 ## We have no valid ticket id 1893 1929 # 1894 1930 if not self.id: … … 1912 1948 1913 1949 for part in message_parts: 1914 # Plain text part, append it 1950 1951 ## Plain text part, append it 1952 # 1915 1953 if not isinstance(part, tuple): 1916 1954 body_text.extend(part.strip().splitlines()) … … 1945 1983 self.parameters.mailto_cc = '' 1946 1984 1947 # use urllib to escape the chars1985 ## use urllib to escape the chars 1948 1986 # 1949 1987 s = 'mailto:%s?Subject=%s&Cc=%s' %( … … 1979 2017 #sys.exit(0) 1980 2018 1981 # create false {abs_}href properties, to trick Notify()2019 ## create false {abs_}href properties, to trick Notify() 1982 2020 # 1983 2021 if not (self.VERSION in [0.11, 0.12]): … … 2024 2062 config = trac_config.Configuration(file) 2025 2063 2026 # Use given project name else use defaults2064 ## Use given project name else use defaults 2027 2065 # 2028 2066 if name: … … 2041 2079 2042 2080 else: 2043 # use some trac internals to get the defaults2081 ## use some trac internals to get the defaults 2044 2082 # 2045 2083 tmp = config.parser.defaults() … … 2083 2121 2084 2122 elif parameters.log_type in ('winlog', 'eventlog', 'nteventlog'): 2085 # Requires win32 extensions 2123 ## Requires win32 extensions 2124 # 2086 2125 log_handler = logging.handlers.NTEventLogHandler(logid, logtype='Application') 2087 2126 … … 2126 2165 2127 2166 if __name__ == '__main__': 2128 # Default config file2167 ## Default config file 2129 2168 # 2130 2169 configfile = '@email2trac_conf@' … … 2168 2207 settings = ReadConfig(configfile, project_name) 2169 2208 2170 # The default prefix for ticket values in email2trac.conf2209 ## The default prefix for ticket values in email2trac.conf 2171 2210 # 2172 2211 settings.ticket_prefix = ticket_prefix … … 2187 2226 settings['component'] = component 2188 2227 2189 # Determine major trac version used to be in email2trac.conf2228 ## Determine major trac version used to be in email2trac.conf 2190 2229 # Quick hack for 0.12 2191 2230 # … … 2248 2287 sys.exit(1) 2249 2288 2250 # Must be set before environment is created2289 ## Must be set before environment is created 2251 2290 # 2252 2291 if settings.has_key('python_egg_cache'):
Note: See TracChangeset
for help on using the changeset viewer.