Closed ScottHull closed 4 years ago
It appears that I've got it working. Leaving my process here for review, in case anyone sees where improvement can be made. I made a few sloppy hacks to satisfy returns that the package expects, and I've commented in the code where those are.
forms.py
(I've subclassed OrganizationUser and Organization models under CompanyUser and Company, respectively)
class CompanyUserAddForm(forms.ModelForm):
"""Form class for adding OrganizationUsers to an existing Organization"""
email = forms.CharField() # change from forms.EmailField so a comma-separated list will be accepted without throwing front-end errors
def __init__(self, request, organization, *args, **kwargs):
self.request = request
self.organization = organization
super(CompanyUserAddForm, self).__init__(*args, **kwargs)
class Meta:
model = models.CompanyUser
exclude = ("user", "organization")
def save(self, *args, **kwargs):
provided_email_addresses = self.cleaned_data["email"].replace(" ", "").split(",") # gather email addresses in a list
added_users = [] # track users added so that one may be returned after loop, as expected by django-organizations
for address in provided_email_addresses: # loop through the collected addresses
print(provided_email_addresses)
print(address)
if self.organization.users.filter(email=address).exists(): # got rid of clean_email(self) to avoid a premature email return, and put its functionality here
raise forms.ValidationError(
_("There is already an organization " "member with the email address {}!".format(address))
)
"""
The save method should create a new OrganizationUser linking the User
matching the provided email address. If not matching User is found it
should kick off the registration process. It needs to create a User in
order to link it to the Organization.
"""
try:
user = get_user_model().objects.get(
email__iexact=address
)
except get_user_model().MultipleObjectsReturned:
raise forms.ValidationError(
_("The email address {} has been used multiple times.".format(address))
)
except get_user_model().DoesNotExist:
user = invitation_backend().invite_by_email(
address,
**{
"domain": get_current_site(self.request),
"organization": self.organization,
"sender": self.request.user,
}
)
# Send a notification email to this user to inform them that they
# have been added to a new organization.
invitation_backend().send_notification(
user,
**{
"domain": get_current_site(self.request),
"organization": self.organization,
"sender": self.request.user,
}
)
u = models.CompanyUser.objects.create(
user=user,
organization=self.organization,
is_admin=self.cleaned_data["is_admin"],
)
added_users.append(u)
return added_users[0] # get_success_url in organizations view requires a returned user
views.py
class CompanyUserCreate(OrganizationUserCreate):
form_class = forms.CompanyUserAddForm
Deleting the clean_email
function caused the ValidationError
to not be passed through the template. Here is my new, working form:
class CompanyUserAddForm(forms.ModelForm):
"""Form class for adding OrganizationUsers to an existing Organization"""
email = forms.CharField() # change from forms.EmailField so a comma-separated list will be accepted without throwing front-end errors
def __init__(self, request, organization, *args, **kwargs):
self.request = request
self.organization = organization
super(CompanyUserAddForm, self).__init__(*args, **kwargs)
class Meta:
model = models.CompanyUser
exclude = ("user", "organization")
def save(self, *args, **kwargs):
email = self.cleaned_data["email"] # gather email addresses in a list
added_users = [] # track users added so that one may be returned after loop, as expected by django-organizations
for address in email: # loop through the collected addresses
if self.organization.users.filter(email=address).exists(): # got rid of clean_email(self) to avoid a premature email return, and put its functionality here
raise forms.ValidationError(
_("There is already an organization " "member with the email address {}!".format(address))
)
"""
The save method should create a new OrganizationUser linking the User
matching the provided email address. If not matching User is found it
should kick off the registration process. It needs to create a User in
order to link it to the Organization.
"""
try:
user = get_user_model().objects.get(
email__iexact=address
)
except get_user_model().MultipleObjectsReturned:
raise forms.ValidationError(
_("The email address {} has been used multiple times.".format(address))
)
except get_user_model().DoesNotExist:
user = invitation_backend().invite_by_email(
address,
**{
"domain": get_current_site(self.request),
"organization": self.organization,
"sender": self.request.user,
}
)
# Send a notification email to this user to inform them that they
# have been added to a new organization.
invitation_backend().send_notification(
user,
**{
"domain": get_current_site(self.request),
"organization": self.organization,
"sender": self.request.user,
}
)
u = models.CompanyUser.objects.create(
user=user,
organization=self.organization,
is_admin=self.cleaned_data["is_admin"],
)
added_users.append(u)
return added_users[0] # get_success_url in organizations view requires a returned user
def clean_email(self):
email = self.cleaned_data['email'].replace(" ", "").split(",")
for address in email:
if self.organization.users.filter(email=address):
raise forms.ValidationError(_("There is already an organization "
"member with the email address {}!".format(address)))
if validate_email(address):
raise forms.ValidationError(_("The email address {} is not valid!".format(address)))
return email
Leaving this here for posterity, thanks @ScottHull.
If anyone finds this and wants to take a stab at modifying the default backends to handle this, that'd be welcome!
I'm a big fan of this app and have spent lots of time pouring over the source code. A feature I'd like to implement is to allow an organization administrator to invite multiple users at once (i.e. CSV file upload with email addresses to send invites to or a comma separated input field with email addresses). The views are pretty dense to parse and I was hoping someone could help me find where exactly I must interface with
django-organizations
in order to do this.In the simplest example, I just convert the comma-separated field of user email addresses given by the form into a list and run a for-loop through the list to invite all of the users that were provided. I just need to figure out where the best place to insert this customization actually is! Any help would be appreciated!
EDIT: I suspect that the best place for this sort of functionality is in the invitation backend. I'm currently working with the default invitation in my project with very, very minor customization.
EDIT 2: Seems as though the for-loop I proposed is best executed in the
OrganizationUserAddForm
atorganizations/forms.py
. I'll update if I can get something working for my case!EDIT 3: Working with forms integration into the front end is a bit painful. Seems as though Django's forms don't want to accept commas in the required fields, even though I plan to split them into a list in the backend.