Tag interface part 2: Better record added/removed tags in the tx log

Summary:
Added or removed tags now appear in the transaction log as revisions of the
Thread object.

Don't have FolderItems inherit from HasRevisions (it doesn't need to).

[Stop reading here if you don't care about implementation details.]

Given related parent and child tables, we add the ability to record updates to
a parent's collection of children as a revision of the parent. See the
docstring in models/revision.py for details.

(Why not just have the TagItems association table subclass HasRevisions? you
may ask. Good question. One reason is that handling removed tags that way is
tricky, because all the transaction log can tell you is that a TagItem with
some ID was deleted. But you can't figure out from that what the corresponding
tag or thread was. Another reason is that delete transactions for the TagItem
table aren't created at all if you use an association proxy
(`thread.tags.remove(tag)`), because in this case sqlalchemy directly issues
sql to the database, without instantiating the association object.

Test Plan: Still working on the tests.

Reviewers: spang

Reviewed By: spang

Differential Revision: https://review.inboxapp.com/D160
Tag interface, part 3.

Summary:
 * Delete local actions code. Local actions are now expressed solely by
   adding/removing tags. Refactor the rest of the existing actions code as a
   result.

 * Add a listener service which checks the transaction log for tag mutations,
   and executes the corresponding syncback actions.

 * Expose 'unread' and 'archive' as tags on threads.

 * Support dependent tag mutations (e.g., remove the 'inbox' tag when you add
   the 'archive' tag, and vice versa).

 * Refactor drafts code to work with new syncback infrastructure. In the
   process, move a bunch of provider-agnostic stuff out of the Gmail-specific
   code.

 * Unrelatedly, ensure that when we do something like
      db_session.query(Object).filter(Object.related_id == some_id),
   we always filter by the *id*, not the object itself (otherwise, with our
   custom primaryjoin condition, sqlalchemy basically loads a Cartesian
   product of both the table and the related table). This was previously the
   source of a gnarly crashing bug in this code.

Test Plan: Unit tests added.

Reviewers: spang, kav-ya

Reviewed By: kav-ya

CC: mg

Differential Revision: https://review.inboxapp.com/D162