Skip to content

Commit

Permalink
Wrap user-facing strings in gettext; add icon_emoji
Browse files Browse the repository at this point in the history
  • Loading branch information
posita authored and mbogosian committed Feb 25, 2018
1 parent 6e51e9e commit f2c2ead
Show file tree
Hide file tree
Showing 11 changed files with 214 additions and 106 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ include \
tests/requirements.txt

recursive-include emojiwatch/static *
recursive-include emojiwatch/templates *
32 changes: 6 additions & 26 deletions docs/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Now you can add it to your ``DJANGO_SETTINGS_MODULE``:
)
EMOJIWATCH = {
'slack_auth_token': '...',
'slack_verification_token': '...',
}
Expand All @@ -88,34 +89,13 @@ If you haven't already, you'll also need to `enable the admin site <https://docs
Configuring Token Encryption in Django's Database
+++++++++++++++++++++++++++++++++++++++++++++++++

By default, workspace IDs will be encrypted using a hash of the ``SECRET_KEY`` Django setting.
To override this, use the ``FERNET_KEYS`` setting. For example:

.. code-block:: python
from os import environ
SECRET_KEY = environ['SECRET_KEY']
# Use only Base64-encoded 32 byte values for keys; don't derive them
# from arbitrary strings
FERNET_USE_HKDF = False
# For supporting any legacy keys that were used when FERNET_USE_HKDF
# was True
from fernet_fields.hkdf import derive_fernet_key
# The keys
FERNET_KEYS = [
# The first entry is the current key (for encrypting and
# decrypting)
environ['FERNET_KEY'],
# Optional additional entries are older keys for decrypting only
# environ['OLD_FERNET_KEY_1'],
# Equivalent to the default key
# derive_fernet_key(SECRET_KEY),
]
Notes associated with a watcher are encrypted in the Django database using |django-fernet-fields|_.
By default, the encryption key is derived from the ``SECRET_KEY`` Django setting.
To override this, use the ``FERNET_KEYS`` and ``FERNET_USE_HKDF`` settings.
See `the docs <http://django-fernet-fields.readthedocs.io/en/latest/#keys>`__ for details.

Slack App Setup
~~~~~~~~~~~~~~~
Slack App and Watcher Setup
~~~~~~~~~~~~~~~~~~~~~~~~~~~

For illustration, we'll create a `workspace-based Slack app <https://api.slack.com/docs/token-types#workspace>`__, but we could just as easily use a traditional one.

Expand Down
7 changes: 3 additions & 4 deletions emojiwatch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@

LOGGER = _logging.getLogger(__name__)
SETTINGS = getattr(d_conf.settings, 'EMOJIWATCH', {})
SLACK_AUTH_TOKEN = SETTINGS.get('slack_auth_token')
SLACK_VERIFICATION_TOKEN = SETTINGS.get('slack_verification_token')

try:
SLACK_VERIFICATION_TOKEN = SETTINGS['slack_verification_token']
except (KeyError, TypeError):
SLACK_VERIFICATION_TOKEN = None
default_app_config = 'emojiwatch.apps.EmojiwatchConfig'
57 changes: 57 additions & 0 deletions emojiwatch/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# -*- encoding: utf-8 -*-
# ======================================================================
"""
Copyright and other protections apply. Please see the accompanying
:doc:`LICENSE <LICENSE>` and :doc:`CREDITS <CREDITS>` file(s) for rights
and restrictions governing use of this software. All rights not
expressly waived or licensed are reserved. If those files are missing or
appear to be modified from their originals, then please contact the
author before viewing or using this software in any capacity.
"""
# ======================================================================

from __future__ import absolute_import, division, print_function

TYPE_CHECKING = False # from typing import TYPE_CHECKING

if TYPE_CHECKING:
import typing # noqa: F401 # pylint: disable=import-error,unused-import,useless-suppression

from builtins import * # noqa: F401,F403 # pylint: disable=redefined-builtin,unused-wildcard-import,useless-suppression,wildcard-import
from future.builtins.disabled import * # noqa: F401,F403 # pylint: disable=no-name-in-module,redefined-builtin,unused-wildcard-import,useless-suppression,wildcard-import
from future.standard_library import install_aliases
install_aliases()

# ---- Imports ---------------------------------------------------------

from gettext import gettext

import django.apps as d_apps

from . import (
LOGGER,
SLACK_AUTH_TOKEN,
SLACK_VERIFICATION_TOKEN,
)

# ---- Classes ---------------------------------------------------------

# ======================================================================
class EmojiwatchConfig(d_apps.AppConfig):

# ---- Data --------------------------------------------------------

name = 'emojiwatch'
verbose_name = gettext('Emojiwatch')

# ---- Overrides ---------------------------------------------------

def ready(self):
# type: (...) -> None
super().ready() # type: ignore # py2

if not SLACK_AUTH_TOKEN:
LOGGER.critical("EMOJIWATCH['slack_auth_token'] setting is missing")

if not SLACK_VERIFICATION_TOKEN:
LOGGER.critical("EMOJIWATCH['slack_verification_token'] setting is missing")
8 changes: 4 additions & 4 deletions emojiwatch/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 2.0.2 on 2018-02-23 08:04
# Generated by Django 2.0.2 on 2018-02-25 20:56

