Example #1
0
    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])
Example #2
0
    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])
Example #3
0
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]))
Example #4
0
 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')
Example #5
0
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)
Example #6
0
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)
Example #7
0
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)
Example #8
0
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)
Example #9
0
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]))
Example #10
0
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'))
Example #11
0
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'))
Example #12
0
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'))
Example #13
0
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'))
Example #14
0
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)
Example #15
0
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)
Example #16
0
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)
Example #17
0
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)
Example #18
0
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]))
Example #19
0
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]))
Example #20
0
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)
Example #21
0
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)
Example #22
0
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)
Example #23
0
    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('&#34;', '"').replace('&#39;', "'")

        send_mail(subject, filtered_message, settings.FROM_NOREPLY,
                  [self.inviter.email])
Example #24
0
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)
Example #25
0
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()
Example #26
0
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)
Example #27
0
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)
Example #28
0
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]))
Example #29
0
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()
Example #30
0
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)
Example #31
0
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()
Example #32
0
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()
Example #33
0
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)
Example #34
0
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()
Example #35
0
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)
Example #36
0
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]))
Example #37
0
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)
Example #38
0
    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")
Example #39
0
    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')
Example #40
0
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]))
Example #41
0
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]))
Example #42
0
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)
Example #43
0
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)
Example #44
0
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)
Example #45
0
 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]))
Example #46
0
    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'))
Example #47
0
 def get_absolute_url(self):
     return reverse('phonebook:profile_view', args=[self.user.username])
Example #48
0
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)
Example #49
0
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)
Example #50
0
 def get_url(self, obj):
     return absolutify(reverse('phonebook:profile_view',
                               kwargs={'username': obj.user.username}))
Example #51
0
 def get_url(self, obj):
     return absolutify(reverse('groups:show_skill', kwargs={'url': obj.url}))
Example #52
0
 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]))
Example #53
0
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)
Example #54
0
 def get_absolute_url(self):
     return reverse('phonebook:profile_view', args=[self.user.username])
Example #55
0
def url(viewname, *args, **kwargs):
    """Helper for Django's ``reverse`` in templates."""
    return reverse(viewname, args=args, kwargs=kwargs)
Example #56
0
def redirect(to, *args, **kwargs):
    """Redirect with locale support."""
    url = reverse(to, args=args, kwargs=kwargs)
    return HttpResponseRedirect(url)
Example #57
0
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)
Example #58
0
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)
Example #59
0
 def get_absolute_url(self):
     return absolutify(reverse('groups:show_group', args=[self.url]))