Test coverage for vnccollab.theme.zimbrautil
1: import urlparse
1: from pyzimbra.z.client import ZimbraClient
1: from pyzimbra.soap import SoapException
1: from zope.interface import implements, Interface
1: from Products.CMFPlone.utils import safe_unicode as su
1: from plone.memoize.instance import memoize
2: class IZimbraUtil(Interface):
1: """Interface for Zimbra Utility"""
1: def get_client(url_or_context, username='', password=''):
"""Returns an authenticated zimbra client.
If no username is given, it's used the current user information.
"""
1: VNC_ZIMBRA_URL = 'https://zcs.vnc.biz'
2: class ZimbraUtil:
1: """Zimbra Utility."""
1: implements(IZimbraUtil)
1: @memoize
1: def get_client(self, url=VNC_ZIMBRA_URL, username='', password=''):
'''Returns a ZimbraUserClient.
Args:
@url - URL of the zimbra server, withou '/service/soap'
@username - Login of the user. If not present, the client
won't be authenticated.
@password - Password of the user.
'''
1: url = url + '/service/soap'
1: client = ZimbraUtilClient(url, username, password)
1: return client
1: def refreshAuthToken(func, *args, **kw):
"""Catches SoapException from passed function call and if the error is
identified as Token Expiration error - authenticate client and then repeat
the call.
"""
9: def decorated(*args, **kw):
6: try:
6: result = func(*args, **kw)
6: except SoapException, e:
>>>>>> msg = unicode(e)
>>>>>> if u'auth credentials have expired' in msg or \
>>>>>> u'AUTH_EXPIRED' in msg:
# authenticate, args[0] is func's method object
>>>>>> args[0].authenticate()
>>>>>> return func(*args, **kw)
else:
>>>>>> raise e
else:
>>>>>> return result
9: return decorated
2: class ZimbraUtilClient:
'''
Zimbra client support class.
It returns ZimbraClient results in a way more digerible by plone.
1: '''
1: def __init__(self, url, username='', password=''):
1: self.url = url
1: p = urlparse.urlparse(self.url)
1: self.server_url = '{0}://{1}'.format(p.scheme, p.netloc)
1: self.client = ZimbraClient(url)
1: self.username = username
1: self.password = password
1: if username:
>>>>>> self.authenticate()
1: def authenticate(self):
>>>>>> self.client.authenticate(self.username, self.password)
1: @refreshAuthToken
1: def get_raw_emails(self, folder=None, searchable_text='',
1: offset=0, limit=10,
1: recip='1', sortBy='dateDesc', types='conversation'):
"""Returns list of email conversations.
Args:
@folder - if given, return list of emails from inside this folder
@serchable_text - Text the email should have to be shown.
@offset - if given, return list of emails starting from start
@limit - return 'limit' number of emails
@recip - whether to return 'to' email adress instead of 'from' for
sent messages and conversations
@sort_by - sort result set by given field
"""
2: query = {
2: 'types': types,
2: 'limit': limit,
2: 'offset': offset,
2: 'recip': recip,
2: 'sortBy': sortBy,
}
2: if folder:
>>>>>> query['query'] = 'in:%s' % folder
2: if searchable_text:
>>>>>> query['query'] = searchable_text
2: result = self.search(query)
>>>>>> return result
1: @refreshAuthToken
1: def get_emails(self, folder=None, searchable_text='',
1: offset=0, limit=10,
1: recip='1', sortBy='dateDesc', types='conversation'):
2: result = self.get_raw_emails(folder=folder,
2: searchable_text=searchable_text, offset=offset, limit=limit,
2: recip=recip, sortBy=sortBy, types=types)
>>>>>> return [self._dict_from_mail(x) for x in result]
1: @refreshAuthToken
1: def get_address_book(self, offset=0, limit=100):
'''Returns the address book of the user.'''
>>>>>> query = {
>>>>>> 'types': 'contact',
>>>>>> 'sortBy': 'nameAsc',
>>>>>> 'offset': offset,
>>>>>> 'limit': limit,
>>>>>> 'query': 'in:contacts'
}
>>>>>> result = self.search(query)
>>>>>> return result
1: @refreshAuthToken
def search(self, query):
'''Returns the result of making the given query.'''
2: result = self.client.invoke('urn:zimbraMail', 'SearchRequest', query)
# if we have activated returnAllAttrs, result is a tuple.
# We're interested here only in its first element
>>>>>> if type(result) == tuple:
>>>>>> result = result[0]
# Get the result out of the list
>>>>>> if not isinstance(result, list):
>>>>>> result = [result]
>>>>>> return result
1: @refreshAuthToken
def get_email(self, eid):
"""Returns email by given id.
It also marks conversation as read.
"""
>>>>>> result = self.client.invoke('urn:zimbraMail', 'GetConvRequest',
>>>>>> {'c': {'id': eid, 'fetch': 'all', 'html': '1'}})[0].m
# TODO: make zimbra conversation as read
>>>>>> if not isinstance(result, list):
>>>>>> result = [result]
>>>>>> return result
1: @refreshAuthToken
def get_email_thread(self, eid):
"""Returns conversation emails by given id.
It also marks conversation as read.
"""
>>>>>> result = self.get_email(eid)
>>>>>> thread = []
>>>>>> for item in result:
>>>>>> from_ = [su(e._getAttr('p')) for e in item.e
>>>>>> if e._getAttr('t') == 'f']
>>>>>> from_ = from_[0] if len(from_) else ''
>>>>>> to = u', '.join([su(e._getAttr('d')) for e in item.e
>>>>>> if e._getAttr('t') == 't'])
>>>>>> thread.append({
>>>>>> 'from': from_,
>>>>>> 'to': to,
>>>>>> 'body': item,
>>>>>> 'id': item._getAttr('_orig_id'),
>>>>>> 'date': item._getAttr('d'),
})
>>>>>> return thread
1: def _dict_from_mail(self, mail):
"""Converts a zimbra mail into a dictionary"""
>>>>>> people = getattr(mail, 'e', [])
>>>>>> if not people:
>>>>>> people = []
>>>>>> elif not isinstance(people, list):
>>>>>> people = [people]
# prepare subject
>>>>>> subject = getattr(mail, 'su', '') or 'No Subject'
>>>>>> dct = {
>>>>>> 'subject': su(subject),
>>>>>> 'body': u'%s (%s) - %s - %s' % (u', '.join([p._getAttr('d')
>>>>>> for p in people]), mail._getAttr('n'), su(mail.su),
>>>>>> su(getattr(mail, 'fr', ''))),
>>>>>> 'unread': u'u' in (mail._getAttr('f') or ''),
>>>>>> 'id': mail._getAttr('_orig_id'),
>>>>>> 'date': mail._getAttr('d'),
>>>>>> 'cid': mail._getAttr('cid'),
}
>>>>>> return dct
1: @refreshAuthToken
def create_task(self, dct):
"""Creates a task, given its description as a dictionary"""
>>>>>> task = dict(**dct)
>>>>>> for k, v in task.items():
>>>>>> if v is None:
>>>>>> task[k] = u''
>>>>>> task['startDate'] = self._stringFromDate(task['startDate'])
>>>>>> task['endDate'] = self._stringFromDate(task['endDate'])
>>>>>> query = {
>>>>>> 'm': {
#'l' : '24486', # List id. It could be ommited
>>>>>> 'inv': {
>>>>>> 'comp': {
>>>>>> 'name': task.get('subject', ''),
>>>>>> 'loc': task.get('location', ''),
>>>>>> 'percentComplete': task.get('percentComplete', '0'),
>>>>>> 'status': task.get('status', 'NEED'), # Not started
>>>>>> 'priority': task.get('priority', '5'), # Normal
>>>>>> 'or': {'a': task['author'], # Required
>>>>>> 'd': task.get('authorName', ''),
},
},
},
>>>>>> 'mp': {
>>>>>> 'ct': 'multipart/alternative',
>>>>>> 'mp': {
>>>>>> 'ct': 'text/plain',
>>>>>> 'content': task.get('content', '')}},
}
}
>>>>>> if task['content']:
>>>>>> query['m']['mp'] = {'ct': 'text/plain',
>>>>>> 'content': task['content']}
>>>>>> if task['startDate']:
>>>>>> query['m']['inv']['comp']['s'] = {'d': task['startDate']}
>>>>>> if task['endDate']:
>>>>>> query['m']['inv']['comp']['e'] = {'d': task['endDate']}
>>>>>> response, _ = self.client.invoke('urn:zimbraMail',
>>>>>> 'CreateTaskRequest', query)
>>>>>> response = self.get_message(response._getAttr(u'invId'))
>>>>>> task = self._taskFromGetMsgResponse(response)
>>>>>> return task
1: @refreshAuthToken
def get_message(self, id):
'''Returns a message (mail, task, etc), given its id.'''
>>>>>> query = {"_jsns": "urn:zimbraMail",
>>>>>> "m": {'id': id, 'html': 1, 'needExp': 1, 'max': 250000}}
>>>>>> response, attrs = self.client.invoke('urn:zimbraMail',
>>>>>> 'GetMsgRequest', query)
>>>>>> return response
1: @refreshAuthToken
def get_all_tasks(self):
'''Returns all the zimbra tasks of the authenticated user.'''
>>>>>> query = {'query': 'in:"tasks"', 'types': 'task', }
>>>>>> response, _ = self.client.invoke('urn:zimbraMail',
>>>>>> 'SearchRequest', query)
>>>>>> if type(response) != list:
>>>>>> response = [response]
>>>>>> return [self._taskFromSearchResponse(x) for x in response]
1: def _taskFromGetMsgResponse(self, response):
'''Returns a ZimbraTask given a zimbra CreateTaskResponse.'''
>>>>>> id = response._getAttr('_orig_id')
>>>>>> title = response.inv.comp._getAttr('name')
>>>>>> body = getattr(response.inv.comp, 'fr', u'')
>>>>>> return ZimbraTask(id, title, self.server_url, body)
1: def _taskFromSearchResponse(self, response):
'''Returns a ZImbraTask given a zimbra SearchResponse.'''
>>>>>> id = response._getAttr('invId')
>>>>>> title = response._getAttr('name')
>>>>>> body = getattr(response, 'fr', u'')
>>>>>> return ZimbraTask(id, title, self.server_url, body)
1: def _stringFromDate(self, date=None):
>>>>>> if not date:
>>>>>> return ''
>>>>>> return date.strftime('%Y%m%d')
2: class ZimbraTask:
1: def __init__(self, id, title, server_url, body):
>>>>>> self.id = id
>>>>>> self.title = title
>>>>>> self.server_url = server_url
>>>>>> self.url = ('{0}/zimbra/h/search?su=1&si=0&so=0&sc=4&st=task'
>>>>>> + '&id={1}&action=edittask').format(self.server_url, id)
>>>>>> self.body = body
1: def __eq__(self, other):
>>>>>> return (self.id == other.id) and (self.server_url == other.server_url)
1: def __repr__(self):
>>>>>> if len(self.body) < 10:
>>>>>> body = repr(self.body)
else:
>>>>>> body = repr(self.body[:10] + '...')
>>>>>> return 'ZimbraTask({0}, {1}, {2})'.format(repr(self.id),
>>>>>> repr(self.title), body)
1: zimbraUtilInstance = ZimbraUtil()