1: import logging
1: from pyactiveresource.activeresource import ActiveResource
1: from Acquisition import aq_inner
1: from ZODB.POSException import ConflictError
1: from zope import schema
1: from zope.component import getMultiAdapter, getUtility
1: from zope.interface import implements, Interface, Invalid
1: from zope.app.pagetemplate.viewpagetemplatefile import ViewPageTemplateFile
1: from Products.CMFCore.utils import getToolByName
1: from Products.CMFPlone.utils import safe_unicode
1: from Products.statusmessages.interfaces import IStatusMessage
1: from z3c.form import form, field, button
1: from z3c.form.interfaces import IErrorViewSnippet
1: from plone.z3cform.layout import wrap_form
1: from plone.registry.interfaces import IRegistry
1: from plone.memoize.instance import memoize
1: from plone.memoize import ram
1: from collective.z3cform.datepicker.widget import DatePickerFieldWidget
1: from vnccollab.theme import messageFactory as _
1: from vnccollab.theme.portlets.zimbra_mail import logException
1: logger = logging.getLogger('vnccollab.theme.redmine_file_ticket')
# TODO: display error message above form
2: class IFileTicketForm(Interface):
1: project = schema.Choice(
1: title=_(u"Project"),
1: description=_(u"Pick project to post issue to."),
1: vocabulary='vnccollab.theme.vocabularies.ProjectsRedmineVocabulary',
1: required=True)
1: tracker = schema.Choice(
1: title=_(u"Tracker"),
1: description=u'',
1: vocabulary='vnccollab.theme.vocabularies.TrackersRedmineVocabulary',
1: required=True)
1: subject = schema.TextLine(
1: title=_(u"Subject"),
1: description=u'',
1: required=True)
1: description = schema.Text(
1: title=_(u"Description"),
1: description=u'',
1: required=False,
1: default=u'')
1: priority = schema.Choice(
1: title=_(u"Priority"),
1: description=u'',
1: vocabulary='vnccollab.theme.vocabularies.PrioritiesRedmineVocabulary',
1: required=True)
1: asignee = schema.Choice(
1: title=_(u"Asignee"),
1: description=u'',
1: vocabulary='vnccollab.theme.vocabularies.UsersRedmineVocabulary',
1: required=False)
1: start_date = schema.Date(
1: title=_(u"Start date"),
1: description=u'',
1: required=False)
1: due_date = schema.Date(
1: title=_(u"Due date"),
1: description=u'',
1: required=False)
1: estimated_time = schema.ASCIILine(
1: title=_(u"Estimated time (hours)"),
1: description=u'',
1: required=False)
2: class FileTicketForm(form.Form):
1: implements(IFileTicketForm)
1: ignoreContext = True
1: label = _(u"New Issue")
#description = u'This form will post new redmine issue.'
1: id = 'file_ticket_form'
1: prefix = 'redmine_task_form'
1: formErrorsMessage = _(u"There were some errors.")
1: successMessage = _(u"Ticket was created successfully.")
1: fields = field.Fields(IFileTicketForm)
1: fields['start_date'].widgetFactory = DatePickerFieldWidget
1: fields['due_date'].widgetFactory = DatePickerFieldWidget
1: @property
def action(self):
"""See interfaces.IInputForm"""
>>>>>> return self.context.absolute_url() + '/@@' + self.__name__
1: @button.buttonAndHandler(_(u"Create"), name='create')
def handleCreate(self, action):
"""Create redmine ticket using REST API."""
>>>>>> data, errors = self.extractData()
>>>>>> if errors:
>>>>>> self.status = self.formErrorsMessage
>>>>>> return
# check user redmine credentials and redmine url/field id
>>>>>> registry = getUtility(IRegistry)
>>>>>> url = registry.get('vnccollab.theme.redmine.url')
>>>>>> field_id = registry.get('vnccollab.theme.redmine.plone_uid_field_id')
>>>>>> username, password = self.getAuthCredentials()
>>>>>> if not username or not password or not url or not field_id:
>>>>>> if not username or not password:
>>>>>> msg = _(u"Please, set correct redmine username and password in "
"your profile form in order to create redmine issue.")
else:
>>>>>> msg = _(u"Please, set Redmine URL and ID settings in Control "
" Panel (Configuration Registry).")
# issue form level error
>>>>>> self.status = msg
>>>>>> error = getMultiAdapter((Invalid(u''), self.request, None,
>>>>>> None, self, self.context), IErrorViewSnippet)
>>>>>> error.update()
>>>>>> self.widgets.errors += (error,)
>>>>>> return
# finally trying to post new issue
>>>>>> Issue = type("Issue", (ActiveResource,), {'_site': url, '_user':
>>>>>> username, '_password': password})
>>>>>> try:
>>>>>> start_date = data.get('start_date') or ''
>>>>>> if start_date:
>>>>>> start_date = start_date.strftime('%Y-%m-%d')
>>>>>> due_date = data.get('due_date') or ''
>>>>>> if due_date:
>>>>>> due_date = due_date.strftime('%Y-%m-%d')
>>>>>> issue = Issue({
>>>>>> 'project_id': data['project'],
>>>>>> 'subject': data['subject'].encode('utf-8'),
>>>>>> 'tracker_id': data['tracker'],
>>>>>> 'description': (data.get('description') or u'').encode('utf-8'),
>>>>>> 'priority_id': data['priority'],
>>>>>> 'assigned_to_id': data.get('asignee') or '',
>>>>>> 'start_date': start_date,
>>>>>> 'due_date': due_date,
>>>>>> 'estimated_hours': data.get('estimated_time') or '',
>>>>>> 'custom_fields': [{'value': self.context.UID(),
>>>>>> 'id': '%d' % field_id}]
})
>>>>>> created = issue.save()
>>>>>> except Exception, e:
# issue form level error
>>>>>> logException(_(u"Error during creating redmine issue at %s" %
>>>>>> self.context.absolute_url()), context=self.context,
>>>>>> logger=logger)
>>>>>> plone_utils = getToolByName(self.context, 'plone_utils')
>>>>>> exception = plone_utils.exceptionString()
>>>>>> self.status = _(u"Unable create issue: ${exception}",
>>>>>> mapping={u'exception' : exception})
>>>>>> error = getMultiAdapter((Invalid(u''), self.request, None,
>>>>>> None, self, self.context), IErrorViewSnippet)
>>>>>> error.update()
>>>>>> self.widgets.errors += (error,)
>>>>>> return
else:
# check if issue was created successfully
>>>>>> if not created:
>>>>>> self.status = _(u"Issue wasn't created, please, check your "
"settings or contact site administrator if you are sure "
"your settings are set properly.")
>>>>>> error = getMultiAdapter((Invalid(u''), self.request, None,
>>>>>> None, self, self.context), IErrorViewSnippet)
>>>>>> error.update()
>>>>>> self.widgets.errors += (error,)
>>>>>> return
# add status message
>>>>>> self.status = self.successMessage
>>>>>> IStatusMessage(self.request).addStatusMessage(self.successMessage,
>>>>>> type='info')
# redirect to success page to gather number of emailed pages
>>>>>> came_from = self.request.get('HTTP_REFERER') or self.context.absolute_url()
>>>>>> return self.request.response.redirect(came_from)
1: @memoize
def getAuthCredentials(self):
"""Returns username and password for redmine user."""
# take username and password from authenticated user Zimbra creds
>>>>>> mtool = getToolByName(self.context, 'portal_membership')
>>>>>> member = mtool.getAuthenticatedMember()
>>>>>> username, password = member.getProperty('redmine_username', ''), \
>>>>>> member.getProperty('redmine_password', '')
# password could contain non-ascii chars, ensure it's properly encoded
>>>>>> return username, safe_unicode(password).encode('utf-8')
1: FileTicketFormView = wrap_form(FileTicketForm)