mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	We now supply a generic URL for our legacy and webhook GitHub integrations, as opposed to a dynamically generated URLs for all other WebhookIntegration(s). Previously, within GithubIntegration, an invalid URL was dynamically generated which wasn't even used. Now, we just manually supply the URL to GithubIntegration. Furthermore, we'll now be able to access the correct URL in `render_markdown_path` for our macros.
		
			
				
	
	
		
			289 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			289 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import os.path
 | 
						|
 | 
						|
from typing import Dict, List, Optional, TypeVar, Any, Text
 | 
						|
from django.conf import settings
 | 
						|
from django.conf.urls import url
 | 
						|
from django.core.urlresolvers import LocaleRegexProvider
 | 
						|
from django.utils.module_loading import import_string
 | 
						|
from django.utils.safestring import mark_safe
 | 
						|
from django.template import loader
 | 
						|
 | 
						|
from zerver.templatetags.app_filters import render_markdown_path
 | 
						|
 | 
						|
 | 
						|
"""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.
 | 
						|
"""
 | 
						|
 | 
						|
class Integration(object):
 | 
						|
    DEFAULT_LOGO_STATIC_PATH_PNG = 'static/images/integrations/logos/{name}.png'
 | 
						|
    DEFAULT_LOGO_STATIC_PATH_SVG = 'static/images/integrations/logos/{name}.svg'
 | 
						|
 | 
						|
    def __init__(self, name, client_name, logo=None, secondary_line_text=None, display_name=None, doc=None):
 | 
						|
        # type: (str, str, Optional[str], Optional[str], Optional[str], Optional[str]) -> None
 | 
						|
        self.name = name
 | 
						|
        self.client_name = client_name
 | 
						|
        self.secondary_line_text = secondary_line_text
 | 
						|
        self.doc = doc
 | 
						|
        self.doc_context = None  # type: Dict[Any, Any]
 | 
						|
 | 
						|
        if logo is None:
 | 
						|
            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)
 | 
						|
        self.logo = logo
 | 
						|
 | 
						|
        if display_name is None:
 | 
						|
            display_name = name.title()
 | 
						|
        self.display_name = display_name
 | 
						|
 | 
						|
    def is_enabled(self):
 | 
						|
        # type: () -> bool
 | 
						|
        return True
 | 
						|
 | 
						|
    def add_doc_context(self, context):
 | 
						|
        # type: (Dict[Any, Any]) -> None
 | 
						|
        self.doc_context = context
 | 
						|
 | 
						|
    @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))
 | 
						|
 | 
						|
class EmailIntegration(Integration):
 | 
						|
    def is_enabled(self):
 | 
						|
        # type: () -> bool
 | 
						|
        return settings.EMAIL_GATEWAY_BOT != ""
 | 
						|
 | 
						|
