Code reference › domain › person

module

pretalx.person.domain.user

Functions 6

change_password(user, new_password)sourceSet new_password, clear any pending reset token, send the confirmation mail and log the change.
@transaction.atomic
def change_password(user, new_password):
    """Set ``new_password``, clear any pending reset token, send the
    confirmation mail and log the change."""
    user.set_password(new_password)
    user.pw_reset_token = None
    user.pw_reset_time = None
    user.save(update_fields=["password", "pw_reset_token", "pw_reset_time"])

    send_system_mail(
        subject=PASSWORD_CHANGED_SUBJECT,
        text=PASSWORD_CHANGED_TEXT,
        to=user.email,
        locale=user.locale,
        context_kwargs={"user": user},
    )

    user.log_action("pretalx.user.password.update")
create_user(*, email, name='', password=None, **kwargs)sourceSingle entry point for creating User objects.

Extra kwargs are forwarded to the User constructor.

def create_user(*, email, name="", password=None, **kwargs):
    """Single entry point for creating User objects.

    Extra kwargs are forwarded to the User constructor.
    """
    name = (name or "").strip()
    kwargs.setdefault("locale", get_language())
    kwargs.setdefault("timezone", timezone.get_current_timezone_name())

    user = User(email=email, name=name, **kwargs)
    if password is None:
        user.set_unusable_password()
    else:
        user.set_password(password)
    user.clean()
    user.save()
    return user
deactivate_user(user)sourceAnonymise user in place: scramble email, blank personal data, drop pictures and team memberships, delete answers flagged as personal data.
@transaction.atomic
def deactivate_user(user):
    """Anonymise ``user`` in place: scramble email, blank personal data,
    drop pictures and team memberships, delete answers flagged as
    personal data."""
    user.email = f"deleted_user_{get_random_string(12)}@localhost"
    while User.objects.filter(email__iexact=user.email).exists():
        user.email = f"deleted_user_{get_random_string(12)}@localhost"
    user.name = "Deleted User"
    user.is_active = False
    user.is_superuser = False
    user.is_administrator = False
    user.locale = "en"
    user.timezone = "UTC"
    user.pw_reset_token = None
    user.pw_reset_time = None
    user.email_verification_state = EmailVerificationState.UNVERIFIED
    user.pending_email = None
    user.pending_email_sent = None
    user.set_unusable_password()
    user.delete_files()
    user.profile_picture = None
    user.save()
    user.profiles.update(biography="")
    for answer in (
        Answer.objects.filter(
            models.Q(speaker__user=user) | models.Q(submission__speakers__user=user),
            question__contains_personal_data=True,
        )
        .select_related("question", "speaker__event", "submission__event")
        .distinct()
    ):
        answer.delete()  # iterate to delete answer files too
    for team in user.teams.all():
        team.members.remove(user)
    delete_user_signal.send(None, user=user, db_delete=True)
get_password_reset_url(user, *, event=None, orga=False)sourceURL the password-reset mail points at, namespaced by the orga flag and (optionally) the event slug.
def get_password_reset_url(user, *, event=None, orga=False):
    """URL the password-reset mail points at, namespaced by the orga
    flag and (optionally) the event slug."""
    if event:
        path = "orga:event.auth.recover" if orga else "cfp:event.recover"
        kwargs = {"token": user.pw_reset_token, "event": event.slug}
    else:
        path = "orga:auth.recover"
        kwargs = {"token": user.pw_reset_token}
    # Returning an :class:`urlman.UrlString` (a ``str`` subclass)
    # lets :func:`pretalx.mail.domain.context.get_mail_context` drop the
    # result into ``safe_extra_context`` without a separate
    # ``mark_safe`` wrap at every call site.
    return UrlString(build_absolute_uri(path, kwargs=kwargs))
reset_password(user, *, event=None, orga=False, log_actor=None, mail_text=None)sourceMint a fresh pw_reset_token and queue the recovery mail.

log_actor (orga who triggered the reset on behalf of the user) is logged on the activity entry; orga=True flips the recovery URL onto the orga namespace.

@transaction.atomic
def reset_password(user, *, event=None, orga=False, log_actor=None, mail_text=None):
    """Mint a fresh ``pw_reset_token`` and queue the recovery mail.

    ``log_actor`` (orga who triggered the reset on behalf of the user)
    is logged on the activity entry; ``orga=True`` flips the recovery
    URL onto the orga namespace."""
    user.pw_reset_token = get_random_string(32)
    user.pw_reset_time = now()
    user.save(update_fields=["pw_reset_token", "pw_reset_time"])

    send_system_mail(
        subject=PASSWORD_RESET_SUBJECT,
        text=mail_text or PASSWORD_RESET_TEXT,
        to=user.email,
        event=event,
        locale=user.locale,
        safe_extra_context={
            "url": get_password_reset_url(user, event=event, orga=orga)
        },
        context_kwargs={"user": user},
    )
    user.log_action(
        action="pretalx.user.password.reset", person=log_actor, orga=bool(log_actor)
    )
shred_user(user)sourceDelete user outright.

Refuses if the user still has submissions, answers or team memberships — call deactivate_user in that case.

@transaction.atomic
def shred_user(user):
    """Delete ``user`` outright. Refuses if the user still has
    submissions, answers or team memberships — call ``deactivate_user``
    in that case."""
    with scopes_disabled():
        if (
            Submission.all_objects.filter(speakers__user=user).exists()
            or user.teams.exists()
            # We use two separate queries here for performance reasons:
            # Q(…) | Q(…) would run an expensive index-less join first
            # before cutting it with a LIMIT 1
            or Answer.objects.filter(speaker__user=user).exists()
            or Answer.objects.filter(submission__speakers__user=user).exists()
        ):
            raise UserDeletionError(
                f"Cannot delete user <{user.email}> because they have submissions, answers, or teams. Please deactivate this user instead."
            )
        user.logged_actions().delete()
        actions_by(user).update(person=None)
        user.delete_files()
        delete_user_signal.send(None, user=user, db_delete=True)
        user.delete()