Remove duplicate messages with optional ability to have duplicates... and more

We sometimes unexpectedly see duplicate messages--in the case where an action happens on the server, but no webpage is generated on the client.  This is especially a concern as we moved to AJAX-based messages: if that AJAX request fails, messages may pile up on the server without ever being emitted to the client.

This change addresses the issue by preventing (by default) the addition of duplicate messages to the message buffer.  A duplicate message is one where the message text, level, and "extra tags" all match.

The design:
* Create a file nodup_messages
* Import Django's messages interface to that file
* override necessary Django functions for checking for duplicates (and allowing an optional flag to enable duplicates)
* Change all of our apps from importing Django messages directly, to importing our nodup_messages as messages

In addition, during the code change I came across the require_admin / require_admin_api code, and made a change suggested by @jamalex to make that more simple and clean (use request.is_ajax() and have only one require_admin decorator).