def _email_now_vouched(self, vouched_by, description=''): """Email this user, letting them know they are now vouched.""" name = None voucher_profile_link = None vouchee_profile_link = utils.absolutify(self.get_absolute_url()) if vouched_by: name = vouched_by.full_name voucher_profile_link = utils.absolutify( vouched_by.get_absolute_url()) number_of_vouches = self.vouches_received.all().count() template = get_template( 'phonebook/emails/vouch_confirmation_email.txt') message = template.render({ 'voucher_name': name, 'voucher_profile_url': voucher_profile_link, 'vouchee_profile_url': vouchee_profile_link, 'vouch_description': description, 'functional_areas_url': utils.absolutify(reverse('groups:index_functional_areas')), 'groups_url': utils.absolutify(reverse('groups:index_groups')), 'first_vouch': number_of_vouches == 1, 'can_vouch_threshold': number_of_vouches == settings.CAN_VOUCH_THRESHOLD, }) subject = _(u'You have been vouched on Mozillians.org') filtered_message = message.replace('"', '"').replace(''', "'") send_mail(subject, filtered_message, settings.FROM_NOREPLY, [self.email])
def _email_now_vouched(self, vouched_by, description=''): """Email this user, letting them know they are now vouched.""" name = None voucher_profile_link = None vouchee_profile_link = utils.absolutify(self.get_absolute_url()) if vouched_by: name = vouched_by.full_name voucher_profile_link = utils.absolutify(vouched_by.get_absolute_url()) number_of_vouches = self.vouches_received.all().count() template = get_template('phonebook/emails/vouch_confirmation_email.txt') message = template.render({ 'voucher_name': name, 'voucher_profile_url': voucher_profile_link, 'vouchee_profile_url': vouchee_profile_link, 'vouch_description': description, 'functional_areas_url': utils.absolutify(reverse('groups:index_functional_areas')), 'groups_url': utils.absolutify(reverse('groups:index_groups')), 'first_vouch': number_of_vouches == 1, 'can_vouch_threshold': number_of_vouches == settings.CAN_VOUCH_THRESHOLD, }) subject = _(u'You have been vouched on Mozillians.org') filtered_message = message.replace('"', '"').replace(''', "'") send_mail(subject, filtered_message, settings.FROM_NOREPLY, [self.email])
def join_group(request, url): """User request to join group.""" group = get_object_or_404(Group, url=url) profile_to_add = request.user.userprofile # TODO: this duplicates some of the logic in Group.user_can_join(), but we # want to give the user a message that's specific to the reason they can't join. # Can we make this DRYer? if group.has_member(profile_to_add): messages.error(request, _('You are already in this group.')) elif group.has_pending_member(profile_to_add): messages.error(request, _('Your request to join this group is still pending.')) elif group.accepting_new_members == 'no': messages.error(request, _('This group is not accepting requests to join.')) else: if group.accepting_new_members == 'yes': status = GroupMembership.MEMBER messages.info(request, _('You have been added to this group.')) if group.terms: status = GroupMembership.PENDING_TERMS elif group.accepting_new_members == 'by_request': status = GroupMembership.PENDING messages.info(request, _('Your membership request has been sent ' 'to the group curator(s).')) group.add_member(profile_to_add, status=status) return redirect(reverse('groups:show_group', args=[group.url]))
def __init__(self, *args, **kwargs): """Override init method.""" super(SkillsForm, self).__init__(*args, **kwargs) # Override the url to pass along the locale. # This is needed in order to post to the correct url through ajax self.fields['skills'].widget.url = reverse( 'groups:skills-autocomplete')
def confirm_member(request, url, user_pk): """ Add a member to a group who has requested membership. """ group = get_object_or_404(Group, url=url) profile = get_object_or_404(UserProfile, pk=user_pk) is_curator = (request.user.userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) # Workaround for using both request.GET and request.POST data next_url = getattr(request, request.method).get('next_url', group_url) if not (is_curator or is_manager): raise http.Http404() try: membership = GroupMembership.objects.get(group=group, userprofile=profile) except GroupMembership.DoesNotExist: messages.error(request, _('This user has not requested membership in this group.')) else: if membership.status == GroupMembership.MEMBER and not membership.needs_renewal: messages.error(request, _('This user is already a member of this group.')) else: status = GroupMembership.MEMBER if group.terms: status = GroupMembership.PENDING_TERMS group.add_member(profile, status=status) if membership.needs_renewal: messages.info(request, _('The membership of the user has been renewed.')) else: messages.info(request, _('This user has been added as a member of this group.')) return redirect(next_url)
def confirm_member(request, url, user_pk): """ Add a member to a group who has requested membership. """ group = get_object_or_404(Group, url=url) profile = get_object_or_404(UserProfile, pk=user_pk) is_curator = (request.user.userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) next_url = request.REQUEST.get('next_url', group_url) if not (is_curator or is_manager): raise Http404() try: membership = GroupMembership.objects.get(group=group, userprofile=profile) except GroupMembership.DoesNotExist: messages.error( request, _('This user has not requested membership in this group.')) else: if membership.status == GroupMembership.MEMBER: messages.error(request, _('This user is already a member of this group.')) else: status = GroupMembership.MEMBER if group.terms: status = GroupMembership.PENDING_TERMS group.add_member(profile, status=status) messages.info( request, _('This user has been added as a member of this group.')) return redirect(next_url)
def confirm_member(request, url, user_pk): """ Add a member to a group who has requested membership. """ group = get_object_or_404(Group, url=url) profile = get_object_or_404(UserProfile, pk=user_pk) is_curator = (request.user.userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) # Workaround for using both request.GET and request.POST data next_url = getattr(request, request.method).get('next_url', group_url) if not (is_curator or is_manager): raise http.Http404() try: membership = GroupMembership.objects.get(group=group, userprofile=profile) except GroupMembership.DoesNotExist: messages.error(request, _('This user has not requested membership in this group.')) else: if membership.status == GroupMembership.MEMBER and not membership.needs_renewal: messages.error(request, _('This user is already a member of this group.')) else: status = GroupMembership.MEMBER if group.terms: status = GroupMembership.PENDING_TERMS group.add_member(profile, status=status, inviter=request.user.userprofile) if membership.needs_renewal: messages.info(request, _('The membership of the user has been renewed.')) else: messages.info(request, _('This user has been added as a member of this group.')) return redirect(next_url)
def confirm_member(request, url, user_pk): """ Add a member to a group who has requested membership. """ group = get_object_or_404(Group, url=url) profile = get_object_or_404(UserProfile, pk=user_pk) is_curator = (request.user.userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) next_url = request.REQUEST.get('next_url', group_url) if not (is_curator or is_manager): raise Http404() try: membership = GroupMembership.objects.get(group=group, userprofile=profile) except GroupMembership.DoesNotExist: messages.error(request, _('This user has not requested membership in this group.')) else: if membership.status == GroupMembership.MEMBER: messages.error(request, _('This user is already a member of this group.')) else: status = GroupMembership.MEMBER if group.terms: status = GroupMembership.PENDING_TERMS group.add_member(profile, status=status) messages.info(request, _('This user has been added as a member of this group.')) return redirect(next_url)
def join_group(request, url): """User request to join group.""" group = get_object_or_404(Group, url=url) profile_to_add = request.user.userprofile # TODO: this duplicates some of the logic in Group.user_can_join(), but we # want to give the user a message that's specific to the reason they can't join. # Can we make this DRYer? if group.has_member(profile_to_add): messages.error(request, _('You are already in this group.')) elif group.has_pending_member(profile_to_add): messages.error(request, _('Your request to join this group is still pending.')) elif group.accepting_new_members == 'no': messages.error(request, _('This group is not accepting requests to join.')) else: if group.accepting_new_members == 'yes': status = GroupMembership.MEMBER messages.info(request, _('You have been added to this group.')) if group.terms: status = GroupMembership.PENDING_TERMS elif group.accepting_new_members == 'by_request': status = GroupMembership.PENDING messages.info( request, _('Your membership request has been sent ' 'to the group curator(s).')) group.add_member(profile_to_add, status=status) return redirect(reverse('groups:show_group', args=[group.url]))
def membership_renewal_notification(request): """View to help test membership renewal notification Manually spawn a task to send membership renewal notifications to the users. """ notify_membership_renewal.apply_async() return redirect(reverse('groups:index_groups'))
def group_delete(request, url): profile = request.user.userprofile # Get the group to delete group = get_object_or_404(Group, url=url) # Only a group curator is allowed to delete a group is_curator = profile in group.curators.all() if not is_curator and not profile.is_manager: messages.error(request, _('You must be a curator to delete a group')) return redirect(reverse('groups:show_group', args=[group.url])) # Cannot delete if anyone else is in it if group.members.all().count() != 1: messages.error(request, _('You cannot delete a group if anyone else is in it.')) return redirect(reverse('groups:show_group', args=[group.url])) # Go to it group.delete() messages.info(request, _('Group %s has been deleted') % group.name) return redirect(reverse('groups:index_groups'))
def group_add_edit(request, url=None): """ Add or edit a group. (If a url is passed in, we're editing.) """ profile = request.user.userprofile is_manager = request.user.userprofile.is_manager if url: # Get the group to edit group = get_object_or_404(Group, url=url) # Only a group curator or an admin is allowed to edit a group is_curator = profile in group.curators.all() if not (is_curator or is_manager): messages.error( request, _('You must be a curator or an admin to edit a group')) return redirect(reverse('groups:show_group', args=[group.url])) else: group = Group() form_class = SuperuserGroupForm if is_manager else GroupForm # Add the creator of a group as curator curators_ids = [profile.id] # If we are editing add the existing curators. If the group has no curator in edit # mode, append an empty list if url: curators_ids = group.curators.all().values_list('id', flat=True) form = form_class(request.POST or None, instance=group, initial={'curators': curators_ids}) if form.is_valid(): group = form.save() return redirect(reverse('groups:show_group', args=[group.url])) context = { 'form': form, 'creating': url is None, 'group': group if url else None } return render(request, 'groups/add_edit.html', context)
def view_profile(request, username): """View a profile by username.""" data = {} privacy_mappings = { "anonymous": PUBLIC, "mozillian": MOZILLIANS, "employee": EMPLOYEES, "private": PRIVATE, "myself": None, } privacy_level = None if request.user.is_authenticated and request.user.username == username: # own profile view_as = request.GET.get("view_as", "myself") privacy_level = privacy_mappings.get(view_as, None) profile = UserProfile.objects.privacy_level(privacy_level).get( user__username=username ) data["privacy_mode"] = view_as else: userprofile_query = UserProfile.objects.filter(user__username=username) public_profile_exists = userprofile_query.public().exists() profile_exists = userprofile_query.exists() profile_complete = userprofile_query.exclude(full_name="").exists() if not public_profile_exists: if not request.user.is_authenticated: # you have to be authenticated to continue messages.warning(request, LOGIN_MESSAGE) return login_required( view_profile, login_url=reverse("phonebook:home") )(request, username) if not request.user.userprofile.is_vouched: # you have to be vouched to continue messages.error(request, GET_VOUCHED_MESSAGE) return redirect("phonebook:home") if not profile_exists or not profile_complete: raise Http404 profile = UserProfile.objects.get(user__username=username) profile.set_instance_privacy_level(PUBLIC) if request.user.is_authenticated: profile.set_instance_privacy_level(request.user.userprofile.privacy_level) data["shown_user"] = profile.user data["profile"] = profile data["primary_identity"] = profile.identity_profiles.filter( primary_contact_identity=True ) data["alternate_identities"] = profile.identity_profiles.filter( primary_contact_identity=False ) return render(request, "phonebook/profile.html", data)
def view_profile(request, username): """View a profile by username.""" data = {} privacy_mappings = { 'anonymous': PUBLIC, 'mozillian': MOZILLIANS, 'employee': EMPLOYEES, 'private': PRIVATE, 'myself': None } privacy_level = None if (request.user.is_authenticated() and request.user.username == username): # own profile view_as = request.GET.get('view_as', 'myself') privacy_level = privacy_mappings.get(view_as, None) profile = UserProfile.objects.privacy_level(privacy_level).get( user__username=username) data['privacy_mode'] = view_as else: userprofile_query = UserProfile.objects.filter(user__username=username) public_profile_exists = userprofile_query.public().exists() profile_exists = userprofile_query.exists() profile_complete = userprofile_query.exclude(full_name='').exists() if not public_profile_exists: if not request.user.is_authenticated(): # you have to be authenticated to continue messages.warning(request, LOGIN_MESSAGE) return (login_required(view_profile, login_url=reverse('phonebook:home'))( request, username)) if not request.user.userprofile.is_vouched: # you have to be vouched to continue messages.error(request, GET_VOUCHED_MESSAGE) return redirect('phonebook:home') if not profile_exists or not profile_complete: raise Http404 profile = UserProfile.objects.get(user__username=username) profile.set_instance_privacy_level(PUBLIC) if request.user.is_authenticated(): profile.set_instance_privacy_level( request.user.userprofile.privacy_level) data['shown_user'] = profile.user data['profile'] = profile data['primary_identity'] = profile.identity_profiles.filter( primary_contact_identity=True) data['alternate_identities'] = profile.identity_profiles.filter( primary_contact_identity=False) return render(request, 'phonebook/profile.html', data)
def group_add_edit(request, url=None): """ Add or edit a group. (If a url is passed in, we're editing.) """ profile = request.user.userprofile is_manager = request.user.userprofile.is_manager if url: # Get the group to edit group = get_object_or_404(Group, url=url) # Only a group curator or an admin is allowed to edit a group is_curator = profile in group.curators.all() if not (is_curator or is_manager): messages.error(request, _('You must be a curator or an admin to edit a group')) return redirect(reverse('groups:show_group', args=[group.url])) else: group = Group() form_class = SuperuserGroupForm if is_manager else GroupForm # Add the creator of a group as curator curators_ids = [profile.id] # If we are editing add the existing curators. If the group has no curator in edit # mode, append an empty list if url: curators_ids = group.curators.all().values_list('id', flat=True) form = form_class(request.POST or None, instance=group, initial={'curators': curators_ids}) if form.is_valid(): group = form.save() return redirect(reverse('groups:show_group', args=[group.url])) context = { 'form': form, 'creating': url is None, 'group': group if url else None } return render(request, 'groups/add_edit.html', context)
def toggle_skill_subscription(request, url): """Toggle the current user's membership of a group.""" skill = get_object_or_404(Skill, url=url) profile = request.user.userprofile if profile.skills.filter(id=skill.id).exists(): profile.skills.remove(skill) else: profile.skills.add(skill) return redirect(reverse('groups:show_skill', args=[skill.url]))
def edit_profile(request): """Edit user profile view.""" # Don't use request.user user = User.objects.get(pk=request.user.id) profile = user.userprofile sections = { "basic_section": ["user_form", "basic_information_form"], } curr_sect = next((s for s in list(sections.keys()) if s in request.POST), None) def get_request_data(form): if curr_sect and form in sections[curr_sect]: return request.POST return None ctx = {} ctx["user_form"] = forms.UserForm(get_request_data("user_form"), instance=user) basic_information_data = get_request_data("basic_information_form") ctx["basic_information_form"] = forms.BasicInformationForm( basic_information_data, request.FILES or None, instance=profile ) forms_valid = True if request.POST: if not curr_sect: raise Http404 curr_forms = [ctx[x] for x in sections[curr_sect]] forms_valid = all([x.is_valid() for x in curr_forms]) if forms_valid: old_username = request.user.username for f in curr_forms: f.save() next_section = request.GET.get("next") next_url = urlparams(reverse("phonebook:profile_edit"), next_section) if user.username != old_username: msg = _( "You changed your username; " "please note your profile URL has also changed." ) messages.info(request, _(msg)) return HttpResponseRedirect(next_url) ctx.update( { "profile": request.user.userprofile, "vouch_threshold": settings.CAN_VOUCH_THRESHOLD, "forms_valid": forms_valid, } ) return render(request, "phonebook/edit_profile.html", ctx)
def review_terms(request, url): """Review group terms page.""" group = get_object_or_404(Group, url=url) if not group.terms: return redirect(reverse('groups:show_group', args=[group.url])) membership = get_object_or_404(GroupMembership, group=group, userprofile=request.user.userprofile, status=GroupMembership.PENDING_TERMS) membership_form = TermsReviewForm(request.POST or None) if membership_form.is_valid(): if membership_form.cleaned_data['terms_accepted'] == 'True': group.add_member(request.user.userprofile, GroupMembership.MEMBER) else: membership.delete() return redirect(reverse('groups:show_group', args=[group.url])) ctx = {'group': group, 'membership_form': membership_form} return render(request, 'groups/terms.html', ctx)
def remove_member(request, url, user_pk, status=None): group = get_object_or_404(Group, url=url) profile_to_remove = get_object_or_404(UserProfile, pk=user_pk) this_userprofile = request.user.userprofile is_curator = (this_userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) # Workaround for using both request.GET and request.POST data next_url = getattr(request, request.method).get('next_url', group_url) # TODO: this duplicates some of the logic in Group.user_can_leave(), but we # want to give the user a message that's specific to the reason they can't leave. # Can we make this DRYer? # You can remove yourself, if group allows it. Curator and superuser can remove anyone. if not (is_curator or is_manager): if not group.members_can_leave: messages.error( request, _('This group does not allow members to remove themselves.')) return redirect(next_url) if profile_to_remove != this_userprofile: raise http.Http404() # Curators cannot be removed, only by themselves and if there is another curator. curators = group.curators.all() if (profile_to_remove in curators and curators.count() <= 1 and profile_to_remove != this_userprofile): messages.error(request, _('The group needs at least one curator.')) return redirect(next_url) if request.method == 'POST': group.remove_member(profile_to_remove, status=status, send_email=(profile_to_remove != this_userprofile)) if profile_to_remove in curators: group.curators.remove(profile_to_remove) if this_userprofile == profile_to_remove: messages.info(request, _('You have been removed from this group.')) else: messages.info(request, _('The group member has been removed.')) return redirect(next_url) # Display confirmation page context = { 'group': group, 'profile': profile_to_remove, 'next_url': next_url } return render(request, 'groups/confirm_remove_member.html', context)
def send_thanks(self): """Sends email to person who friend accepted invitation.""" template = get_template('phonebook/emails/invite_accepted.txt') subject = _('%s created a Mozillians profile') % self.redeemer.full_name profile_url = reverse('phonebook:profile_view', kwargs={'username': self.redeemer.user.username}) message = template.render({ 'inviter': self.inviter.full_name, 'friend': self.redeemer.full_name, 'profile': absolutify(profile_url)}) filtered_message = message.replace('"', '"').replace(''', "'") send_mail(subject, filtered_message, settings.FROM_NOREPLY, [self.inviter.email])
def review_terms(request, url): """Review group terms page.""" group = get_object_or_404(Group, url=url) if not group.terms: return redirect(reverse('groups:show_group', args=[group.url])) membership = get_object_or_404(GroupMembership, group=group, userprofile=request.user.userprofile, status=GroupMembership.PENDING_TERMS) membership_form = forms.TermsReviewForm(request.POST or None) if membership_form.is_valid(): if membership_form.cleaned_data['terms_accepted'] == 'True': group.add_member(request.user.userprofile, GroupMembership.MEMBER) else: membership.delete() return redirect(reverse('groups:show_group', args=[group.url])) ctx = { 'group': group, 'membership_form': membership_form } return render(request, 'groups/terms.html', ctx)
def delete_invite(request, invite_pk): """Delete an invite to join a group.""" invite = get_object_or_404(Invite, pk=invite_pk) group = invite.group if (group.curators.filter(id=request.user.userprofile.id).exists() or request.user.userprofile.is_manager): redeemer = invite.redeemer invite.delete() next_section = request.GET.get('next') next_url = urlparams(reverse('groups:group_edit', args=[group.url]), next_section) return http.HttpResponseRedirect(next_url) raise http.Http404()
def remove_member(request, url, user_pk, status=None): group = get_object_or_404(Group, url=url) profile_to_remove = get_object_or_404(UserProfile, pk=user_pk) this_userprofile = request.user.userprofile is_curator = (this_userprofile in group.curators.all()) is_manager = request.user.userprofile.is_manager group_url = reverse('groups:show_group', args=[group.url]) # Workaround for using both request.GET and request.POST data next_url = getattr(request, request.method).get('next_url', group_url) # TODO: this duplicates some of the logic in Group.user_can_leave(), but we # want to give the user a message that's specific to the reason they can't leave. # Can we make this DRYer? # You can remove yourself, if group allows it. Curator and superuser can remove anyone. if not (is_curator or is_manager): if not group.members_can_leave: messages.error(request, _('This group does not allow members to remove themselves.')) return redirect(next_url) if profile_to_remove != this_userprofile: raise http.Http404() # Curators cannot be removed, only by themselves and if there is another curator. curators = group.curators.all() if (profile_to_remove in curators and curators.count() <= 1 and profile_to_remove != this_userprofile): messages.error(request, _('The group needs at least one curator.')) return redirect(next_url) if request.method == 'POST': group.remove_member(profile_to_remove, status=status, send_email=(profile_to_remove != this_userprofile)) if profile_to_remove in curators: group.curators.remove(profile_to_remove) if this_userprofile == profile_to_remove: messages.info(request, _('You have been removed from this group.')) else: messages.info(request, _('The group member has been removed.')) return redirect(next_url) # Display confirmation page context = { 'group': group, 'profile': profile_to_remove, 'next_url': next_url } return render(request, 'groups/confirm_remove_member.html', context)
def send_invitation_email(request, invite_pk): """Send group invitation email.""" invite = get_object_or_404(Invite, pk=invite_pk) is_curator = invite.group.curators.filter( pk=request.user.userprofile.pk).exists() is_manager = request.user.userprofile.is_manager if not (is_curator or is_manager): raise http.Http404 next_section = request.GET.get('next') next_url = urlparams(reverse('groups:group_edit', args=[invite.group.url]), next_section) return http.HttpResponseRedirect(next_url)
def accept_reject_invitation(request, invite_pk, action): """Accept or reject group invitation.""" redeemer = request.user.userprofile invite = get_object_or_404(Invite, pk=invite_pk, redeemer=redeemer) if action == 'accept': if invite.group.terms: invite.group.add_member(redeemer, GroupMembership.PENDING_TERMS) else: invite.group.add_member(redeemer, GroupMembership.MEMBER) invite.accepted = True invite.save() else: invite.delete() return redirect(reverse('groups:show_group', args=[invite.group.url]))
def delete_invite(request, invite_pk): """Delete an invite to join a group.""" invite = get_object_or_404(Invite, pk=invite_pk) group = invite.group if (group.curators.filter(id=request.user.userprofile.id).exists() or request.user.userprofile.is_manager): redeemer = invite.redeemer invite.delete() # TODO:Revoke any celery tasks if needed and shoot revokation emails. msg = _(u'The invitation to {0} has been successfully revoked. ').format(redeemer) messages.success(request, msg) return redirect(reverse('groups:group_edit', args=[group.url])) raise Http404()
def index_groups(request): """Lists all public groups (in use) on Mozillians. Doesn't list functional areas, invisible groups, and groups with no vouched members """ group_form = forms.CreateGroupForm(request.POST or None) if group_form.is_valid(): group = group_form.save() group.curators.add(request.user.userprofile) return redirect(reverse('groups:group_edit', args=[group.url])) query = Group.get_non_functional_areas() template = 'groups/index_groups.html' context = {'group_form': group_form} return _list_groups(request, template, query, context)
def delete_invite(request, invite_pk): """Delete an invite to join a group.""" invite = get_object_or_404(Invite, pk=invite_pk) group = invite.group if (group.curators.filter(id=request.user.userprofile.id).exists() or request.user.userprofile.is_manager): redeemer = invite.redeemer invite.delete() notify_redeemer_invitation_invalid.delay(redeemer.pk, group.pk) msg = _(u'The invitation to {0} has been successfully revoked.').format(redeemer) messages.success(request, msg) next_section = request.GET.get('next') next_url = urlparams(reverse('groups:group_edit', args=[group.url]), next_section) return http.HttpResponseRedirect(next_url) raise http.Http404()
def delete_invite(request, invite_pk): """Delete an invite to join a group.""" invite = get_object_or_404(Invite, pk=invite_pk) group = invite.group if (group.curators.filter(id=request.user.userprofile.id).exists() or request.user.userprofile.is_manager): redeemer = invite.redeemer invite.delete() # TODO:Revoke any celery tasks if needed and shoot revokation emails. msg = _(u'The invitation to {0} has been successfully revoked. ' ).format(redeemer) messages.success(request, msg) return redirect(reverse('groups:group_edit', args=[group.url])) raise Http404()
def send_invitation_email(request, invite_pk): """Send group invitation email.""" invite = get_object_or_404(Invite, pk=invite_pk) is_curator = invite.group.curators.filter(pk=request.user.userprofile.pk).exists() is_manager = request.user.userprofile.is_manager if not (is_curator or is_manager): raise http.Http404 notify_redeemer_invitation.delay(invite.pk, invite.group.invite_email_text) msg = _(u'Invitation to {0} has been sent successfully.'.format(invite.redeemer)) messages.success(request, msg) next_section = request.GET.get('next') next_url = urlparams(reverse('groups:group_edit', args=[invite.group.url]), next_section) return http.HttpResponseRedirect(next_url)
def accept_reject_invitation(request, invite_pk, action): """Accept or reject group invitation.""" redeemer = request.user.userprofile invite = get_object_or_404(Invite, pk=invite_pk, redeemer=redeemer) if action == 'accept': if invite.group.terms: invite.group.add_member(redeemer, GroupMembership.PENDING_TERMS) else: invite.group.add_member(redeemer, GroupMembership.MEMBER) invite.accepted = True invite.save() notify_curators_invitation_accepted.delay(invite.pk) else: notify_curators_invitation_rejected.delay(redeemer.pk, invite.inviter.pk, invite.group.pk) invite.delete() return redirect(reverse('groups:show_group', args=[invite.group.url]))
def index_groups(request): """Lists all public groups (in use) on Mozillians. Doesn't list functional areas, invisible groups, and groups with no vouched members """ group_form = forms.CreateGroupForm(request.POST or None) if group_form.is_valid(): group = group_form.save() group.curators.add(request.user.userprofile) return redirect(reverse('groups:group_edit', args=[group.url])) query = Group.get_non_functional_areas() template = 'groups/index_groups.html' context = { 'group_form': group_form } return _list_groups(request, template, query, context)
def process_view(self, request, view_func, view_args, view_kwargs): for view_url in self.exceptions: if re.match(view_url, request.path): return None allow_public = getattr(view_func, "_allow_public", None) if allow_public: return None if not request.user.is_authenticated(): messages.warning(request, LOGIN_MESSAGE) return login_required(view_func, login_url=reverse("phonebook:home"))(request, *view_args, **view_kwargs) if request.user.userprofile.is_vouched: return None allow_unvouched = getattr(view_func, "_allow_unvouched", None) if allow_unvouched: return None messages.error(request, GET_VOUCHED_MESSAGE) return redirect("phonebook:home")
def process_view(self, request, view_func, view_args, view_kwargs): for view_url in self.exceptions: if re.match(view_url, request.path): return None allow_public = getattr(view_func, '_allow_public', None) if allow_public: return None if not request.user.is_authenticated(): messages.warning(request, LOGIN_MESSAGE) return (login_required(view_func, login_url=reverse('phonebook:home')) (request, *view_args, **view_kwargs)) if request.user.userprofile.is_vouched: return None allow_unvouched = getattr(view_func, '_allow_unvouched', None) if allow_unvouched: return None messages.error(request, GET_VOUCHED_MESSAGE) return redirect('phonebook:home')
def force_group_invalidation(request, url, alias_model, template=''): """View to help test different scenarios. Forces an immediate invalidation in the case that a group is set to expire its memberships. """ group_alias = get_object_or_404(alias_model, url=url) group = group_alias.alias is_curator = group.curators.filter(id=request.user.userprofile.pk).exists() if group.invalidation_days and is_curator: curator_ids = group.curators.all().values_list('id', flat=True) memberships = group.groupmembership_set.exclude(userprofile__id__in=curator_ids) for member in memberships: status = None if group.accepting_new_members != Group.OPEN: status = GroupMembership.PENDING group.remove_member(member.userprofile, status=status) else: raise http.Http404 return redirect(reverse('groups:show_group', args=[group.url]))
def view_profile(request, username): """View a profile by username.""" data = {} privacy_mappings = {'anonymous': PUBLIC, 'mozillian': MOZILLIANS, 'employee': EMPLOYEES, 'privileged': PRIVILEGED, 'myself': None} privacy_level = None abuse_form = None if (request.user.is_authenticated() and request.user.username == username): # own profile view_as = request.GET.get('view_as', 'myself') privacy_level = privacy_mappings.get(view_as, None) profile = UserProfile.objects.privacy_level(privacy_level).get(user__username=username) data['privacy_mode'] = view_as else: userprofile_query = UserProfile.objects.filter(user__username=username) public_profile_exists = userprofile_query.public().exists() profile_exists = userprofile_query.exists() profile_complete = userprofile_query.exclude(full_name='').exists() if not public_profile_exists: if not request.user.is_authenticated(): # you have to be authenticated to continue messages.warning(request, LOGIN_MESSAGE) return (login_required(view_profile, login_url=reverse('phonebook:home')) (request, username)) if not request.user.userprofile.is_vouched: # you have to be vouched to continue messages.error(request, GET_VOUCHED_MESSAGE) return redirect('phonebook:home') if not profile_exists or not profile_complete: raise Http404 profile = UserProfile.objects.get(user__username=username) profile.set_instance_privacy_level(PUBLIC) if request.user.is_authenticated(): profile.set_instance_privacy_level( request.user.userprofile.privacy_level) if (request.user.is_authenticated() and request.user.userprofile.is_vouched and not profile.can_vouch): abuse_report = get_object_or_none(AbuseReport, reporter=request.user.userprofile, profile=profile) if not abuse_report: abuse_report = AbuseReport(reporter=request.user.userprofile, profile=profile) abuse_form = forms.AbuseReportForm(request.POST or None, instance=abuse_report) if abuse_form.is_valid(): abuse_form.save() msg = _(u'Thanks for helping us improve mozillians.org!') messages.info(request, msg) return redirect('phonebook:profile_view', profile.user.username) if (request.user.is_authenticated() and profile.is_vouchable(request.user.userprofile)): vouch_form = forms.VouchForm(request.POST or None) data['vouch_form'] = vouch_form if vouch_form.is_valid(): # We need to re-fetch profile from database. profile = UserProfile.objects.get(user__username=username) profile.vouch(request.user.userprofile, vouch_form.cleaned_data['description']) # Notify the current user that they vouched successfully. msg = _(u'Thanks for vouching for a fellow Mozillian! This user is now vouched!') messages.info(request, msg) return redirect('phonebook:profile_view', profile.user.username) data['shown_user'] = profile.user data['profile'] = profile data['groups'] = profile.get_annotated_groups() data['abuse_form'] = abuse_form # Only show pending groups if user is looking at their own profile, # or current user is a superuser if not (request.user.is_authenticated() and (request.user.username == username or request.user.is_superuser)): data['groups'] = [grp for grp in data['groups'] if not (grp.pending or grp.pending_terms)] return render(request, 'phonebook/profile.html', data)
def edit_profile(request): """Edit user profile view.""" # Don't use request.user user = User.objects.get(pk=request.user.id) profile = user.userprofile user_groups = profile.groups.all().order_by('name') emails = ExternalAccount.objects.filter(type=ExternalAccount.TYPE_EMAIL) accounts_qs = ExternalAccount.objects.exclude(type=ExternalAccount.TYPE_EMAIL) sections = { 'registration_section': ['user_form', 'registration_form'], 'basic_section': ['user_form', 'basic_information_form'], 'groups_section': ['groups_privacy_form'], 'skills_section': ['skills_form'], 'email_section': ['email_privacy_form', 'alternate_email_formset'], 'languages_section': ['language_privacy_form', 'language_formset'], 'accounts_section': ['accounts_formset'], 'location_section': ['location_form'], 'irc_section': ['irc_form'], 'contribution_section': ['contribution_form'], 'tshirt_section': ['tshirt_form'], } curr_sect = next((s for s in sections.keys() if s in request.POST), None) def get_request_data(form): if curr_sect and form in sections[curr_sect]: return request.POST return None ctx = {} ctx['user_form'] = forms.UserForm(get_request_data('user_form'), instance=user) ctx['registration_form'] = forms.RegisterForm(get_request_data('registration_form'), request.FILES or None, instance=profile) basic_information_data = get_request_data('basic_information_form') ctx['basic_information_form'] = forms.BasicInformationForm(basic_information_data, request.FILES or None, instance=profile) ctx['accounts_formset'] = forms.AccountsFormset(get_request_data('accounts_formset'), instance=profile, queryset=accounts_qs) ctx['location_form'] = forms.LocationForm(get_request_data('location_form'), instance=profile) ctx['language_formset'] = forms.LanguagesFormset(get_request_data('language_formset'), instance=profile, locale=request.locale) language_privacy_data = get_request_data('language_privacy_form') ctx['language_privacy_form'] = forms.LanguagesPrivacyForm(language_privacy_data, instance=profile) ctx['skills_form'] = forms.SkillsForm(get_request_data('skills_form'), instance=profile) ctx['contribution_form'] = forms.ContributionForm(get_request_data('contribution_form'), instance=profile) ctx['tshirt_form'] = forms.TshirtForm(get_request_data('tshirt_form'), instance=profile) ctx['groups_privacy_form'] = forms.GroupsPrivacyForm(get_request_data('groups_privacy_form'), instance=profile) ctx['irc_form'] = forms.IRCForm(get_request_data('irc_form'), instance=profile) ctx['email_privacy_form'] = forms.EmailPrivacyForm(get_request_data('email_privacy_form'), instance=profile) alternate_email_formset_data = get_request_data('alternate_email_formset') ctx['alternate_email_formset'] = forms.AlternateEmailFormset(alternate_email_formset_data, instance=profile, queryset=emails) ctx['autocomplete_form_media'] = ctx['registration_form'].media + ctx['skills_form'].media forms_valid = True if request.POST: if not curr_sect: raise Http404 curr_forms = map(lambda x: ctx[x], sections[curr_sect]) forms_valid = all(map(lambda x: x.is_valid(), curr_forms)) if forms_valid: old_username = request.user.username for f in curr_forms: f.save() # Spawn task to check for spam if not profile.can_vouch: params = { 'instance_id': profile.id, 'user_ip': request.META.get('REMOTE_ADDR'), 'user_agent': request.META.get('HTTP_USER_AGENT'), 'referrer': request.META.get('HTTP_REFERER'), 'comment_author': profile.full_name, 'comment_author_email': profile.email, 'comment_content': profile.bio } check_spam_account.delay(**params) next_section = request.GET.get('next') next_url = urlparams(reverse('phonebook:profile_edit'), next_section) if curr_sect == 'registration_section': settings_url = reverse('phonebook:profile_edit') settings_link = '<a href="{0}">settings</a>'.format(settings_url) msg = _(u'Your registration is complete. ' u'Feel free to visit the {0} page to add ' u'additional information to your profile.'.format(settings_link)) messages.info(request, mark_safe(msg)) redeem_invite(profile, request.session.get('invite-code')) next_url = reverse('phonebook:profile_view', args=[user.username]) elif user.username != old_username: msg = _(u'You changed your username; ' u'please note your profile URL has also changed.') messages.info(request, _(msg)) return HttpResponseRedirect(next_url) ctx.update({ 'user_groups': user_groups, 'profile': request.user.userprofile, 'vouch_threshold': settings.CAN_VOUCH_THRESHOLD, 'appsv2': profile.apps.filter(enabled=True), 'forms_valid': forms_valid }) return render(request, 'phonebook/edit_profile.html', ctx)
def edit_profile(request): """Edit user profile view.""" # Don't use request.user user = User.objects.get(pk=request.user.id) profile = user.userprofile idp_profiles = IdpProfile.objects.filter(profile=profile) idp_primary_profile = get_object_or_none(IdpProfile, profile=profile, primary=True) # The accounts that a user can select as the primary login identity accounts_qs = ExternalAccount.objects.exclude( type=ExternalAccount.TYPE_EMAIL) sections = { 'basic_section': ['user_form', 'basic_information_form'], 'idp_section': ['idp_profile_formset'], 'accounts_section': ['accounts_formset'], 'location_section': ['location_form'], 'contribution_section': ['contribution_form'], } curr_sect = next((s for s in sections.keys() if s in request.POST), None) def get_request_data(form): if curr_sect and form in sections[curr_sect]: return request.POST return None ctx = {} ctx['user_form'] = forms.UserForm(get_request_data('user_form'), instance=user) basic_information_data = get_request_data('basic_information_form') ctx['basic_information_form'] = forms.BasicInformationForm( basic_information_data, request.FILES or None, instance=profile) ctx['accounts_formset'] = forms.AccountsFormset( get_request_data('accounts_formset'), instance=profile, queryset=accounts_qs) ctx['location_form'] = forms.LocationForm( get_request_data('location_form'), instance=profile) ctx['contribution_form'] = forms.ContributionForm( get_request_data('contribution_form'), instance=profile) ctx['idp_profile_formset'] = forms.IdpProfileFormset( get_request_data('idp_profile_formset'), instance=profile, queryset=idp_profiles) ctx['idp_primary_profile'] = idp_primary_profile forms_valid = True if request.POST: if not curr_sect: raise Http404 curr_forms = map(lambda x: ctx[x], sections[curr_sect]) forms_valid = all(map(lambda x: x.is_valid(), curr_forms)) if forms_valid: old_username = request.user.username for f in curr_forms: f.save() next_section = request.GET.get('next') next_url = urlparams(reverse('phonebook:profile_edit'), next_section) if user.username != old_username: msg = _(u'You changed your username; ' u'please note your profile URL has also changed.') messages.info(request, _(msg)) return HttpResponseRedirect(next_url) ctx.update({ 'profile': request.user.userprofile, 'vouch_threshold': settings.CAN_VOUCH_THRESHOLD, 'appsv2': profile.apps.filter(enabled=True), 'forms_valid': forms_valid }) return render(request, 'phonebook/edit_profile.html', ctx)
def get_absolute_url(self): cls_name = self.__class__.__name__ url_pattern = 'groups:show_{0}'.format(cls_name.lower()) return absolutify(reverse(url_pattern, args=[self.url]))
def get(self, request): """Callback handler for OIDC authorization code flow. This is based on the mozilla-django-oidc library. This callback is used to verify the identity added by the user. Users are already logged in and we do not care about authentication. The JWT token is used to prove the identity of the user. """ profile = request.user.userprofile # This is a difference nonce than the one used to login! nonce = request.session.get('oidc_verify_nonce') if nonce: # Make sure that nonce is not used twice del request.session['oidc_verify_nonce'] # Check for all possible errors and display a message to the user. errors = [ 'code' not in request.GET, 'state' not in request.GET, 'oidc_verify_state' not in request.session, request.GET['state'] != request.session['oidc_verify_state'] ] if any(errors): msg = 'Something went wrong, account verification failed.' messages.error(request, msg) return redirect('phonebook:profile_edit') token_payload = { 'client_id': self.OIDC_RP_VERIFICATION_CLIENT_ID, 'client_secret': self.OIDC_RP_VERIFICATION_CLIENT_SECRET, 'grant_type': 'authorization_code', 'code': request.GET['code'], 'redirect_uri': absolutify( self.request, nonprefixed_url('phonebook:verify_identity_callback') ), } response = requests.post(self.OIDC_OP_TOKEN_ENDPOINT, data=token_payload, verify=import_from_settings('OIDC_VERIFY_SSL', True)) response.raise_for_status() token_response = response.json() id_token = token_response.get('id_token') # Verify JWT jws = JWS.from_compact(force_bytes(id_token)) jwk = JWK.load(smart_bytes(self.OIDC_RP_VERIFICATION_CLIENT_SECRET)) verified_token = None if jws.verify(jwk): verified_token = jws.payload # Create the new Identity Profile. if verified_token: user_info = json.loads(verified_token) email = user_info['email'] if not user_info.get('email_verified'): msg = 'Account verification failed: Email is not verified.' messages.error(request, msg) return redirect('phonebook:profile_edit') user_q = { 'auth0_user_id': user_info['sub'], 'email': email } # If we are linking GitHub we need to save # the username too. if 'github|' in user_info['sub']: user_q['username'] = user_info['nickname'] # Check that the identity doesn't exist in another Identity profile # or in another mozillians profile error_msg = '' if IdpProfile.objects.filter(**user_q).exists(): error_msg = 'Account verification failed: Identity already exists.' elif User.objects.filter(email__iexact=email).exclude(pk=profile.user.pk).exists(): error_msg = 'The email in this identity is used by another user.' if error_msg: messages.error(request, error_msg) next_url = self.request.session.get('oidc_login_next', None) return HttpResponseRedirect(next_url or reverse('phonebook:profile_edit')) # Save the new identity to the IdpProfile user_q['profile'] = profile idp, created = IdpProfile.objects.get_or_create(**user_q) current_idp = get_object_or_none(IdpProfile, profile=profile, primary=True) # The new identity is stronger than the one currently used. Let's swap append_msg = '' # We need to check for equality too in the case a user updates the primary email in # the same identity (matching auth0_user_id). If there is an addition of the same type # we are not swapping login identities if ((current_idp and current_idp.type < idp.type) or (current_idp and current_idp.auth0_user_id == idp.auth0_user_id) or (not current_idp and created and idp.type >= IdpProfile.PROVIDER_GITHUB)): IdpProfile.objects.filter(profile=profile).exclude(pk=idp.pk).update(primary=False) idp.primary = True idp.save() # Also update the primary email of the user update_email_in_basket(profile.user.email, idp.email) User.objects.filter(pk=profile.user.id).update(email=idp.email) append_msg = ' You need to use this identity the next time you will login.' send_userprofile_to_cis.delay(profile.pk) if created: msg = 'Account successfully verified.' if append_msg: msg += append_msg messages.success(request, msg) else: msg = 'Account verification failed: Identity already exists.' messages.error(request, msg) next_url = self.request.session.get('oidc_login_next', None) return HttpResponseRedirect(next_url or reverse('phonebook:profile_edit'))
def get_absolute_url(self): return reverse('phonebook:profile_view', args=[self.user.username])
def edit_profile(request): """Edit user profile view.""" # Don't use request.user user = User.objects.get(pk=request.user.id) profile = user.userprofile user_groups = profile.groups.all().order_by('name') emails = ExternalAccount.objects.filter(type=ExternalAccount.TYPE_EMAIL) accounts_qs = ExternalAccount.objects.exclude(type=ExternalAccount.TYPE_EMAIL) sections = { 'registration_section': ['user_form', 'registration_form'], 'basic_section': ['user_form', 'basic_information_form'], 'groups_section': ['groups_privacy_form'], 'skills_section': ['skills_form'], 'email_section': ['email_privacy_form', 'alternate_email_formset'], 'languages_section': ['language_privacy_form', 'language_formset'], 'accounts_section': ['accounts_formset'], 'irc_section': ['irc_form'], 'location_section': ['location_form'], 'contribution_section': ['contribution_form'], 'tshirt_section': ['tshirt_form'], 'developer_section': ['developer_form'] } curr_sect = next((s for s in sections.keys() if s in request.POST), None) def get_request_data(form): if curr_sect and form in sections[curr_sect]: return request.POST return None ctx = {} ctx['user_form'] = forms.UserForm(get_request_data('user_form'), instance=user) ctx['registration_form'] = forms.RegisterForm(get_request_data('registration_form'), request.FILES or None, instance=profile) basic_information_data = get_request_data('basic_information_form') ctx['basic_information_form'] = forms.BasicInformationForm(basic_information_data, request.FILES or None, instance=profile) ctx['accounts_formset'] = forms.AccountsFormset(get_request_data('accounts_formset'), instance=profile, queryset=accounts_qs) ctx['language_formset'] = forms.LanguagesFormset(get_request_data('language_formset'), instance=profile, locale=request.locale) language_privacy_data = get_request_data('language_privacy_form') ctx['language_privacy_form'] = forms.LanguagesPrivacyForm(language_privacy_data, instance=profile) ctx['skills_form'] = forms.SkillsForm(get_request_data('skills_form'), instance=profile) location_initial = { 'saveregion': True if profile.geo_region else False, 'savecity': True if profile.geo_city else False, 'lat': profile.lat, 'lng': profile.lng } ctx['location_form'] = forms.LocationForm(get_request_data('location_form'), instance=profile, initial=location_initial) ctx['contribution_form'] = forms.ContributionForm(get_request_data('contribution_form'), instance=profile) ctx['tshirt_form'] = forms.TshirtForm(get_request_data('tshirt_form'), instance=profile) ctx['groups_privacy_form'] = forms.GroupsPrivacyForm(get_request_data('groups_privacy_form'), instance=profile) ctx['irc_form'] = forms.IRCForm(get_request_data('irc_form'), instance=profile) ctx['developer_form'] = forms.DeveloperForm(get_request_data('developer_form'), instance=profile) ctx['email_privacy_form'] = forms.EmailPrivacyForm(get_request_data('email_privacy_form'), instance=profile) alternate_email_formset_data = get_request_data('alternate_email_formset') ctx['alternate_email_formset'] = forms.AlternateEmailFormset(alternate_email_formset_data, instance=profile, queryset=emails) forms_valid = True if request.POST: if not curr_sect: raise Http404 curr_forms = map(lambda x: ctx[x], sections[curr_sect]) forms_valid = all(map(lambda x: x.is_valid(), curr_forms)) if forms_valid: old_username = request.user.username for f in curr_forms: f.save() # Spawn task to check for spam if not profile.can_vouch: params = { 'instance_id': profile.id, 'user_ip': request.META.get('REMOTE_ADDR'), 'user_agent': request.META.get('HTTP_USER_AGENT'), 'referrer': request.META.get('HTTP_REFERER'), 'comment_author': profile.full_name, 'comment_author_email': profile.email, 'comment_content': profile.bio } check_spam_account.delay(**params) next_section = request.GET.get('next') next_url = urlparams(reverse('phonebook:profile_edit'), next_section) if curr_sect == 'registration_section': settings_url = reverse('phonebook:profile_edit') settings_link = '<a href="{0}">settings</a>'.format(settings_url) msg = _(u'Your registration is complete. ' u'Feel free to visit the {0} page to add ' u'additional information to your profile.'.format(settings_link)) messages.info(request, mark_safe(msg)) redeem_invite(profile, request.session.get('invite-code')) next_url = reverse('phonebook:profile_view', args=[user.username]) elif user.username != old_username: msg = _(u'You changed your username; ' u'please note your profile URL has also changed.') messages.info(request, _(msg)) return HttpResponseRedirect(next_url) ctx.update({ 'user_groups': user_groups, 'profile': request.user.userprofile, 'vouch_threshold': settings.CAN_VOUCH_THRESHOLD, 'mapbox_id': settings.MAPBOX_PROFILE_ID, 'apps': user.apiapp_set.filter(is_active=True), 'appsv2': profile.apps.filter(enabled=True), 'forms_valid': forms_valid }) return render(request, 'phonebook/edit_profile.html', ctx)
def view_profile(request, username): """View a profile by username.""" data = {} privacy_mappings = {'anonymous': PUBLIC, 'mozillian': MOZILLIANS, 'employee': EMPLOYEES, 'privileged': PRIVILEGED, 'myself': None} privacy_level = None abuse_form = None if (request.user.is_authenticated() and request.user.username == username): # own profile view_as = request.GET.get('view_as', 'myself') privacy_level = privacy_mappings.get(view_as, None) profile = UserProfile.objects.privacy_level(privacy_level).get(user__username=username) data['privacy_mode'] = view_as else: userprofile_query = UserProfile.objects.filter(user__username=username) public_profile_exists = userprofile_query.public().exists() profile_exists = userprofile_query.exists() profile_complete = userprofile_query.exclude(full_name='').exists() if not public_profile_exists: if not request.user.is_authenticated(): # you have to be authenticated to continue messages.warning(request, LOGIN_MESSAGE) return (login_required(view_profile, login_url=reverse('phonebook:home')) (request, username)) if not request.user.userprofile.is_vouched: # you have to be vouched to continue messages.error(request, GET_VOUCHED_MESSAGE) return redirect('phonebook:home') if not profile_exists or not profile_complete: raise Http404 profile = UserProfile.objects.get(user__username=username) profile.set_instance_privacy_level(PUBLIC) if request.user.is_authenticated(): profile.set_instance_privacy_level( request.user.userprofile.privacy_level) if (request.user.is_authenticated() and request.user.userprofile.is_vouched and not profile.can_vouch): abuse_report = get_object_or_none(AbuseReport, reporter=request.user.userprofile, profile=profile) if not abuse_report: abuse_report = AbuseReport(reporter=request.user.userprofile, profile=profile) abuse_form = forms.AbuseReportForm(request.POST or None, instance=abuse_report) if abuse_form.is_valid(): abuse_form.save() msg = _(u'Thanks for helping us improve mozillians.org!') messages.info(request, msg) return redirect('phonebook:profile_view', profile.user.username) if (request.user.is_authenticated() and profile.is_vouchable(request.user.userprofile)): vouch_form = forms.VouchForm(request.POST or None) data['vouch_form'] = vouch_form if vouch_form.is_valid(): # We need to re-fetch profile from database. profile = UserProfile.objects.get(user__username=username) profile.vouch(request.user.userprofile, vouch_form.cleaned_data['description']) # Notify the current user that they vouched successfully. msg = _(u'Thanks for vouching for a fellow Mozillian! This user is now vouched!') messages.info(request, msg) return redirect('phonebook:profile_view', profile.user.username) data['shown_user'] = profile.user data['profile'] = profile data['groups'] = profile.get_annotated_groups() data['abuse_form'] = abuse_form # Only show pending groups if user is looking at their own profile, # or current user is a superuser if not (request.user.is_authenticated() and (request.user.username == username or request.user.is_superuser)): data['groups'] = [grp for grp in data['groups'] if not grp.pending] return render(request, 'phonebook/profile.html', data)
def get_url(self, obj): return absolutify(reverse('phonebook:profile_view', kwargs={'username': obj.user.username}))
def get_url(self, obj): return absolutify(reverse('groups:show_skill', kwargs={'url': obj.url}))
def group_edit(request, url=None): """Add or edit a group. (if there is a url we are editing)""" profile = request.user.userprofile is_manager = request.user.userprofile.is_manager invites = None forms_valid = True group_forms = {} form_key = None show_delete_group_button = False if not url: return redirect(reverse('groups:index_groups')) # Get the group to edit group = get_object_or_404(Group, url=url) # Only a group curator or an admin is allowed to edit a group is_curator = profile in group.curators.all() is_manager = request.user.userprofile.is_manager if not (is_curator or is_manager): messages.error(request, _('You must be a curator or an admin to edit a group')) return redirect(reverse('groups:show_group', args=[group.url])) invites = group.invites.all() show_delete_group_button = is_curator and group.members.all().count() == 1 # Prepare the forms for rendering group_forms['basic_form'] = forms.GroupBasicForm group_forms['invalidation_form'] = forms.GroupInvalidationForm group_forms['curator_form'] = forms.GroupCuratorsForm group_forms['terms_form'] = forms.GroupTermsForm group_forms['invite_form'] = forms.GroupInviteForm group_forms['admin_form'] = forms.GroupAdminForm group_forms['criteria_form'] = forms.GroupCriteriaForm def _init_group_forms(request, group_forms): form_args = { 'data': None, 'instance': group, 'request': request } key = None if request.POST: form_args['data'] = request.POST key, form = next(((k, v(**form_args)) for k, v in group_forms.items() if k in request.POST), (None, None)) if key and form: group_forms[key] = form # Initialize the rest of the forms with non-POST data form_args['data'] = None for k in group_forms.keys(): if k != key: group_forms[k] = group_forms[k](**form_args) return key form_key = _init_group_forms(request, group_forms) form = group_forms[form_key] if form_key else None if form and form.is_bound and form.is_valid(): form.save() next_section = request.GET.get('next') next_url = urlparams(reverse('groups:group_edit', args=[group.url]), next_section) return HttpResponseRedirect(next_url) context = { 'group': group if url else None, 'invites': invites if group else None, 'forms_valid': forms_valid, 'user_is_curator': is_curator, 'user_is_manager': is_manager, 'show_delete_group_button': show_delete_group_button } context.update(group_forms) return render(request, 'groups/edit_group.html', context)
def url(viewname, *args, **kwargs): """Helper for Django's ``reverse`` in templates.""" return reverse(viewname, args=args, kwargs=kwargs)
def redirect(to, *args, **kwargs): """Redirect with locale support.""" url = reverse(to, args=args, kwargs=kwargs) return HttpResponseRedirect(url)
def show(request, url, alias_model, template): """List all members in this group.""" group_alias = get_object_or_404(alias_model, url=url) if group_alias.alias.url != url: return redirect('groups:show_group', url=group_alias.alias.url) is_curator = False is_manager = request.user.userprofile.is_manager is_pending = False show_delete_group_button = False membership_filter_form = forms.MembershipFilterForm(request.GET) group = group_alias.alias profile = request.user.userprofile in_group = group.has_member(profile) memberships = group.members.all() data = {} if isinstance(group, Group): # Has the user accepted the group terms if group.terms: membership = get_object_or_none(GroupMembership, group=group, userprofile=profile, status=GroupMembership.PENDING_TERMS) if membership: return redirect(reverse('groups:review_terms', args=[group.url])) # Is this user's membership pending? is_pending = group.has_pending_member(profile) is_curator = is_manager or (request.user.userprofile in group.curators.all()) # initialize the form only when the group is moderated and user is curator of the group if is_curator and group.accepting_new_members == 'by_request': membership_filter_form = forms.MembershipFilterForm(request.GET) else: membership_filter_form = None if is_curator: statuses = [GroupMembership.MEMBER, GroupMembership.PENDING] if membership_filter_form and membership_filter_form.is_valid(): filtr = membership_filter_form.cleaned_data['filtr'] if filtr == 'members': statuses = [GroupMembership.MEMBER] elif filtr == 'pending_members': statuses = [GroupMembership.PENDING] memberships = group.groupmembership_set.filter(status__in=statuses) # Curators can delete their group if there are no other members. show_delete_group_button = is_curator and group.members.all().count() == 1 else: # only show full members, or this user memberships = group.groupmembership_set.filter( Q(status=GroupMembership.MEMBER) | Q(userprofile=profile)) # Order by UserProfile.Meta.ordering memberships = memberships.order_by('userprofile') # Find the most common skills of the group members. # Order by popularity in the group. shared_skill_ids = (group.members.filter(groupmembership__status=GroupMembership.MEMBER) .values_list('skills', flat=True)) count_skills = defaultdict(int) for skill_id in shared_skill_ids: count_skills[skill_id] += 1 common_skills_ids = [k for k, v in sorted(count_skills.items(), key=lambda x: x[1], reverse=True) if count_skills[k] > 1] # Translate ids to Skills preserving order. skills = [Skill.objects.get(id=skill_id) for skill_id in common_skills_ids if skill_id] data.update(skills=skills, membership_filter_form=membership_filter_form) page = request.GET.get('page', 1) paginator = Paginator(memberships, settings.ITEMS_PER_PAGE) try: people = paginator.page(page) except PageNotAnInteger: people = paginator.page(1) except EmptyPage: people = paginator.page(paginator.num_pages) show_pagination = paginator.count > settings.ITEMS_PER_PAGE extra_data = dict( people=people, group=group, in_group=in_group, is_curator=is_curator, is_pending=is_pending, show_pagination=show_pagination, show_delete_group_button=show_delete_group_button, show_join_button=group.user_can_join(request.user.userprofile), show_leave_button=group.user_can_leave(request.user.userprofile), members=group.member_count, ) data.update(extra_data) return render(request, template, data)
def show(request, url, alias_model, template): """List all members in this group.""" group_alias = get_object_or_404(alias_model, url=url) if group_alias.alias.url != url: return redirect('groups:show_group', url=group_alias.alias.url) is_curator = False is_manager = request.user.userprofile.is_manager is_pending = False show_delete_group_button = False membership_filter_form = MembershipFilterForm(request.GET) group = group_alias.alias profile = request.user.userprofile in_group = group.has_member(profile) memberships = group.members.all() data = {} if isinstance(group, Group): # Has the user accepted the group terms if group.terms: membership = get_object_or_none( GroupMembership, group=group, userprofile=profile, status=GroupMembership.PENDING_TERMS) if membership: return redirect( reverse('groups:review_terms', args=[group.url])) # Is this user's membership pending? is_pending = group.has_pending_member(profile) is_curator = is_manager or (request.user.userprofile in group.curators.all()) # initialize the form only when the group is moderated and user is curator of the group if is_curator and group.accepting_new_members == 'by_request': membership_filter_form = MembershipFilterForm(request.GET) else: membership_filter_form = None if is_curator: statuses = [GroupMembership.MEMBER, GroupMembership.PENDING] if membership_filter_form and membership_filter_form.is_valid(): filtr = membership_filter_form.cleaned_data['filtr'] if filtr == 'members': statuses = [GroupMembership.MEMBER] elif filtr == 'pending_members': statuses = [GroupMembership.PENDING] memberships = group.groupmembership_set.filter(status__in=statuses) # Curators can delete their group if there are no other members. show_delete_group_button = is_curator and group.members.all( ).count() == 1 else: # only show full members, or this user memberships = group.groupmembership_set.filter( Q(status=GroupMembership.MEMBER) | Q(userprofile=profile)) # Order by UserProfile.Meta.ordering memberships = memberships.order_by('userprofile') # Find the most common skills of the group members. # Order by popularity in the group. shared_skill_ids = (group.members.filter( groupmembership__status=GroupMembership.MEMBER).values_list( 'skills', flat=True)) count_skills = defaultdict(int) for skill_id in shared_skill_ids: count_skills[skill_id] += 1 common_skills_ids = [ k for k, v in sorted( count_skills.items(), key=lambda x: x[1], reverse=True) if count_skills[k] > 1 ] # Translate ids to Skills preserving order. skills = [ Skill.objects.get(id=skill_id) for skill_id in common_skills_ids if skill_id ] data.update(skills=skills, membership_filter_form=membership_filter_form) page = request.GET.get('page', 1) paginator = Paginator(memberships, settings.ITEMS_PER_PAGE) try: people = paginator.page(page) except PageNotAnInteger: people = paginator.page(1) except EmptyPage: people = paginator.page(paginator.num_pages) show_pagination = paginator.count > settings.ITEMS_PER_PAGE extra_data = dict( people=people, group=group, in_group=in_group, is_curator=is_curator, is_pending=is_pending, show_pagination=show_pagination, show_delete_group_button=show_delete_group_button, show_join_button=group.user_can_join(request.user.userprofile), show_leave_button=group.user_can_leave(request.user.userprofile), members=group.member_count, ) data.update(extra_data) return render(request, template, data)
def get_absolute_url(self): return absolutify(reverse('groups:show_group', args=[self.url]))