import django.core.validators
from django.db import migrations, models
Expand All @@ -18,9 +18,9 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('_version', models.IntegerField(default=-2147483648)),
('team_id', models.CharField(max_length=63, unique=True, validators=[django.core.validators.RegexValidator('^T[0-9A-Z]*$', message='Must be of the format (e.g.) T123ABC...')], verbose_name='Team ID')),
('workspace_token', fernet_fields.fields.EncryptedCharField(max_length=255, validators=[django.core.validators.RegexValidator('^xoxa-([0-9A-Fa-f]+)+', message='Must be of the format (e.g.) xoxa-1f2e3d-4c5b6a...')], verbose_name='Workspace Token')),
('channel_id', models.CharField(max_length=63, validators=[django.core.validators.RegexValidator('^C[0-9A-Z]*$', message='Must be of the format (e.g.) C123ABC...')], verbose_name='Channel ID')),
('team_id', models.CharField(default='T', max_length=63, unique=True, validators=[django.core.validators.RegexValidator('\\AT[0-9A-Z]+\\Z', message='Must be of the format (e.g.) T123ABC...')], verbose_name='Team ID')),
('channel_id', models.CharField(default='C', max_length=63, validators=[django.core.validators.RegexValidator('\\AC[0-9A-Z]+\\Z', message='Must be of the format (e.g.) C123ABC...')], verbose_name='Channel ID')),
('icon_emoji', models.CharField(default=':robot_face:', max_length=255, validators=[django.core.validators.RegexValidator('\\A:[\\w-]+:\\Z', message='Must be of the format (e.g.) :emoji_name:...')], verbose_name='Icon Emoji')),
('notes', fernet_fields.fields.EncryptedTextField(blank=True, default='', verbose_name='Notes')),
],
options={
Expand Down
39 changes: 21 additions & 18 deletions emojiwatch/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
# ---- Imports -----------------------------------------------------------

import fernet_fields
import slacker

from gettext import gettext

Expand All @@ -36,11 +35,12 @@
__all__ = ()

CHANNEL_ID_MAX_LEN = 63
CHANNEL_ID_RE = r'^C[0-9A-Z]*$'
CHANNEL_ID_RE = r'\AC[0-9A-Z]+\Z'
ICON_EMOJI_DEFAULT = ':robot_face:'
ICON_EMOJI_MAX_LEN = 255
ICON_EMOJI_RE = r'\A:[\w-]+:\Z'
TEAM_ID_MAX_LEN = 63
TEAM_ID_RE = r'^T[0-9A-Z]*$'
WORKSPACE_TOKEN_MAX_LEN = 255
WORKSPACE_TOKEN_RE = r'^xoxa-([0-9A-Fa-f]+)+'
TEAM_ID_RE = r'\AT[0-9A-Z]+\Z'

# ---- Exceptions --------------------------------------------------------

Expand Down Expand Up @@ -96,7 +96,7 @@ def _do_update(self, base_qs, using, pk_val, values, update_fields, forced_updat
updated = filtered._update(values) # pylint: disable=protected-access

if updated == 0:
raise StaleVersionError('{!r}._version {} is stale'.format(self, self._version - 1))
raise StaleVersionError(gettext('{!r}._version {} is stale').format(self, self._version - 1))

assert updated == 1

Expand All @@ -116,6 +116,7 @@ class Meta(object):
# ---- Properties ----------------------------------------------------

team_id = d_d_models.CharField(
default='T',
max_length=TEAM_ID_MAX_LEN,
null=False,
unique=True,
Expand All @@ -127,30 +128,32 @@ class Meta(object):

team_id.short_description = gettext('Team ID (e.g., T123ABC...)')

workspace_token = fernet_fields.EncryptedCharField(
max_length=WORKSPACE_TOKEN_MAX_LEN,
channel_id = d_d_models.CharField(
default='C',
max_length=CHANNEL_ID_MAX_LEN,
null=False,
validators=[
d_c_validators.RegexValidator(WORKSPACE_TOKEN_RE, message=gettext('Must be of the format (e.g.) xoxa-1f2e3d-4c5b6a...')),
d_c_validators.RegexValidator(CHANNEL_ID_RE, message=gettext('Must be of the format (e.g.) C123ABC...')),
],
verbose_name=gettext('Workspace Token'),
verbose_name=gettext('Channel ID'),
)

channel_id = d_d_models.CharField(
max_length=CHANNEL_ID_MAX_LEN,
channel_id.short_description = gettext('Channel ID (e.g., C123ABC...)')

icon_emoji = d_d_models.CharField(
default=ICON_EMOJI_DEFAULT,
max_length=ICON_EMOJI_MAX_LEN,
null=False,
validators=[
d_c_validators.RegexValidator(CHANNEL_ID_RE, message=gettext('Must be of the format (e.g.) C123ABC...')),
d_c_validators.RegexValidator(ICON_EMOJI_RE, message=gettext('Must be of the format (e.g.) :emoji_name:...')),
],
verbose_name=gettext('Channel ID'),
verbose_name=gettext('Icon Emoji'),
)

icon_emoji.short_description = gettext('Icon Emoji (e.g., {}...)').format(ICON_EMOJI_DEFAULT)

notes = fernet_fields.EncryptedTextField(
blank=True,
default='',
verbose_name=gettext('Notes'),
)

@property
def slack(self):
return slacker.Slacker(self.workspace_token)
5 changes: 5 additions & 0 deletions emojiwatch/templates/admin/emojiwatch/change_form.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block after_field_sets %}<p>
{% trans 'A watcher for receiving <a alt="emoji_changed event API documentation" href="https://api.slack.com/events/emoji_changed"><code>emoji_changed</code></a> events for a given team and posting updates to a given channel via <a alt="chat.postMessage API documentation" href="https://api.slack.com/methods/chat.postMessage"><code>chat.postMessage</code></a>.' %}
</p>{% endblock %}
Loading

0 comments on commit f2c2ead

Please sign in to comment.