Changeset 477


Ignore:
Timestamp:
08/10/10 09:28:13 (14 years ago)
Author:
bas
Message:

email2trac.py.in:

  • uniform formot for comments
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/email2trac.py.in

    r476 r477  
    319319
    320320                ## Trac can not handle author's name that contains spaces
    321                 #  and forbid the ticket email address as author field
    322 
     321                #
    323322                if self.email_addr == self.trac_smtp_from:
    324323                        if self.email_name:
     
    640639                        return (self.check_permission_participants(tkt, action))       
    641640
    642                 # Default is to allow everybody ticket updates and ticket creation
     641                ## Default is to allow everybody ticket updates and ticket creation
     642                #
    643643                else:
    644644                                return True
     
    666666                                        return False
    667667
    668                 # Build a system dictionary from the ticket fields
    669                 # with field as index and option as value
     668                ## Build a system dictionary from the ticket fields
     669                #  with field as index and option as value
    670670                #
    671671                sys_dict = dict()
     
    679679
    680680                ## Check user supplied fields an compare them with the
    681                 # system one's
     681                #  system one's
    682682                #
    683683                for field,value in user_dict.items():
     
    702702                        if sys_dict.has_key(field):
    703703
    704                                 # Check if value is an allowed system option, if TypeError then
    705                                 # every value is allowed
     704                                ## Check if value is an allowed system option, if TypeError then
     705                                #  every value is allowed
    706706                                #
    707707                                try:
     
    709709                                                ticket[field] = value
    710710                                        else:
    711                                                 # Must we set a default if value is not allowed
     711                                                ## Must we set a default if value is not allowed
    712712                                                #
    713713                                                if new:
     
    738738                        return False
    739739
    740                 # Must we update ticket fields
     740                ## Must we update ticket fields
    741741                #
    742742                update_fields = dict()
     
    746746                        update_fields = self.str_to_dict(keywords)
    747747
    748                         # Strip '#'
     748                        ## Strip '#'
    749749                        #
    750750                        self.id = int(id[1:])
     
    752752                except ValueError:
    753753
    754                         # Strip '#'
     754                        ## Strip '#'
    755755                        #
    756756                        self.id = int(id[1:])
     
    758758                self.logger.debug("ticket_update id %s" %id)
    759759
    760                 # When is the change committed
     760                ## When is the change committed
    761761                #
    762762                if self.VERSION < 0.11:
     
    770770                except util.TracError, detail:
    771771
    772                         # Not a valid ticket
    773 
     772                        ## Not a valid ticket
     773                        #
    774774                        self.id = None
    775775                        return False
    776776
    777                 # Check the permission of the reporter
     777                ## Check the permission of the reporter
    778778                #
    779779                if self.parameters.ticket_permission_system:
     
    852852                message_parts = self.unique_attachment_names(message_parts)
    853853
    854                 # Must we update some ticket fields properties via body_text
     854                ## Must we update some ticket fields properties via body_text
    855855                #
    856856                if self.properties:
     
    954954
    955955                        if result:
    956                                 # This is a reply
     956                                ## This is a reply
     957                                #
    957958                                orig_subject = result.group(3)
    958959
     
    962963                                summaries = [orig_subject, '%%: %s' % orig_subject]
    963964
    964                                 ##
    965                                 # Convert days to seconds
     965                                ## Convert days to seconds
     966                                #
    966967                                lookback = int(time.mktime(time.gmtime())) - \
    967968                                                self.parameters.ticket_update_by_subject_lookback * 24 * 3600
     
    10021003                self.set_ticket_fields(tkt)
    10031004
    1004                 # Check the permission of the reporter
     1005                ## Check the permission of the reporter
    10051006                #
    10061007                if self.parameters.ticket_permission_system:
     
    10091010                                return False
    10101011
    1011                 # Old style setting for component, will be removed
     1012                ## Old style setting for component, will be removed
    10121013                #
    10131014                if spam:
     
    10331034                message_parts = self.get_message_parts(msg)
    10341035
    1035                 # Must we update some ticket fields properties via body_text
     1036                ## Must we update some ticket fields properties via body_text
    10361037                #
    10371038                if self.properties:
     
    10401041                message_parts = self.unique_attachment_names(message_parts)
    10411042               
    1042                 # produce e-mail like header
     1043                ## produce e-mail like header
    10431044                #
    10441045                head = ''
     
    10511052                tkt['description'] = body_text
    10521053
    1053                 # When is the change committed
     1054                ## When is the change committed
    10541055                #
    10551056                if self.VERSION < 0.11:
     
    10661067                comment = ''
    10671068
    1068                 # some routines in trac are dependend on ticket id     
    1069                 # like alternate notify template
     1069                ## some routines in trac are dependend on ticket id     
     1070                #  like alternate notify template
    10701071                #
    10711072                if self.parameters.alternate_notify_template:
     
    11141115                count = 0
    11151116
    1116                 # Get Maxium attachment size
     1117                ## Get Maxium attachment size
    11171118                #
    11181119                max_size = int(self.get_config('attachment', 'max_size'))
     
    11201121               
    11211122                for item in message_parts:
    1122                         # Skip body parts
     1123                        ## Skip body parts
     1124                        #
    11231125                        if not isinstance(item, tuple):
    11241126                                continue
    11251127                               
    11261128                        (original, filename, part) = item
    1127                         #
    1128                         # We have to determine the size so we use this temporary solution. we must escape it
    1129                         # 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.
    11301132                        #
    11311133                        path, fd =  util.create_unique_file(os.path.join(self.parameters.tmpdir, util.text.unicode_quote(filename)))
     
    11361138                        fd.close()
    11371139
    1138                         # get the file_size
     1140                        ## get the file_size
    11391141                        #
    11401142                        stats = os.lstat(path)
    11411143                        file_size = stats[ST_SIZE]
    11421144
    1143                         # Check if the attachment size is allowed
     1145                        ## Check if the attachment size is allowed
    11441146                        #
    11451147                        if (max_size != -1) and (file_size > max_size):
     
    11521154                                count = count + 1
    11531155                                       
    1154                         # Insert the attachment
     1156                        ## Insert the attachment
    11551157                        #
    11561158                        fd = open(path, 'rb')
     
    11621164                                att = attachment.Attachment(self.env, 'ticket', self.id)
    11631165 
    1164                         # This will break the ticket_update system, the body_text is vaporized
    1165                         # ;-(
     1166                        ## This will break the ticket_update system, the body_text is vaporized
     1167                        #  ;-(
    11661168                        #
    11671169                        if not update:
     
    11761178                                status = '%s\nFilename %s could not be saved, problem: %s' %(status, filename, detail)
    11771179
    1178                         # Remove the created temporary filename
     1180                        ## Remove the created temporary filename
    11791181                        #
    11801182                        fd.close()
     
    11911193                The blog create/update function
    11921194                """
    1193                 # import the modules
     1195                ## import the modules
    11941196                #
    11951197                from tracfullblog.core import FullBlogCore
     
    11971199                from trac.test import Mock, MockPerm
    11981200
    1199                 # instantiate blog core
     1201                ## instantiate blog core
     1202                #
    12001203                blog = FullBlogCore(self.env)
    12011204                req = Mock(authname='anonymous', perm=MockPerm(), args={})
     
    12031206                if id:
    12041207
    1205                         # update blog
     1208                        ## update blog
    12061209                        #
    12071210                        comment = BlogComment(self.env, id)
     
    12141217
    12151218                else:
    1216                         # create blog
     1219                        ## create blog
    12171220                        #
    12181221                        import time
     
    12341237        def discussion_topic(self, content, subject):
    12351238
    1236                 # Import modules.
     1239                ## Import modules.
     1240                #
    12371241                from tracdiscussion.api import DiscussionApi
    12381242                from trac.util.datefmt import to_timestamp, utc
     
    12401244                self.logger.debug('Creating a new topic in forum:', self.id)
    12411245
    1242                 # Get dissussion API component.
     1246                ## Get dissussion API component.
     1247                #
    12431248                api = self.env[DiscussionApi]
    12441249                context = self._create_context(content, subject)
    12451250
    1246                 # Get forum for new topic.
     1251                ## Get forum for new topic.
     1252                #
    12471253                forum = api.get_forum(context, self.id)
    12481254
     
    12501256                        self.logger.error("ERROR: Replied forum doesn't exist")
    12511257
    1252                 # Prepare topic.
     1258                ## Prepare topic.
     1259                #
    12531260                topic = {'forum' : forum['id'],
    12541261                                 'subject' : context.subject,
     
    12581265                                 'body' : self.body_text(context.content_parts)}
    12591266
    1260                 # Add topic to DB and commit it.
     1267                ## Add topic to DB and commit it.
     1268                #
    12611269                self._add_topic(api, context, topic)
    12621270                self.db.commit()
     
    12641272        def discussion_topic_reply(self, content, subject):
    12651273
    1266                 # Import modules.
     1274                ## Import modules.
     1275                #
    12671276                from tracdiscussion.api import DiscussionApi
    12681277                from trac.util.datefmt import to_timestamp, utc
     
    12701279                self.logger.debug('Replying to discussion topic', self.id)
    12711280
    1272                 # Get dissussion API component.
     1281                ## Get dissussion API component.
     1282                #
    12731283                api = self.env[DiscussionApi]
    12741284                context = self._create_context(content, subject)
    12751285
    1276                 # Get replied topic.
     1286                ## Get replied topic.
     1287                #
    12771288                topic = api.get_topic(context, self.id)
    12781289
     
    12801291                        self.logger.error("ERROR: Replied topic doesn't exist")
    12811292
    1282                 # Prepare message.
     1293                ## Prepare message.
     1294                #
    12831295                message = {'forum' : topic['forum'],
    12841296                                   'topic' : topic['id'],
     
    12881300                                   'body' : self.body_text(context.content_parts)}
    12891301
    1290                 # Add message to DB and commit it.
     1302                ## Add message to DB and commit it.
     1303                #
    12911304                self._add_message(api, context, message)
    12921305                self.db.commit()
     
    12941307        def discussion_message_reply(self, content, subject):
    12951308
    1296                 # Import modules.
     1309                ## Import modules.
     1310                #
    12971311                from tracdiscussion.api import DiscussionApi
    12981312                from trac.util.datefmt import to_timestamp, utc
     
    13001314                self.loggger.debug('Replying to discussion message', self.id)
    13011315
    1302                 # Get dissussion API component.
     1316                ## Get dissussion API component.
     1317                #
    13031318                api = self.env[DiscussionApi]
    13041319                context = self._create_context(content, subject)
    13051320
    1306                 # Get replied message.
     1321                ## Get replied message.
     1322                #
    13071323                message = api.get_message(context, self.id)
    13081324
     
    13101326                        self.logger.error("ERROR: Replied message doesn't exist")
    13111327
    1312                 # Prepare message.
     1328                ## Prepare message.
     1329                #
    13131330                message = {'forum' : message['forum'],
    13141331                                   'topic' : message['topic'],
     
    13181335                                   'body' : self.body_text(context.content_parts)}
    13191336
    1320                 # Add message to DB and commit it.
     1337                ## Add message to DB and commit it.
     1338                #
    13211339                self._add_message(api, context, message)
    13221340                self.db.commit()
     
    13241342        def _create_context(self, content, subject):
    13251343
    1326                 # Import modules.
     1344                ## Import modules.
     1345                #
    13271346                from trac.mimeview import Context
    13281347                from trac.web.api import Request
    13291348                from trac.perm import PermissionCache
    13301349
    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.
    13331352                #
    13341353                environ = {'SERVER_PORT' : 80,
     
    13521371                req.perm = PermissionCache(self.env, self.author)
    13531372
    1354                 # Create and return context.
     1373                ## Create and return context.
     1374                #
    13551375                context = Context.from_request(req)
    13561376                context.realm = 'discussion-email2trac'
     
    13591379                context.subject = subject
    13601380
    1361                 # Read content parts from content.
     1381                ## Read content parts from content.
     1382                #
    13621383                context.content_parts = self.get_message_parts(content)
    13631384                context.content_parts = self.unique_attachment_names(
     
    13691390                context.req.perm.assert_permission('DISCUSSION_APPEND')
    13701391
    1371                 # Filter topic.
     1392                ## Filter topic.
     1393                #
    13721394                for discussion_filter in api.discussion_filters:
    13731395                        accept, topic_or_error = discussion_filter.filter_topic(
     
    13781400                                raise TracError(topic_or_error)
    13791401
    1380                 # Add a new topic.
     1402                ## Add a new topic.
     1403                #
    13811404                api.add_topic(context, topic)
    13821405
    1383                 # Get inserted topic with new ID.
     1406                ## Get inserted topic with new ID.
     1407                #
    13841408                topic = api.get_topic_by_time(context, topic['time'])
    13851409
    1386                 # Attach attachments.
     1410                ## Attach attachments.
     1411                #
    13871412                self.id = topic['id']
    13881413                self.attach_attachments(context.content_parts, True)
    13891414
    1390                 # Notify change listeners.
     1415                ## Notify change listeners.
     1416                #
    13911417                for listener in api.topic_change_listeners:
    13921418                        listener.topic_created(context, topic)
     
    13951421                context.req.perm.assert_permission('DISCUSSION_APPEND')
    13961422
    1397                 # Filter message.
     1423                ## Filter message.
     1424                #
    13981425                for discussion_filter in api.discussion_filters:
    13991426                        accept, message_or_error = discussion_filter.filter_message(
     
    14041431                                raise TracError(message_or_error)
    14051432
    1406                 # Add message.
     1433                ## Add message.
     1434                #
    14071435                api.add_message(context, message)
    14081436
    1409                 # Get inserted message with new ID.
     1437                ## Get inserted message with new ID.
     1438                #
    14101439                message = api.get_message_by_time(context, message['time'])
    14111440
    1412                 # Attach attachments.
     1441                ## Attach attachments.
     1442                #
    14131443                self.id = message['topic']
    14141444                self.attach_attachments(context.content_parts, True)
    14151445
    1416                 # Notify change listeners.
     1446                ## Notify change listeners.
     1447                #
    14171448                for listener in api.message_change_listeners:
    14181449                        listener.message_created(context, message)
     
    14321463                        return
    14331464                       
    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                #
    14351467                try:
    14361468                        m.replace_header('Subject', m['Subject'].replace('\r', '').replace('\n', ''))
     
    14561488                        return False
    14571489
    1458                 # If spam drop the message
     1490                ## If spam drop the message
    14591491                #
    14601492                if self.spam(m) == 'drop':
     
    14731505                self.logger.debug('subject: %s' %subject)
    14741506
    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
    14781509                #
    14791510                ticket_regex = r'''
     
    14811512                        |(?P<reply>(?P<id>[#][\d]+)(?P<fields>\?.*)?:)
    14821513                        '''
    1483                 # Check if  FullBlogPlugin is installed
     1514                ## Check if  FullBlogPlugin is installed
    14841515                #
    14851516                blog_enabled = None
     
    14901521
    14911522
    1492                 # Check if DiscussionPlugin is installed
     1523                ## Check if DiscussionPlugin is installed
    14931524                #
    14941525                discussion_enabled = None
     
    15061537                SYSTEM_RE = re.compile(regex_str, re.VERBOSE)
    15071538
    1508                 # Find out if this is a ticket, a blog or a discussion
     1539                ## Find out if this is a ticket, a blog or a discussion
    15091540                #
    15101541                result =  SYSTEM_RE.search(subject)
    15111542
    15121543                if result:
    1513                         # update ticket + fields
    1514                         #
    1515                         #if result.group('reply') and self.parameters.ticket_update:
     1544                        ## update ticket + fields
     1545                        #
    15161546                        if result.group('reply'):
    15171547                                self.system = 'ticket'
    15181548
    1519                                 # Skip the last ':' character
     1549                                ## Skip the last ':' character
    15201550                                #
    15211551                                if not self.ticket_update(m, result.group('reply')[:-1], spam_msg):
    15221552                                        self.new_ticket(m, subject, spam_msg)
    15231553
    1524                         # New ticket + fields
     1554                        ## New ticket + fields
    15251555                        #
    15261556                        elif result.group('new_fields'):
     
    15341564
    15351565                        if discussion_enabled:
    1536                                 # New topic.
     1566                                ## New topic.
    15371567                                #
    15381568                                if result.group('forum'):
     
    15411571                                        self.discussion_topic(m, subject[result.end('forum'):])
    15421572
    1543                                 # Reply to topic.
     1573                                ## Reply to topic.
    15441574                                #
    15451575                                elif result.group('topic'):
     
    15481578                                        self.discussion_topic_reply(m, subject[result.end('topic'):])
    15491579
    1550                                 # Reply to topic message.
     1580                                ## Reply to topic message.
    15511581                                #
    15521582                                elif result.group('message'):
     
    15621592                                        self.new_ticket(m, subject, spam_msg)
    15631593                        else:
    1564                                 # No update by subject, so just create a new ticket
     1594                                ## No update by subject, so just create a new ticket
     1595                                #
    15651596                                self.new_ticket(m, subject, spam_msg)
    15661597
     
    15931624                        from re import match
    15941625                       
    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                        #
    15961628                        m = match('(>*)( ?)(.*)', line)
    15971629                        linequotelevel = len(m.group(1))
    15981630                        line = m.group(3)
    15991631
    1600                         # Determine whether this line is flowed
     1632                        ## Determine whether this line is flowed
     1633                        #
    16011634                        if line and line != '-- ' and line[-1] == ' ':
    16021635                                flowed = 1
     
    16071640                                line = line[:-1]
    16081641
    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                        #
    16101644                        if prevflowed and line != '-- ' and linequotelevel == quotelevel:
    16111645                                flowedlines[-1] += line
    1612                         # Otherwise, start a new line
     1646
     1647                        ## Otherwise, start a new line
     1648                        #
    16131649                        else:
    16141650                                flowedlines.append('>' * linequotelevel + line)
     
    17511787                                self.logger.debug('               Inline body part')
    17521788
    1753                                 # Try to decode, if fails then do not decode
     1789                                ## Try to decode, if fails then do not decode
    17541790                                #
    17551791                                body_text = part.get_payload(decode=1)
     
    17991835                                        self.print_unicode(s)
    18001836
    1801                                 ##
    1802                                 #  First try to use email header function to convert filename.
     1837                                ## First try to use email header function to convert filename.
    18031838                                #  If this fails the use the plan filename
     1839                                #
    18041840                                try:
    18051841                                        filename = self.email_to_unicode(part.get_filename())
     
    18321868                                filename = 'untitled-part'
    18331869
    1834                                 # Guess the extension from the content type, use non strict mode
    1835                                 # some additional non-standard but commonly used MIME types
    1836                                 # are also recognized
     1870                                ## 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
    18371873                                #
    18381874                                ext = mimetypes.guess_extension(part.get_content_type(), False)
     
    18531889                                filename = filename.replace(forbidden_char,'')
    18541890
    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
    18591894                        #
    18601895                        #if sys.version_info[0] > 2 or (sys.version_info[0] == 2 and sys.version_info[1] >= 3):
     
    18641899                        #               pass
    18651900
    1866                         # Make the filename unique for this ticket
     1901                        ## Make the filename unique for this ticket
     1902                        #
    18671903                        num = 0
    18681904                        unique_filename = filename
     
    18901926                        self.print_unicode(s)
    18911927
    1892                 # We have no valid ticket id
     1928                ## We have no valid ticket id
    18931929                #
    18941930                if not self.id:
     
    19121948               
    19131949                for part in message_parts:
    1914                         # Plain text part, append it
     1950
     1951                        ## Plain text part, append it
     1952                        #
    19151953                        if not isinstance(part, tuple):
    19161954                                body_text.extend(part.strip().splitlines())
     
    19451983                        self.parameters.mailto_cc = ''
    19461984
    1947                 # use urllib to escape the chars
     1985                ## use urllib to escape the chars
    19481986                #
    19491987                s = 'mailto:%s?Subject=%s&Cc=%s' %(
     
    19792017                        #sys.exit(0)
    19802018
    1981                         # create false {abs_}href properties, to trick Notify()
     2019                        ## create false {abs_}href properties, to trick Notify()
    19822020                        #
    19832021                        if not (self.VERSION in [0.11, 0.12]):
     
    20242062        config = trac_config.Configuration(file)
    20252063
    2026         # Use given project name else use defaults
     2064        ## Use given project name else use defaults
    20272065        #
    20282066        if name:
     
    20412079
    20422080        else:
    2043                 # use some trac internals to get the defaults
     2081                ## use some trac internals to get the defaults
    20442082                #
    20452083                tmp = config.parser.defaults()
     
    20832121
    20842122        elif parameters.log_type in ('winlog', 'eventlog', 'nteventlog'):
    2085                 # Requires win32 extensions
     2123                ## Requires win32 extensions
     2124                #
    20862125                log_handler = logging.handlers.NTEventLogHandler(logid, logtype='Application')
    20872126
     
    21262165
    21272166if __name__ == '__main__':
    2128         # Default config file
     2167        ## Default config file
    21292168        #
    21302169        configfile = '@email2trac_conf@'
     
    21682207        settings = ReadConfig(configfile, project_name)
    21692208
    2170         # The default prefix for ticket values in email2trac.conf
     2209        ## The default prefix for ticket values in email2trac.conf
    21712210        #
    21722211        settings.ticket_prefix = ticket_prefix
     
    21872226                settings['component'] = component
    21882227
    2189         # Determine major trac version used to be in email2trac.conf
     2228        ## Determine major trac version used to be in email2trac.conf
    21902229        # Quick hack for 0.12
    21912230        #
     
    22482287                        sys.exit(1)
    22492288
    2250                 # Must be set before environment is created
     2289                ## Must be set before environment is created
    22512290                #
    22522291                if settings.has_key('python_egg_cache'):
Note: See TracChangeset for help on using the changeset viewer.