Skip to content

Commit

Permalink
Merge branch 'percipient-escapeslack'
Browse files Browse the repository at this point in the history
* percipient-escapeslack:
  Misc cosmetic tidying.
  Remove unused import.
  Null out an __init__ file.
  Expand documentation.
  Automatically include the template tag.
  Add a templatetag which escapes only the characters Slack wants to escape.
  • Loading branch information
lamby committed Feb 20, 2016
2 parents 3dd6fe2 + 902c096 commit 598d8b7
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 3 deletions.
12 changes: 11 additions & 1 deletion django_slack/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,17 @@
Required blocks:
* **text** -- contains the message you wish to send. HTML entities are
automatically escaped. (See https://api.slack.com/docs/formatting for more)
automatically escaped. (See the
`Slack documentation <https://api.slack.com/docs/formatting>`_ for more
information.) If you wish to escape only the characters that MUST be escaped,
you can use the ``escapeslack`` tag, which is automatically available::
{% extends django_slack %}
{% block text %}
This message will escape only the necessary characters for slack:
{{ user.get_username|escapeslack }}
{% endblock %}
Required blocks which can be defaulted globally and overridden (see *Configuration*):
Expand Down
9 changes: 7 additions & 2 deletions django_slack/api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import json

from django.conf import settings
from django.template.loader import render_to_string
from django.template import Engine

from . import app_settings
from .utils import from_dotted_path

engine = Engine(
app_dirs=True,
builtins=['django_slack.templatetags.escapeslack'],
)

backend = from_dotted_path(app_settings.BACKEND)()

def slack_message(template, context=None, attachments=None, fail_silently=app_settings.FAIL_SILENTLY):
Expand Down Expand Up @@ -55,7 +60,7 @@ def slack_message(template, context=None, attachments=None, fail_silently=app_se
# Render template if necessary
if v.get('render', True):
try:
val = render_to_string(template, dict(
val = engine.render_to_string(template, dict(
context,
django_slack='django_slack/%s' % k,
)).strip().encode('utf8', 'ignore')
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions django_slack/templatetags/escapeslack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from django import template
from django.utils import six
from django.utils.encoding import force_text
from django.utils.functional import allow_lazy
from django.utils.safestring import SafeText, mark_safe
from django.template.defaultfilters import stringfilter

register = template.Library()

_slack_escapes = {
ord('&'): u'&amp;',
ord('<'): u'&lt;',
ord('>'): u'&gt;',
}

@register.filter(is_safe=True)
@stringfilter
def escapeslack(value):
"""
Returns the given text with ampersands and angle brackets encoded for use in
the Slack API, per the Slack API documentation:
<https://api.slack.com/docs/formatting#how_to_escape_characters>
This is based on django.template.defaultfilters.escapejs.
"""
return mark_safe(force_text(value).translate(_slack_escapes))
escapeslack = allow_lazy(escapeslack, six.text_type, SafeText)
8 changes: 8 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings'

from django.core import management

if __name__ == "__main__":
management.execute_from_command_line()
Empty file added tests/__init__.py
Empty file.
21 changes: 21 additions & 0 deletions tests/backends.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from django_slack.utils import Backend

class StorageBackend(Backend):
"""
A backend that stores all messages sent.
"""

def __init__(self):
self.reset()

def reset(self):
"""
Clear any messages.
"""
self.messages = []

def send(self, url, data):
self.messages.append({
'url': url,
'data': data,
})
25 changes: 25 additions & 0 deletions tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
SECRET_KEY = 'not_empty'

TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages'
],
},
},
]

INSTALLED_APPS = (
'tests',
'django_slack',
)

SLACK_BACKEND = 'tests.backends.StorageBackend'
SLACK_TOKEN = 'fake-token'
5 changes: 5 additions & 0 deletions tests/templates/escape.slack
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends django_slack %}

{% block text %}
{{ text|escapeslack }}
{% endblock %}
5 changes: 5 additions & 0 deletions tests/templates/test.slack
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends django_slack %}

{% block text %}
{{ text }}
{% endblock %}
62 changes: 62 additions & 0 deletions tests/test_escaping.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import unittest

from django_slack import slack_message
from django_slack.api import backend

class SlackTestCase(unittest.TestCase):
def setUp(self):
backend.reset()

def assertMessageCount(self, count):
self.assertEqual(len(backend.messages), count)

def assertMessage(self, url=None, **kwargs):
"""
Ensure there was only one message sent with a URL and data values.
"""

self.assertMessageCount(1)
message = backend.messages[0]

# Optionally ensure the URL.
if url is not None:
self.assertEqual(url, message['url'])

# Ensure each input value in data.
for kwarg, value in kwargs.items():
self.assertEqual(value, message['data'][kwarg])

class TestEscaping(SlackTestCase):
def test_simple_message(self):
slack_message('test.slack', {'text': 'test'})
self.assertMessage(text='test')

def test_escaped(self):
"""
Simple test of the Django escaping to illustrate problem.
"""
slack_message('test.slack', {'text': '< > & " \''})
self.assertMessage(text='&lt; &gt; &amp; &quot; &#39;')

def test_escape_tag(self):
"""
Test using the escape tag, but not escaping anything.
"""
slack_message('escape.slack', {'text': 'test'})
self.assertMessage(text='test')

def test_escape_chars(self):
"""
Test the characters Slack wants escaped.
See <https://api.slack.com/docs/formatting#how_to_escape_characters>
"""
slack_message('escape.slack', {'text': '< > &'})
self.assertMessage(text='&lt; &gt; &amp;')

def test_not_escape_chars(self):
"""
Test normal HTML escaped characters that Slack doesn't want escaped.
"""
slack_message('escape.slack', {'text': '" \''})
self.assertMessage(text='" \'')

0 comments on commit 598d8b7

Please sign in to comment.