2017-03-28 14:27:39 +02:00
|
|
|
import os.path
|
2017-04-05 07:21:42 +02:00
|
|
|
|
2017-04-05 07:29:27 +02:00
|
|
|
from typing import Dict, List, Optional, TypeVar, Any, Text
|
2016-07-25 22:12:12 +02:00
|
|
|
from django.conf import settings
|
|
|
|
from django.conf.urls import url
|
2016-09-11 23:57:44 +02:00
|
|
|
from django.core.urlresolvers import LocaleRegexProvider
|
2016-10-27 14:52:33 +02:00
|
|
|
from django.utils.module_loading import import_string
|
2017-04-05 07:29:27 +02:00
|
|
|
from django.utils.safestring import mark_safe
|
|
|
|
from django.template import loader
|
|
|
|
|
2017-04-05 07:34:02 +02:00
|
|
|
from zerver.templatetags.app_filters import render_markdown_path
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
|
2016-08-04 21:13:25 +02:00
|
|
|
"""This module declares all of the (documented) integrations available
|
|
|
|
in the Zulip server. The Integration class is used as part of
|
|
|
|
generating the documentation on the /integrations page, while the
|
|
|
|
WebhookIntegration class is also used to generate the URLs in
|
|
|
|
`zproject/urls.py` for webhook integrations.
|
|
|
|
|
|
|
|
To add a new non-webhook integration, add code to the INTEGRATIONS
|
|
|
|
dictionary below.
|
|
|
|
|
|
|
|
To add a new webhook integration, declare a WebhookIntegration in the
|
|
|
|
WEBHOOK_INTEGRATIONS list below (it will be automatically added to
|
|
|
|
INTEGRATIONS).
|
|
|
|
|
|
|
|
Over time, we expect this registry to grow additional convenience
|
|
|
|
features for writing and configuring integrations efficiently.
|
|
|
|
"""
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
class Integration(object):
|
2017-03-28 14:27:39 +02:00
|
|
|
DEFAULT_LOGO_STATIC_PATH_PNG = 'static/images/integrations/logos/{name}.png'
|
|
|
|
DEFAULT_LOGO_STATIC_PATH_SVG = 'static/images/integrations/logos/{name}.svg'
|
2016-07-25 22:12:12 +02:00
|
|
|
|
2017-06-12 23:25:30 +02:00
|
|
|
def __init__(self, name, client_name, logo=None, secondary_line_text=None,
|
|
|
|
display_name=None, doc=None, stream_name=None):
|
|
|
|
# type: (str, str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None
|
2016-07-25 22:12:12 +02:00
|
|
|
self.name = name
|
|
|
|
self.client_name = client_name
|
|
|
|
self.secondary_line_text = secondary_line_text
|
2016-11-26 00:25:05 +01:00
|
|
|
self.doc = doc
|
2017-04-05 07:21:42 +02:00
|
|
|
self.doc_context = None # type: Dict[Any, Any]
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
if logo is None:
|
2017-03-28 14:27:39 +02:00
|
|
|
if os.path.isfile(self.DEFAULT_LOGO_STATIC_PATH_SVG.format(name=name)):
|
|
|
|
logo = self.DEFAULT_LOGO_STATIC_PATH_SVG.format(name=name)
|
|
|
|
else:
|
|
|
|
logo = self.DEFAULT_LOGO_STATIC_PATH_PNG.format(name=name)
|
2016-07-25 22:12:12 +02:00
|
|
|
self.logo = logo
|
|
|
|
|
|
|
|
if display_name is None:
|
|
|
|
display_name = name.title()
|
|
|
|
self.display_name = display_name
|
|
|
|
|
2017-06-12 23:25:30 +02:00
|
|
|
if stream_name is None:
|
|
|
|
stream_name = self.name
|
|
|
|
self.stream_name = stream_name
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
def is_enabled(self):
|
|
|
|
# type: () -> bool
|
|
|
|
return True
|
|
|
|
|
2017-04-05 07:21:42 +02:00
|
|
|
def add_doc_context(self, context):
|
|
|
|
# type: (Dict[Any, Any]) -> None
|
|
|
|
self.doc_context = context
|
|
|
|
|
2017-05-10 20:17:53 +02:00
|
|
|
@property
|
|
|
|
def help_content(self):
|
|
|
|
# type: () -> Text
|
|
|
|
doc_context = self.doc_context or {}
|
|
|
|
|
|
|
|
if self.doc.endswith('.md'):
|
|
|
|
return render_markdown_path(self.doc, doc_context)
|
|
|
|
else:
|
|
|
|
template = loader.get_template(self.doc)
|
|
|
|
return mark_safe(template.render(doc_context))
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
class EmailIntegration(Integration):
|
|
|
|
def is_enabled(self):
|
2016-09-11 23:57:44 +02:00
|
|
|
# type: () -> bool
|
2016-07-25 22:12:12 +02:00
|
|
|
return settings.EMAIL_GATEWAY_BOT != ""
|
|
|
|
|
|
|
|
class WebhookIntegration(Integration):
|
2016-11-23 20:15:23 +01:00
|
|
|
DEFAULT_FUNCTION_PATH = 'zerver.webhooks.{name}.view.api_{name}_webhook'
|
2016-07-25 22:12:12 +02:00
|
|
|
DEFAULT_URL = 'api/v1/external/{name}'
|
|
|
|
DEFAULT_CLIENT_NAME = 'Zulip{name}Webhook'
|
2017-04-05 07:34:02 +02:00
|
|
|
DEFAULT_DOC_PATH = '{name}/doc.{ext}'
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
def __init__(self, name, client_name=None, logo=None, secondary_line_text=None,
|
2017-06-12 23:25:30 +02:00
|
|
|
function=None, url=None, display_name=None, doc=None, stream_name=None):
|
|
|
|
# type: (str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None
|
2016-07-25 22:12:12 +02:00
|
|
|
if client_name is None:
|
|
|
|
client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title())
|
2017-06-13 16:08:07 +02:00
|
|
|
super(WebhookIntegration, self).__init__(
|
|
|
|
name,
|
|
|
|
client_name,
|
|
|
|
logo=logo,
|
|
|
|
secondary_line_text=secondary_line_text,
|
|
|
|
display_name=display_name,
|
|
|
|
stream_name=stream_name
|
|
|
|
)
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
if function is None:
|
|
|
|
function = self.DEFAULT_FUNCTION_PATH.format(name=name)
|
2016-10-27 14:52:33 +02:00
|
|
|
|
|
|
|
if isinstance(function, str):
|
|
|
|
function = import_string(function)
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
self.function = function
|
|
|
|
|
|
|
|
if url is None:
|
|
|
|
url = self.DEFAULT_URL.format(name=name)
|
|
|
|
self.url = url
|
|
|
|
|
2016-11-26 00:25:05 +01:00
|
|
|
if doc is None:
|
2017-06-15 21:32:53 +02:00
|
|
|
doc = self.DEFAULT_DOC_PATH.format(name=name, ext='md')
|
2017-04-05 07:34:02 +02:00
|
|
|
|
2016-11-26 00:25:05 +01:00
|
|
|
self.doc = doc
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
@property
|
|
|
|
def url_object(self):
|
2016-09-11 23:57:44 +02:00
|
|
|
# type: () -> LocaleRegexProvider
|
2016-07-25 22:12:12 +02:00
|
|
|
return url(self.url, self.function)
|
|
|
|
|
2016-11-23 18:58:59 +01:00
|
|
|
class HubotLozenge(Integration):
|
|
|
|
GIT_URL_TEMPLATE = "https://github.com/hubot-scripts/hubot-{}"
|
|
|
|
|
|
|
|
def __init__(self, name, display_name=None, logo=None, logo_alt=None, git_url=None):
|
|
|
|
# type: (str, Optional[str], Optional[str], Optional[str], Optional[str]) -> None
|
|
|
|
if logo_alt is None:
|
|
|
|
logo_alt = "{} logo".format(name.title())
|
|
|
|
self.logo_alt = logo_alt
|
|
|
|
|
|
|
|
if git_url is None:
|
|
|
|
git_url = self.GIT_URL_TEMPLATE.format(name)
|
|
|
|
self.git_url = git_url
|
2017-06-13 16:08:07 +02:00
|
|
|
super(HubotLozenge, self).__init__(
|
|
|
|
name, name,
|
|
|
|
logo=logo, display_name=display_name
|
|
|
|
)
|
2016-11-23 18:58:59 +01:00
|
|
|
|
2016-11-14 21:06:39 +01:00
|
|
|
class GithubIntegration(WebhookIntegration):
|
|
|
|
"""
|
|
|
|
We need this class to don't creating url object for git integrations.
|
|
|
|
We want to have one generic url with dispatch function for github service and github webhook.
|
|
|
|
"""
|
2017-06-04 23:38:10 +02:00
|
|
|
def __init__(self, name, client_name=None, logo=None, secondary_line_text=None,
|
2017-06-12 23:25:30 +02:00
|
|
|
function=None, url=None, display_name=None, doc=None, stream_name=None):
|
|
|
|
# type: (str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None
|
2017-06-04 23:38:10 +02:00
|
|
|
url = self.DEFAULT_URL.format(name='github')
|
|
|
|
|
|
|
|
super(GithubIntegration, self).__init__(
|
2017-06-13 16:08:07 +02:00
|
|
|
name,
|
|
|
|
client_name=client_name,
|
|
|
|
logo=logo,
|
|
|
|
secondary_line_text=secondary_line_text,
|
|
|
|
function=function,
|
|
|
|
url=url,
|
|
|
|
display_name=display_name,
|
|
|
|
doc=doc,
|
|
|
|
stream_name=stream_name
|
|
|
|
)
|
2017-06-04 23:38:10 +02:00
|
|
|
|
2016-11-14 21:06:39 +01:00
|
|
|
@property
|
|
|
|
def url_object(self):
|
|
|
|
# type: () -> None
|
|
|
|
return
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
WEBHOOK_INTEGRATIONS = [
|
|
|
|
WebhookIntegration('airbrake'),
|
2016-12-13 15:50:24 +01:00
|
|
|
WebhookIntegration('appfollow', display_name='AppFollow'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('beanstalk'),
|
2017-03-14 20:04:11 +01:00
|
|
|
WebhookIntegration('basecamp'),
|
2017-06-12 23:25:30 +02:00
|
|
|
WebhookIntegration(
|
|
|
|
'bitbucket2',
|
|
|
|
logo='static/images/integrations/logos/bitbucket.svg',
|
|
|
|
display_name='Bitbucket',
|
|
|
|
stream_name='bitbucket'
|
|
|
|
),
|
2017-06-12 23:48:16 +02:00
|
|
|
WebhookIntegration(
|
|
|
|
'bitbucket',
|
|
|
|
display_name='Bitbucket',
|
|
|
|
secondary_line_text='(Enterprise)',
|
|
|
|
stream_name='commits'
|
|
|
|
),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('circleci', display_name='CircleCI'),
|
|
|
|
WebhookIntegration('codeship'),
|
|
|
|
WebhookIntegration('crashlytics'),
|
2016-12-24 15:50:28 +01:00
|
|
|
WebhookIntegration('delighted', display_name='Delighted'),
|
2017-06-13 00:34:47 +02:00
|
|
|
WebhookIntegration(
|
|
|
|
'deskdotcom',
|
|
|
|
logo='static/images/integrations/logos/deskcom.png',
|
|
|
|
display_name='Desk.com',
|
|
|
|
stream_name='desk'
|
|
|
|
),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('freshdesk'),
|
2016-11-14 21:06:39 +01:00
|
|
|
GithubIntegration(
|
2016-10-25 14:50:42 +02:00
|
|
|
'github',
|
2016-11-23 20:15:23 +01:00
|
|
|
function='zerver.webhooks.github.view.api_github_landing',
|
2016-10-25 14:50:42 +02:00
|
|
|
display_name='GitHub',
|
2017-06-12 23:52:22 +02:00
|
|
|
secondary_line_text='(deprecated)',
|
|
|
|
stream_name='commits'
|
2016-10-25 14:50:42 +02:00
|
|
|
),
|
2016-11-14 21:06:39 +01:00
|
|
|
GithubIntegration(
|
2016-10-25 14:50:42 +02:00
|
|
|
'github_webhook',
|
|
|
|
display_name='GitHub',
|
2017-03-28 14:27:39 +02:00
|
|
|
logo='static/images/integrations/logos/github.svg',
|
2016-10-25 14:50:42 +02:00
|
|
|
secondary_line_text='(webhook)',
|
2017-06-12 23:54:45 +02:00
|
|
|
function='zerver.webhooks.github_webhook.view.api_github_webhook',
|
|
|
|
stream_name='github'
|
2016-10-25 14:50:42 +02:00
|
|
|
),
|
2016-08-18 20:57:50 +02:00
|
|
|
WebhookIntegration('gitlab', display_name='GitLab'),
|
2017-01-28 18:42:59 +01:00
|
|
|
WebhookIntegration('gogs'),
|
2016-12-18 02:08:22 +01:00
|
|
|
WebhookIntegration('gosquared', display_name='GoSquared'),
|
2016-12-24 15:50:46 +01:00
|
|
|
WebhookIntegration('greenhouse', display_name='Greenhouse'),
|
2016-12-22 16:11:51 +01:00
|
|
|
WebhookIntegration('hellosign', display_name='HelloSign'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('helloworld', display_name='Hello World'),
|
2016-12-02 12:06:20 +01:00
|
|
|
WebhookIntegration('heroku', display_name='Heroku'),
|
2017-02-08 18:43:16 +01:00
|
|
|
WebhookIntegration('homeassistant', display_name='Home Assistant'),
|
2016-11-23 20:15:23 +01:00
|
|
|
WebhookIntegration('ifttt', function='zerver.webhooks.ifttt.view.api_iftt_app_webhook', display_name='IFTTT'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('jira', secondary_line_text='(hosted or v5.2+)', display_name='JIRA'),
|
2016-08-31 21:23:20 +02:00
|
|
|
WebhookIntegration('librato'),
|
2016-12-04 02:50:29 +01:00
|
|
|
WebhookIntegration('mention', display_name='Mention'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('newrelic', display_name='New Relic'),
|
|
|
|
WebhookIntegration('pagerduty'),
|
2016-12-06 00:41:14 +01:00
|
|
|
WebhookIntegration('papertrail'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('pingdom'),
|
|
|
|
WebhookIntegration('pivotal', display_name='Pivotal Tracker'),
|
2017-06-13 05:50:11 +02:00
|
|
|
WebhookIntegration('semaphore', stream_name='builds'),
|
2016-09-06 21:17:18 +02:00
|
|
|
WebhookIntegration('sentry'),
|
2017-01-30 21:18:41 +01:00
|
|
|
WebhookIntegration('slack'),
|
2016-12-31 18:06:29 +01:00
|
|
|
WebhookIntegration('solano', display_name='Solano Labs'),
|
2017-01-26 00:37:23 +01:00
|
|
|
WebhookIntegration('splunk', display_name='Splunk'),
|
2016-12-03 18:51:33 +01:00
|
|
|
WebhookIntegration('stripe', display_name='Stripe'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('taiga'),
|
|
|
|
WebhookIntegration('teamcity'),
|
|
|
|
WebhookIntegration('transifex'),
|
|
|
|
WebhookIntegration('travis', display_name='Travis CI'),
|
|
|
|
WebhookIntegration('trello', secondary_line_text='(webhook)'),
|
|
|
|
WebhookIntegration('updown'),
|
|
|
|
WebhookIntegration(
|
|
|
|
'yo',
|
2016-11-23 20:15:23 +01:00
|
|
|
function='zerver.webhooks.yo.view.api_yo_app_webhook',
|
2016-07-25 22:12:12 +02:00
|
|
|
display_name='Yo App'
|
|
|
|
),
|
2016-11-29 22:48:22 +01:00
|
|
|
WebhookIntegration('wordpress', display_name='WordPress'),
|
2017-01-17 19:41:29 +01:00
|
|
|
WebhookIntegration('zapier'),
|
2016-07-25 22:12:12 +02:00
|
|
|
WebhookIntegration('zendesk')
|
|
|
|
] # type: List[WebhookIntegration]
|
|
|
|
|
|
|
|
INTEGRATIONS = {
|
2017-06-20 03:10:42 +02:00
|
|
|
'asana': Integration('asana', 'asana', doc='zerver/integrations/asana.md'),
|
2017-06-05 17:15:20 +02:00
|
|
|
'capistrano': Integration('capistrano', 'capistrano', display_name='Capistrano', doc='zerver/integrations/capistrano.md'),
|
2017-06-14 01:12:01 +02:00
|
|
|
'codebase': Integration('codebase', 'codebase', doc='zerver/integrations/codebase.md'),
|
2017-06-20 03:18:20 +02:00
|
|
|
'email': EmailIntegration('email', 'email', doc='zerver/integrations/email.md'),
|
2017-06-05 19:03:01 +02:00
|
|
|
'git': Integration('git', 'git', doc='zerver/integrations/git.md'),
|
2017-04-05 07:18:57 +02:00
|
|
|
'google-calendar': Integration(
|
|
|
|
'google-calendar',
|
|
|
|
'google-calendar',
|
|
|
|
display_name='Google Calendar',
|
2017-06-05 22:10:18 +02:00
|
|
|
doc='zerver/integrations/google-calendar.md'
|
2017-04-05 07:18:57 +02:00
|
|
|
),
|
2017-06-21 05:56:25 +02:00
|
|
|
'hubot': Integration('hubot', 'hubot', doc='zerver/integrations/hubot.md'),
|
2017-04-05 07:25:43 +02:00
|
|
|
'jenkins': Integration(
|
|
|
|
'jenkins',
|
|
|
|
'jenkins',
|
|
|
|
secondary_line_text='(or Hudson)',
|
|
|
|
doc='zerver/integrations/jenkins.html'
|
|
|
|
),
|
2016-07-25 22:12:12 +02:00
|
|
|
'jira-plugin': Integration(
|
|
|
|
'jira-plugin',
|
|
|
|
'jira-plugin',
|
2017-03-28 14:27:39 +02:00
|
|
|
logo='static/images/integrations/logos/jira.svg',
|
2016-07-25 22:12:12 +02:00
|
|
|
secondary_line_text='(locally installed)',
|
2017-04-05 07:30:40 +02:00
|
|
|
display_name='JIRA',
|
2017-06-20 03:33:39 +02:00
|
|
|
doc='zerver/integrations/jira-plugin.md',
|
|
|
|
stream_name='jira',
|
2016-07-25 22:12:12 +02:00
|
|
|
),
|
2017-04-05 07:35:34 +02:00
|
|
|
'mercurial': Integration(
|
|
|
|
'mercurial',
|
|
|
|
'mercurial',
|
|
|
|
display_name='Mercurial (hg)',
|
2017-06-05 18:54:52 +02:00
|
|
|
doc='zerver/integrations/mercurial.md'
|
2017-04-05 07:35:34 +02:00
|
|
|
),
|
2017-06-05 19:16:22 +02:00
|
|
|
'nagios': Integration('nagios', 'nagios', doc='zerver/integrations/nagios.md'),
|
2017-04-05 07:46:08 +02:00
|
|
|
'openshift': Integration(
|
|
|
|
'openshift',
|
|
|
|
'openshift',
|
|
|
|
display_name='OpenShift',
|
2017-06-21 20:48:34 +02:00
|
|
|
doc='zerver/integrations/openshift.md',
|
|
|
|
stream_name='deployments',
|
2017-04-05 07:46:08 +02:00
|
|
|
),
|
2017-04-05 07:47:59 +02:00
|
|
|
'perforce': Integration('perforce', 'perforce', doc='zerver/integrations/perforce.html'),
|
2017-06-20 04:57:01 +02:00
|
|
|
'phabricator': Integration('phabricator', 'phabricator', doc='zerver/integrations/phabricator.md'),
|
2017-06-20 05:00:52 +02:00
|
|
|
'puppet': Integration('puppet', 'puppet', doc='zerver/integrations/puppet.md'),
|
2017-04-05 07:53:27 +02:00
|
|
|
'redmine': Integration('redmine', 'redmine', doc='zerver/integrations/redmine.html'),
|
2017-04-05 07:55:33 +02:00
|
|
|
'rss': Integration('rss', 'rss', display_name='RSS', doc='zerver/integrations/rss.html'),
|
2017-06-22 02:42:43 +02:00
|
|
|
'svn': Integration(
|
|
|
|
'svn', 'svn',
|
|
|
|
display_name='Subversion',
|
|
|
|
doc='zerver/integrations/svn.md',
|
|
|
|
stream_name='commits',
|
|
|
|
),
|
2017-06-05 19:45:52 +02:00
|
|
|
'trac': Integration('trac', 'trac', doc='zerver/integrations/trac.md'),
|
2016-07-25 22:12:12 +02:00
|
|
|
'trello-plugin': Integration(
|
|
|
|
'trello-plugin',
|
|
|
|
'trello-plugin',
|
2017-03-28 14:27:39 +02:00
|
|
|
logo='static/images/integrations/logos/trello.svg',
|
2016-07-25 22:12:12 +02:00
|
|
|
secondary_line_text='(legacy)',
|
2017-04-05 08:09:53 +02:00
|
|
|
display_name='Trello',
|
|
|
|
doc='zerver/integrations/trello-plugin.html'
|
2016-07-25 22:12:12 +02:00
|
|
|
),
|
2017-06-05 22:29:31 +02:00
|
|
|
'twitter': Integration('twitter', 'twitter', doc='zerver/integrations/twitter.md'),
|
2016-07-25 22:12:12 +02:00
|
|
|
|
|
|
|
} # type: Dict[str, Integration]
|
|
|
|
|
2016-11-23 18:58:59 +01:00
|
|
|
HUBOT_LOZENGES = {
|
|
|
|
'assembla': HubotLozenge('assembla'),
|
|
|
|
'bonusly': HubotLozenge('bonusly'),
|
|
|
|
'chartbeat': HubotLozenge('chartbeat'),
|
|
|
|
'darksky': HubotLozenge('darksky', display_name='Dark Sky', logo_alt='Dark Sky logo'),
|
|
|
|
'hangouts': HubotLozenge('google-hangouts', display_name="Hangouts"),
|
2017-03-28 14:27:39 +02:00
|
|
|
'instagram': HubotLozenge('instagram', logo='static/images/integrations/logos/instagram.png'),
|
2016-12-25 21:21:17 +01:00
|
|
|
'mailchimp': HubotLozenge('mailchimp', display_name='MailChimp', logo_alt='MailChimp logo'),
|
2016-11-23 18:58:59 +01:00
|
|
|
'translate': HubotLozenge('google-translate', display_name="Translate", logo_alt='Google Translate logo'),
|
|
|
|
'youtube': HubotLozenge('youtube', display_name='YouTube', logo_alt='YouTube logo')
|
|
|
|
}
|
|
|
|
|
2016-07-25 22:12:12 +02:00
|
|
|
for integration in WEBHOOK_INTEGRATIONS:
|
|
|
|
INTEGRATIONS[integration.name] = integration
|