mirror of
https://github.com/zulip/zulip.git
synced 2025-11-07 15:33:30 +00:00
We've been getting reports from users that our Freshdesk webhook
isn't working correctly. It turns out that the issue had nothing
to do with the webhook implementation itself!
In freshdesk/doc.md, we have a JSON template we ask users to
copy/paste into a textbox in the Freshdesk UI. That JSON template
contains "{{" and "}}" characters which we escaped as Unicode
decimals to prevent clashes with Jinja2 syntax in other parts
of the same template. This worked for a while!
But thanks to the changes introduced as part of the
nested_code_blocks extension, such escaped characters were never
decoded, leading users to copy/paste the same template but with
raw escaped unicode representations of "{{" and "}}" inside. And
that eventually broke our webhook implementation.
This commit makes sure that such characters are properly "unescaped",
just for Freshdesk docs.
161 lines
6.4 KiB
Python
161 lines
6.4 KiB
Python
from typing import Optional, Any, Dict
|
|
from collections import OrderedDict
|
|
from django.views.generic import TemplateView
|
|
from django.conf import settings
|
|
from django.http import HttpRequest, HttpResponse, HttpResponseNotFound
|
|
from django.template import loader
|
|
from django.shortcuts import render
|
|
|
|
import os
|
|
import ujson
|
|
|
|
from zerver.lib import bugdown
|
|
from zerver.lib.integrations import CATEGORIES, INTEGRATIONS, HubotIntegration, \
|
|
WebhookIntegration, EmailIntegration
|
|
from zerver.lib.request import has_request_variables, REQ
|
|
from zerver.lib.subdomains import get_subdomain
|
|
from zerver.models import Realm
|
|
from zerver.templatetags.app_filters import render_markdown_path
|
|
from zerver.context_processors import zulip_default_context
|
|
|
|
def add_api_uri_context(context: Dict[str, Any], request: HttpRequest) -> None:
|
|
context.update(zulip_default_context(request))
|
|
|
|
subdomain = get_subdomain(request)
|
|
if (subdomain != Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
|
|
or not settings.ROOT_DOMAIN_LANDING_PAGE):
|
|
display_subdomain = subdomain
|
|
html_settings_links = True
|
|
else:
|
|
display_subdomain = 'yourZulipDomain'
|
|
html_settings_links = False
|
|
|
|
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
|
|
|
|
context['external_uri_scheme'] = settings.EXTERNAL_URI_SCHEME
|
|
context['api_url'] = api_url
|
|
context['api_url_scheme_relative'] = api_url_scheme_relative
|
|
|
|
context["html_settings_links"] = html_settings_links
|
|
if html_settings_links:
|
|
settings_html = '<a href="/#settings">Zulip settings page</a>'
|
|
subscriptions_html = '<a target="_blank" href="/#streams">streams page</a>'
|
|
else:
|
|
settings_html = 'Zulip settings page'
|
|
subscriptions_html = 'streams page'
|
|
context['settings_html'] = settings_html
|
|
context['subscriptions_html'] = subscriptions_html
|
|
|
|
class ApiURLView(TemplateView):
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, str]:
|
|
context = super().get_context_data(**kwargs)
|
|
add_api_uri_context(context, self.request)
|
|
return context
|
|
|
|
class APIView(ApiURLView):
|
|
template_name = 'zerver/api.html'
|
|
|
|
|
|
class MarkdownDirectoryView(ApiURLView):
|
|
path_template = ""
|
|
|
|
def get_path(self, article: str) -> str:
|
|
if article == "":
|
|
article = "index"
|
|
elif "/" in article:
|
|
article = "missing"
|
|
return self.path_template % (article,)
|
|
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
|
article = kwargs["article"]
|
|
context = super().get_context_data() # type: Dict[str, Any]
|
|
path = self.get_path(article)
|
|
try:
|
|
loader.get_template(path)
|
|
context["article"] = path
|
|
except loader.TemplateDoesNotExist:
|
|
context["article"] = self.get_path("missing")
|
|
|
|
# For disabling the "Back to home" on the homepage
|
|
context["not_index_page"] = not path.endswith("/index.md")
|
|
if self.template_name == "zerver/help/main.html":
|
|
context["page_is_help_center"] = True
|
|
else:
|
|
context["page_is_api_center"] = True
|
|
# An "article" might require the api_uri_context to be rendered
|
|
api_uri_context = {} # type: Dict[str, Any]
|
|
add_api_uri_context(api_uri_context, self.request)
|
|
context["api_uri_context"] = api_uri_context
|
|
return context
|
|
|
|
def get(self, request: HttpRequest, article: str="") -> HttpResponse:
|
|
path = self.get_path(article)
|
|
result = super().get(self, article=article)
|
|
try:
|
|
loader.get_template(path)
|
|
except loader.TemplateDoesNotExist:
|
|
# Ensure a 404 response code if no such document
|
|
result.status_code = 404
|
|
if "/" in article:
|
|
result.status_code = 404
|
|
return result
|
|
|
|
|
|
def add_integrations_context(context: Dict[str, Any]) -> None:
|
|
alphabetical_sorted_categories = OrderedDict(sorted(CATEGORIES.items()))
|
|
alphabetical_sorted_integration = OrderedDict(sorted(INTEGRATIONS.items()))
|
|
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
|
|
context['categories_dict'] = alphabetical_sorted_categories
|
|
context['integrations_dict'] = alphabetical_sorted_integration
|
|
context['integrations_count_display'] = integrations_count_display
|
|
|
|
|
|
class IntegrationView(ApiURLView):
|
|
template_name = 'zerver/integrations/index.html'
|
|
|
|
def get_context_data(self, **kwargs: Any) -> Dict[str, Any]:
|
|
context = super().get_context_data(**kwargs) # type: Dict[str, Any]
|
|
add_integrations_context(context)
|
|
return context
|
|
|
|
|
|
@has_request_variables
|
|
def integration_doc(request: HttpRequest, integration_name: str=REQ(default=None)) -> HttpResponse:
|
|
if not request.is_ajax():
|
|
return HttpResponseNotFound()
|
|
try:
|
|
integration = INTEGRATIONS[integration_name]
|
|
except KeyError:
|
|
return HttpResponseNotFound()
|
|
|
|
context = {} # type: Dict[str, Any]
|
|
add_api_uri_context(context, request)
|
|
|
|
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
|
|
if isinstance(integration, EmailIntegration):
|
|
context['email_gateway_example'] = settings.EMAIL_GATEWAY_EXAMPLE
|
|
if integration.name == 'freshdesk':
|
|
# In our Freshdesk docs, some nested code blocks have characters such
|
|
# as '{' encoded as '{' to prevent clashes with Jinja2 syntax,
|
|
# but the encoded form never gets rendered because the text ends up
|
|
# inside a <pre> tag. So here, we explicitly set a directive that
|
|
# a particular template should be "unescaped" before being displayed.
|
|
# Note that this value is used by render_markdown_path.
|
|
context['unescape_rendered_html'] = True
|
|
|
|
doc_html_str = render_markdown_path(integration.doc, context)
|
|
|
|
return HttpResponse(doc_html_str)
|