Modifications to the mailer cron script.

The script will no longer halt on exceptions. Instead, it will send the exceptions to the sender address and the configured bounce address. It will attempt to retry sending to recipients who it failed to deliver to the first time (this only happens the next time the cron script is executed).
Shows class-hours by category on the dashboard
Used hashed URL as identifier in client side QSD editing

We used to identify QSDs by ID, and now after schema simplification,
we mainly identify them by URL.  (This is helpful in our inline QSD
block rendering, where we don't want to create a QSD object unless the
default text is edited by an admin... so we don't have an ID to work
with.)  The problem is you can't name an HTML DOM element after a URL.

To fix this, pass around URLs instead of IDs in our Javascript, and
compute an 8-character hash of the URL to use as a "slug" for DOM
IDs.

This is another method of addressing the underlying problem that
I (incorrectly) hacked around in ca061ad.
Calculate QSD edit ID only in Python

Instead of having functions in both Javascript and Python to hash the
QSD url, we can just pass it into Javascript in the template.  This
requires passing both the url and the edit_id around on the JS side,
but it lets us remove some unnecessary code.

Thanks to Anthony for pointing this out.
Adds "Grades" section on the dashboard, which shows students per grade
and number of subjects/sections available to each grade.

It still should probably be optimized better, and students per grade
currently doesn't work for programs before the current year.
Shows additional info in dashboard status popup
Changes getGrade(program) to return the grade the student was in when that program ran
Removes user from InlineQSDNode

The InlineQSDNode class and the inline_qsd_block tag used to take a
template.Variable parameter called user. On the rendered page, the
inline QSD would be editable if and only if the resolved user had the
required permissions.

A while ago, we stopped doing this at the template level, and started
doing this at the CSS/JS level. But we didn't remove the user parameter
from InlineQSDNode / inline_qsd_block, even though it mostly doesn't do
anything anymore.

This commit removes the user parameter, and updates templates to remove
the remaining user arguments.

At the same time, this commit also simplifies some templates (by
replacing trivial uses of inline_qsd_block with the render_inline_qsd
tag), fixes a typo, makes some minor improvements to
InlineQSDNode.render(), and removes the unused QSDStringNode class.

NOTE: This commit changes the behavior of new inline QSDs. Previously, a
QSD object was created and saved on the first load of a page, using the
resolution of the user parameter as the author. Now, since InlineQSDNode
no longer has a user parameter, this will not occur, and the QSD object
will be created on first edit. This may or may not have caching
implications. If it does, the old functionality can easily be restored,
by getting request.user from the context parameter to
InlineQSDNode.render(), and using that user as the author.
Updates server_setup scripts to use packages_*.txt
Removes Dropbox install
Moves some logic out of getGrade() into other methods
Make extra_steps inline QSD URL consistent with previous usage
Properly cache {% render_inline_program_qsd %}

Fixes the bogus depend_on_row() call that was there before.
This requires a "reverse lookup" of the Program object and QSD name
from the full URL.  This is inherently inexact, but I think it should
be acceptable.  (See QuasiStaticData.program_from_url().)
Fix QuasiStaticData.program_from_url for certain URLs
Fix some messed up CSS for inline QSD
Merge branch 'main' into qsd_edit_by_url

Conflicts:
	esp/templates/inclusion/qsd/render_qsd_inline.html
Deduplicates render_qsd_inline

Deduplicates render_qsd_inline.html by initializing QSD objects for
uncreated inline QSDs.

Adds the method QSDManager.get_by_url_else_init(), which tries to get a
QSD object by url, and in the failure case initializes a new, unsaved
QSD object. Updates the render_qsd template tags to use this method,
rather than returning different types of context dictionaries in the two
cases. Since the template tags now always return a QSD object context
variable, deduplicates render_qsd_inline.html to remove the special case
for uncreated inline QSDs. Also deduplicates qsd.views.ajax_qsd() to
remove the create command, and removes the now-unnecessary Javascript
function qsd_inline_create.
Revert "Make extra_steps inline QSD URL consistent with previous usage"

This reverts commit 2aee84054b77a58182cc9e440973e040e1bafe20.
Fixes misuse of kwargs with argcache
Fixes ajax request bug with debug_toolbar

The Django debug toolbar should not be included in all requests, only
page loads. The best way I could think of to accomplish this was by
having SHOW_TOOLBAR_CALLBACK return False if request.is_ajax() is True.

This may have been causing multiple bugs. The bug I noticed was with
inline QSD editing. In esp/public/media/scripts/qsd.js, in
qsd_inline_update(), the function call JSON.parse(data) was failing
because data.content included, in addition to the QSD content, the
content of the Django debug toolbar. So whenever inline QSD was edited,
the new content would be saved correctly, but it would not render the
updated content because qsd_inline_update() was failing.