There is a specific limit of how many other users a given user
can invite in their projects (depends on the plan they are on).
When the limit is reached, the user cannot invite *new* users
in their projects, but they can still invite team members
from one project into another project. In other words, we count
the number of unique invited users, not the number of memberships.
There was an UI bug in the "Invite a Team Member" dialog. The
dialog has an editable "Email" text field. When an user has reached
the team limit, and they open the "Invite" dialog, they could
enter a new user's email address in the Email field and try to invite
them. The server would refuse to exceed the team limit and would
return a plain HTTP 403 page. This is of course confusing to the
end user.
The fix is to show "Email" as a text field only if the user has
not yet exceeded their team size. If they have, then show "Email"
as non-editable text.
- Refactor transport classes to raise exceptions
on delivery problems, instead of returning error
message as string. Exceptions can carry extra meta
information (see TransportError.permanent field, see
MigrationRequiredError subclass). I considered attaching
the extra information to strings by subclassing str, but
using exceptions felt cleaner and less hacky.
- Add Channel.disabled field, for disabling integrations
on permanent errors. For example, if Slack returns
HTTP 404, we will now mark the integration as disabled
and will not make requests to that Slack endpoint again.
Redirect unauthenticated users to the sign in page
instead. Rationale:
- The content on the welcome page is what often belongs
to a separate "marketing site". The marketing content
is of no use on self-hosted instances, which typically
have new signups disabled and are for internal use only
- (the real reason, let's be honest) a number of
self-hosted instances are accessible over the public
internet. Search engines index the nearly identical
landing pages and see them as duplicated content.
Channel.last_notify is the datetime of the most recent
notification sent via the channel. Channel.last_error is
the error message (blank if the delivery was successful).
In the integrations list, "Last Notification" column,
use these fields instead of looking up the most recent
Notification object. This saves some db queries,
and also fixes a subtle issue: if prunenotifications
cleans up all notifications for a given channel, the
"Last Notification" column would display "Never", which
would not be correct – not any more.
If the email body is above a certain size, Gmail trims it
and displays "[Message clipped] View entire message" at
the end. The spaceless tag is a quick fix to reduce
HTML size a bit and allow more table rows to fit before
clipping.
In the Details page, if the user click "Ping Now",
and the project is using {ping-key}/{slug} URLs,
but the ping key is not set, then show a
"Ping Key Required" message instead of trying to ping
and invalid URL.