-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Use form validation errors for important UI feedback #11095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 4 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
bee983d
Some fixes for notifications
agjohnson 1cf32db
Initial example of using notification system for view-specific errors
agjohnson b98defe
Add more conditional placeholders and messages
agjohnson 07463bc
Drop exception handling from project create view
agjohnson f185989
Add base classes for prevalidation Form and rich validation errors
agjohnson 8cf28b3
Refactor project create form to use form validation errors
agjohnson 89adaae
Merge branch 'agj/project-import-fixes-drop-notifications' into agj/p…
agjohnson f76d325
Add logic for team, owner, and SSO validations
agjohnson f38e7a8
Invert conditional to avoid `not any()`
agjohnson 1281de9
Clarify some of the prevalidation form internals
agjohnson ebc7866
Fix is_valid call
agjohnson baabbf3
Fix not implemented throw
agjohnson 0e01677
Add tests for form prevalidation
agjohnson 1836b74
Pytest tests -> unittest tests
agjohnson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
from re import fullmatch | ||
from urllib.parse import urlparse | ||
|
||
from allauth.socialaccount.models import SocialAccount | ||
from django import forms | ||
from django.conf import settings | ||
from django.contrib.auth.models import User | ||
|
@@ -12,12 +13,15 @@ | |
from django.utils.translation import gettext_lazy as _ | ||
|
||
from readthedocs.builds.constants import INTERNAL | ||
from readthedocs.core.forms import PrevalidatedForm, RichValidationError | ||
from readthedocs.core.history import SimpleHistoryModelForm | ||
from readthedocs.core.permissions import AdminPermission | ||
from readthedocs.core.utils import slugify, trigger_build | ||
from readthedocs.core.utils.extend import SettingsOverrideObject | ||
from readthedocs.integrations.models import Integration | ||
from readthedocs.invitations.models import Invitation | ||
from readthedocs.oauth.models import RemoteRepository | ||
from readthedocs.organizations.models import Team | ||
from readthedocs.projects.models import ( | ||
AddonsConfig, | ||
Domain, | ||
|
@@ -78,6 +82,109 @@ class ProjectBackendForm(forms.Form): | |
backend = forms.CharField() | ||
|
||
|
||
class ProjectFormParamMixin: | ||
agjohnson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def __init__(self, *args, **kwargs): | ||
self.user = kwargs.pop("user", None) | ||
super().__init__(*args, **kwargs) | ||
|
||
def clean_prevalidation(self): | ||
# Shared conditionals between automatic and manual forms | ||
self.user_has_connected_account = SocialAccount.objects.filter( | ||
user=self.user, | ||
).exists() | ||
self.user_is_nonowner_with_sso = None | ||
self.user_missing_admin_permission = None | ||
if settings.RTD_ALLOW_ORGANIZATIONS: | ||
# TODO there should be some way to initially select the organization | ||
# and maybe the team too. It's mostly safe to automatically select | ||
# the first organization, but explicit would be better. Reusing the | ||
# organization selection UI works, we only really need a query param | ||
# here. | ||
self.user_is_nonowner_with_sso = all( | ||
[ | ||
AdminPermission.has_sso_enabled(self.user), | ||
AdminPermission.organizations( | ||
user=self.user, | ||
owner=False, | ||
).exists(), | ||
] | ||
) | ||
|
||
# TODO this logic should be possible from AdminPermission | ||
# AdminPermssion.is_admin only inspects organization owners, so the | ||
# additional team check is necessary | ||
self.user_missing_admin_permission = not any( | ||
agjohnson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[ | ||
AdminPermission.organizations( | ||
user=self.user, | ||
owner=True, | ||
).exists(), | ||
Team.objects.admin(self.user).exists(), | ||
] | ||
) | ||
|
||
|
||
class ProjectAutomaticForm(ProjectFormParamMixin, PrevalidatedForm): | ||
def clean_prevalidation(self): | ||
""" | ||
Block user from using this form for important blocking states. | ||
|
||
We know before the user gets a chance to use this form that the user | ||
might not have the ability to add a project into their organization. | ||
These errors are raised before the user submits the form. | ||
""" | ||
super().clean_prevalidation() | ||
if not self.user_has_connected_account: | ||
url = reverse("socialaccount_connections") | ||
raise RichValidationError( | ||
_( | ||
f"You must first <a href='{url}'>add a connected service " | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this is rendered as text, not HTML, or at least the "normal" errors are. If we are rendering errors as HTML, we should be careful, there are other places where we include user input in them. |
||
f"to your account</a> to enable automatic configuration of " | ||
f"repositories." | ||
), | ||
header=_("No connected services found"), | ||
) | ||
if settings.RTD_ALLOW_ORGANIZATIONS: | ||
if self.user_is_nonowner_with_sso: | ||
raise RichValidationError( | ||
_( | ||
"Only organization owners may create new projects " | ||
"when single sign-on is enabled." | ||
), | ||
header=_("Organization single sign-on enabled"), | ||
) | ||
if self.user_missing_admin_permission: | ||
raise RichValidationError( | ||
_( | ||
"You must be on a team with admin permissions " | ||
"in order to add a new project to your organization." | ||
), | ||
header=_("Admin permission required"), | ||
) | ||
|
||
|
||
class ProjectManualForm(ProjectFormParamMixin, PrevalidatedForm): | ||
def clean_prevalidation(self): | ||
super().clean_prevalidation() | ||
if settings.RTD_ALLOW_ORGANIZATIONS: | ||
if self.user_is_nonowner_with_sso: | ||
raise RichValidationError( | ||
_( | ||
"Projects cannot be manually configured when " | ||
"single sign-on is enabled for your organization." | ||
), | ||
header=_("Organization single sign-on enabled"), | ||
) | ||
if self.user_missing_admin_permission: | ||
raise RichValidationError( | ||
_( | ||
"You must be on a team with admin permissions " | ||
"in order to add a new project to your organization." | ||
), | ||
header=_("Admin permission required"), | ||
) | ||
|
||
|
||
class ProjectBasicsForm(ProjectForm): | ||
|
||
"""Form for basic project fields.""" | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.