* Announcements are per group, independent of user's groups.
* Tabs are all about user's groups. Tab titles are group names.
* Add migration for unique group id in GroupDashboard because of OneToOneField.
* Create utils.py to avoid circular imports.
* Add GroupDashboard model
* Change dashboard_tabs from a macro to a helper so it can get at dashboards(). Rename it "user_dashboard_tabs". Remove explicit references to tabs; look them up from dashboards.user instead.
* Enable Questions dashboard.
* Announcement model + migration.
* Model utils: get_for_group, get_side_wide, is_visible
* Site-wide announcements are in no group.
* Minimally formatted and styled for review/question dashboards.
* Drop the `Activity` class and table, replace with `Action`.
* And associated renaming.
* Define the concept of an `ActionFormatter` and a base class.
* Add a README.
Adds the default permissions for upload.imageattachment and switches the
views to check for the correct permission. Also ensures that del_image_async
returns JSON and improves test coverage of that view.
* Add an is_active column.
* Add stub implementation for confirmation email.
* Update .notify() to send a confirmation email and raise an ActivationRequestFailed exception if the email message fails to send.
* Add function Event._activation_email, which receives a watch and an email and returns an EmailMessage.
* Make questions anonymous watches work, add extra views for confirming/unsubscribing from watches.
* Add Event.get_activation_url() for use in email templates.
* Add Event.get_watch_description() for events to describe their watches in string form. This comes in handy when sending out emails, to explain why the receiver is getting this.
* Catch SMTPRecipientsRefused exception and show message about it.
* Update wiki tests.
* New: EventUnion. Made some interesting building blocks in the process. For example, peekable iterators are really useful sometimes. Also wrote a merge() routine which is handy for merging sorted lists.
* Make (watch, name) unique on WatchFilter.
* Make _unique_by_email() usable not only on (possibly-no-email-having-User, Watch) pairs but on ones with EmailUsers as well. Users will trump (the anonymous) EmailUsers when merging.
* Make _unique_by_email() responsible for the creation of EmailUsers when necessary.
* Quit skipping the cross-event de-duping integration test in forums.
* Adjust forums' ThreadReplyEvent to use EventUnion.
* Rename watch() and its brethren to notify(). Before, it sounded like Events were watching Users, which is wrong and also spooky.
* Rename _watches() to _users_watching(), since it returns Users after all.
* Rename _build_mails() to mails() for brevity and to reflect its return value.
* Tweak DB field lengths. 10 chars of secret is plenty when our alphabet is 52 chars. 30 for event_type is plenty. 20 for watch filter name makes James happy (and causes Event.filters to become a set rather than a map).
* Make a user() test helper.
* Add `save` kwargs to watch() and watch_filter() helpers. watch() now uses the user() helper instead of get_user(), which means it no longer needs the users.json fixture.
* Reorder fields in Watch to put the required fields first.
* Add a confirm() method to Watch.
* Stop speccing CRC32; it's handy since MySQL knows it and it's portable, but it's not much of a hash function.
* Abstract some stuff from RegistrationProfile into ConfirmationProfile
* Add EmailChange model with an EmailChangeManager
* Store emails temporarily in EmailChange model and use email activation to confirm the newly set email.
* Update notifications to send to the new email. (This should be removed once we have a better notifications model.)