mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-22 05:31:10 -05:00
okay fine
This commit is contained in:
321
.venv/lib/python3.12/site-packages/allauth/utils.py
Normal file
321
.venv/lib/python3.12/site-packages/allauth/utils.py
Normal file
@@ -0,0 +1,321 @@
|
||||
import base64
|
||||
import importlib
|
||||
import json
|
||||
import random
|
||||
import re
|
||||
import string
|
||||
import unicodedata
|
||||
from collections import OrderedDict
|
||||
from urllib.parse import urlsplit
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import (
|
||||
FieldDoesNotExist,
|
||||
ImproperlyConfigured,
|
||||
ValidationError,
|
||||
)
|
||||
from django.core.files.base import ContentFile
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.core.validators import validate_email
|
||||
from django.db.models import FileField
|
||||
from django.db.models.fields import (
|
||||
BinaryField,
|
||||
DateField,
|
||||
DateTimeField,
|
||||
EmailField,
|
||||
TimeField,
|
||||
)
|
||||
from django.utils import dateparse
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
|
||||
from allauth import app_settings
|
||||
|
||||
|
||||
# Magic number 7: if you run into collisions with this number, then you are
|
||||
# of big enough scale to start investing in a decent user model...
|
||||
MAX_USERNAME_SUFFIX_LENGTH = 7
|
||||
USERNAME_SUFFIX_CHARS = [string.digits] * 4 + [string.ascii_letters] * (
|
||||
MAX_USERNAME_SUFFIX_LENGTH - 4
|
||||
)
|
||||
|
||||
|
||||
def _generate_unique_username_base(txts, regex=None):
|
||||
from .account.adapter import get_adapter
|
||||
|
||||
adapter = get_adapter()
|
||||
username = None
|
||||
regex = regex or r"[^\w\s@+.-]"
|
||||
for txt in txts:
|
||||
if not txt:
|
||||
continue
|
||||
username = unicodedata.normalize("NFKD", force_str(txt))
|
||||
username = username.encode("ascii", "ignore").decode("ascii")
|
||||
if len(username) == 0:
|
||||
continue
|
||||
username = force_str(re.sub(regex, "", username).lower())
|
||||
# Django allows for '@' in usernames in order to accommodate for
|
||||
# project wanting to use email for username. In allauth we don't
|
||||
# use this, we already have a proper place for putting email
|
||||
# addresses (EmailAddress), so let's not use the full email
|
||||
# address and only take the part leading up to the '@'.
|
||||
username = username.split("@")[0]
|
||||
username = username.strip()
|
||||
username = re.sub(r"\s+", "_", username)
|
||||
# Finally, validating base username without database lookups etc.
|
||||
try:
|
||||
username = adapter.clean_username(username, shallow=True)
|
||||
break
|
||||
except ValidationError:
|
||||
pass
|
||||
return username or "user"
|
||||
|
||||
|
||||
def get_username_max_length():
|
||||
from .account.app_settings import USER_MODEL_USERNAME_FIELD
|
||||
|
||||
if USER_MODEL_USERNAME_FIELD is not None:
|
||||
User = get_user_model()
|
||||
max_length = User._meta.get_field(USER_MODEL_USERNAME_FIELD).max_length
|
||||
else:
|
||||
max_length = 0
|
||||
return max_length
|
||||
|
||||
|
||||
def generate_username_candidate(basename, suffix_length):
|
||||
max_length = get_username_max_length()
|
||||
suffix = "".join(
|
||||
random.choice(USERNAME_SUFFIX_CHARS[i]) for i in range(suffix_length)
|
||||
)
|
||||
return basename[0 : max_length - len(suffix)] + suffix
|
||||
|
||||
|
||||
def generate_username_candidates(basename):
|
||||
from .account.app_settings import USERNAME_MIN_LENGTH
|
||||
|
||||
if len(basename) >= USERNAME_MIN_LENGTH:
|
||||
ret = [basename]
|
||||
else:
|
||||
ret = []
|
||||
min_suffix_length = max(1, USERNAME_MIN_LENGTH - len(basename))
|
||||
max_suffix_length = min(get_username_max_length(), MAX_USERNAME_SUFFIX_LENGTH)
|
||||
for suffix_length in range(min_suffix_length, max_suffix_length):
|
||||
ret.append(generate_username_candidate(basename, suffix_length))
|
||||
return ret
|
||||
|
||||
|
||||
def generate_unique_username(txts, regex=None):
|
||||
from allauth.account.utils import filter_users_by_username
|
||||
|
||||
from .account.adapter import get_adapter
|
||||
from .account.app_settings import USER_MODEL_USERNAME_FIELD
|
||||
|
||||
adapter = get_adapter()
|
||||
basename = _generate_unique_username_base(txts, regex)
|
||||
candidates = generate_username_candidates(basename)
|
||||
existing_usernames = filter_users_by_username(*candidates).values_list(
|
||||
USER_MODEL_USERNAME_FIELD, flat=True
|
||||
)
|
||||
existing_usernames = set([n.lower() for n in existing_usernames])
|
||||
for candidate in candidates:
|
||||
if candidate.lower() not in existing_usernames:
|
||||
try:
|
||||
return adapter.clean_username(candidate, shallow=True)
|
||||
except ValidationError:
|
||||
pass
|
||||
# This really should not happen
|
||||
raise NotImplementedError("Unable to find a unique username")
|
||||
|
||||
|
||||
def valid_email_or_none(email):
|
||||
ret = None
|
||||
try:
|
||||
if email:
|
||||
validate_email(email)
|
||||
if len(email) <= EmailField().max_length:
|
||||
ret = email.lower()
|
||||
except ValidationError:
|
||||
pass
|
||||
return ret
|
||||
|
||||
|
||||
def import_attribute(path):
|
||||
assert isinstance(path, str)
|
||||
pkg, attr = path.rsplit(".", 1)
|
||||
ret = getattr(importlib.import_module(pkg), attr)
|
||||
return ret
|
||||
|
||||
|
||||
def import_callable(path_or_callable):
|
||||
if not callable(path_or_callable):
|
||||
ret = import_attribute(path_or_callable)
|
||||
else:
|
||||
ret = path_or_callable
|
||||
return ret
|
||||
|
||||
|
||||
SERIALIZED_DB_FIELD_PREFIX = "_db_"
|
||||
|
||||
|
||||
def serialize_instance(instance):
|
||||
"""
|
||||
Since Django 1.6 items added to the session are no longer pickled,
|
||||
but JSON encoded by default. We are storing partially complete models
|
||||
in the session (user, account, token, ...). We cannot use standard
|
||||
Django serialization, as these are models are not "complete" yet.
|
||||
Serialization will start complaining about missing relations et al.
|
||||
"""
|
||||
data = {}
|
||||
for k, v in instance.__dict__.items():
|
||||
if k.startswith("_") or callable(v):
|
||||
continue
|
||||
try:
|
||||
field = instance._meta.get_field(k)
|
||||
if isinstance(field, BinaryField):
|
||||
if v is not None:
|
||||
v = force_str(base64.b64encode(v))
|
||||
elif isinstance(field, FileField):
|
||||
if v and not isinstance(v, str):
|
||||
v = {
|
||||
"name": v.name,
|
||||
"content": base64.b64encode(v.read()).decode("ascii"),
|
||||
}
|
||||
# Check if the field is serializable. If not, we'll fall back
|
||||
# to serializing the DB values which should cover most use cases.
|
||||
try:
|
||||
json.dumps(v, cls=DjangoJSONEncoder)
|
||||
except TypeError:
|
||||
v = field.get_prep_value(v)
|
||||
k = SERIALIZED_DB_FIELD_PREFIX + k
|
||||
except FieldDoesNotExist:
|
||||
pass
|
||||
data[k] = v
|
||||
return json.loads(json.dumps(data, cls=DjangoJSONEncoder))
|
||||
|
||||
|
||||
def deserialize_instance(model, data):
|
||||
ret = model()
|
||||
for k, v in data.items():
|
||||
is_db_value = False
|
||||
if k.startswith(SERIALIZED_DB_FIELD_PREFIX):
|
||||
k = k[len(SERIALIZED_DB_FIELD_PREFIX) :]
|
||||
is_db_value = True
|
||||
if v is not None:
|
||||
try:
|
||||
f = model._meta.get_field(k)
|
||||
if isinstance(f, DateTimeField):
|
||||
v = dateparse.parse_datetime(v)
|
||||
elif isinstance(f, TimeField):
|
||||
v = dateparse.parse_time(v)
|
||||
elif isinstance(f, DateField):
|
||||
v = dateparse.parse_date(v)
|
||||
elif isinstance(f, BinaryField):
|
||||
v = force_bytes(base64.b64decode(force_bytes(v)))
|
||||
elif isinstance(f, FileField):
|
||||
if isinstance(v, dict):
|
||||
v = ContentFile(base64.b64decode(v["content"]), name=v["name"])
|
||||
elif is_db_value:
|
||||
try:
|
||||
# This is quite an ugly hack, but will cover most
|
||||
# use cases...
|
||||
# The signature of `from_db_value` changed in Django 3
|
||||
# https://docs.djangoproject.com/en/3.0/releases/3.0/#features-removed-in-3-0
|
||||
v = f.from_db_value(v, None, None)
|
||||
except Exception:
|
||||
raise ImproperlyConfigured(
|
||||
"Unable to auto serialize field '{}', custom"
|
||||
" serialization override required".format(k)
|
||||
)
|
||||
except FieldDoesNotExist:
|
||||
pass
|
||||
setattr(ret, k, v)
|
||||
return ret
|
||||
|
||||
|
||||
def set_form_field_order(form, field_order):
|
||||
"""
|
||||
This function is a verbatim copy of django.forms.Form.order_fields() to
|
||||
support field ordering below Django 1.9.
|
||||
|
||||
field_order is a list of field names specifying the order. Append fields
|
||||
not included in the list in the default order for backward compatibility
|
||||
with subclasses not overriding field_order. If field_order is None, keep
|
||||
all fields in the order defined in the class. Ignore unknown fields in
|
||||
field_order to allow disabling fields in form subclasses without
|
||||
redefining ordering.
|
||||
"""
|
||||
if field_order is None:
|
||||
return
|
||||
fields = OrderedDict()
|
||||
for key in field_order:
|
||||
try:
|
||||
fields[key] = form.fields.pop(key)
|
||||
except KeyError: # ignore unknown fields
|
||||
pass
|
||||
fields.update(form.fields) # add remaining fields in original order
|
||||
form.fields = fields
|
||||
|
||||
|
||||
def build_absolute_uri(request, location, protocol=None):
|
||||
"""request.build_absolute_uri() helper
|
||||
|
||||
Like request.build_absolute_uri, but gracefully handling
|
||||
the case where request is None.
|
||||
"""
|
||||
from .account import app_settings as account_settings
|
||||
|
||||
if request is None:
|
||||
if not app_settings.SITES_ENABLED:
|
||||
raise ImproperlyConfigured(
|
||||
"Passing `request=None` requires `sites` to be enabled."
|
||||
)
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
site = Site.objects.get_current()
|
||||
bits = urlsplit(location)
|
||||
if not (bits.scheme and bits.netloc):
|
||||
uri = "{proto}://{domain}{url}".format(
|
||||
proto=account_settings.DEFAULT_HTTP_PROTOCOL,
|
||||
domain=site.domain,
|
||||
url=location,
|
||||
)
|
||||
else:
|
||||
uri = location
|
||||
else:
|
||||
uri = request.build_absolute_uri(location)
|
||||
# NOTE: We only force a protocol if we are instructed to do so
|
||||
# (via the `protocol` parameter, or, if the default is set to
|
||||
# HTTPS. The latter keeps compatibility with the debatable use
|
||||
# case of running your site under both HTTP and HTTPS, where one
|
||||
# would want to make sure HTTPS links end up in password reset
|
||||
# mails even while they were initiated on an HTTP password reset
|
||||
# form.
|
||||
if not protocol and account_settings.DEFAULT_HTTP_PROTOCOL == "https":
|
||||
protocol = account_settings.DEFAULT_HTTP_PROTOCOL
|
||||
# (end NOTE)
|
||||
if protocol:
|
||||
uri = protocol + ":" + uri.partition(":")[2]
|
||||
return uri
|
||||
|
||||
|
||||
def get_form_class(forms, form_id, default_form):
|
||||
form_class = forms.get(form_id, default_form)
|
||||
if isinstance(form_class, str):
|
||||
form_class = import_attribute(form_class)
|
||||
return form_class
|
||||
|
||||
|
||||
def get_request_param(request, param, default=None):
|
||||
if request is None:
|
||||
return default
|
||||
return request.POST.get(param) or request.GET.get(param, default)
|
||||
|
||||
|
||||
def get_setting(name, dflt):
|
||||
getter = getattr(
|
||||
settings,
|
||||
"ALLAUTH_SETTING_GETTER",
|
||||
lambda name, dflt: getattr(settings, name, dflt),
|
||||
)
|
||||
getter = import_callable(getter)
|
||||
return getter(name, dflt)
|
||||
Reference in New Issue
Block a user