This makes the flow more intuitive, since the user sets a password
with the other parts of the form where they are configuring their own
account (as opposed to configuring the organization they are
creating).
Adds a database migration, adds a new string_id argument to the management
realm creation command, and adds a short name field to the web realm
creation form when REALMS_HAVE_SUBDOMAINS is False.
The lightbox will now distinguish between whether or not something is a
photo and a YouTube video by the class name of the message inline
preview. It embeds the YouTube video in the lightbox as an iFrame
rather than previewing the video screenshot.
Filter behaves similarly to filter in left sidebar, see PR #684. Added
stream input field to the stream creation modal along with other settings,
for clarity.
Fixes#455, #563.
Previously, we sent users to an "invite your friends" page after they
created an organization. This commit removes that step in the flow and sends
users directly to the home page. We also remove the now-unused
initial_invite_page.html template, initial_invite.js (which pre-filled the
invite emails with characters from literature), and the /invite URL route.
- Expand a box full of emojis into the
compose window for users to graphically select emojis.
- Append an emoji to the end of the message when a user
clicks the emoji in the emoji box.
- Trap the escape key to always close the emoji box
before closing anything else if the box is open.
- Fixes: #147.
Adds a new field org_type to Realm. Defaults for restricted_to_domain
and invite_required are now controlled by org_type at time of realm
creation (see zerver.lib.actions.do_create_realm), rather than at the
database level. Note that the backend defaults are all
org_type=corporate, since that matches the current assumptions in the
codebase, whereas the frontend default is org_type=community, since if
a user isn't sure they probably want community.
Since we will likely in the future enable/disable various
administrative features based on whether an organization is corporate
or community, we discuss those issues in the realm creation form.
Before we actually implement any such features, we'll want to make
sure users understand what type of organization they are a member of.
Choice of org_type (via radio button) has been added to the realm
creation flow and the realm creation management command, and the
open-realm option removed.
The database defaults have not been changed, which allows our testing code
to work unchanged.
[includes some HTML/CSS work by Brock Whittaker to make it look nice]
This includes reduced title font size to bring the total info bar
height to the same height as the buttons, and an image preview that
doesn’t hit the walls of the container.
This adds support for running a Zulip production server with each
realm on its own unique subdomain, e.g. https://realm_name.example.com.
This patch includes a ton of important features:
* Configuring the Zulip sesion middleware to issue cookier correctly
for the subdomains case.
* Throwing an error if the user tries to visit an invalid subdomain.
* Runs a portion of the Casper tests with REALMS_HAVE_SUBDOMAINS
enabled to test the subdomain signup process.
* Updating our integrations documentation to refer to the current subdomain.
* Enforces that users can only login to the subdomain of their realm
(but does not restrict the API; that will be tightened in a future commit).
Note that toggling settings.REALMS_HAVE_SUBDOMAINS on a live server is
not supported without manual intervention (the main problem will be
adding "subdomain" values for all the existing realms).
[substantially modified by tabbott as part of merging]
This adds an event listener (by way of delegation) to the
.message_inline_image elements that pops up the overlay and hides it
when the overlay exit is clicked.
Fixes#654.
This adds a support a notification at the top of the screen that
alerts a user they’ve muted a stream and gives them the option to
unmute if it was an accident.
The notification disappears automatically after 4s, but if a user
moves their mouse over the notification, the timer resets to 2s after
the user moves their mouse off the notification, to make it easy for
users to read the full message and decide what to do.
This changes the JavaScript to fix the hash system to correctly save
state to allow a user to deep link to a particular app platform.
The mechanism is handled by a click event on #apps-tabs [data-toggle]
that fires a hash setter which then fires a hash getter which loads the
correct tab if necessary.
The registration pages — both the landing page and the follow through
page after receiving an email have been restyled to be more linear in
nature and centered using flex box.
The portico template inherits base but not index, but index inherits
base. Therefore the viewport meta-tag should be moved to base so that
the entire site can have the meta tag.
Also changes all links to /integrations to be relative rather than
absolute, so that users will primarily access the /integrations page
for their subdomain.
When the feedback module is hidden the #userlist_header border would
brush against the navbar. Check if the feedback header isn’t there and
remove the border top.
The policy here is essentially:
* Pages on the central server (documentation, etc.) are server_uri.
* Pages associated with a user's realm server (anything logged-in, plus
things like their login page) are realm_uri.
Changes relative path to an absolute path (that doesn't contain the
subdomain) for various links to
/create_realm, /api, /apps, /integrations, /hello, /terms, and the logged
out / (the Zulip in the upper left corner of portico)
I typically left links internal to the relevant pages (e.g. a link from
integrations.html to a subpage of integrations/) as relative links, and
changed external links from within the app to the absolute path (e.g. the
link to integrations from the gear menu).
Helps in case the development environment is not using https.
We get the value from zerver/context_processors.py (which is
ultimately reading it from settings)
Most directly useful for the migration to zulipchat.com.
Creates a new field in UserProfile to store the tos_version, as well as two
new settings TOS_VERSION and FIRST_TIME_TOS_TEMPLATE. We check for a version
mismatch between what the user has signed and the current
settings.TOS_VERSION whenever the user hits the home page, and redirect them
if needed.
Note that accounts_accept_terms.html and
zerver.views.accounts_accept_terms were unused before this commit
(they date from c327446537)
Bitbucket changed the format of their API. The old format is still
useful for BitBucket enterprise, but for the main cloud verison of
Bitbucket, we need a new BitBucket integration supporting the new API.
This works around a nasty problem with Webpack that you can't run two
copies of the Webpack development server on the same project at the
same time (even if on different ports). The second copy doesn't fail,
it just hangs waiting for some lock, which is confusing; but even if
that were to be solved, we don't actually need the webpack development
server running to run the Casper tests; we just need bundle.js built.
So the easy solution is to just run webpack manually and be sure to
include bundle.js in the JS_SPECS entry.
As a follow-up to this change, we should clean up how test_settings.py
is implemented to not require duplicating code from settings.py.
Fixes#878.
This prototype from Dropbox Hack Week turned out to be too inefficient
to be used for realms with any significant amount of history, so we're
removing it.
It will be replaced by https://github.com/zulip/zulip/pull/673.
Assigns hotkey 'w' to search streams.
Only show search box when active. Activate with hotkey or by clicking
STREAMS.
Filter matches at the beginning of words in stream name.
Behaviour is otherwise almost identical to user search.
Casper tests.
The `email` parameter to this template is only set if the user is
redirected to the login form after attempting to register for a new
account with an email address that already has an account.
Following strings are marked translatable:
- All strings which are passed to `button.text` or which affect the
text of buttons.
- All strings passed to `placeholder`.
- All strings passed to `compose_error`.
Fixes#969
* The warning contains a count of the number of people in the stream.
* An error appears if the warning is ignored and the user tries to
send the message anyway.
* The message cannot be sent until the warning is acknowledged or @all
/ @everyone is removed.
* This only applies to stream messages and not private messages.
Fixes#853.
In order to genericize use of Zulip outside companies,
all instances of coworkers have been changed to users.
NOTABLE EXCEPTION: When the Zulip instance is domain-
locked, the reference to coworkers remains. The reason
for this is twofold: first, the majority of Zulip instances
which require a particular domain will be locked to a
company, and second, the template variable for the domain
necessary should be added to the alert so it is clear
to the user what the domain needs to be for access.
Fixes: #861.
This results in a substantial performance improvement for all of
Zulip's backend templates.
Changes in templates:
- Change `block.super` to `super()`.
- Remove `load` tag because Jinja2 doesn't support it.
- Use `minified_js()|safe` instead of `{% minified_js %}`.
- Use `compressed_css()|safe` instead of `{% compressed_css %}`.
- `forloop.first` -> `loop.first`.
- Use `{{ csrf_input }}` instead of `{% csrf_token %}`.
- Use `{# ... #}` instead of `{% comment %}`.
- Use `url()` instead of `{% url %}`.
- Use `_()` instead of `{% trans %}` because in Jinja `trans` is a block tag.
- Use `{% trans %}` instead of `{% blocktrans %}`.
- Use `{% raw %}` instead of `{% verbatim %}`.
Changes in tools:
- Check for `trans` block in `check-templates` instead of `blocktrans`
Changes in backend:
- Create custom `render_to_response` function which takes `request` objects
instead of `RequestContext` object. There are two reasons to do this:
1. `RequestContext` is not compatible with Jinja2
2. `RequestContext` in `render_to_response` is deprecated.
- Add Jinja2 related support files in zproject/jinja2 directory. It
includes a custom backend and a template renderer, compressors for js
and css and Jinja2 environment handler.
- Enable `slugify` and `pluralize` filters in Jinja2 environment.
Fixes#620.
This integration relies on the Teamcity "tcWebHooks" plugin which is
available at
https://netwolfuk.wordpress.com/category/teamcity/tcplugins/tcwebhooks/
It posts build fail and success notifications to a stream specified in
the webhook URL.
It uses the name of the build configuration as the topic.
For personal builds, it tries to map the Teamcity username to a Zulip
username, and sends a private message to that person.
Previously, the Zulip subscriptions page's error bar would always be
at the very top of the scrollable view, and thus would likely be out
of view when an error happened. This fixes it by having the error bar
always placed below the search box (and thus visible regardless of
where in the scrollable streams view we are).
Fixes: #515.
[commit message and comments expanded by tabbott]
While we already don't link to /terms anywhere on the site, they can still be
accessed if you navigate to /terms directly. Now, those routes will only be
exported on the Zulip.com service.
We should ideally provide a mechanism for deployments to specify their own
terms without modifying source code; in the interim, sites that have already
customised the provided Zulip.com terms can simply carry a patch reverting this
commit.
This allows full-screen mode when launching from a saved app link
(mobile browser -> save link to home screen). This works on Android,
too, despite the "apple-" prefix.
The SSO build of the desktop app is intended only for those users who
who have settings.SSO_ONLY set, i.e. the only way to login is via the
site's SSO REMOTE_USER authentication. We were incorrectly linking to
it on all production installations :(.
In order to enable internationalization support in Zulip, and to use
Django internationalization tools, all strings in Zulip frontend needs
to be marked for translation.
New users will no longer get desktop and audible notifications for all streams
by default.
This also updates the `day1` follow-up email to let users know they can
customize how and when Zulip notifies them of new messages.
Lastly, this adds a `changelog.md` file, following the conventions from
keepachangelog.com, to track changes for new releases.
Ideally some of these templates should really point to the
local installation's support email address, but this is a
good start.
Exceptions:
* Where to report security incidents
* MIT Zephyr-related pages
* zulip.com terms and conditions
Include new field on Realm to control whether e-mail invitations are required
separately from whether the e-mail domain must match.
Allow control of these fields from admin panel.
Update logic in registration page to use these fields.
(imported from commit edc7f0a4c43b57361d9349e258ad4f217b426f88)
Meant to be used in tandem with the manage.py import command.
The following sensitive data is scrubbed:
* user api keys
* user password hashes
* stream email keys
* invite-only streams
* messages from invite-only streams
* messages from users from other domains
(imported from commit 8e58dcdcb80ef1c7127d3ab15accf40c6187633f)
It's been very buggy for a while, has limited usefulness compared with
unread counts, and profiling over the weekend indicates that it's very
slow.
(imported from commit 716fe47f2bbec1bd8a6e4d265ded5c64efe2ad5c)
This commit should only be pushed to stage after c290b630e has been
pushed to prod otherwise it will create a redirect loop.
(imported from commit 408407b845ded596705b1abd8ad13c0aedf6d732)
This change will redirect armooo@dropbox.com from stage to prod. It also
removes the prod to stage redirect for all users. This will be rolled
out in two commits to prevent a redirect loop.
(imported from commit c290b630e746f757429b8bbdadbe7768367a5e33)
* Fixes a few bugs with missed message address for PMs and huddles.
* Uses missed message address for all missed message reply-to headers on
the zulip.com realm.
(imported from commit 61dd09386e1bbdf9a5096e2400984d31e73a5b74)
Known issues:
* No support for whitelabeling in the email
* No whitelabeling for any externally-visible branding
(imported from commit 9eab7b0744e56a87007b8621a8bb18bbb1080256)
Display a red warning box to get users to direct users to staging for
the zulip.com (dropbox) realm.
(imported from commit 01ad4209d9247406bc82f5dedaf21371101a1d84)
We use this error page for all 5xx errors, so I'm removing the error
number from the text.
(imported from commit c0c4c8829d466dd20c76dbbc920fc57cafbd6e6c)
We don't have room in the dialog to show every variation of
the negated search syntax. If we start by documenting negated
topic searches, we may get some discovery on the negated search
features without cluttering out the way more common search
options.
(imported from commit 3b2c2672d125788dc71a5eeb2b6f1e551380c62e)
This helps the common case of not liking our default of having audible
and desktop notifications enabled, and not making users adjust the
settings on every existing stream to fix it.
(imported from commit be75edb2c1385d1bd9a289416e2dffd8007f5e0a)
They have weird properties like not sending anything for unchecked
boxes, which makes it hard to wrap a client-agnostic API around.
(imported from commit fef73a57a55b218b55dab6be3453dd6eac73c789)
Also:
* Change fixed element positioning and fix bugs
* Move settings dropdown back to the right and add left padding to left sidebar
(imported from commit fcf903b59617687f94618a01ce7544b69f408130)
Adding one row to the Navigation table made the
Keyboard shortcuts dialog look ridiculous, and it
was caused by a design based around float-left-ing
that broke down when the size of the "Navigation" box
was bigger than the "Composing messages" box.
Now we use a div to enclose the top two boxes in
the modal.
(imported from commit 90288ec288d4cf3d50ed7f5bcb48c8bb3d033f19)
This reverts commit 462a3eb5e6b83f9d8091b83e3f8dc458236938ed.
We're reverting this to see if it is the cause of our recent CSS
performance issues.
(imported from commit 6a0b041cfcb6770bbfda0d354444bad2d64459ab)
The old layout was misleading, because users would make the perfectly
reasonable assumption that their avatar change wouldn't be made until
they hit the Save button. Moving the avatar stuff below the Save button
makes the UI clear and also unclutters the form for updating name and
password.
(imported from commit 9154ba69a2d61844701c88c2afdf399532ccddd9)
The message to use /register if your group is already signed up is
below the fold on my laptop -- this probably contributes to people
trying to sign up their group when they are just trying to register.
(imported from commit 517df29d10182c05780017c15225c9f95b844225)
Previously, while you'd get the event saying you'd been knighted,
which would make the Administration tab visible, clicking on the tab
would error out because the admin page HTML was never sent over on
page load (since you weren't an admin at that point).
(imported from commit 90ad351533515bebece630d67baf4b142d320754)
Always render the Administration menu item from the back end, but
make it be hidden by default until the page is loaded. Then, the
client can un-hide it as needed.
(imported from commit 66e607eec430d7179b4d5ac3f5416f5be8ac26c9)
Added a default_desktop_notifications boolean to userprofile with a UI
in Zulip Labs. This flag is used to default the notification flag on new
subscriptions.
(imported from commit a25223cc5ecf09980cf877991e25034bb3fd4046)
Whatever text is entered into the search box under users is used
to filter users by their full names. You can use commas to search
for multiple users. Search terms must be at the start of names, so
"st,fr" would match "Steve Howell" and "Leo Franchi" but not
"jesstess." Names are case insensitive.
(imported from commit 822b72883928d3c941d38e9798774d71b0689f30)
These may not work at time of writing due to propegation delays, but I
promise they'll be active soonish.
(imported from commit 4ed9e85f282fb868a75fa427f897bad8469d9e03)
After deploying to both staging and prod, double check the docs
are correct here. This fixes the API docs on prod, which had
"POST /api/v1/messages", despite "/api" not being part of the
prod path. Prod docs are here:
https://zulip.com/api/endpoints/
(imported from commit a2c4d316128f88171f4a76074314be64d9bc9728)
This shows the number of messages sent by humans for the last
eight 24-hour periods, for each realm. "Messages sent" isn't a
perfect metric of activity, but it's easier to query with our
current data model than certain other statistics.
(imported from commit 9de3c479640a0b9dbc017b245dda21d951f4efa4)
We now have a stand-in message that says "Loading CSS...", which
only gets hidden once the CSS loads. This is a better user
experience than seeing completely unformatted Zulip HTML on the
page. The message is set to fill the whole page, and it's cleared
once zulip.css gets loaded.
(imported from commit 613fe3d6a39197aabfeb63823b7fad75834bb9eb)
Also compactify and reorder it a bit to ensure that emoji and
@-mentions are near the top, since they are compact and important to
all users.
(imported from commit 7a3e6c915d1ef1461a43302c71bda74852566ffc)
Keith is going to try to use Zulip again during the next conference;
CoNEXT, so we need to rebrand. We use generic language here so that
we don't need to do this every time.
(imported from commit c97f7d4bb84f2dfd4c6db265884ba17be0fd5677)
* The "message" event type was incorrectly listed as "messages".
* The curl docs incorrectly used GET for the register API call.
* Fix missing \ in GET events documentation.
* We didn't have proper JSON format for the "message" argument.
(imported from commit f00e2f187375d24f8be4b8075b7cd7a38584fe01)
This doesn't actually prevent a user from making the API call into our
servers to actually go and edit a message, so this isn't a bulletproof
solution for realms where messages ABSOLUTELY MUST NOT be edited.
(imported from commit 5bf043a201e2952189b45f93b8c5ca7648f6aee7)
Because the first thing under it is a stream and not a topic, this
could be confusing for new users, and experienced users probably don't
need the reminder.
(imported from commit 479521d96963ed4011ff2c8300f4e5027a482a41)
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
When your left sidebar is zoomed to show just one stream,
there is a link to to show all streams again.
(imported from commit 92f39b042168c443cbb9f524bf892557ef492551)
This commit doesn't actually add the final UI to zoom/unzoom
topics, because I want to keep those in separate commits, in
case we change how to enable the feature. But this commit
adds a toggle_zoom() function that zooms/unzooms topics.
Zooming is minimally invasive, because we don't really introduce
many extra elements to the UI; instead, we just make the list of
streams be a list of length one (i.e. the active stream). This
gives us a lot of stuff for free, basically, like unread counts, etc.
(imported from commit 814c1361b6210d1591b4174bed1d6e0c98a3f255)
If authoritative data is available from say the LDAP database, we now
ignore the POSTed user name, and don't offer it as a form field.
We fall back to giving the user a text field if they aren't in LDAP.
If users do not have any form fields to fill out, we simply bring them
to the app without the registration page, logging them in using a dummy
backend.
(imported from commit 6bee87430ba46ff753ea3408251e8a80c45c713f)
UserProfile.show_admin was intended to be a check for users that have
administrative rights in other realms, which we've harmlessly but
erroneously been using to check if they are an admin in their realm.
Use the more straightforward check instead, with a more intuitive
name.
(imported from commit d81050c7dbbb19e59c5e31750be303a4630e1456)
The check-handlebars-templates script now looks at most of our
back end templates to try and find imbalanced tags. This commit
fixes a bunch of the existing templates.
(imported from commit fad4a5d85d68160370dd588b41d6f125f64d198f)
This moves the notify-not-in-view notifications into the composebox area.
It also tries to be a bit smarter about what action it links and what it displays.
(imported from commit 1c79bd0d9ef972059a006b17501a09b72e961ee3)
I broke Zulip Enterprise because I tried to make the /hello link in our day1 email
point to the local server (EXTERNAL_HOST).
(imported from commit 70262b34474ae292ecf9cfe59bebc3c7de904926)
The problem here is that some error-related templates render without
the context managers that we specify, and so we want the handling of
"the context was not set" to default to the enterprise case (because
the enterprise version will work basically fine on !enterprise, but
the opposite is not true because the enterprise distribution doesn't
even have the templates for certain !enterprise pages that may be
linked to).
(imported from commit 7547311d87e048d33221587f44b82fe0ba320ca1)
For legacy reasons, this template wanted each tab's content as
a one-key dictionary, instead of a string. Each tab already has
a tuple to allow for fields like title, so this wasn't really giving us
any long term flexibility; it was just crufting up the calling
code.
(imported from commit 2a316107ec223a83efa8735f4810a6fa43107541)
Show up to 10 of your recent group PM conversations in the right
sidebar. Clicking on the links narrows to the huddle and opens the
compose box for the huddle. The green circles have opacity
proportional to the number of users present in the huddle.
This is feature flagged to staging only.
Some of this code was written by Allen before commits were squashed.
Known issue: unread counts disappear on certain refresh events.
(imported from commit 3b44665150ba20594d8b0295cb30df03601c1d52)
The weird formality of using your complete name I think feels even
spammier than saying nothing.
(imported from commit b2326de91866fa3c13c2023723f64d1beadf435e)
You need your password to log into the mobile apps, and it's kind
of hard to find where to reset it if you don't remember it (but
logged in via SSO)
(imported from commit fd902bcd0c21d7cb308535722e6789fb4a6114cd)
The corporate "app" is not a full-fledged Django app, but it has
a urls.py and a templates directory. This commit creates the app
and moves the jobs pages into it. Localserver deployments will
not see any of the corporate code.
(imported from commit 35889c3cf92329258c30741fdfa564769a4fac1a)
Displays a notification above the composebox in cases where the message
is not visible (further down), or where it's outside the current narrow or search.
It also offers a link to the appropriate narrow when it makes sense, and offers
timetravel when appropriate. There is currently a bug with timetravel (and you can
see this when using the popover menu) that makes "narrow to messages around this time"
not work for muted messages.
This resolves Trac #1518.
(imported from commit 391ca0b9c07d91496f6585a4fd8e15723d1170e2)
Looking at the historical data, fewer than 50% of active users have
completed the checklist, which means that it is just persistent
clutter. We also have other better ways of encouraging people to send
traffic and get the apps now.
This commit removes both the frontend UI and backend work but leaves
the db row for now for the historical data.
(imported from commit e8f5780be37bbc75f794fb118e4dd41d8811f2bf)
To help reduce visual clutter.
From our observations this is almost never used, and there are many
more intuitive ways to get back to your messages.
(imported from commit 01448ec2f28b88c3b1674b6a9e02d56f47a23fdf)
Nobody uses it, and it causes confusion. (How is it different from 'Home'?)
For now, leave in the ability to get to the narrow, although we may
ultimately want to do away with it as well.
(imported from commit 35b3f27e39c4de3391bc5571b32f7242a29f4cfa)
We were using Gravatar for user avatars, but now users can
upload their avatars directly to Zulip, and we will store
their avatar for them. This removes the old Gravatar-related
interface and polling code.
This commit does not attempt to update the avatars in
messages that have already been loaded, either for the user
making the change or other users.
(imported from commit 301dc48f96f83de0136c93de57055638c79e0961)
The "Your Account" and "Notifications" boxes on the Settings
page each had their own border and their own "Save changes"
button, but they were within the same form and sending to the
same back end point.
This commit creates a separate form and endpoint for each
of the two boxes.
(imported from commit 04d4d16938f20749a18d2c6887da3ed3cf21ef74)
Check that settings.html has at least balanced tags, and
automate the checking of those tags.
(imported from commit 35e9be269caa211803d64f2b54cb0287e13707b3)
The queued email gets deleted if the user signs up before it gets sent.
Otherwise, they are reminded in 2 days that they still haven't signed up.
This addressses Trac #1812
(imported from commit c1bdc09c03ac576b08986e56994de72d52fd293b)
These are some queries on API usage, desktop usage, and
Android usage that would be of interest to Waseem. These
will eventually be subsumed into /activity, but some interim
data issues may make them easier to keep separate for now.
(imported from commit 697a8496cbf4447d557a3fc89f64c1c4d3e67e70)
A customer gave us feedback that he didn't see the instructions
to create a bot, because it's below the fold. Also, the image
was horribly out of date. I moved the instructions up and
simplified them.
Please follow up with user1@customer10.invalid and thank
him for the feedback, once we push to prod.
(imported from commit 22fe3b3c5f94183c402c10005504b94b35e6f7e4)
Handled by the queue processor for signups. Added a management command
that accomplishes the same task, in case it's needed for manually added users,
or in case we goof and need to remove queued emails for a given user.
This addresses Trac #1807
(imported from commit 6727b82a07fa6a3ea3d827860c9e60fd0602297a)
Lots of users don't realize you can do this. This commit is probably
not sufficient but is a start.
(imported from commit 730a43c6966b9bc6db1bdd6fb3995953d35ffe89)
This will hopefully incentivize people to click one and get back into
the app.
We'll also need this for digest emails.
(imported from commit 57191c3fcca3b12df93a81e4692bb7eb8ccc83b2)