Erste Version mit erstem Datenmodell realisiert. Noch kein Template, kein View.
This commit is contained in:
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,7 @@
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class SessionsConfig(AppConfig):
|
||||
name = "django.contrib.sessions"
|
||||
verbose_name = _("Sessions")
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,525 @@
|
||||
import logging
|
||||
import string
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
|
||||
from django.conf import settings
|
||||
from django.core import signing
|
||||
from django.utils import timezone
|
||||
from django.utils.crypto import get_random_string
|
||||
from django.utils.module_loading import import_string
|
||||
|
||||
# session_key should not be case sensitive because some backends can store it
|
||||
# on case insensitive file systems.
|
||||
VALID_KEY_CHARS = string.ascii_lowercase + string.digits
|
||||
|
||||
|
||||
class CreateError(Exception):
|
||||
"""
|
||||
Used internally as a consistent exception type to catch from save (see the
|
||||
docstring for SessionBase.save() for details).
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class UpdateError(Exception):
|
||||
"""
|
||||
Occurs if Django tries to update a session that was deleted.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SessionBase:
|
||||
"""
|
||||
Base class for all Session classes.
|
||||
"""
|
||||
|
||||
TEST_COOKIE_NAME = "testcookie"
|
||||
TEST_COOKIE_VALUE = "worked"
|
||||
|
||||
__not_given = object()
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
self._session_key = session_key
|
||||
self.accessed = False
|
||||
self.modified = False
|
||||
self.serializer = import_string(settings.SESSION_SERIALIZER)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._session
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._session[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self._session[key] = value
|
||||
self.modified = True
|
||||
|
||||
async def aset(self, key, value):
|
||||
(await self._aget_session())[key] = value
|
||||
self.modified = True
|
||||
|
||||
def __delitem__(self, key):
|
||||
del self._session[key]
|
||||
self.modified = True
|
||||
|
||||
@property
|
||||
def key_salt(self):
|
||||
return "django.contrib.sessions." + self.__class__.__qualname__
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self._session.get(key, default)
|
||||
|
||||
async def aget(self, key, default=None):
|
||||
return (await self._aget_session()).get(key, default)
|
||||
|
||||
def pop(self, key, default=__not_given):
|
||||
self.modified = self.modified or key in self._session
|
||||
args = () if default is self.__not_given else (default,)
|
||||
return self._session.pop(key, *args)
|
||||
|
||||
async def apop(self, key, default=__not_given):
|
||||
self.modified = self.modified or key in (await self._aget_session())
|
||||
args = () if default is self.__not_given else (default,)
|
||||
return (await self._aget_session()).pop(key, *args)
|
||||
|
||||
def setdefault(self, key, value):
|
||||
if key in self._session:
|
||||
return self._session[key]
|
||||
else:
|
||||
self[key] = value
|
||||
return value
|
||||
|
||||
async def asetdefault(self, key, value):
|
||||
session = await self._aget_session()
|
||||
if key in session:
|
||||
return session[key]
|
||||
else:
|
||||
await self.aset(key, value)
|
||||
return value
|
||||
|
||||
def set_test_cookie(self):
|
||||
self[self.TEST_COOKIE_NAME] = self.TEST_COOKIE_VALUE
|
||||
|
||||
async def aset_test_cookie(self):
|
||||
await self.aset(self.TEST_COOKIE_NAME, self.TEST_COOKIE_VALUE)
|
||||
|
||||
def test_cookie_worked(self):
|
||||
return self.get(self.TEST_COOKIE_NAME) == self.TEST_COOKIE_VALUE
|
||||
|
||||
async def atest_cookie_worked(self):
|
||||
return (await self.aget(self.TEST_COOKIE_NAME)) == self.TEST_COOKIE_VALUE
|
||||
|
||||
def delete_test_cookie(self):
|
||||
del self[self.TEST_COOKIE_NAME]
|
||||
|
||||
async def adelete_test_cookie(self):
|
||||
del (await self._aget_session())[self.TEST_COOKIE_NAME]
|
||||
|
||||
def encode(self, session_dict):
|
||||
"""
|
||||
Return the given session dictionary serialized and encoded as a string.
|
||||
"""
|
||||
return signing.dumps(
|
||||
session_dict,
|
||||
salt=self.key_salt,
|
||||
serializer=self.serializer,
|
||||
compress=True,
|
||||
)
|
||||
|
||||
def decode(self, session_data):
|
||||
try:
|
||||
return signing.loads(
|
||||
session_data, salt=self.key_salt, serializer=self.serializer
|
||||
)
|
||||
except signing.BadSignature:
|
||||
logger = logging.getLogger("django.security.SuspiciousSession")
|
||||
logger.warning("Session data corrupted")
|
||||
except Exception:
|
||||
# ValueError, unpickling exceptions. If any of these happen, just
|
||||
# return an empty dictionary (an empty session).
|
||||
pass
|
||||
return {}
|
||||
|
||||
def update(self, dict_):
|
||||
self._session.update(dict_)
|
||||
self.modified = True
|
||||
|
||||
async def aupdate(self, dict_):
|
||||
(await self._aget_session()).update(dict_)
|
||||
self.modified = True
|
||||
|
||||
def has_key(self, key):
|
||||
return key in self._session
|
||||
|
||||
async def ahas_key(self, key):
|
||||
return key in (await self._aget_session())
|
||||
|
||||
def keys(self):
|
||||
return self._session.keys()
|
||||
|
||||
async def akeys(self):
|
||||
return (await self._aget_session()).keys()
|
||||
|
||||
def values(self):
|
||||
return self._session.values()
|
||||
|
||||
async def avalues(self):
|
||||
return (await self._aget_session()).values()
|
||||
|
||||
def items(self):
|
||||
return self._session.items()
|
||||
|
||||
async def aitems(self):
|
||||
return (await self._aget_session()).items()
|
||||
|
||||
def clear(self):
|
||||
# To avoid unnecessary persistent storage accesses, we set up the
|
||||
# internals directly (loading data wastes time, since we are going to
|
||||
# set it to an empty dict anyway).
|
||||
self._session_cache = {}
|
||||
self.accessed = True
|
||||
self.modified = True
|
||||
|
||||
def is_empty(self):
|
||||
"Return True when there is no session_key and the session is empty."
|
||||
try:
|
||||
return not self._session_key and not self._session_cache
|
||||
except AttributeError:
|
||||
return True
|
||||
|
||||
def _get_new_session_key(self):
|
||||
"Return session key that isn't being used."
|
||||
while True:
|
||||
session_key = get_random_string(32, VALID_KEY_CHARS)
|
||||
if not self.exists(session_key):
|
||||
return session_key
|
||||
|
||||
async def _aget_new_session_key(self):
|
||||
while True:
|
||||
session_key = get_random_string(32, VALID_KEY_CHARS)
|
||||
if not await self.aexists(session_key):
|
||||
return session_key
|
||||
|
||||
def _get_or_create_session_key(self):
|
||||
if self._session_key is None:
|
||||
self._session_key = self._get_new_session_key()
|
||||
return self._session_key
|
||||
|
||||
async def _aget_or_create_session_key(self):
|
||||
if self._session_key is None:
|
||||
self._session_key = await self._aget_new_session_key()
|
||||
return self._session_key
|
||||
|
||||
def _validate_session_key(self, key):
|
||||
"""
|
||||
Key must be truthy and at least 8 characters long. 8 characters is an
|
||||
arbitrary lower bound for some minimal key security.
|
||||
"""
|
||||
return key and len(key) >= 8
|
||||
|
||||
def _get_session_key(self):
|
||||
return self.__session_key
|
||||
|
||||
def _set_session_key(self, value):
|
||||
"""
|
||||
Validate session key on assignment. Invalid values will set to None.
|
||||
"""
|
||||
if self._validate_session_key(value):
|
||||
self.__session_key = value
|
||||
else:
|
||||
self.__session_key = None
|
||||
|
||||
session_key = property(_get_session_key)
|
||||
_session_key = property(_get_session_key, _set_session_key)
|
||||
|
||||
def _get_session(self, no_load=False):
|
||||
"""
|
||||
Lazily load session from storage (unless "no_load" is True, when only
|
||||
an empty dict is stored) and store it in the current instance.
|
||||
"""
|
||||
self.accessed = True
|
||||
try:
|
||||
return self._session_cache
|
||||
except AttributeError:
|
||||
if self.session_key is None or no_load:
|
||||
self._session_cache = {}
|
||||
else:
|
||||
self._session_cache = self.load()
|
||||
return self._session_cache
|
||||
|
||||
async def _aget_session(self, no_load=False):
|
||||
self.accessed = True
|
||||
try:
|
||||
return self._session_cache
|
||||
except AttributeError:
|
||||
if self.session_key is None or no_load:
|
||||
self._session_cache = {}
|
||||
else:
|
||||
self._session_cache = await self.aload()
|
||||
return self._session_cache
|
||||
|
||||
_session = property(_get_session)
|
||||
|
||||
def get_session_cookie_age(self):
|
||||
return settings.SESSION_COOKIE_AGE
|
||||
|
||||
def get_expiry_age(self, **kwargs):
|
||||
"""Get the number of seconds until the session expires.
|
||||
|
||||
Optionally, this function accepts `modification` and `expiry` keyword
|
||||
arguments specifying the modification and expiry of the session.
|
||||
"""
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
# Make the difference between "expiry=None passed in kwargs" and
|
||||
# "expiry not passed in kwargs", in order to guarantee not to trigger
|
||||
# self.load() when expiry is provided.
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
except KeyError:
|
||||
expiry = self.get("_session_expiry")
|
||||
|
||||
if not expiry: # Checks both None and 0 cases
|
||||
return self.get_session_cookie_age()
|
||||
if not isinstance(expiry, (datetime, str)):
|
||||
return expiry
|
||||
if isinstance(expiry, str):
|
||||
expiry = datetime.fromisoformat(expiry)
|
||||
delta = expiry - modification
|
||||
return delta.days * 86400 + delta.seconds
|
||||
|
||||
async def aget_expiry_age(self, **kwargs):
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
except KeyError:
|
||||
expiry = await self.aget("_session_expiry")
|
||||
|
||||
if not expiry: # Checks both None and 0 cases
|
||||
return self.get_session_cookie_age()
|
||||
if not isinstance(expiry, (datetime, str)):
|
||||
return expiry
|
||||
if isinstance(expiry, str):
|
||||
expiry = datetime.fromisoformat(expiry)
|
||||
delta = expiry - modification
|
||||
return delta.days * 86400 + delta.seconds
|
||||
|
||||
def get_expiry_date(self, **kwargs):
|
||||
"""Get session the expiry date (as a datetime object).
|
||||
|
||||
Optionally, this function accepts `modification` and `expiry` keyword
|
||||
arguments specifying the modification and expiry of the session.
|
||||
"""
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
# Same comment as in get_expiry_age
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
except KeyError:
|
||||
expiry = self.get("_session_expiry")
|
||||
|
||||
if isinstance(expiry, datetime):
|
||||
return expiry
|
||||
elif isinstance(expiry, str):
|
||||
return datetime.fromisoformat(expiry)
|
||||
expiry = expiry or self.get_session_cookie_age()
|
||||
return modification + timedelta(seconds=expiry)
|
||||
|
||||
async def aget_expiry_date(self, **kwargs):
|
||||
try:
|
||||
modification = kwargs["modification"]
|
||||
except KeyError:
|
||||
modification = timezone.now()
|
||||
try:
|
||||
expiry = kwargs["expiry"]
|
||||
except KeyError:
|
||||
expiry = await self.aget("_session_expiry")
|
||||
|
||||
if isinstance(expiry, datetime):
|
||||
return expiry
|
||||
elif isinstance(expiry, str):
|
||||
return datetime.fromisoformat(expiry)
|
||||
expiry = expiry or self.get_session_cookie_age()
|
||||
return modification + timedelta(seconds=expiry)
|
||||
|
||||
def set_expiry(self, value):
|
||||
"""
|
||||
Set a custom expiration for the session. ``value`` can be an integer,
|
||||
a Python ``datetime`` or ``timedelta`` object or ``None``.
|
||||
|
||||
If ``value`` is an integer, the session will expire after that many
|
||||
seconds of inactivity. If set to ``0`` then the session will expire on
|
||||
browser close.
|
||||
|
||||
If ``value`` is a ``datetime`` or ``timedelta`` object, the session
|
||||
will expire at that specific future time.
|
||||
|
||||
If ``value`` is ``None``, the session uses the global session expiry
|
||||
policy.
|
||||
"""
|
||||
if value is None:
|
||||
# Remove any custom expiration for this session.
|
||||
try:
|
||||
del self["_session_expiry"]
|
||||
except KeyError:
|
||||
pass
|
||||
return
|
||||
if isinstance(value, timedelta):
|
||||
value = timezone.now() + value
|
||||
if isinstance(value, datetime):
|
||||
value = value.isoformat()
|
||||
self["_session_expiry"] = value
|
||||
|
||||
async def aset_expiry(self, value):
|
||||
if value is None:
|
||||
# Remove any custom expiration for this session.
|
||||
try:
|
||||
await self.apop("_session_expiry")
|
||||
except KeyError:
|
||||
pass
|
||||
return
|
||||
if isinstance(value, timedelta):
|
||||
value = timezone.now() + value
|
||||
if isinstance(value, datetime):
|
||||
value = value.isoformat()
|
||||
await self.aset("_session_expiry", value)
|
||||
|
||||
def get_expire_at_browser_close(self):
|
||||
"""
|
||||
Return ``True`` if the session is set to expire when the browser
|
||||
closes, and ``False`` if there's an expiry date. Use
|
||||
``get_expiry_date()`` or ``get_expiry_age()`` to find the actual expiry
|
||||
date/age, if there is one.
|
||||
"""
|
||||
if (expiry := self.get("_session_expiry")) is None:
|
||||
return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
|
||||
return expiry == 0
|
||||
|
||||
async def aget_expire_at_browser_close(self):
|
||||
if (expiry := await self.aget("_session_expiry")) is None:
|
||||
return settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
|
||||
return expiry == 0
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
Remove the current session data from the database and regenerate the
|
||||
key.
|
||||
"""
|
||||
self.clear()
|
||||
self.delete()
|
||||
self._session_key = None
|
||||
|
||||
async def aflush(self):
|
||||
self.clear()
|
||||
await self.adelete()
|
||||
self._session_key = None
|
||||
|
||||
def cycle_key(self):
|
||||
"""
|
||||
Create a new session key, while retaining the current session data.
|
||||
"""
|
||||
data = self._session
|
||||
key = self.session_key
|
||||
self.create()
|
||||
self._session_cache = data
|
||||
if key:
|
||||
self.delete(key)
|
||||
|
||||
async def acycle_key(self):
|
||||
"""
|
||||
Create a new session key, while retaining the current session data.
|
||||
"""
|
||||
data = await self._aget_session()
|
||||
key = self.session_key
|
||||
await self.acreate()
|
||||
self._session_cache = data
|
||||
if key:
|
||||
await self.adelete(key)
|
||||
|
||||
# Methods that child classes must implement.
|
||||
|
||||
def exists(self, session_key):
|
||||
"""
|
||||
Return True if the given session_key already exists.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide an exists() method"
|
||||
)
|
||||
|
||||
async def aexists(self, session_key):
|
||||
return await sync_to_async(self.exists)(session_key)
|
||||
|
||||
def create(self):
|
||||
"""
|
||||
Create a new session instance. Guaranteed to create a new object with
|
||||
a unique key and will have saved the result once (with empty data)
|
||||
before the method returns.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a create() method"
|
||||
)
|
||||
|
||||
async def acreate(self):
|
||||
return await sync_to_async(self.create)()
|
||||
|
||||
def save(self, must_create=False):
|
||||
"""
|
||||
Save the session data. If 'must_create' is True, create a new session
|
||||
object (or raise CreateError). Otherwise, only update an existing
|
||||
object and don't create one (raise UpdateError if needed).
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a save() method"
|
||||
)
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
return await sync_to_async(self.save)(must_create)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
"""
|
||||
Delete the session data under this key. If the key is None, use the
|
||||
current session key value.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a delete() method"
|
||||
)
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
return await sync_to_async(self.delete)(session_key)
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the session data and return a dictionary.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"subclasses of SessionBase must provide a load() method"
|
||||
)
|
||||
|
||||
async def aload(self):
|
||||
return await sync_to_async(self.load)()
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
"""
|
||||
Remove expired sessions from the session store.
|
||||
|
||||
If this operation isn't possible on a given backend, it should raise
|
||||
NotImplementedError. If it isn't necessary, because the backend has
|
||||
a built-in expiration mechanism, it should be a no-op.
|
||||
"""
|
||||
raise NotImplementedError("This backend does not support clear_expired().")
|
||||
|
||||
@classmethod
|
||||
async def aclear_expired(cls):
|
||||
return await sync_to_async(cls.clear_expired)()
|
||||
@@ -0,0 +1,145 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
|
||||
from django.core.cache import caches
|
||||
|
||||
KEY_PREFIX = "django.contrib.sessions.cache"
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
"""
|
||||
A cache-based session store.
|
||||
"""
|
||||
|
||||
cache_key_prefix = KEY_PREFIX
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
self._cache = caches[settings.SESSION_CACHE_ALIAS]
|
||||
super().__init__(session_key)
|
||||
|
||||
@property
|
||||
def cache_key(self):
|
||||
return self.cache_key_prefix + self._get_or_create_session_key()
|
||||
|
||||
async def acache_key(self):
|
||||
return self.cache_key_prefix + await self._aget_or_create_session_key()
|
||||
|
||||
def load(self):
|
||||
try:
|
||||
session_data = self._cache.get(self.cache_key)
|
||||
except Exception:
|
||||
# Some backends (e.g. memcache) raise an exception on invalid
|
||||
# cache keys. If this happens, reset the session. See #17810.
|
||||
session_data = None
|
||||
if session_data is not None:
|
||||
return session_data
|
||||
self._session_key = None
|
||||
return {}
|
||||
|
||||
async def aload(self):
|
||||
try:
|
||||
session_data = await self._cache.aget(await self.acache_key())
|
||||
except Exception:
|
||||
session_data = None
|
||||
if session_data is not None:
|
||||
return session_data
|
||||
self._session_key = None
|
||||
return {}
|
||||
|
||||
def create(self):
|
||||
# Because a cache can fail silently (e.g. memcache), we don't know if
|
||||
# we are failing to create a new session because of a key collision or
|
||||
# because the cache is missing. So we try for a (large) number of times
|
||||
# and then raise an exception. That's the risk you shoulder if using
|
||||
# cache backing.
|
||||
for i in range(10000):
|
||||
self._session_key = self._get_new_session_key()
|
||||
try:
|
||||
self.save(must_create=True)
|
||||
except CreateError:
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
raise RuntimeError(
|
||||
"Unable to create a new session key. "
|
||||
"It is likely that the cache is unavailable."
|
||||
)
|
||||
|
||||
async def acreate(self):
|
||||
for i in range(10000):
|
||||
self._session_key = await self._aget_new_session_key()
|
||||
try:
|
||||
await self.asave(must_create=True)
|
||||
except CreateError:
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
raise RuntimeError(
|
||||
"Unable to create a new session key. "
|
||||
"It is likely that the cache is unavailable."
|
||||
)
|
||||
|
||||
def save(self, must_create=False):
|
||||
if self.session_key is None:
|
||||
return self.create()
|
||||
if must_create:
|
||||
func = self._cache.add
|
||||
elif self._cache.get(self.cache_key) is not None:
|
||||
func = self._cache.set
|
||||
else:
|
||||
raise UpdateError
|
||||
result = func(
|
||||
self.cache_key,
|
||||
self._get_session(no_load=must_create),
|
||||
self.get_expiry_age(),
|
||||
)
|
||||
if must_create and not result:
|
||||
raise CreateError
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
if self.session_key is None:
|
||||
return await self.acreate()
|
||||
if must_create:
|
||||
func = self._cache.aadd
|
||||
elif await self._cache.aget(await self.acache_key()) is not None:
|
||||
func = self._cache.aset
|
||||
else:
|
||||
raise UpdateError
|
||||
result = await func(
|
||||
await self.acache_key(),
|
||||
await self._aget_session(no_load=must_create),
|
||||
await self.aget_expiry_age(),
|
||||
)
|
||||
if must_create and not result:
|
||||
raise CreateError
|
||||
|
||||
def exists(self, session_key):
|
||||
return (
|
||||
bool(session_key) and (self.cache_key_prefix + session_key) in self._cache
|
||||
)
|
||||
|
||||
async def aexists(self, session_key):
|
||||
return bool(session_key) and await self._cache.ahas_key(
|
||||
self.cache_key_prefix + session_key
|
||||
)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
self._cache.delete(self.cache_key_prefix + session_key)
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
await self._cache.adelete(self.cache_key_prefix + session_key)
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
async def aclear_expired(cls):
|
||||
pass
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Cached, database-backed sessions.
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.db import SessionStore as DBStore
|
||||
from django.core.cache import caches
|
||||
|
||||
KEY_PREFIX = "django.contrib.sessions.cached_db"
|
||||
|
||||
logger = logging.getLogger("django.contrib.sessions")
|
||||
|
||||
|
||||
class SessionStore(DBStore):
|
||||
"""
|
||||
Implement cached, database backed sessions.
|
||||
"""
|
||||
|
||||
cache_key_prefix = KEY_PREFIX
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
self._cache = caches[settings.SESSION_CACHE_ALIAS]
|
||||
super().__init__(session_key)
|
||||
|
||||
@property
|
||||
def cache_key(self):
|
||||
return self.cache_key_prefix + self._get_or_create_session_key()
|
||||
|
||||
async def acache_key(self):
|
||||
return self.cache_key_prefix + await self._aget_or_create_session_key()
|
||||
|
||||
def load(self):
|
||||
try:
|
||||
data = self._cache.get(self.cache_key)
|
||||
except Exception:
|
||||
# Some backends (e.g. memcache) raise an exception on invalid
|
||||
# cache keys. If this happens, reset the session. See #17810.
|
||||
data = None
|
||||
|
||||
if data is None:
|
||||
s = self._get_session_from_db()
|
||||
if s:
|
||||
data = self.decode(s.session_data)
|
||||
self._cache.set(
|
||||
self.cache_key, data, self.get_expiry_age(expiry=s.expire_date)
|
||||
)
|
||||
else:
|
||||
data = {}
|
||||
return data
|
||||
|
||||
async def aload(self):
|
||||
try:
|
||||
data = await self._cache.aget(await self.acache_key())
|
||||
except Exception:
|
||||
# Some backends (e.g. memcache) raise an exception on invalid
|
||||
# cache keys. If this happens, reset the session. See #17810.
|
||||
data = None
|
||||
|
||||
if data is None:
|
||||
s = await self._aget_session_from_db()
|
||||
if s:
|
||||
data = self.decode(s.session_data)
|
||||
await self._cache.aset(
|
||||
await self.acache_key(),
|
||||
data,
|
||||
await self.aget_expiry_age(expiry=s.expire_date),
|
||||
)
|
||||
else:
|
||||
data = {}
|
||||
return data
|
||||
|
||||
def exists(self, session_key):
|
||||
return (
|
||||
session_key
|
||||
and (self.cache_key_prefix + session_key) in self._cache
|
||||
or super().exists(session_key)
|
||||
)
|
||||
|
||||
async def aexists(self, session_key):
|
||||
return (
|
||||
session_key
|
||||
and (self.cache_key_prefix + session_key) in self._cache
|
||||
or await super().aexists(session_key)
|
||||
)
|
||||
|
||||
def save(self, must_create=False):
|
||||
super().save(must_create)
|
||||
try:
|
||||
self._cache.set(self.cache_key, self._session, self.get_expiry_age())
|
||||
except Exception:
|
||||
logger.exception("Error saving to cache (%s)", self._cache)
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
await super().asave(must_create)
|
||||
try:
|
||||
await self._cache.aset(
|
||||
await self.acache_key(),
|
||||
self._session,
|
||||
await self.aget_expiry_age(),
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Error saving to cache (%s)", self._cache)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
super().delete(session_key)
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
self._cache.delete(self.cache_key_prefix + session_key)
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
await super().adelete(session_key)
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
await self._cache.adelete(self.cache_key_prefix + session_key)
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
Remove the current session data from the database and regenerate the
|
||||
key.
|
||||
"""
|
||||
self.clear()
|
||||
self.delete(self.session_key)
|
||||
self._session_key = None
|
||||
|
||||
async def aflush(self):
|
||||
"""See flush()."""
|
||||
self.clear()
|
||||
await self.adelete(self.session_key)
|
||||
self._session_key = None
|
||||
@@ -0,0 +1,198 @@
|
||||
import logging
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
|
||||
from django.contrib.sessions.backends.base import CreateError, SessionBase, UpdateError
|
||||
from django.core.exceptions import SuspiciousOperation
|
||||
from django.db import DatabaseError, IntegrityError, router, transaction
|
||||
from django.utils import timezone
|
||||
from django.utils.functional import cached_property
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
"""
|
||||
Implement database session store.
|
||||
"""
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
super().__init__(session_key)
|
||||
|
||||
@classmethod
|
||||
def get_model_class(cls):
|
||||
# Avoids a circular import and allows importing SessionStore when
|
||||
# django.contrib.sessions is not in INSTALLED_APPS.
|
||||
from django.contrib.sessions.models import Session
|
||||
|
||||
return Session
|
||||
|
||||
@cached_property
|
||||
def model(self):
|
||||
return self.get_model_class()
|
||||
|
||||
def _get_session_from_db(self):
|
||||
try:
|
||||
return self.model.objects.get(
|
||||
session_key=self.session_key, expire_date__gt=timezone.now()
|
||||
)
|
||||
except (self.model.DoesNotExist, SuspiciousOperation) as e:
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger("django.security.%s" % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
self._session_key = None
|
||||
|
||||
async def _aget_session_from_db(self):
|
||||
try:
|
||||
return await self.model.objects.aget(
|
||||
session_key=self.session_key, expire_date__gt=timezone.now()
|
||||
)
|
||||
except (self.model.DoesNotExist, SuspiciousOperation) as e:
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger("django.security.%s" % e.__class__.__name__)
|
||||
logger.warning(str(e))
|
||||
self._session_key = None
|
||||
|
||||
def load(self):
|
||||
s = self._get_session_from_db()
|
||||
return self.decode(s.session_data) if s else {}
|
||||
|
||||
async def aload(self):
|
||||
s = await self._aget_session_from_db()
|
||||
return self.decode(s.session_data) if s else {}
|
||||
|
||||
def exists(self, session_key):
|
||||
return self.model.objects.filter(session_key=session_key).exists()
|
||||
|
||||
async def aexists(self, session_key):
|
||||
return await self.model.objects.filter(session_key=session_key).aexists()
|
||||
|
||||
def create(self):
|
||||
while True:
|
||||
self._session_key = self._get_new_session_key()
|
||||
try:
|
||||
# Save immediately to ensure we have a unique entry in the
|
||||
# database.
|
||||
self.save(must_create=True)
|
||||
except CreateError:
|
||||
# Key wasn't unique. Try again.
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
|
||||
async def acreate(self):
|
||||
while True:
|
||||
self._session_key = await self._aget_new_session_key()
|
||||
try:
|
||||
# Save immediately to ensure we have a unique entry in the
|
||||
# database.
|
||||
await self.asave(must_create=True)
|
||||
except CreateError:
|
||||
# Key wasn't unique. Try again.
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
|
||||
def create_model_instance(self, data):
|
||||
"""
|
||||
Return a new instance of the session model object, which represents the
|
||||
current session state. Intended to be used for saving the session data
|
||||
to the database.
|
||||
"""
|
||||
return self.model(
|
||||
session_key=self._get_or_create_session_key(),
|
||||
session_data=self.encode(data),
|
||||
expire_date=self.get_expiry_date(),
|
||||
)
|
||||
|
||||
async def acreate_model_instance(self, data):
|
||||
"""See create_model_instance()."""
|
||||
return self.model(
|
||||
session_key=await self._aget_or_create_session_key(),
|
||||
session_data=self.encode(data),
|
||||
expire_date=await self.aget_expiry_date(),
|
||||
)
|
||||
|
||||
def save(self, must_create=False):
|
||||
"""
|
||||
Save the current session data to the database. If 'must_create' is
|
||||
True, raise a database error if the saving operation doesn't create a
|
||||
new entry (as opposed to possibly updating an existing entry).
|
||||
"""
|
||||
if self.session_key is None:
|
||||
return self.create()
|
||||
data = self._get_session(no_load=must_create)
|
||||
obj = self.create_model_instance(data)
|
||||
using = router.db_for_write(self.model, instance=obj)
|
||||
try:
|
||||
with transaction.atomic(using=using):
|
||||
obj.save(
|
||||
force_insert=must_create, force_update=not must_create, using=using
|
||||
)
|
||||
except IntegrityError:
|
||||
if must_create:
|
||||
raise CreateError
|
||||
raise
|
||||
except DatabaseError:
|
||||
if not must_create:
|
||||
raise UpdateError
|
||||
raise
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
"""See save()."""
|
||||
if self.session_key is None:
|
||||
return await self.acreate()
|
||||
data = await self._aget_session(no_load=must_create)
|
||||
obj = await self.acreate_model_instance(data)
|
||||
using = router.db_for_write(self.model, instance=obj)
|
||||
try:
|
||||
# This code MOST run in a transaction, so it requires
|
||||
# @sync_to_async wrapping until transaction.atomic() supports
|
||||
# async.
|
||||
@sync_to_async
|
||||
def sync_transaction():
|
||||
with transaction.atomic(using=using):
|
||||
obj.save(
|
||||
force_insert=must_create,
|
||||
force_update=not must_create,
|
||||
using=using,
|
||||
)
|
||||
|
||||
await sync_transaction()
|
||||
except IntegrityError:
|
||||
if must_create:
|
||||
raise CreateError
|
||||
raise
|
||||
except DatabaseError:
|
||||
if not must_create:
|
||||
raise UpdateError
|
||||
raise
|
||||
|
||||
def delete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
try:
|
||||
self.model.objects.get(session_key=session_key).delete()
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
try:
|
||||
obj = await self.model.objects.aget(session_key=session_key)
|
||||
await obj.adelete()
|
||||
except self.model.DoesNotExist:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
cls.get_model_class().objects.filter(expire_date__lt=timezone.now()).delete()
|
||||
|
||||
@classmethod
|
||||
async def aclear_expired(cls):
|
||||
await cls.get_model_class().objects.filter(
|
||||
expire_date__lt=timezone.now()
|
||||
).adelete()
|
||||
@@ -0,0 +1,226 @@
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sessions.backends.base import (
|
||||
VALID_KEY_CHARS,
|
||||
CreateError,
|
||||
SessionBase,
|
||||
UpdateError,
|
||||
)
|
||||
from django.contrib.sessions.exceptions import InvalidSessionKey
|
||||
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
"""
|
||||
Implement a file based session store.
|
||||
"""
|
||||
|
||||
def __init__(self, session_key=None):
|
||||
self.storage_path = self._get_storage_path()
|
||||
self.file_prefix = settings.SESSION_COOKIE_NAME
|
||||
super().__init__(session_key)
|
||||
|
||||
@classmethod
|
||||
def _get_storage_path(cls):
|
||||
try:
|
||||
return cls._storage_path
|
||||
except AttributeError:
|
||||
storage_path = (
|
||||
getattr(settings, "SESSION_FILE_PATH", None) or tempfile.gettempdir()
|
||||
)
|
||||
# Make sure the storage path is valid.
|
||||
if not os.path.isdir(storage_path):
|
||||
raise ImproperlyConfigured(
|
||||
"The session storage path %r doesn't exist. Please set your"
|
||||
" SESSION_FILE_PATH setting to an existing directory in which"
|
||||
" Django can store session data." % storage_path
|
||||
)
|
||||
|
||||
cls._storage_path = storage_path
|
||||
return storage_path
|
||||
|
||||
def _key_to_file(self, session_key=None):
|
||||
"""
|
||||
Get the file associated with this session key.
|
||||
"""
|
||||
if session_key is None:
|
||||
session_key = self._get_or_create_session_key()
|
||||
|
||||
# Make sure we're not vulnerable to directory traversal. Session keys
|
||||
# should always be md5s, so they should never contain directory
|
||||
# components.
|
||||
if not set(session_key).issubset(VALID_KEY_CHARS):
|
||||
raise InvalidSessionKey("Invalid characters in session key")
|
||||
|
||||
return os.path.join(self.storage_path, self.file_prefix + session_key)
|
||||
|
||||
def _last_modification(self):
|
||||
"""
|
||||
Return the modification time of the file storing the session's content.
|
||||
"""
|
||||
modification = os.stat(self._key_to_file()).st_mtime
|
||||
tz = datetime.UTC if settings.USE_TZ else None
|
||||
return datetime.datetime.fromtimestamp(modification, tz=tz)
|
||||
|
||||
def _expiry_date(self, session_data):
|
||||
"""
|
||||
Return the expiry time of the file storing the session's content.
|
||||
"""
|
||||
return session_data.get("_session_expiry") or (
|
||||
self._last_modification()
|
||||
+ datetime.timedelta(seconds=self.get_session_cookie_age())
|
||||
)
|
||||
|
||||
def load(self):
|
||||
session_data = {}
|
||||
try:
|
||||
with open(self._key_to_file(), encoding="ascii") as session_file:
|
||||
file_data = session_file.read()
|
||||
# Don't fail if there is no data in the session file.
|
||||
# We may have opened the empty placeholder file.
|
||||
if file_data:
|
||||
try:
|
||||
session_data = self.decode(file_data)
|
||||
except (EOFError, SuspiciousOperation) as e:
|
||||
if isinstance(e, SuspiciousOperation):
|
||||
logger = logging.getLogger(
|
||||
"django.security.%s" % e.__class__.__name__
|
||||
)
|
||||
logger.warning(str(e))
|
||||
self.create()
|
||||
|
||||
# Remove expired sessions.
|
||||
expiry_age = self.get_expiry_age(expiry=self._expiry_date(session_data))
|
||||
if expiry_age <= 0:
|
||||
session_data = {}
|
||||
self.delete()
|
||||
self.create()
|
||||
except (OSError, SuspiciousOperation):
|
||||
self._session_key = None
|
||||
return session_data
|
||||
|
||||
async def aload(self):
|
||||
return self.load()
|
||||
|
||||
def create(self):
|
||||
while True:
|
||||
self._session_key = self._get_new_session_key()
|
||||
try:
|
||||
self.save(must_create=True)
|
||||
except CreateError:
|
||||
continue
|
||||
self.modified = True
|
||||
return
|
||||
|
||||
async def acreate(self):
|
||||
return self.create()
|
||||
|
||||
def save(self, must_create=False):
|
||||
if self.session_key is None:
|
||||
return self.create()
|
||||
# Get the session data now, before we start messing
|
||||
# with the file it is stored within.
|
||||
session_data = self._get_session(no_load=must_create)
|
||||
|
||||
session_file_name = self._key_to_file()
|
||||
|
||||
try:
|
||||
# Make sure the file exists. If it does not already exist, an
|
||||
# empty placeholder file is created.
|
||||
flags = os.O_WRONLY | getattr(os, "O_BINARY", 0)
|
||||
if must_create:
|
||||
flags |= os.O_EXCL | os.O_CREAT
|
||||
fd = os.open(session_file_name, flags)
|
||||
os.close(fd)
|
||||
except FileNotFoundError:
|
||||
if not must_create:
|
||||
raise UpdateError
|
||||
except FileExistsError:
|
||||
if must_create:
|
||||
raise CreateError
|
||||
|
||||
# Write the session file without interfering with other threads
|
||||
# or processes. By writing to an atomically generated temporary
|
||||
# file and then using the atomic os.rename() to make the complete
|
||||
# file visible, we avoid having to lock the session file, while
|
||||
# still maintaining its integrity.
|
||||
#
|
||||
# Note: Locking the session file was explored, but rejected in part
|
||||
# because in order to be atomic and cross-platform, it required a
|
||||
# long-lived lock file for each session, doubling the number of
|
||||
# files in the session storage directory at any given time. This
|
||||
# rename solution is cleaner and avoids any additional overhead
|
||||
# when reading the session data, which is the more common case
|
||||
# unless SESSION_SAVE_EVERY_REQUEST = True.
|
||||
#
|
||||
# See ticket #8616.
|
||||
dir, prefix = os.path.split(session_file_name)
|
||||
|
||||
try:
|
||||
output_file_fd, output_file_name = tempfile.mkstemp(
|
||||
dir=dir, prefix=prefix + "_out_"
|
||||
)
|
||||
renamed = False
|
||||
try:
|
||||
try:
|
||||
os.write(output_file_fd, self.encode(session_data).encode())
|
||||
finally:
|
||||
os.close(output_file_fd)
|
||||
|
||||
# This will atomically rename the file (os.rename) if the OS
|
||||
# supports it. Otherwise this will result in a shutil.copy2
|
||||
# and os.unlink (for example on Windows). See #9084.
|
||||
shutil.move(output_file_name, session_file_name)
|
||||
renamed = True
|
||||
finally:
|
||||
if not renamed:
|
||||
os.unlink(output_file_name)
|
||||
except (EOFError, OSError):
|
||||
pass
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
return self.save(must_create=must_create)
|
||||
|
||||
def exists(self, session_key):
|
||||
return os.path.exists(self._key_to_file(session_key))
|
||||
|
||||
async def aexists(self, session_key):
|
||||
return self.exists(session_key)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
if session_key is None:
|
||||
if self.session_key is None:
|
||||
return
|
||||
session_key = self.session_key
|
||||
try:
|
||||
os.unlink(self._key_to_file(session_key))
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
return self.delete(session_key=session_key)
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
storage_path = cls._get_storage_path()
|
||||
file_prefix = settings.SESSION_COOKIE_NAME
|
||||
|
||||
for session_file in os.listdir(storage_path):
|
||||
if not session_file.startswith(file_prefix):
|
||||
continue
|
||||
session_key = session_file.removeprefix(file_prefix)
|
||||
session = cls(session_key)
|
||||
# When an expired session is loaded, its file is removed, and a
|
||||
# new file is immediately created. Prevent this by disabling
|
||||
# the create() method.
|
||||
session.create = lambda: None
|
||||
session.load()
|
||||
|
||||
@classmethod
|
||||
async def aclear_expired(cls):
|
||||
cls.clear_expired()
|
||||
@@ -0,0 +1,103 @@
|
||||
from django.contrib.sessions.backends.base import SessionBase
|
||||
from django.core import signing
|
||||
|
||||
|
||||
class SessionStore(SessionBase):
|
||||
def load(self):
|
||||
"""
|
||||
Load the data from the key itself instead of fetching from some
|
||||
external data store. Opposite of _get_session_key(), raise BadSignature
|
||||
if signature fails.
|
||||
"""
|
||||
try:
|
||||
return signing.loads(
|
||||
self.session_key,
|
||||
serializer=self.serializer,
|
||||
# This doesn't handle non-default expiry dates, see #19201
|
||||
max_age=self.get_session_cookie_age(),
|
||||
salt="django.contrib.sessions.backends.signed_cookies",
|
||||
)
|
||||
except Exception:
|
||||
# BadSignature, ValueError, or unpickling exceptions. If any of
|
||||
# these happen, reset the session.
|
||||
self.create()
|
||||
return {}
|
||||
|
||||
async def aload(self):
|
||||
return self.load()
|
||||
|
||||
def create(self):
|
||||
"""
|
||||
To create a new key, set the modified flag so that the cookie is set
|
||||
on the client for the current request.
|
||||
"""
|
||||
self.modified = True
|
||||
|
||||
async def acreate(self):
|
||||
return self.create()
|
||||
|
||||
def save(self, must_create=False):
|
||||
"""
|
||||
To save, get the session key as a securely signed string and then set
|
||||
the modified flag so that the cookie is set on the client for the
|
||||
current request.
|
||||
"""
|
||||
self._session_key = self._get_session_key()
|
||||
self.modified = True
|
||||
|
||||
async def asave(self, must_create=False):
|
||||
return self.save(must_create=must_create)
|
||||
|
||||
def exists(self, session_key=None):
|
||||
"""
|
||||
This method makes sense when you're talking to a shared resource, but
|
||||
it doesn't matter when you're storing the information in the client's
|
||||
cookie.
|
||||
"""
|
||||
return False
|
||||
|
||||
async def aexists(self, session_key=None):
|
||||
return self.exists(session_key=session_key)
|
||||
|
||||
def delete(self, session_key=None):
|
||||
"""
|
||||
To delete, clear the session key and the underlying data structure
|
||||
and set the modified flag so that the cookie is set on the client for
|
||||
the current request.
|
||||
"""
|
||||
self._session_key = ""
|
||||
self._session_cache = {}
|
||||
self.modified = True
|
||||
|
||||
async def adelete(self, session_key=None):
|
||||
return self.delete(session_key=session_key)
|
||||
|
||||
def cycle_key(self):
|
||||
"""
|
||||
Keep the same data but with a new key. Call save() and it will
|
||||
automatically save a cookie with a new key at the end of the request.
|
||||
"""
|
||||
self.save()
|
||||
|
||||
async def acycle_key(self):
|
||||
return self.cycle_key()
|
||||
|
||||
def _get_session_key(self):
|
||||
"""
|
||||
Instead of generating a random string, generate a secure url-safe
|
||||
base64-encoded string of data as our session key.
|
||||
"""
|
||||
return signing.dumps(
|
||||
self._session,
|
||||
compress=True,
|
||||
salt="django.contrib.sessions.backends.signed_cookies",
|
||||
serializer=self.serializer,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def clear_expired(cls):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
async def aclear_expired(cls):
|
||||
pass
|
||||
@@ -0,0 +1,48 @@
|
||||
"""
|
||||
This module allows importing AbstractBaseSession even
|
||||
when django.contrib.sessions is not in INSTALLED_APPS.
|
||||
"""
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
class BaseSessionManager(models.Manager):
|
||||
def encode(self, session_dict):
|
||||
"""
|
||||
Return the given session dictionary serialized and encoded as a string.
|
||||
"""
|
||||
session_store_class = self.model.get_session_store_class()
|
||||
return session_store_class().encode(session_dict)
|
||||
|
||||
def save(self, session_key, session_dict, expire_date):
|
||||
s = self.model(session_key, self.encode(session_dict), expire_date)
|
||||
if session_dict:
|
||||
s.save()
|
||||
else:
|
||||
s.delete() # Clear sessions with no data.
|
||||
return s
|
||||
|
||||
|
||||
class AbstractBaseSession(models.Model):
|
||||
session_key = models.CharField(_("session key"), max_length=40, primary_key=True)
|
||||
session_data = models.TextField(_("session data"))
|
||||
expire_date = models.DateTimeField(_("expire date"), db_index=True)
|
||||
|
||||
objects = BaseSessionManager()
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
verbose_name = _("session")
|
||||
verbose_name_plural = _("sessions")
|
||||
|
||||
def __str__(self):
|
||||
return self.session_key
|
||||
|
||||
@classmethod
|
||||
def get_session_store_class(cls):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_decoded(self):
|
||||
session_store_class = self.get_session_store_class()
|
||||
return session_store_class().decode(self.session_data)
|
||||
@@ -0,0 +1,19 @@
|
||||
from django.core.exceptions import BadRequest, SuspiciousOperation
|
||||
|
||||
|
||||
class InvalidSessionKey(SuspiciousOperation):
|
||||
"""Invalid characters in session key"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SuspiciousSession(SuspiciousOperation):
|
||||
"""The session may be tampered with"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SessionInterrupted(BadRequest):
|
||||
"""The session was interrupted."""
|
||||
|
||||
pass
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# F Wolff <friedel@translate.org.za>, 2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2019-01-04 18:57+0000\n"
|
||||
"Last-Translator: F Wolff <friedel@translate.org.za>\n"
|
||||
"Language-Team: Afrikaans (http://www.transifex.com/django/django/language/"
|
||||
"af/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: af\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessies"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "sessiesleutel"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "sessiedata"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "vervaldatum"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sessie"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessies"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Bashar Al-Abdulhadi, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Arabic (http://www.transifex.com/django/django/language/ar/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "جلسات"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "مفتاح الجلسة"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "بيانات الجلسة"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "تاريخ الانتهاء"
|
||||
|
||||
msgid "session"
|
||||
msgstr "جلسة"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "جلسات"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Riterix <infosrabah@gmail.com>, 2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2019-12-14 22:50+0000\n"
|
||||
"Last-Translator: Riterix <infosrabah@gmail.com>\n"
|
||||
"Language-Team: Arabic (Algeria) (http://www.transifex.com/django/django/"
|
||||
"language/ar_DZ/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ar_DZ\n"
|
||||
"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
|
||||
"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "جلسات"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "مفتاح الجلسة"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "بيانات الجلسة"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "تاريخ الانتهاء"
|
||||
|
||||
msgid "session"
|
||||
msgstr "جلسة"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "جلسات"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Ḷḷumex03 <tornes@opmbx.org>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 19:51+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Asturian (http://www.transifex.com/django/django/language/"
|
||||
"ast/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ast\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clave de sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos de sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "data de caducidá"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesión"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiones"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Ali Ismayilov <ali@ismailov.info>, 2011
|
||||
# Emin Mastizada <emin@linux.com>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2018-04-27 13:19+0000\n"
|
||||
"Last-Translator: Emin Mastizada <emin@linux.com>\n"
|
||||
"Language-Team: Azerbaijani (http://www.transifex.com/django/django/language/"
|
||||
"az/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: az\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Seanslar"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "seans açarı"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "seansın məlumatları"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "bitmə tarixi"
|
||||
|
||||
msgid "session"
|
||||
msgstr "seans"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "seanslar"
|
||||
BIN
Binary file not shown.
+38
@@ -0,0 +1,38 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Viktar Palstsiuk <vipals@gmail.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Viktar Palstsiuk <vipals@gmail.com>\n"
|
||||
"Language-Team: Belarusian (http://www.transifex.com/django/django/language/"
|
||||
"be/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: be\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n"
|
||||
"%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Сесіі"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "ключ сэансу"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "зьвесткі сэансу"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "тэрмін"
|
||||
|
||||
msgid "session"
|
||||
msgstr "сэсія"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "сэсіі"
|
||||
BIN
Binary file not shown.
+38
@@ -0,0 +1,38 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# arneatec <arneatec@gmail.com>, 2022
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# vestimir <vestimir@gmail.com>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2022-01-14 11:56+0000\n"
|
||||
"Last-Translator: arneatec <arneatec@gmail.com>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/django/django/language/"
|
||||
"bg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bg\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Сесии"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "ключ на сесията"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "данни от сесията"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "дата на изтичане на валидност"
|
||||
|
||||
msgid "session"
|
||||
msgstr "сесия"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "сесии"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Bengali (http://www.transifex.com/django/django/language/"
|
||||
"bn/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bn\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr "সেশন কি"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "সেশন ডাটা"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "মেয়াদ শেষের তারিখ"
|
||||
|
||||
msgid "session"
|
||||
msgstr "সেশন"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "সেশনসমূহ"
|
||||
BIN
Binary file not shown.
+40
@@ -0,0 +1,40 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Fulup <fulup.jakez@gmail.com>, 2012
|
||||
# Irriep Nala Novram <allannkorh@yahoo.fr>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2018-10-19 23:05+0000\n"
|
||||
"Last-Translator: Irriep Nala Novram <allannkorh@yahoo.fr>\n"
|
||||
"Language-Team: Breton (http://www.transifex.com/django/django/language/br/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: br\n"
|
||||
"Plural-Forms: nplurals=5; plural=((n%10 == 1) && (n%100 != 11) && (n%100 !"
|
||||
"=71) && (n%100 !=91) ? 0 :(n%10 == 2) && (n%100 != 12) && (n%100 !=72) && (n"
|
||||
"%100 !=92) ? 1 :(n%10 ==3 || n%10==4 || n%10==9) && (n%100 < 10 || n% 100 > "
|
||||
"19) && (n%100 < 70 || n%100 > 79) && (n%100 < 90 || n%100 > 99) ? 2 :(n != 0 "
|
||||
"&& n % 1000000 == 0) ? 3 : 4);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Dalc'hoù"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "alc'hwez dalc'h"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "roadennoù an dalc'h"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "deiziad fin"
|
||||
|
||||
msgid "session"
|
||||
msgstr "dalc'h"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "dalc'hoù"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Bosnian (http://www.transifex.com/django/django/language/"
|
||||
"bs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: bs\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr "ključ sesije"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "podaci sesije"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "datum isteka"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesija"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesije"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Carles Barrobés <carles@barrobes.com>, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Catalan (http://www.transifex.com/django/django/language/"
|
||||
"ca/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ca\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessions"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clau de la sessió"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "dades de la sessió"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "data de caducitat"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sessió"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessions"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# kosar tofiq <kosar.belana@gmail.com>, 2020
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2023-04-24 18:26+0000\n"
|
||||
"Last-Translator: kosar tofiq <kosar.belana@gmail.com>, 2020\n"
|
||||
"Language-Team: Central Kurdish (http://www.transifex.com/django/django/"
|
||||
"language/ckb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ckb\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "کۆڕەکان"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "کلیلی کۆڕ"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "دراوەی کۆڕ"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "بەرواری بەسەرچوون"
|
||||
|
||||
msgid "session"
|
||||
msgstr "کۆڕ"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "کۆڕەکان"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Vláďa Macek <macek@sandbox.cz>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/django/django/language/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cs\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
|
||||
"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sezení"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "klíč sezení"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "data sezení"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "datum expirace"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sezení"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sezení"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Maredudd ap Gwyndaf <maredudd@maredudd.com>, 2013-2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Maredudd ap Gwyndaf <maredudd@maredudd.com>\n"
|
||||
"Language-Team: Welsh (http://www.transifex.com/django/django/language/cy/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: cy\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != "
|
||||
"11) ? 2 : 3;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiynau"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "allwedd sesiwn"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "data sesiwn"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "dyddiad terfyn"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesiwn"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiynau"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Erik Wognsen <r4mses@gmail.com>, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Danish (http://www.transifex.com/django/django/language/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: da\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessioner"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "sessionsnøgle"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "sessionsdata"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "udløbsdato"
|
||||
|
||||
msgid "session"
|
||||
msgstr "session"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessioner"
|
||||
BIN
Binary file not shown.
+35
@@ -0,0 +1,35 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011,2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: German (http://www.transifex.com/django/django/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: de\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessions"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "Sitzungs-ID"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "Sitzungsdaten"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "Verfallsdatum"
|
||||
|
||||
msgid "session"
|
||||
msgstr "Sitzung"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "Sitzungen"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Michael Wolf <milupo@sorbzilla.de>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 00:02+0000\n"
|
||||
"Last-Translator: Michael Wolf <milupo@sorbzilla.de>\n"
|
||||
"Language-Team: Lower Sorbian (http://www.transifex.com/django/django/"
|
||||
"language/dsb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: dsb\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
|
||||
"%100==4 ? 2 : 3);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Póseźenja"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "pósejźeński kluc"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "pósejźeńske daty"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "datum spadnjenja"
|
||||
|
||||
msgid "session"
|
||||
msgstr "pósejźenje"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "pósejźenja"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Pãnoș <panos.laganakos@gmail.com>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Pãnoș <panos.laganakos@gmail.com>\n"
|
||||
"Language-Team: Greek (http://www.transifex.com/django/django/language/el/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: el\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Συνεδρίες"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "κλειδί συνεδρίας"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "δεδομένα συνεδρίας"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "ημερομηνία λήξης"
|
||||
|
||||
msgid "session"
|
||||
msgstr "συνεδρία"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "συνεδρίες"
|
||||
BIN
Binary file not shown.
+38
@@ -0,0 +1,38 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2010-05-13 15:35+0200\n"
|
||||
"Last-Translator: Django team\n"
|
||||
"Language-Team: English <en@li.org>\n"
|
||||
"Language: en\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: contrib/sessions/apps.py:8
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/sessions/models.py:44
|
||||
msgid "session key"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/sessions/models.py:46
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/sessions/models.py:47
|
||||
msgid "expire date"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/sessions/models.py:52
|
||||
msgid "session"
|
||||
msgstr ""
|
||||
|
||||
#: contrib/sessions/models.py:53
|
||||
msgid "sessions"
|
||||
msgstr ""
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Tom Fifield <tom@tomfifield.net>, 2021
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2021-04-11 13:15+0000\n"
|
||||
"Last-Translator: Tom Fifield <tom@tomfifield.net>\n"
|
||||
"Language-Team: English (Australia) (http://www.transifex.com/django/django/"
|
||||
"language/en_AU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en_AU\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessions"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "session key"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "session data"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "expire date"
|
||||
|
||||
msgid "session"
|
||||
msgstr "session"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessions"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Ross Poulton <ross@rossp.org>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: English (United Kingdom) (http://www.transifex.com/django/"
|
||||
"django/language/en_GB/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: en_GB\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr "session key"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "session data"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "expire date"
|
||||
|
||||
msgid "session"
|
||||
msgstr "session"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessions"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Baptiste Darthenay <baptiste+transifex@darthenay.fr>, 2014
|
||||
# kristjan <kristjan.schmidt@googlemail.com>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Esperanto (http://www.transifex.com/django/django/language/"
|
||||
"eo/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: eo\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Seancoj"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "seanco-ŝlosilo"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "seanco-datumo"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "senvalidiĝ-dato"
|
||||
|
||||
msgid "session"
|
||||
msgstr "seanco"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "seancoj"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Ernesto Avilés Vázquez <whippiii@gmail.com>, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/django/django/language/"
|
||||
"es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiones"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clave de sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos de sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "fecha de caducidad"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesión"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiones"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Ramiro Morales, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Ramiro Morales\n"
|
||||
"Language-Team: Spanish (Argentina) (http://www.transifex.com/django/django/"
|
||||
"language/es_AR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_AR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiones"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clave de sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos de la sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "fecha de caducidad"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesión"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiones"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Carlos Muñoz <cmuozdiaz@outlook.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 22:00+0000\n"
|
||||
"Last-Translator: Carlos Muñoz <cmuozdiaz@outlook.com>\n"
|
||||
"Language-Team: Spanish (Colombia) (http://www.transifex.com/django/django/"
|
||||
"language/es_CO/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_CO\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiones"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clave de sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos de sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "fecha de caducidad"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesión"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiones"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Abraham Estrada, 2011
|
||||
# zodman <zodman@gmail.com>, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: zodman <zodman@gmail.com>\n"
|
||||
"Language-Team: Spanish (Mexico) (http://www.transifex.com/django/django/"
|
||||
"language/es_MX/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_MX\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesiones"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clave de sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos de la sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "fecha de caducidad"
|
||||
|
||||
msgid "session"
|
||||
msgstr "período de sesiones"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesiones"
|
||||
BIN
Binary file not shown.
+35
@@ -0,0 +1,35 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2014-10-05 20:12+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Spanish (Venezuela) (http://www.transifex.com/projects/p/"
|
||||
"django/language/es_VE/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_VE\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr ""
|
||||
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
|
||||
msgid "expire date"
|
||||
msgstr ""
|
||||
|
||||
msgid "session"
|
||||
msgstr ""
|
||||
|
||||
msgid "sessions"
|
||||
msgstr ""
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Marti Raudsepp <marti@juffo.org>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Estonian (http://www.transifex.com/django/django/language/"
|
||||
"et/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: et\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessioonid"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "sessioonivõti"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "sessiooni andmed"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "kehtivusaja lõpp"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sessioon"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessioonid"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Eneko Illarramendi <eneko@illarra.com>, 2017
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Eneko Illarramendi <eneko@illarra.com>\n"
|
||||
"Language-Team: Basque (http://www.transifex.com/django/django/language/eu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: eu\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesioak"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "sesioaren giltza"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "sesioaren datuak"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "amaiera data"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesioa"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesioak"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Reza Mohammadi <reza@teeleh.ir>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-23 18:54+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Persian (http://www.transifex.com/django/django/language/"
|
||||
"fa/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fa\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "نشستها"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "کلید نشست"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "اطلاعات نشست"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "تاریخ انقضاء"
|
||||
|
||||
msgid "session"
|
||||
msgstr "نشست"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "نشستها"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Klaus Dahlén <klaus.dahlen@gmail.com>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Finnish (http://www.transifex.com/django/django/language/"
|
||||
"fi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fi\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Istunnot"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "istunnon avain"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "istunnon tiedot"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "vanhenee"
|
||||
|
||||
msgid "session"
|
||||
msgstr "istunto"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "istunnot"
|
||||
BIN
Binary file not shown.
+36
@@ -0,0 +1,36 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Claude Paroz <claude@2xlibre.net>, 2014
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: French (http://www.transifex.com/django/django/language/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sessions"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "clé de session"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "données de session"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "date d'expiration"
|
||||
|
||||
msgid "session"
|
||||
msgstr "session"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sessions"
|
||||
BIN
Binary file not shown.
+35
@@ -0,0 +1,35 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2014-10-05 20:13+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Western Frisian (http://www.transifex.com/projects/p/django/"
|
||||
"language/fy/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fy\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr ""
|
||||
|
||||
msgid "session key"
|
||||
msgstr ""
|
||||
|
||||
msgid "session data"
|
||||
msgstr ""
|
||||
|
||||
msgid "expire date"
|
||||
msgstr ""
|
||||
|
||||
msgid "session"
|
||||
msgstr ""
|
||||
|
||||
msgid "sessions"
|
||||
msgstr ""
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Luke Blaney <transifex@lukeblaney.co.uk>, 2019
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2019-06-22 21:45+0000\n"
|
||||
"Last-Translator: Luke Blaney <transifex@lukeblaney.co.uk>\n"
|
||||
"Language-Team: Irish (http://www.transifex.com/django/django/language/ga/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: ga\n"
|
||||
"Plural-Forms: nplurals=5; plural=(n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n<11 ? 3 : "
|
||||
"4);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Seisiúin"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "eochair an seisiún"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "sonraíocht an seisiún"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "Data dul as feidhm"
|
||||
|
||||
msgid "session"
|
||||
msgstr "seisiún"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "seisúin"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# GunChleoc, 2015
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-22 17:29+0000\n"
|
||||
"Last-Translator: GunChleoc\n"
|
||||
"Language-Team: Gaelic, Scottish (http://www.transifex.com/django/django/"
|
||||
"language/gd/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: gd\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : "
|
||||
"(n > 2 && n < 20) ? 2 : 3;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Seiseanan"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "iuchair an t-seisein"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "dàta an t-seisein"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "falbhaidh an ùine air"
|
||||
|
||||
msgid "session"
|
||||
msgstr "seisean"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "seiseanan"
|
||||
BIN
Binary file not shown.
+38
@@ -0,0 +1,38 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Leandro Regueiro <leandro.regueiro@gmail.com>, 2013
|
||||
# X Bello <xbello@gmail.com>, 2023
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2023-04-24 18:26+0000\n"
|
||||
"Last-Translator: X Bello <xbello@gmail.com>, 2023\n"
|
||||
"Language-Team: Galician (http://www.transifex.com/django/django/language/"
|
||||
"gl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: gl\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "Sesións"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "chave da sesión"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "datos da sesión"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "data de caducidade"
|
||||
|
||||
msgid "session"
|
||||
msgstr "sesión"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "sesións"
|
||||
BIN
Binary file not shown.
+37
@@ -0,0 +1,37 @@
|
||||
# This file is distributed under the same license as the Django package.
|
||||
#
|
||||
# Translators:
|
||||
# Jannis Leidel <jannis@leidel.info>, 2011
|
||||
# Meir Kriheli <mkriheli@gmail.com>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: django\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2015-01-17 11:07+0100\n"
|
||||
"PO-Revision-Date: 2017-09-19 16:40+0000\n"
|
||||
"Last-Translator: Jannis Leidel <jannis@leidel.info>\n"
|
||||
"Language-Team: Hebrew (http://www.transifex.com/django/django/language/he/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: he\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % "
|
||||
"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n"
|
||||
|
||||
msgid "Sessions"
|
||||
msgstr "התחברויות"
|
||||
|
||||
msgid "session key"
|
||||
msgstr "מפתח התחברות (session key)"
|
||||
|
||||
msgid "session data"
|
||||
msgstr "מידע התחברות (session data)"
|
||||
|
||||
msgid "expire date"
|
||||
msgstr "תאריך פג תוקף"
|
||||
|
||||
msgid "session"
|
||||
msgstr "התחברות"
|
||||
|
||||
msgid "sessions"
|
||||
msgstr "התחברויות"
|
||||
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user