From 5359e6b0d4af5d0b9a4180d074252290f49cd5e7 Mon Sep 17 00:00:00 2001 From: Umair Khan Date: Thu, 21 Apr 2016 11:48:33 +0500 Subject: [PATCH] Convert Zulip to use Jinja2 templates. This results in a substantial performance improvement for all of Zulip's backend templates. Changes in templates: - Change `block.super` to `super()`. - Remove `load` tag because Jinja2 doesn't support it. - Use `minified_js()|safe` instead of `{% minified_js %}`. - Use `compressed_css()|safe` instead of `{% compressed_css %}`. - `forloop.first` -> `loop.first`. - Use `{{ csrf_input }}` instead of `{% csrf_token %}`. - Use `{# ... #}` instead of `{% comment %}`. - Use `url()` instead of `{% url %}`. - Use `_()` instead of `{% trans %}` because in Jinja `trans` is a block tag. - Use `{% trans %}` instead of `{% blocktrans %}`. - Use `{% raw %}` instead of `{% verbatim %}`. Changes in tools: - Check for `trans` block in `check-templates` instead of `blocktrans` Changes in backend: - Create custom `render_to_response` function which takes `request` objects instead of `RequestContext` object. There are two reasons to do this: 1. `RequestContext` is not compatible with Jinja2 2. `RequestContext` in `render_to_response` is deprecated. - Add Jinja2 related support files in zproject/jinja2 directory. It includes a custom backend and a template renderer, compressors for js and css and Jinja2 environment handler. - Enable `slugify` and `pluralize` filters in Jinja2 environment. Fixes #620. --- analytics/views.py | 11 +- confirmation/views.py | 4 +- docs/directory-structure.rst | 14 +- docs/new-feature-tutorial.rst | 2 +- puppet/zulip/manifests/app_frontend_base.pp | 1 + templates/500.html | 2 +- templates/analytics/activity.html | 15 +- templates/confirmation/confirm_mituser.html | 2 +- .../confirm_preregistrationuser.html | 6 +- .../registration/password_reset_email.txt | 2 +- templates/zerver/accounts_accept_terms.html | 29 ++-- templates/zerver/accounts_home.html | 17 +-- templates/zerver/accounts_send_confirm.html | 13 +- templates/zerver/api.html | 4 +- templates/zerver/api_endpoints.html | 6 +- templates/zerver/apps.html | 53 ++++--- templates/zerver/bankruptcy.html | 7 +- templates/zerver/base.html | 6 +- templates/zerver/closed_realm.html | 7 +- templates/zerver/compose.html | 29 ++-- templates/zerver/deactivated.html | 9 +- templates/zerver/debug.html | 4 +- .../zerver/emails/digest/digest_email.txt | 4 +- .../emails/digest/digest_email_html.txt | 4 +- templates/zerver/features.html | 137 +++++++++--------- templates/zerver/hello.html | 63 ++++---- templates/zerver/home.html | 47 +++--- templates/zerver/index.html | 16 +- templates/zerver/initial_invite_page.html | 16 +- templates/zerver/integrations.html | 18 +-- templates/zerver/invite_user.html | 13 +- templates/zerver/keyboard_shortcuts.html | 55 ++++--- templates/zerver/left-sidebar.html | 25 ++-- templates/zerver/login.html | 53 ++++--- templates/zerver/logout.html | 2 +- templates/zerver/markdown_help.html | 15 +- templates/zerver/missed_message_email.txt | 4 +- .../zerver/missed_message_email_html.txt | 4 +- templates/zerver/navbar.html | 35 +++-- templates/zerver/portico.html | 27 ++-- templates/zerver/portico_signup.html | 6 +- templates/zerver/register.html | 37 +++-- templates/zerver/reset.html | 11 +- templates/zerver/reset_confirm.html | 19 ++- templates/zerver/reset_done.html | 5 +- templates/zerver/reset_emailed.html | 7 +- templates/zerver/right-sidebar.html | 27 ++-- templates/zerver/search_operators.html | 41 +++--- templates/zerver/stream_creation_prompt.html | 17 +-- templates/zerver/subscriptions.html | 3 +- templates/zerver/tos_accept_body.txt | 4 +- templates/zerver/tutorial_finale.html | 11 +- templates/zerver/unsubscribe_link_error.html | 9 +- templates/zerver/unsubscribe_success.html | 7 +- .../zilencer/enterprise_tos_accept_body.txt | 4 +- tools/check-templates | 2 + zerver/forms.py | 2 +- zerver/lib/upload.py | 2 +- zerver/management/commands/makemessages.py | 75 ++++++++++ zerver/views/__init__.py | 19 +-- zproject/jinja2/__init__.py | 33 +++++ zproject/jinja2/backends.py | 66 +++++++++ zproject/jinja2/compressors.py | 22 +++ zproject/settings.py | 20 ++- 64 files changed, 706 insertions(+), 524 deletions(-) create mode 100644 zerver/management/commands/makemessages.py create mode 100644 zproject/jinja2/__init__.py create mode 100644 zproject/jinja2/backends.py create mode 100644 zproject/jinja2/compressors.py diff --git a/analytics/views.py b/analytics/views.py index ed5caf7b80..613716f757 100644 --- a/analytics/views.py +++ b/analytics/views.py @@ -4,10 +4,9 @@ from typing import Any, Dict, List, Tuple from django.db import connection from django.template import RequestContext, loader -from django.utils.html import mark_safe -from django.shortcuts import render_to_response from django.core import urlresolvers from django.http import HttpResponseNotFound +from jinja2 import Markup as mark_safe from zerver.decorator import has_request_variables, REQ, zulip_internal from zerver.models import get_realm, UserActivity, UserActivityInterval, Realm @@ -25,6 +24,8 @@ from six.moves import range from six.moves import zip eastern_tz = pytz.timezone('US/Eastern') +from zproject.jinja2 import render_to_response + def make_table(title, cols, rows, has_row_class=False): if not has_row_class: @@ -569,7 +570,7 @@ def get_activity(request): return render_to_response( 'analytics/activity.html', dict(data=data, title=title, is_home=True), - context_instance=RequestContext(request) + request=request ) def get_user_activity_records_for_realm(realm, is_bot): @@ -863,7 +864,7 @@ def get_realm_activity(request, realm): return render_to_response( 'analytics/activity.html', dict(data=data, realm_link=realm_link, title=title), - context_instance=RequestContext(request) + request=request ) @zulip_internal @@ -883,5 +884,5 @@ def get_user_activity(request, email): return render_to_response( 'analytics/activity.html', dict(data=data, title=title), - context_instance=RequestContext(request) + request=request ) diff --git a/confirmation/views.py b/confirmation/views.py index ea8fed825f..11d8425188 100644 --- a/confirmation/views.py +++ b/confirmation/views.py @@ -10,6 +10,7 @@ from django.template import RequestContext from django.conf import settings from confirmation.models import Confirmation +from zproject.jinja2 import render_to_response def confirm(request, confirmation_key): @@ -39,5 +40,4 @@ def confirm(request, confirmation_key): if obj: # if we have an object, we can use specific template templates.insert(0, 'confirmation/confirm_%s.html' % obj._meta.model_name) - return render_to_response(templates, ctx, - context_instance=RequestContext(request)) + return render_to_response(templates, ctx, request=request) diff --git a/docs/directory-structure.rst b/docs/directory-structure.rst index b02d927f89..aa2132002e 100644 --- a/docs/directory-structure.rst +++ b/docs/directory-structure.rst @@ -47,6 +47,18 @@ Views | ``zerver/views/__init__.py`` | other Django views | +--------------------------------+-----------------------------------------+ +Jinja2 Compatibility Files +========================== + ++-------------------------------------+--------------------------------------------------------------------+ +| ``zproject/jinja2/__init__.py`` | Jinja2 environment | ++-------------------------------------+--------------------------------------------------------------------+ +| ``zproject/jinja2/backends.py`` | Jinja2 backend | ++-------------------------------------+--------------------------------------------------------------------+ +| ``zproject/jinja2/compressors.py`` | Jinja2 compatible functions of Django-Pipeline | ++-------------------------------------+--------------------------------------------------------------------+ + + Static assets ============= @@ -67,7 +79,7 @@ Templates ========= +--------------------------+--------------------------------------------------------+ -| ``templates/zerver`` | For templates related to zerver views | +| ``templates/zerver`` | For Jinja2 templates for the backend (for zerver app) | +--------------------------+--------------------------------------------------------+ | ``static/templates`` | Handlebars templates for the frontend | +--------------------------+--------------------------------------------------------+ diff --git a/docs/new-feature-tutorial.rst b/docs/new-feature-tutorial.rst index d9550e3729..588df5332e 100644 --- a/docs/new-feature-tutorial.rst +++ b/docs/new-feature-tutorial.rst @@ -55,7 +55,7 @@ new event that is sent to clients, be sure to add a handler for it to **CSS:** The primary CSS file is ``static/styles/zulip.css``. If your new feature requires UI changes, you may need to add additional CSS to this file. -**Templates:** The initial page structure is rendered via Django templates +**Templates:** The initial page structure is rendered via Jinja2 templates located in ``templates/zerver``. For JavaScript, Zulip uses Handlebars templates located in ``static/templates``. Templates are precompiled as part of the build/deploy process. diff --git a/puppet/zulip/manifests/app_frontend_base.pp b/puppet/zulip/manifests/app_frontend_base.pp index 16741a3970..cb342dfa93 100644 --- a/puppet/zulip/manifests/app_frontend_base.pp +++ b/puppet/zulip/manifests/app_frontend_base.pp @@ -13,6 +13,7 @@ class zulip::app_frontend_base { "python-django-guardian", "python-django-pipeline", "python-django-bitfield", + "python-jinja2", # Needed for mock objects in decorators "python-mock", # Tornado dependencies diff --git a/templates/500.html b/templates/500.html index 402165fc41..75310d179f 100644 --- a/templates/500.html +++ b/templates/500.html @@ -3,7 +3,7 @@ {% block for_you %} isn't feeling too good. {% endblock %} {% block customhead %} -{{ block.super }} +{{ super() }} {% endblock %} diff --git a/templates/analytics/activity.html b/templates/analytics/activity.html index dbfe3ee4e6..fcdfab3584 100644 --- a/templates/analytics/activity.html +++ b/templates/analytics/activity.html @@ -1,8 +1,5 @@ {% extends "zerver/base.html" %} -{% load compressed %} -{% load minified_js %} - {# User Activity. #} {% block title %} @@ -11,9 +8,9 @@ {% block customhead %} -{{ block.super }} -{% minified_js 'activity' %} -{% compressed_css 'activity' %} +{{ super() }} +{{ minified_js('activity')|safe }} +{{ compressed_css('activity')|safe }} {% endblock %} {% block content %} @@ -31,7 +28,7 @@