Implements keyword sub feature for bulk emails

This commit pulls in changes from #4487 that implement keyword
substitution for bulk emails. With these changes, an instructor can
include keywords in their bulk emails which will be automatically substituted
with the corresponding value for the recipient of the email. Keywords are
of the form %%keyword%%, and the keywords implemented in this commit include:

%%USER_ID$$ => anonymous_user_id
%%USER_FULLNAME%% => user profile name
%%COURSE_DISPLAY_NAME%% => display name of the course
%%COURSE_END_DATE%% => end date of the course

Client-side validations have also been implemented to ensure that only emails
with well-formed keywords can be sent.
The architecture is designed such that adding in new keywords in the future
would be relatively straight-forward.
It's really kind of unfortunate that the keywords have to be mapped separately in Coffee and in Python. I can't think of a nice way to only define the mapping in one place, but wouldn't it be neat? Perhaps if you meditate on it over the weekend you'll think of something.

(It would be almost trivial, but what if keyword substitution was it's own python module, with all the variables and utility functions in one place, and you could do like:
```pip install my_really_great_keyword_substituter```
and then 
```from my_really_great_keyword_substituter import KeyWordSubber
...
Subber = KeyWordSubber(escape='%%') # Subber is a singleton, or else a borg
Subber.register('USER_ID', user_id_sub)
...
```
and also
```
Subber = KeyWordSubber(escape='%%')

@require_http_methods(["GET"]) # this is how I tip you off that this is a view function
def get_valid_keyword_substitutions():
    return json.dumps(Subber.keys()) # because of course Subber should conform to the mapping protocol
```
and then in coffee or js or whatever, you could do an AJAX call and get the set of valid substitutions. You'd want that to be a singleton too, so you only did it once per user session. 

I'm not necessarily proposing that you build all this; it's just how I'd want it to be in an ideal world, not least because I've written code very similar to what this all does on a few occasions in the past, and now I wish it was a library I could just pip install. :-)  (On the other hand, if you found you had a lot of free time on your hands and wanted to persue the ideal, I'd be very encouraging.)