4

2

It's nice being able to create cases via email, but troublesome to have to go and sort them from the inbox. Is there a way to specify these attributes when sending the mail message?

flag

2 Answers

5

Here's a URLTrigger script that will do this.

If the Subject: line starts with //, the line will be parsed for these parameters:

  • -p project-name
  • -a area-name
  • -c category-name
  • -P assignee-name

The remainder of the line is the case title.

You can download the code here:

http://markharrison.net/fogbugz/fogbugz_EmailReceived.py.txt

Setup

  • create a Fogbugz inbox
  • install the URLTrigger plugin
  • Configure the URLTrigger like this: http://myweb/fogbugz_EmailReceived?case={CaseNumber}&subj={EmailSubject}
  • set the admin_login information as specified.

Code

#!/usr/bin/python
"""
This is a fogbugz URLTrigger script.

If the subject line begins with "//" these parameters will be extracted:
    -p project
    -a area
    -c category
    -t assigned-to

example:
    // -p myproject -a myarea -c mycategory -t myperson  this is the title

bugs:
    currently your project, area, etc can't have spaces.
    it should also accept an appreviated project, area, etc name.

configure it like this on the url-trigger plugin:
    http://myweb/fogbugz_EmailReceived?case={CaseNumber}&subj={EmailSubject} 
"""

# set this to your fogbugz url, admin login, and password
admin_login=['http://spolsky','mh','']

import sys
import cgi
import urllib
import optparse
import xml.dom.minidom

def P(s):
    sys.stderr.write('%s\n'%(s))
    sys.stderr.flush()

#-----------------------------------------------------------------------
# fog bugz xml helpers
#-----------------------------------------------------------------------

class BabyFogBugzParser:
    """this is stripped down from the parser in fogbugz.py"""
    def sendit(self,url):
        P('url=:%s:'%(url))
        f = urllib.urlopen(url)
        resp=f.read()
        return resp
    def pp(self,s):
        doc=xml.dom.minidom.parseString(s)
        return doc.toprettyxml(indent='  ')
    def raiseerror(self,s):
        xdom = xml.dom.minidom.parseString(s)
        xresponse=xdom.getElementsByTagName("response")[0]
        xerror=xresponse.getElementsByTagName("error")
        if xerror == []:
            return False
        else:
            xerror=xerror[0]
            code=xerror.getAttribute('code')
            errtext=self.getText(xerror.childNodes)
            sys.stderr.write('error %s %s\n'%(code,errtext))
            sys.exit(1)
    def generic_2level(self,s):
        xdom = xml.dom.minidom.parseString(s)
        xresponse=xdom.getElementsByTagName("response")[0]
        qq={}
        for nn in xresponse.childNodes:
            if nn.nodeType == nn.ELEMENT_NODE:
                qq[str(nn.nodeName)]=str(nn.childNodes[0].nodeValue.strip())
        return qq
    def api(self,s):    return self.generic_2level(s)
    def login2(self,s): return self.generic_2level(s)

#-----------------------------------------------------------------------
# cgi-code
#-----------------------------------------------------------------------

form = cgi.FieldStorage()
case=form.getvalue('case','0')
subj=form.getvalue('subj','--no-subj--')

# case='167'
# subj='// -p proj -a area  -c category -T who this has opts'

P('----------------------------------')
P('subj=%s'%(subj))

if subj.startswith('//'):

    a=subj[2:].split()

    import optparse
    p=optparse.OptionParser()
    p.add_option("-p")
    p.add_option("-a")
    p.add_option("-c")
    p.add_option("-P")

    (opts,args) = p.parse_args(a)
    title=' '.join(args)

    if opts.p or opts.a or opts.c or opts.P:
        if opts.p is None: opts.p=''
        if opts.a is None: opts.a=''
        if opts.c is None: opts.c=''
        if opts.P is None: opts.P=''
        P('opts=%s'%(opts))
        P('title=%s'%(title))

        fb=BabyFogBugzParser()

        #-------------------------------------------------- api info
        resp=fb.sendit('%s/api.xml'%(admin_login[0]))
        fb.raiseerror(resp)
        api_info=fb.api(resp)
        api_url=api_info['url']

        #-------------------------------------------------- login
        resp=fb.sendit('%s/%s%s'%(admin_login[0],api_url,urllib.urlencode({
            'cmd'      : 'logon',
            'email'    : admin_login[1],
            'password' : admin_login[2],
        })))
        fb.raiseerror(resp)
        login_info=fb.login2(resp)
        tok=login_info['token']

        #-------------------------------------------------- edit item
        resp=fb.sendit('%s/%s%s'%(admin_login[0],api_url,urllib.urlencode({
            'token'             : tok,
            'cmd'               : 'edit',
            'ixBug'             : case,
            'sProject'          : opts.p,
            'sArea'             : opts.a,
            'sCategory'         : opts.c,
            'sPersonAssignedTo' : opts.P,
            'sTitle'            : title,
        })))
        P(resp)
        fb.raiseerror(resp)

sys.stdout.write("\n<body>thanks</body>\n")
sys.stdout.flush()
link|flag
Excellent, Mark! It's awesome to have programmers as customers! – Rich Armstrong Nov 18 2009 at 22:48
Note that you can create a logon token and put that in your script instead of including your credentials there. tokens are tracked separately and are good until you call cmd=logoff with that token. – adambox Oct 26 2010 at 22:22
0

Does this work with Fogbugz on demand?

link|flag

Your Answer

Not the answer you're looking for? Browse other questions tagged or ask your own question.