Ticket #197: discussion_plugin_support.diff

File discussion_plugin_support.diff, 12.7 KB (added by bas, 11 years ago)

Just added for reference

  • email2trac.py.in

     
    8585
    8686from datetime import tzinfo, timedelta, datetime
    8787from trac import config as trac_config
     88from trac.core import TracError
     89from trac.web.api import Request
     90from trac.perm import PermissionCache
    8891
    8992# Some global variables
    9093#
     
    112115class TicketEmailParser(object):
    113116        env = None
    114117        comment = '> '
    115    
     118
    116119        def __init__(self, env, parameters, version):
    117120                self.env = env
    118121
     122                # TODO: Read server base URL from config.
     123                # Create request object to mockup context creation.
     124                #
     125                environ = {'SERVER_PORT' : 80,
     126                                   'SERVER_NAME' : 'test',
     127                                   'REQUEST_METHOD' : 'POST',
     128                                   'wsgi.url_scheme' : 'http',
     129                                   'wsgi.input' : sys.stdin}
     130                chrome =  {'links': {},
     131                                   'scripts': [],
     132                                   'ctxtnav': [],
     133                                   'warnings': [],
     134                                   'notices': []}
     135                if self.env.base_url_for_redirect:
     136                        environ['trac.base_url'] = self.env.base_url
     137                self.req = Request(environ, None)
     138                self.req.chrome = chrome
     139                self.req.tz = 'missing'
     140
    119141                # Database connection
    120142                #
    121143                self.db = None
     
    283305                        self.SUBJECT_FIELD_SEPARATOR = '&'
    284306
    285307                self.trac_smtp_from = self.get_config('notification', 'smtp_from')
     308                self.system = None
    286309
    287310########## Email Header Functions ###########################################################
    288311
     
    403426                        self.email_from = users[0][0]
    404427                        self.author = users[0][0]
    405428
     429                # Fill in authname and prermission of the request.
     430                #
     431                self.req.authname = self.author
     432                self.req.perm = PermissionCache(self.env, self.author)
     433
    406434        def set_reply_fields(self, ticket, message):
    407435                """
    408436                Set all the right fields for a new ticket
     
    11021130                        # Insert the attachment
    11031131                        #
    11041132                        fd = open(path, 'rb')
    1105                         att = attachment.Attachment(self.env, 'ticket', self.id)
    1106 
     1133                        if self.system == 'discussion':
     1134                                att = attachment.Attachment(self.env, 'discussion', 'topic/%s'
     1135                                  % (self.id,))
     1136                        else:
     1137                                att = attachment.Attachment(self.env, 'ticket', self.id)
     1138 
    11071139                        # This will break the ticket_update system, the body_text is vaporized
    11081140                        # ;-(
    11091141                        #
     
    11251157                #
    11261158                return status
    11271159
    1128 ########## Fullblog functions  ###########################################################
     1160########## Fullblog functions  #################################################
    11291161
    11301162        def blog(self, id):
    11311163                """
     
    11701202                        blog.create_post(req, post, self.author, u'Created by email2trac', False)
    11711203
    11721204
     1205########## Discussion functions  ##############################################
    11731206
    1174 ########## MAIN function  ###########################################################
     1207        def discussion_topic(self, content, subject):
    11751208
     1209                # Import modules.
     1210                from tracdiscussion.api import DiscussionApi
     1211                from trac.util.datefmt import to_timestamp, utc
     1212
     1213                if self.DEBUG:
     1214                        print 'TD: Creating a new topic in forum:', self.id
     1215
     1216                # Get dissussion API component.
     1217                api = self.env[DiscussionApi]
     1218                context = self._create_context(content, subject)
     1219
     1220                # Get forum for new topic.
     1221                forum = api.get_forum(context, self.id)
     1222
     1223                if not forum and self.DEBUG:
     1224                        print 'ERROR: Replied forum doesn\'t exist'
     1225
     1226                # Prepare topic.
     1227                topic = {'forum' : forum['id'],
     1228                                 'subject' : context.subject,
     1229                                 'time': to_timestamp(datetime.now(utc)),
     1230                                 'author' : self.author,
     1231                                 'subscribers' : [self.email_addr],
     1232                                 'body' : self.body_text(context.content_parts)}
     1233
     1234                # Add topic to DB and commit it.
     1235                self._add_topic(api, context, topic)
     1236                self.db.commit()
     1237
     1238        def discussion_topic_reply(self, content, subject):
     1239
     1240                # Import modules.
     1241                from tracdiscussion.api import DiscussionApi
     1242                from trac.util.datefmt import to_timestamp, utc
     1243
     1244                if self.DEBUG:
     1245                        print 'TD: Replying to discussion topic', self.id
     1246
     1247                # Get dissussion API component.
     1248                api = self.env[DiscussionApi]
     1249                context = self._create_context(content, subject)
     1250
     1251                # Get replied topic.
     1252                topic = api.get_topic(context, self.id)
     1253
     1254                if not topic and self.DEBUG:
     1255                        print 'ERROR: Replied topic doesn\'t exist'
     1256
     1257                # Prepare message.
     1258                message = {'forum' : topic['forum'],
     1259                                   'topic' : topic['id'],
     1260                                   'replyto' : -1,
     1261                                   'time' : to_timestamp(datetime.now(utc)),
     1262                                   'author' : self.author,
     1263                                   'body' : self.body_text(context.content_parts)}
     1264
     1265                # Add message to DB and commit it.
     1266                self._add_message(api, context, message)
     1267                self.db.commit()
     1268
     1269        def discussion_message_reply(self, content, subject):
     1270
     1271                # Import modules.
     1272                from tracdiscussion.api import DiscussionApi
     1273                from trac.util.datefmt import to_timestamp, utc
     1274
     1275                if self.DEBUG:
     1276                        print 'TD: Replying to discussion message', self.id
     1277
     1278                # Get dissussion API component.
     1279                api = self.env[DiscussionApi]
     1280                context = self._create_context(content, subject)
     1281
     1282                # Get replied message.
     1283                message = api.get_message(context, self.id)
     1284
     1285                if not message and self.DEBUG:
     1286                        print 'ERROR: Replied message doesn\'t exist'
     1287
     1288                # Prepare message.
     1289                message = {'forum' : message['forum'],
     1290                                   'topic' : message['topic'],
     1291                                   'replyto' : message['id'],
     1292                                   'time' : to_timestamp(datetime.now(utc)),
     1293                                   'author' : self.author,
     1294                                   'body' : self.body_text(context.content_parts)}
     1295
     1296                # Add message to DB and commit it.
     1297                self._add_message(api, context, message)
     1298                self.db.commit()
     1299
     1300        def _create_context(self, content, subject):
     1301
     1302                # Import modules.
     1303                from trac.mimeview import Context
     1304
     1305                # Create and return context.
     1306                context = Context.from_request(self.req)
     1307                context.realm = 'discussion-email2trac'
     1308                context.cursor = self.db.cursor()
     1309                context.content = content
     1310                context.subject = subject
     1311
     1312                # Read content parts from content.
     1313                context.content_parts = self.get_message_parts(content)
     1314                context.content_parts = self.unique_attachment_names(
     1315                  context.content_parts)
     1316
     1317                return context
     1318
     1319        def _add_topic(self, api, context, topic):
     1320                context.req.perm.assert_permission('DISCUSSION_APPEND')
     1321
     1322                # Filter topic.
     1323                for discussion_filter in api.discussion_filters:
     1324                        accept, topic_or_error = discussion_filter.filter_topic(
     1325                          context, topic)
     1326                        if accept:
     1327                                topic = topic_or_error
     1328                        else:
     1329                                raise TracError(topic_or_error)
     1330
     1331                # Add a new topic.
     1332                api.add_topic(context, topic)
     1333
     1334                # Get inserted topic with new ID.
     1335                topic = api.get_topic_by_time(context, topic['time'])
     1336
     1337                # Attach attachments.
     1338                self.id = topic['id']
     1339                self.attach_attachments(context.content_parts, self.VERSION == 0.9)
     1340
     1341                # Notify change listeners.
     1342                for listener in api.topic_change_listeners:
     1343                        listener.topic_created(context, topic)
     1344
     1345        def _add_message(self, api, context, message):
     1346                context.req.perm.assert_permission('DISCUSSION_APPEND')
     1347
     1348                # Filter message.
     1349                for discussion_filter in api.discussion_filters:
     1350                        accept, message_or_error = discussion_filter.filter_message(
     1351                          context, message)
     1352                        if accept:
     1353                                message = message_or_error
     1354                        else:
     1355                                raise TracError(message_or_error)
     1356
     1357                # Add message.
     1358                api.add_message(context, message)
     1359
     1360                # Get inserted message with new ID.
     1361                message = api.get_message_by_time(context, message['time'])
     1362
     1363                # Attach attachments.
     1364                self.id = message['topic']
     1365                self.attach_attachments(context.content_parts, self.VERSION == 0.9)
     1366
     1367                # Notify change listeners.
     1368                for listener in api.message_change_listeners:
     1369                        listener.message_created(context, message)
     1370
     1371########## MAIN function  ######################################################
     1372
    11761373        def parse(self, fp):
    11771374                """
    11781375                """
     
    12361433                if self.get_config('components', 'tracfullblog.*') in ['enabled']:
    12371434                        blog_enabled = True
    12381435
     1436                # Check if DiscussionPlugin is installed
     1437                #
     1438                discussion_enabled = None
     1439                if self.get_config('components', 'tracdiscussion.api.*') in ['enabled']:
     1440                        discussion_enabled = True
     1441
    12391442                if not m['Subject']:
    12401443                        subject  = 'No Subject'
    12411444                else:
    1242                         subject  = self.email_to_unicode(m['Subject'])         
     1445                        subject  = self.email_to_unicode(m['Subject'])
    12431446
    1244                 #
     1447                if self.DEBUG:
     1448                         print "TD:", subject
     1449
     1450                #
    12451451                # [hic] #1529: Re: LRZ
    12461452                # [hic] #1529?owner=bas,priority=medium: Re: LRZ
    12471453                #
    12481454                TICKET_RE = re.compile(r"""
    12491455                        (?P<blog>blog:(?P<blog_id>\w*))
     1456                        |(?P<forum>Forum[ ][#](?P<forum_id>\d+)[ ]-[ ]?)
     1457                        |(?P<topic>Topic[ ][#](?P<topic_id>\d+)[ ]-[ ]?)
     1458                        |(?P<message>Message[ ][#](?P<message_id>\d+)[ ]-[ ]?)
    12501459                        |(?P<new_fields>[#][?].*)
    12511460                        |(?P<reply>[#][\d]+:)
    12521461                        |(?P<reply_fields>[#][\d]+\?.*?:)
    12531462                        """, re.VERBOSE)
    12541463
    1255                 # Find out if this is a ticket or a blog
     1464                # Find out if this is a ticket, a blog or a discussion
    12561465                #
    12571466                result =  TICKET_RE.search(subject)
    12581467
    12591468                if result:
    12601469                        if result.group('blog'):
    12611470                                if blog_enabled:
     1471                                        self.system = 'blog'
    12621472                                        self.blog(result.group('blog_id'))
    12631473                                else:
    12641474                                        if self.DEBUG:
    1265                                                 print "Fullblog plugin is not installed"
     1475                                                print "ERROR: Fullblog plugin is not installed"
    12661476                                                return
    12671477
     1478                        # New topic.
     1479                        #
     1480                        elif result.group('forum'):
     1481                                if discussion_enabled:
     1482                                        self.id = int(result.group('forum_id'))
     1483                                        self.system = 'discussion'
     1484                                        self.discussion_topic(m, subject[result.end('forum'):])
     1485                                else:
     1486                                        if self.DEBUG:
     1487                                                print "ERROR: Discussion plugin is not installed"
     1488                                                return
     1489
     1490                        # Reply to topic.
     1491                        #
     1492                        elif result.group('topic'):
     1493                                if discussion_enabled:
     1494                                        self.id = int(result.group('topic_id'))
     1495                                        self.system = 'discussion'
     1496                                        self.discussion_topic_reply(m, subject[result.end(
     1497                                          'topic'):])
     1498                                else:
     1499                                        if self.DEBUG:
     1500                                                print "ERROR: Discussion plugin is not installed"
     1501                                                return
     1502
     1503                        # Reply to topic message.
     1504                        #
     1505                        elif result.group('message'):
     1506                                if discussion_enabled:
     1507                                        self.id = int(result.group('message_id'))
     1508                                        self.system = 'discussion'
     1509                                        self.discussion_message_reply(m, subject[result.end(
     1510                                          'message'):])
     1511                                else:
     1512                                        if self.DEBUG:
     1513                                                print "ERROR: Discussion plugin is not installed"
     1514                                                return
     1515
    12681516                        # update ticket + fields
    12691517                        #
    1270                         if result.group('reply_fields') and self.TICKET_UPDATE:
     1518                        elif result.group('reply_fields') and self.TICKET_UPDATE:
     1519                                self.system = 'ticket'
    12711520                                self.ticket_update(m, result.group('reply_fields'), spam_msg)
    12721521
    12731522                        # Update ticket
    12741523                        #
    12751524                        elif result.group('reply') and self.TICKET_UPDATE:
     1525                                self.system = 'ticket'
    12761526                                self.ticket_update(m, result.group('reply'), spam_msg)
    12771527
    12781528                        # New ticket + fields
    12791529                        #
    12801530                        elif result.group('new_fields'):
     1531                                self.system = 'ticket'
    12811532                                self.new_ticket(m, subject[:result.start('new_fields')], spam_msg, result.group('new_fields'))
    12821533
    12831534                else:
     1535                        self.system = 'ticket'
    12841536                        result = self.ticket_update_by_subject(subject)
    12851537                        if result:
    12861538                                self.ticket_update(m, result, spam_msg)
     
    12881540                                # No update by subject, so just create a new ticket
    12891541                                self.new_ticket(m, subject, spam_msg)
    12901542
    1291 
    12921543########## BODY TEXT functions  ###########################################################
    12931544
    12941545        def strip_signature(self, text):
     
    16001851        def attachment_exists(self, filename):
    16011852
    16021853                if self.DEBUG:
    1603                         s = 'TD: attachment already exists: Ticket id : '
     1854                        s = 'TD: attachment already exists: Id : '
    16041855                        try:
    1605                                 print "%s%s, Filename : %s" %(s, self.id, filename)
     1856                                print "%s%s, Filename : %s" % (s, self.id, filename)
    16061857                        except UnicodeEncodeError, detail:
    16071858                                print "%s%s, Filename : Can not be printed due to non-ascii characters" %(s, self.id)
    16081859
     
    16121863                        return False
    16131864
    16141865                try:
    1615                         att = attachment.Attachment(self.env, 'ticket', self.id, filename)
     1866                        if self.system == 'discussion':
     1867                                att = attachment.Attachment(self.env, 'discussion', 'ticket/%s'
     1868                                  % (self.id,), filename)
     1869                        else:
     1870                                att = attachment.Attachment(self.env, 'ticket', self.id,
     1871                                  filename)
    16161872                        return True
    16171873                except attachment.ResourceNotFound:
    16181874                        return False
     
    16331889                        inline = self.inline_part(part)
    16341890                       
    16351891                        if part.get_content_maintype() == 'image' and inline:
    1636                                 body_text.append('[[Image(%s)]]' % filename)
     1892                                if self.system != 'discussion':
     1893                                        body_text.append('[[Image(%s)]]' % filename)
    16371894                                body_text.append("")
    16381895                        else:
    1639                                 body_text.append('[attachment:"%s"]' % filename)
     1896                                if self.system != 'discussion':
     1897                                        body_text.append('[attachment:"%s"]' % filename)
    16401898                                body_text.append("")
    16411899                               
    16421900                body_text = '\r\n'.join(body_text)
     
    17492007
    17502008        ENABLE_SYSLOG = 0
    17512009
    1752 
    17532010        SHORT_OPT = 'chf:np:t:v'
    17542011        LONG_OPT  =  ['component=', 'dry-run', 'help', 'file=', 'project=', 'ticket_prefix=', 'verbose']
    17552012
     
    18512108                        python_egg_cache = str(settings['python_egg_cache'])
    18522109                        os.environ['PYTHON_EGG_CACHE'] = python_egg_cache
    18532110
     2111                print 'Loading environment', settings['project']
    18542112                env = Environment(settings['project'], create=0)
    18552113
    18562114                tktparser = TicketEmailParser(env, settings, float(version))