Skip to content

Instantly share code, notes, and snippets.

@rmoff
Last active December 30, 2015 03:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rmoff/7771522 to your computer and use it in GitHub Desktop.
Save rmoff/7771522 to your computer and use it in GitHub Desktop.
Zendesk to confluence migration script. Hacked together and not pretty, but it does the job!
#!/usr/bin/python
#
# Migrate Zendesk topics/forums to Confluence pages/child pages
#
# Hacky as hell, I'm not proud!
#
# @rmoff 2013-12-03
#
# Known bugs:
# 1. Embedded images with non-safe characters in the filename (eg +) get encoded by Confluence at upload but the
# resulting page will still reference the non-encoded filename.
import requests, json, os
from xmlrpclib import Server,Binary
headers = {'Accept':'application/json'}
s=Server("https://yourconfluence.url/wiki/rpc/xmlrpc")
conf_token = s.confluence2.login("confluence_user","confluence_password")
## user vars
#
# This is a list of Zendesk forum IDs to migrate.
# Use zd_categories_and_forums.py to get the forum IDs
forums_to_migrate=[21415286]
#
# This is the key of the target Confluence space to move articles to
spacekey='INF'
# This page_id is the id of the root page of the space. You can get it by going to the page on Confluence and then
# clicking on Tools -> Page Information and picking the ID out of the uRL (https://yourconfluence.url/wiki/pages/viewinfo.action?pageId=xxxxxxxxxx)
# Or, run script con_list_spaces.py to see a list of all pages in all spaces and associated IDs
space_page_id='7964269'
## Forums
for forum_id in forums_to_migrate:
url='https://zendesk.url/api/v2/forums/%s.json' % (forum_id)
forumresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
forum=json.loads(forumresponse.content)['forum']
print 'Processing Zendesk forum %s (id: %s)' % (forum['name'],forum['id'])
try:
conf_parent_page=s.confluence2.getPage(conf_token,spacekey,forum['name'])
print '-> Found existing Confluence page: %s (%s)' % (conf_parent_page['title'],conf_parent_page['url'])
except:
conf_page_data = {}
conf_page_data['space'] = spacekey
conf_page_data['title'] = forum['name']
conf_page_data['content'] = '%s<br/><em>Zendesk import</em>' % (forum['description'])
conf_page_data['parentId'] = space_page_id
conf_parent_page = s.confluence2.storePage(conf_token, conf_page_data)
print '-> Created Confluence page: %s (%s)' % (conf_parent_page['title'],conf_parent_page['url'])
print 'Debug : s.confluence2.removePage(conf_token,\'%s\')' % (conf_parent_page['id'])
## Topics
url='https://zendesk.url/api/v2/forums/%s/topics.json' % (forum_id)
topicsr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
topics=json.loads(topicsr.content)['topics']
for topic in topics:
print '\tTopic %s (id: %s)' % (topic['title'].encode('utf-8'),topic['id'])
# Get the name of the source page's author
url='https://zendesk.url/api/v2/users/%s.json' % (topic['submitter_id'])
userr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
user=json.loads(userr.content)['user']
# Build the new page
# NB the API doesn't honour created, modified, creator, modifier
conf_page_data = {}
conf_page_data['content'] = '<em>Content imported from a Zendesk article written by <strong>%s</strong></em> at %s<hr/>%s' % (user['name'],topic['created_at'],topic['body'])
conf_page_data['space'] = spacekey
conf_page_data['title'] = topic['title']
conf_page_data['parentId'] = conf_parent_page['id']
try:
# Create page
conf_page = s.confluence2.storePage(conf_token, conf_page_data)
#print '\tConfluence page created, id: %s' % (conf_page['id'])
print '\tDebug : s.confluence2.removePage(conf_token,\'%s\')' % (conf_page['id'])
# Embedded images - these are a pain in the arse, since they are attachments that need fetching
# but are not modelling in the zendesk API as attachments
if (conf_page_data['content'].find('<img src="/attachments/token')>0):
for img in conf_page_data['content'].split('<img src="/attachments/token')[1:]:
raw_filename=img.split('"')[0]
filename=raw_filename.split('name=')[1]
path='https://zendesk.url/attachments/token%s' % (raw_filename)
image_file=requests.get(path, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']))
conf_attachment={}
conf_attachment['fileName']=filename
conf_attachment['contentType']=image_file.headers['content-type']
conf_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_attachment,Binary(image_file.content))
print '\t\tAdded Attachment: %s' % (filename)
conf_page['content']=conf_page['content'].replace('/attachments/token%s'%(raw_filename),'/wiki/download/attachments/%s/%s'%(conf_page['id'],filename))
pageupdateoptions={"versionComment":"Fix img src path for %s"%(filename),"minorEdit":True}
conf_page=s.confluence2.updatePage(conf_token,conf_page,pageupdateoptions)
# Attachments
for zd_attachment in topic['attachments']:
conf_attachment={}
conf_attachment['fileName']=zd_attachment['file_name']
conf_attachment['contentType']=zd_attachment['content_type']
attachment_data=requests.get(zd_attachment['content_url'], auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']))
conf_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_attachment,Binary(attachment_data.content))
print '\t\tAdded Attachment: %s' % (zd_attachment['file_name'])
# Comments
if (topic['comments_count']> 0):
url='https://zendesk.url/api/v2/topics/%s/comments.json' % (topic['id'])
commentsr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
comments=json.loads(commentsr.content)['topic_comments']
comments.reverse()
for zd_comment in comments:
url='https://zendesk.url/api/v2/users/%s.json' % (zd_comment['user_id'])
userr=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
user=json.loads(userr.content)['user']
conf_comment={}
conf_comment['content']='Comment from <strong>%s</strong> at %s:<br/>\n\n%s' % (user['name'],zd_comment['created_at'],zd_comment['body'])
conf_comment['pageId']=conf_page['id']
conf_comment=s.confluence2.addComment(conf_token,conf_comment)
for zd_attachment in zd_comment['attachments']:
# Comment Attachments
conf_comment_attachment={}
conf_comment_attachment['fileName']=zd_attachment['file_name']
conf_comment_attachment['contentType']=zd_attachment['content_type']
conf_comment_attachment['comment']=user['name']
attachment_data=requests.get(zd_attachment['content_url'], auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']))
conf_comment_attachment = s.confluence2.addAttachment(conf_token,conf_page['id'],conf_comment_attachment,Binary(attachment_data.content))
print '\t\tAdded Comments Attachment: %s' % (zd_attachment['file_name'])
conf_comment['content']+='<br/><em>Attachment : %s</em>' % (conf_comment_attachment['fileName'])
s.confluence2.editComment(conf_token,conf_comment)
# TODO
# Tags - good luck, the confluence API is not helpful when it comes to labels.
#for tag in topic['tags']:
# print 'tag: %s' % (tag)
print '\tMigrated to: %s\n' % (conf_page['url'])
except Exception,err:
print '** failed to migrate **'
print err
#!/usr/bin/python
#
# export your Zendesk username and password as ZD_USER and ZD_PASS
# environment variables or replace them inline below
#
# -----------
# Lists out all categories and forums
# from Zendesk with the respective IDs.
#
# @rmoff 2013-12-03
import requests, json, os
url='https://zendesk.url/api/v2/categories.json'
headers = {'Accept':'application/json'}
categoryresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
if (categoryresponse.headers['status']=='200 OK'):
categories=json.loads(categoryresponse.content)
for category in categories['categories']:
#print '%s' % (category['name'])
#print 'Category %s: %s (%s)' % (category['id'],category['name'],category['description'])
url='https://zendesk.url/api/v2/categories/%s/forums.json'% (category['id'])
forumresponse=requests.get(url, auth=(os.environ['ZD_USER'], os.environ['ZD_PASS']), headers=headers)
if (forumresponse.headers['status']=='200 OK'):
forums=json.loads(forumresponse.content)
for forum in forums['forums']:
print '%s,%s,%s,%s' % (category['id'],category['name'],forum['id'],forum['name'])
# print '\t%s: %s (%s)' % (forum['id'],forum['name'],forum['description'])
else:
print 'Request failed'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment