2019-02-02 23:53:22 +01:00
|
|
|
from typing import Any, Dict, Tuple
|
2016-07-29 15:06:41 +02:00
|
|
|
from collections import OrderedDict
|
|
|
|
from django.views.generic import TemplateView
|
2016-08-14 03:32:11 +02:00
|
|
|
from django.conf import settings
|
2016-11-09 01:45:36 +01:00
|
|
|
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
|
2017-04-03 11:49:13 +02:00
|
|
|
from django.template import loader
|
2016-07-29 15:06:41 +02:00
|
|
|
|
2016-11-09 01:45:36 +01:00
|
|
|
import os
|
2018-12-14 23:28:00 +01:00
|
|
|
import random
|
2018-12-03 23:03:19 +01:00
|
|
|
import re
|
2016-09-13 19:18:22 +02:00
|
|
|
|
2017-11-20 03:27:04 +01:00
|
|
|
from zerver.lib.integrations import CATEGORIES, INTEGRATIONS, HubotIntegration, \
|
2019-08-04 03:08:29 +02:00
|
|
|
WebhookIntegration
|
2017-10-28 00:07:31 +02:00
|
|
|
from zerver.lib.request import has_request_variables, REQ
|
2017-10-19 07:21:57 +02:00
|
|
|
from zerver.lib.subdomains import get_subdomain
|
2017-10-20 02:56:49 +02:00
|
|
|
from zerver.models import Realm
|
2017-07-12 02:50:27 +02:00
|
|
|
from zerver.templatetags.app_filters import render_markdown_path
|
2018-06-01 20:31:16 +02:00
|
|
|
from zerver.context_processors import zulip_default_context
|
2016-07-29 15:06:41 +02:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def add_api_uri_context(context: Dict[str, Any], request: HttpRequest) -> None:
|
2018-06-01 20:31:16 +02:00
|
|
|
context.update(zulip_default_context(request))
|
|
|
|
|
2017-10-02 07:59:20 +02:00
|
|
|
subdomain = get_subdomain(request)
|
2017-10-20 02:56:49 +02:00
|
|
|
if (subdomain != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
|
|
|
|
or not settings.ROOT_DOMAIN_LANDING_PAGE):
|
2017-10-02 07:59:20 +02:00
|
|
|
display_subdomain = subdomain
|
|
|
|
html_settings_links = True
|
|
|
|
else:
|
|
|
|
display_subdomain = 'yourZulipDomain'
|
|
|
|
html_settings_links = False
|
2016-07-19 14:35:08 +02:00
|
|
|
|
2017-10-30 22:13:13 +01:00
|
|
|
display_host = Realm.host_for_subdomain(display_subdomain)
|
|
|
|
api_url_scheme_relative = display_host + "/api"
|
|
|
|
api_url = settings.EXTERNAL_URI_SCHEME + api_url_scheme_relative
|
2019-03-30 23:04:52 +01:00
|
|
|
zulip_url = settings.EXTERNAL_URI_SCHEME + display_host
|
2016-09-14 07:07:21 +02:00
|
|
|
|
2018-02-17 23:04:17 +01:00
|
|
|
context['external_uri_scheme'] = settings.EXTERNAL_URI_SCHEME
|
2017-10-30 22:04:15 +01:00
|
|
|
context['api_url'] = api_url
|
2017-10-30 22:13:13 +01:00
|
|
|
context['api_url_scheme_relative'] = api_url_scheme_relative
|
2019-03-30 23:04:52 +01:00
|
|
|
context['zulip_url'] = zulip_url
|
2018-04-05 20:31:43 +02:00
|
|
|
|
2016-07-19 14:35:08 +02:00
|
|
|
context["html_settings_links"] = html_settings_links
|
2018-04-05 20:31:43 +02:00
|
|
|
if html_settings_links:
|
2018-04-05 23:39:35 +02:00
|
|
|
settings_html = '<a href="/#settings">Zulip settings page</a>'
|
|
|
|
subscriptions_html = '<a target="_blank" href="/#streams">streams page</a>'
|
2018-04-05 20:31:43 +02:00
|
|
|
else:
|
|
|
|
settings_html = 'Zulip settings page'
|
|
|
|
subscriptions_html = 'streams page'
|
|
|
|
context['settings_html'] = settings_html
|
|
|
|
context['subscriptions_html'] = subscriptions_html
|
2016-09-14 07:07:21 +02:00
|
|
|
|
2016-09-13 19:09:03 +02:00
|
|
|
class ApiURLView(TemplateView):
|
2017-11-27 09:28:57 +01:00
|
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, str]:
|
2017-10-27 08:28:23 +02:00
|
|
|
context = super().get_context_data(**kwargs)
|
2016-09-14 07:07:21 +02:00
|
|
|
add_api_uri_context(context, self.request)
|
2016-09-13 19:09:03 +02:00
|
|
|
return context
|
|
|
|
|
2016-08-14 09:44:12 +02:00
|
|
|
|
2017-07-25 02:35:22 +02:00
|
|
|
class MarkdownDirectoryView(ApiURLView):
|
|
|
|
path_template = ""
|
2016-11-09 01:45:36 +01:00
|
|
|
|
2018-12-03 23:03:19 +01:00
|
|
|
def get_path(self, article: str) -> Tuple[str, int]:
|
|
|
|
http_status = 200
|
2016-11-09 01:45:36 +01:00
|
|
|
if article == "":
|
|
|
|
article = "index"
|
2018-08-18 15:42:01 +02:00
|
|
|
elif article == "include/sidebar_index":
|
|
|
|
pass
|
2017-10-02 19:51:17 +02:00
|
|
|
elif "/" in article:
|
|
|
|
article = "missing"
|
2018-12-03 23:03:19 +01:00
|
|
|
http_status = 404
|
2018-12-06 19:11:02 +01:00
|
|
|
elif len(article) > 100 or not re.match('^[0-9a-zA-Z_-]+$', article):
|
2018-12-03 23:03:19 +01:00
|
|
|
article = "missing"
|
|
|
|
http_status = 404
|
2018-12-06 19:11:02 +01:00
|
|
|
|
2018-12-03 23:03:19 +01:00
|
|
|
path = self.path_template % (article,)
|
|
|
|
try:
|
|
|
|
loader.get_template(path)
|
|
|
|
return (path, http_status)
|
|
|
|
except loader.TemplateDoesNotExist:
|
|
|
|
return (self.path_template % ("missing",), 404)
|
2016-11-09 01:45:36 +01:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
2016-11-09 01:45:36 +01:00
|
|
|
article = kwargs["article"]
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
context: Dict[str, Any] = super().get_context_data()
|
2018-12-03 23:03:19 +01:00
|
|
|
(context["article"], http_status_ignored) = self.get_path(article)
|
2017-04-03 11:49:13 +02:00
|
|
|
|
2017-02-18 23:58:54 +01:00
|
|
|
# For disabling the "Back to home" on the homepage
|
2018-12-03 23:03:19 +01:00
|
|
|
context["not_index_page"] = not context["article"].endswith("/index.md")
|
2018-08-18 15:42:01 +02:00
|
|
|
if self.path_template == '/zerver/help/%s.md':
|
2017-11-08 22:08:23 +01:00
|
|
|
context["page_is_help_center"] = True
|
2018-08-18 15:42:01 +02:00
|
|
|
context["doc_root"] = "/help/"
|
2018-12-03 23:03:19 +01:00
|
|
|
(sidebar_index, http_status_ignored) = self.get_path("include/sidebar_index")
|
2018-08-18 15:42:01 +02:00
|
|
|
# We want the sliding/collapsing behavior for /help pages only
|
|
|
|
sidebar_class = "sidebar slide"
|
2018-12-14 23:28:00 +01:00
|
|
|
title_base = "Zulip Help Center"
|
2017-11-08 22:08:23 +01:00
|
|
|
else:
|
|
|
|
context["page_is_api_center"] = True
|
2018-08-18 15:42:01 +02:00
|
|
|
context["doc_root"] = "/api/"
|
2018-12-03 23:03:19 +01:00
|
|
|
(sidebar_index, http_status_ignored) = self.get_path("sidebar_index")
|
2018-08-18 15:42:01 +02:00
|
|
|
sidebar_class = "sidebar"
|
2018-12-14 23:28:00 +01:00
|
|
|
title_base = "Zulip API Documentation"
|
|
|
|
|
|
|
|
# The following is a somewhat hacky approach to extract titles from articles.
|
|
|
|
# Hack: `context["article"] has a leading `/`, so we use + to add directories.
|
|
|
|
article_path = os.path.join(settings.DEPLOY_ROOT, 'templates') + context["article"]
|
|
|
|
if os.path.exists(article_path):
|
|
|
|
with open(article_path) as article_file:
|
|
|
|
first_line = article_file.readlines()[0]
|
|
|
|
# Strip the header and then use the first line to get the article title
|
2020-03-20 18:28:24 +01:00
|
|
|
article_title = first_line.lstrip("#").strip()
|
2018-12-14 23:28:00 +01:00
|
|
|
if context["not_index_page"]:
|
|
|
|
context["OPEN_GRAPH_TITLE"] = "%s (%s)" % (article_title, title_base)
|
|
|
|
else:
|
|
|
|
context["OPEN_GRAPH_TITLE"] = title_base
|
|
|
|
self.request.placeholder_open_graph_description = (
|
|
|
|
"REPLACMENT_OPEN_GRAPH_DESCRIPTION_%s" % (int(2**24 * random.random()),))
|
|
|
|
context["OPEN_GRAPH_DESCRIPTION"] = self.request.placeholder_open_graph_description
|
2018-08-18 15:42:01 +02:00
|
|
|
|
|
|
|
context["sidebar_index"] = sidebar_index
|
|
|
|
context["sidebar_class"] = sidebar_class
|
2017-11-08 05:39:42 +01:00
|
|
|
# An "article" might require the api_uri_context to be rendered
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
api_uri_context: Dict[str, Any] = {}
|
2017-11-08 05:39:42 +01:00
|
|
|
add_api_uri_context(api_uri_context, self.request)
|
2019-05-16 22:38:53 +02:00
|
|
|
api_uri_context["run_content_validators"] = True
|
2017-11-08 05:39:42 +01:00
|
|
|
context["api_uri_context"] = api_uri_context
|
2016-11-09 01:45:36 +01:00
|
|
|
return context
|
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def get(self, request: HttpRequest, article: str="") -> HttpResponse:
|
2018-12-03 23:03:19 +01:00
|
|
|
(path, http_status) = self.get_path(article)
|
2017-07-25 02:35:22 +02:00
|
|
|
result = super().get(self, article=article)
|
2018-12-03 23:03:19 +01:00
|
|
|
if http_status != 200:
|
|
|
|
result.status_code = http_status
|
2016-11-09 01:45:36 +01:00
|
|
|
return result
|
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def add_integrations_context(context: Dict[str, Any]) -> None:
|
2017-07-05 22:07:46 +02:00
|
|
|
alphabetical_sorted_categories = OrderedDict(sorted(CATEGORIES.items()))
|
2017-02-28 07:18:45 +01:00
|
|
|
alphabetical_sorted_integration = OrderedDict(sorted(INTEGRATIONS.items()))
|
2018-02-06 16:55:20 +01:00
|
|
|
enabled_integrations_count = len(list(filter(lambda v: v.is_enabled(), INTEGRATIONS.values())))
|
|
|
|
# Subtract 1 so saying "Over X integrations" is correct. Then,
|
|
|
|
# round down to the nearest multiple of 10.
|
|
|
|
integrations_count_display = ((enabled_integrations_count - 1) // 10) * 10
|
2017-07-05 22:07:46 +02:00
|
|
|
context['categories_dict'] = alphabetical_sorted_categories
|
2017-02-28 07:18:45 +01:00
|
|
|
context['integrations_dict'] = alphabetical_sorted_integration
|
2018-02-06 16:55:20 +01:00
|
|
|
context['integrations_count_display'] = integrations_count_display
|
2017-02-28 07:18:45 +01:00
|
|
|
|
2019-06-15 07:19:57 +02:00
|
|
|
def add_integrations_open_graph_context(context: Dict[str, Any], request: HttpRequest) -> None:
|
|
|
|
path_name = request.path.rstrip('/').split('/')[-1]
|
|
|
|
description = ('Zulip comes with over a hundred native integrations out of the box, '
|
|
|
|
'and integrates with Zapier, IFTTT, and Hubot to provide hundreds more. '
|
|
|
|
'Connect the apps you use everyday to Zulip.')
|
|
|
|
|
|
|
|
if path_name in INTEGRATIONS:
|
|
|
|
integration = INTEGRATIONS[path_name]
|
|
|
|
context['OPEN_GRAPH_TITLE'] = 'Connect {name} to Zulip'.format(name=integration.display_name)
|
|
|
|
context['OPEN_GRAPH_DESCRIPTION'] = description
|
|
|
|
|
|
|
|
elif path_name in CATEGORIES:
|
|
|
|
category = CATEGORIES[path_name]
|
|
|
|
context['OPEN_GRAPH_TITLE'] = 'Connect your {category} tools to Zulip'.format(category=category)
|
|
|
|
context['OPEN_GRAPH_DESCRIPTION'] = description
|
2017-02-28 07:18:45 +01:00
|
|
|
|
2019-07-10 21:30:06 +02:00
|
|
|
elif path_name == 'integrations':
|
|
|
|
context['OPEN_GRAPH_TITLE'] = 'Connect the tools you use to Zulip'
|
|
|
|
context['OPEN_GRAPH_DESCRIPTION'] = description
|
|
|
|
|
2016-09-13 19:09:03 +02:00
|
|
|
class IntegrationView(ApiURLView):
|
2017-03-31 07:40:52 +02:00
|
|
|
template_name = 'zerver/integrations/index.html'
|
2016-07-29 15:06:41 +02:00
|
|
|
|
2017-11-27 09:28:57 +01:00
|
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
context: Dict[str, Any] = super().get_context_data(**kwargs)
|
2017-02-28 07:18:45 +01:00
|
|
|
add_integrations_context(context)
|
2019-06-15 07:19:57 +02:00
|
|
|
add_integrations_open_graph_context(context, self.request)
|
2016-07-29 15:06:41 +02:00
|
|
|
return context
|
2016-09-13 19:18:22 +02:00
|
|
|
|
|
|
|
|
2017-07-12 02:50:27 +02:00
|
|
|
@has_request_variables
|
2019-11-13 08:17:49 +01:00
|
|
|
def integration_doc(request: HttpRequest, integration_name: str=REQ()) -> HttpResponse:
|
2018-05-11 16:35:03 +02:00
|
|
|
if not request.is_ajax():
|
|
|
|
return HttpResponseNotFound()
|
2017-07-12 02:50:27 +02:00
|
|
|
try:
|
|
|
|
integration = INTEGRATIONS[integration_name]
|
|
|
|
except KeyError:
|
|
|
|
return HttpResponseNotFound()
|
|
|
|
|
python: Convert assignment type annotations to Python 3.6 style.
This commit was split by tabbott; this piece covers the vast majority
of files in Zulip, but excludes scripts/, tools/, and puppet/ to help
ensure we at least show the right error messages for Xenial systems.
We can likely further refine the remaining pieces with some testing.
Generated by com2ann, with whitespace fixes and various manual fixes
for runtime issues:
- invoiced_through: Optional[LicenseLedger] = models.ForeignKey(
+ invoiced_through: Optional["LicenseLedger"] = models.ForeignKey(
-_apns_client: Optional[APNsClient] = None
+_apns_client: Optional["APNsClient"] = None
- notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- signup_notifications_stream: Optional[Stream] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
+ signup_notifications_stream: Optional["Stream"] = models.ForeignKey('Stream', related_name='+', null=True, blank=True, on_delete=CASCADE)
- author: Optional[UserProfile] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
+ author: Optional["UserProfile"] = models.ForeignKey('UserProfile', blank=True, null=True, on_delete=CASCADE)
- bot_owner: Optional[UserProfile] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
+ bot_owner: Optional["UserProfile"] = models.ForeignKey('self', null=True, on_delete=models.SET_NULL)
- default_sending_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
- default_events_register_stream: Optional[Stream] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_sending_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
+ default_events_register_stream: Optional["Stream"] = models.ForeignKey('zerver.Stream', null=True, related_name='+', on_delete=CASCADE)
-descriptors_by_handler_id: Dict[int, ClientDescriptor] = {}
+descriptors_by_handler_id: Dict[int, "ClientDescriptor"] = {}
-worker_classes: Dict[str, Type[QueueProcessingWorker]] = {}
-queues: Dict[str, Dict[str, Type[QueueProcessingWorker]]] = {}
+worker_classes: Dict[str, Type["QueueProcessingWorker"]] = {}
+queues: Dict[str, Dict[str, Type["QueueProcessingWorker"]]] = {}
-AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional[LDAPSearch] = None
+AUTH_LDAP_REVERSE_EMAIL_SEARCH: Optional["LDAPSearch"] = None
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-22 01:09:50 +02:00
|
|
|
context: Dict[str, Any] = {}
|
2018-04-05 20:31:43 +02:00
|
|
|
add_api_uri_context(context, request)
|
2017-11-20 03:27:04 +01:00
|
|
|
|
|
|
|
context['integration_name'] = integration.name
|
|
|
|
context['integration_display_name'] = integration.display_name
|
|
|
|
if hasattr(integration, 'stream_name'):
|
|
|
|
context['recommended_stream_name'] = integration.stream_name
|
|
|
|
if isinstance(integration, WebhookIntegration):
|
|
|
|
context['integration_url'] = integration.url[3:]
|
|
|
|
if isinstance(integration, HubotIntegration):
|
|
|
|
context['hubot_docs_url'] = integration.hubot_docs_url
|
|
|
|
|
2017-07-12 02:50:27 +02:00
|
|
|
doc_html_str = render_markdown_path(integration.doc, context)
|
|
|
|
|
|
|
|
return HttpResponse(doc_html_str)
|