## ## Copyright (c) 2006 Jason Dillon ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## you may not use this file except in compliance with the License. ## You may obtain a copy of the License at ## ## http://www.apache.org/licenses/LICENSE-2.0 ## ## Unless required by applicable law or agreed to in writing, software ## distributed under the License is distributed on an "AS IS" BASIS, ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## See the License for the specific language governing permissions and ## limitations under the License. ## ## ## $Id: //guest/jason_dillon/p4spam/main/pylib/p4spam/htmlmessage.py#2 $ $Date: 2006/04/12 $ ## from p4spam import jiralinker from p4spam.message import MessageBuilder ## ## HACK: Need config to be fixed!!! ## from p4spam.config import * class HtmlMessageBuilder(MessageBuilder): def __init__(this, info): MessageBuilder.__init__(this, info) this.contentType = 'html' def escapeLine(this, line): ## ## HACK: Need a library to handle all this mess ## line = line.replace('&', '&') line = line.replace('>', '>') line = line.replace('<', '<') ## ## HACK: Trim the line here too... :-( ## l = len(line) if l > MAX_DIFF_LINE_LENGTH: this.log.warn("Diff line longer than %s; truncating %s remaining characters" % (MAX_DIFF_LINE_LENGTH, l - MAX_DIFF_LINE_LENGTH)) line = line[:MAX_DIFF_LINE_LENGTH] return line def generatePathAnchor(this, path): anchor = path # Condense the path... anchor = anchor.replace('/', '') anchor = anchor.replace('.', '') anchor = anchor.replace(' ', '') return anchor def getStyle(this): return DEFAULT_STYLE def writeDocument(this, buff): buff.writeln('') buff.writeln('') this.writeHeader(buff) this.writeBody(buff) buff.writeln('') def writeHeader(this, buff): buff.writeln('') buff.writeln("" % this.getStyle()) # Write user-header if USER_HEADER != None: buff.writeln(USER_HEADER) buff.writeln("%s" % this.getTitle()) buff.writeln('') def writeOverview(this, buff): # Change description header buff.writeln('
') buff.writeln("
Change
%s
" % this.info.getChange()) buff.writeln("
Author
%s
" % this.info.getAuthor()) buff.writeln("
Client
%s
" % this.info.getClient()) buff.writeln("
Date
%s
" % this.info.getDateTime()) buff.writeln('
') buff.writeln() # Change description/comment buff.writeln('

Description

') buff.writeln('
')
        
        lines = this.info.getComments()
        for line in lines:
            line = this.escapeLine(line)
            line = jiralinker.render(line)
            buff.writeln("%s
" % line) buff.writeln('
') buff.writeln() # Link to change description URL buff.writeln('

URL

') url = CHANGE_LIST_URL % this.info.getChange() buff.writeln("%s" % (url, url)) ## ## TODO: If this change fixes a job, include the job detail ## def writeFileActionTOC(this, buff): actions = ('edit', 'add', 'delete', 'branch', 'integrate') actionLables = { 'edit': 'Edited Files', 'add': 'Added Files', 'delete': 'Deleted Files', 'branch': 'Branched Files', 'integrate': 'Integrated Files' } # Init the map actionToFilesMap = {} for a in actions: actionToFilesMap[a] = [] # Fill the map for f in this.info.getAffectedFiles(): actionToFilesMap[f.action].append(f) this.log.debug("Action to files map: %s" % (actionToFilesMap)) for action in actions: files = actionToFilesMap[action] if len(files) == 0: # Skip section if there are no files this.log.debug("Skipping '%s' section; no files" % action) continue buff.writeln("

%s

" % actionLables[action]) buff.writeln('') buff.writeln() def writeBody(this, buff): buff.writeln('') # Write user-body-header if USER_BODY_HEADER != None: buff.writeln(USER_BODY_HEADER) buff.writeln('
') this.writeOverview(buff) buff.writeln() this.writeFileActionTOC(buff) buff.writeln('
') buff.writeln() this.writeDifferences(buff) buff.writeln() # Write user-body-footer if USER_BODY_FOOTER != None: buff.writeln(USER_BODY_FOOTER) buff.writeln('') def writeDifferences(this, buff): buff.writeln('
') buff.writeln('

Differences

') for diff in this.info.getDifferences(): # Write the anchor anchor = this.generatePathAnchor(diff.path) buff.writeln("" % anchor) buff.writeln('
') buff.writeln("

%s#%s (%s)

" % (diff.path, diff.rev, diff.filetype)) buff.writeln('
')
            
            i = 0
            maxlines = MAX_DIFF_LINES # TODO: Get from config
            style = None
            lastStyle = None
            
            for line in diff.lines:
                line = this.escapeLine(line)
                
                if line.startswith('@@'):
                    style = 'lines'
                elif line.startswith('+'):
                    style = 'add'
                elif line.startswith('-'):
                    style = 'rem'
                else:
                    style = 'cx'
                
                # Only write style spans if the sytle was changed
                if style != lastStyle:
                    if lastStyle != None:
                        buff.write('')
                    buff.write('' % style)
                
                buff.write(line)
                
                lastStyle = style
                
                i = i + 1
                if i >= maxlines:
                    break
            
            # Need to close up the last span
            buff.writeln('')
            
            buff.writeln('
') # If we truncated, then spit out a message totallines = len(diff.lines) if i >= maxlines and totallines != maxlines: buff.write('') buff.write("Truncated at %s lines; %s more skipped" % (maxlines, totallines - maxlines)) buff.writeln('') buff.writeln('
') buff.writeln('
')