class WebhookIntegration(Integration):
 | 
						|
    DEFAULT_FUNCTION_PATH = 'zerver.webhooks.{name}.view.api_{name}_webhook'
 | 
						|
    DEFAULT_URL = 'api/v1/external/{name}'
 | 
						|
    DEFAULT_CLIENT_NAME = 'Zulip{name}Webhook'
 | 
						|
    DEFAULT_DOC_PATH = '{name}/doc.{ext}'
 | 
						|
 | 
						|
    def __init__(self, name, client_name=None, logo=None, secondary_line_text=None,
 | 
						|
                 function=None, url=None, display_name=None, doc=None):
 | 
						|
        # type: (str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None
 | 
						|
        if client_name is None:
 | 
						|
            client_name = self.DEFAULT_CLIENT_NAME.format(name=name.title())
 | 
						|
        super(WebhookIntegration, self).__init__(name, client_name, logo, secondary_line_text, display_name)
 | 
						|
 | 
						|
        if function is None:
 | 
						|
            function = self.DEFAULT_FUNCTION_PATH.format(name=name)
 | 
						|
 | 
						|
        if isinstance(function, str):
 | 
						|
            function = import_string(function)
 | 
						|
 | 
						|
        self.function = function
 | 
						|
 | 
						|
        if url is None:
 | 
						|
            url = self.DEFAULT_URL.format(name=name)
 | 
						|
        self.url = url
 | 
						|
 | 
						|
        if doc is None:
 | 
						|
            path = os.path.join(settings.DEPLOY_ROOT, 'zerver', 'webhooks')
 | 
						|
            md_doc = self.DEFAULT_DOC_PATH.format(name=name, ext='md')
 | 
						|
            if os.path.exists(os.path.join(path, md_doc)):
 | 
						|
                doc = md_doc
 | 
						|
            else:
 | 
						|
                doc = self.DEFAULT_DOC_PATH.format(name=name, ext='html')
 | 
						|
 | 
						|
        self.doc = doc
 | 
						|
 | 
						|
    @property
 | 
						|
    def url_object(self):
 | 
						|
        # type: () -> LocaleRegexProvider
 | 
						|
        return url(self.url, self.function)
 | 
						|
 | 
						|
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
 | 
						|
        super(HubotLozenge, self).__init__(name, name, logo, display_name=display_name)
 | 
						|
 | 
						|
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.
 | 
						|
    """
 | 
						|
    def __init__(self, name, client_name=None, logo=None, secondary_line_text=None,
 | 
						|
                 function=None, url=None, display_name=None, doc=None):
 | 
						|
        # type: (str, Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str], Optional[str]) -> None
 | 
						|
        url = self.DEFAULT_URL.format(name='github')
 | 
						|
 | 
						|
        super(GithubIntegration, self).__init__(
 | 
						|
            name, client_name, logo, secondary_line_text,
 | 
						|
            function, url, display_name, doc)
 | 
						|
 | 
						|
    @property
 | 
						|
    def url_object(self):
 | 
						|
        # type: () -> None
 | 
						|
        return
 | 
						|
 | 
						|
WEBHOOK_INTEGRATIONS = [
 | 
						|
    WebhookIntegration('airbrake'),
 | 
						|
    WebhookIntegration('appfollow', display_name='AppFollow'),
 | 
						|
    WebhookIntegration('beanstalk'),
 | 
						|
    WebhookIntegration('basecamp'),
 | 
						|
    WebhookIntegration('bitbucket2', logo='static/images/integrations/logos/bitbucket.svg', display_name='Bitbucket'),
 | 
						|
    WebhookIntegration('bitbucket', display_name='Bitbucket', secondary_line_text='(Enterprise)'),
 | 
						|
    WebhookIntegration('circleci', display_name='CircleCI'),
 | 
						|
    WebhookIntegration('codeship'),
 | 
						|
    WebhookIntegration('crashlytics'),
 | 
						|
    WebhookIntegration('delighted', display_name='Delighted'),
 | 
						|
    WebhookIntegration('deskdotcom', logo='static/images/integrations/logos/deskcom.png', display_name='Desk.com'),
 | 
						|
    WebhookIntegration('freshdesk'),
 | 
						|
    GithubIntegration(
 | 
						|
        'github',
 | 
						|
        function='zerver.webhooks.github.view.api_github_landing',
 | 
						|
        display_name='GitHub',
 | 
						|
        secondary_line_text='(deprecated)'
 | 
						|
    ),
 | 
						|
    GithubIntegration(
 | 
						|
        'github_webhook',
 | 
						|
        display_name='GitHub',
 | 
						|
        logo='static/images/integrations/logos/github.svg',
 | 
						|
        secondary_line_text='(webhook)',
 | 
						|
        function='zerver.webhooks.github_webhook.view.api_github_webhook'
 | 
						|
    ),
 | 
						|
    WebhookIntegration('gitlab', display_name='GitLab'),
 | 
						|
    WebhookIntegration('gogs'),
 | 
						|
    WebhookIntegration('gosquared', display_name='GoSquared'),
 | 
						|
    WebhookIntegration('greenhouse', display_name='Greenhouse'),
 | 
						|
    WebhookIntegration('hellosign', display_name='HelloSign'),
 | 
						|
    WebhookIntegration('helloworld', display_name='Hello World'),
 | 
						|
    WebhookIntegration('heroku', display_name='Heroku'),
 | 
						|
    WebhookIntegration('homeassistant', display_name='Home Assistant'),
 | 
						|
    WebhookIntegration('ifttt', function='zerver.webhooks.ifttt.view.api_iftt_app_webhook', display_name='IFTTT'),
 | 
						|
    WebhookIntegration('jira', secondary_line_text='(hosted or v5.2+)', display_name='JIRA'),
 | 
						|
    WebhookIntegration('librato'),
 | 
						|
    WebhookIntegration('mention', display_name='Mention'),
 | 
						|
    WebhookIntegration('newrelic', display_name='New Relic'),
 | 
						|
    WebhookIntegration('pagerduty'),
 | 
						|
    WebhookIntegration('papertrail'),
 | 
						|
    WebhookIntegration('pingdom'),
 | 
						|
    WebhookIntegration('pivotal', display_name='Pivotal Tracker'),
 | 
						|
    WebhookIntegration('semaphore'),
 | 
						|
    WebhookIntegration('sentry'),
 | 
						|
    WebhookIntegration('slack'),
 | 
						|
    WebhookIntegration('solano', display_name='Solano Labs'),
 | 
						|
    WebhookIntegration('splunk', display_name='Splunk'),
 | 
						|
    WebhookIntegration('stripe', display_name='Stripe'),
 | 
						|
    WebhookIntegration('taiga'),
 | 
						|
    WebhookIntegration('teamcity'),
 | 
						|
    WebhookIntegration('transifex'),
 | 
						|
    WebhookIntegration('travis', display_name='Travis CI'),
 | 
						|
    WebhookIntegration('trello', secondary_line_text='(webhook)'),
 | 
						|
    WebhookIntegration('updown'),
 | 
						|
    WebhookIntegration(
 | 
						|
        'yo',
 | 
						|
        function='zerver.webhooks.yo.view.api_yo_app_webhook',
 | 
						|
        display_name='Yo App'
 | 
						|
    ),
 | 
						|
    WebhookIntegration('wordpress', display_name='WordPress'),
 | 
						|
    WebhookIntegration('zapier'),
 | 
						|
    WebhookIntegration('zendesk')
 | 
						|
]  # type: List[WebhookIntegration]
 | 
						|
 | 
						|
INTEGRATIONS = {
 | 
						|
    'asana': Integration('asana', 'asana', doc='zerver/integrations/asana.html'),
 | 
						|
    'capistrano': Integration('capistrano', 'capistrano', doc='zerver/integrations/capistrano.html'),
 | 
						|
    'codebase': Integration('codebase', 'codebase', doc='zerver/integrations/codebase.html'),
 | 
						|
    'email': EmailIntegration('email', 'email', doc='zerver/integrations/email.html'),
 | 
						|
    'git': Integration('git', 'git', doc='zerver/integrations/git.html'),
 | 
						|
    'google-calendar': Integration(
 | 
						|
        'google-calendar',
 | 
						|
        'google-calendar',
 | 
						|
        display_name='Google Calendar',
 | 
						|
        doc='zerver/integrations/google-calendar.html'
 | 
						|
    ),
 | 
						|
    'hubot': Integration('hubot', 'hubot', doc='zerver/integrations/hubot.html'),
 | 
						|
    'jenkins': Integration(
 | 
						|
        'jenkins',
 | 
						|
        'jenkins',
 | 
						|
        secondary_line_text='(or Hudson)',
 | 
						|
        doc='zerver/integrations/jenkins.html'
 | 
						|
    ),
 | 
						|
    'jira-plugin': Integration(
 | 
						|
        'jira-plugin',
 | 
						|
        'jira-plugin',
 | 
						|
        logo='static/images/integrations/logos/jira.svg',
 | 
						|
        secondary_line_text='(locally installed)',
 | 
						|
        display_name='JIRA',
 | 
						|
        doc='zerver/integrations/jira-plugin.html'
 | 
						|
    ),
 | 
						|
    'mercurial': Integration(
 | 
						|
        'mercurial',
 | 
						|
        'mercurial',
 | 
						|
        display_name='Mercurial (hg)',
 | 
						|
        doc='zerver/integrations/mercurial.html'
 | 
						|
    ),
 | 
						|
    'nagios': Integration('nagios', 'nagios', doc='zerver/integrations/nagios.html'),
 | 
						|
    'openshift': Integration(
 | 
						|
        'openshift',
 | 
						|
        'openshift',
 | 
						|
        display_name='OpenShift',
 | 
						|
        doc='zerver/integrations/openshift.html'
 | 
						|
    ),
 | 
						|
    'perforce': Integration('perforce', 'perforce', doc='zerver/integrations/perforce.html'),
 | 
						|
    'phabricator': Integration('phabricator', 'phabricator', doc='zerver/integrations/phabricator.html'),
 | 
						|
    'puppet': Integration('puppet', 'puppet', doc='zerver/integrations/puppet.html'),
 | 
						|
    'redmine': Integration('redmine', 'redmine', doc='zerver/integrations/redmine.html'),
 | 
						|
    'rss': Integration('rss', 'rss', display_name='RSS', doc='zerver/integrations/rss.html'),
 | 
						|
    'subversion': Integration('subversion', 'subversion', doc='zerver/integrations/subversion.html'),
 | 
						|
    'trac': Integration('trac', 'trac', doc='zerver/integrations/trac.html'),
 | 
						|
    'trello-plugin': Integration(
 | 
						|
        'trello-plugin',
 | 
						|
        'trello-plugin',
 | 
						|
        logo='static/images/integrations/logos/trello.svg',
 | 
						|
        secondary_line_text='(legacy)',
 | 
						|
        display_name='Trello',
 | 
						|
        doc='zerver/integrations/trello-plugin.html'
 | 
						|
    ),
 | 
						|
    'twitter': Integration('twitter', 'twitter', doc='zerver/integrations/twitter.html'),
 | 
						|
 | 
						|
}  # type: Dict[str, Integration]
 | 
						|
 | 
						|
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"),
 | 
						|
    'instagram': HubotLozenge('instagram', logo='static/images/integrations/logos/instagram.png'),
 | 
						|
    'mailchimp': HubotLozenge('mailchimp', display_name='MailChimp', logo_alt='MailChimp logo'),
 | 
						|
    'translate': HubotLozenge('google-translate', display_name="Translate", logo_alt='Google Translate logo'),
 | 
						|
    'youtube': HubotLozenge('youtube', display_name='YouTube', logo_alt='YouTube logo')
 | 
						|
}
 | 
						|
 | 
						|
for integration in WEBHOOK_INTEGRATIONS:
 | 
						|
    INTEGRATIONS[integration.name] = integration
 |