mirror of
https://github.com/wger-project/wger.git
synced 2026-02-18 00:17:51 +01:00
Merge branch 'master' into fork/justin-pinheiro/pr-trophies
# Conflicts: # settings/settings_global.py
This commit is contained in:
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -16,6 +16,11 @@ on:
|
||||
jobs:
|
||||
ci-job:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
DJANGO_SETTINGS_MODULE: settings.ci
|
||||
DJANGO_MEDIA_ROOT: /tmp/wger-test
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
# Note: removed 3.14 because lingua-language-detector has no wheels yet
|
||||
@@ -41,22 +46,19 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
uv sync --group dev
|
||||
mkdir /tmp/wger-test
|
||||
|
||||
# Only run the tests with coverage for one version of python
|
||||
- name: Test the application with coverage
|
||||
if: matrix.python-version == 3.13
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
wger create-settings
|
||||
coverage run --source='.' ./manage.py test
|
||||
coverage lcov
|
||||
uv run coverage run --source='.' ./manage.py test
|
||||
uv run coverage lcov
|
||||
|
||||
- name: Test the application
|
||||
if: matrix.python-version != 3.13
|
||||
run: |
|
||||
source .venv/bin/activate
|
||||
wger create-settings
|
||||
python manage.py test
|
||||
uv run ./manage.py test
|
||||
|
||||
- name: Coveralls
|
||||
if: matrix.python-version == 3.13
|
||||
|
||||
2
.github/workflows/docker-base.yml
vendored
2
.github/workflows/docker-base.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and push base Docker images
|
||||
name: Build base Docker images
|
||||
|
||||
# Only build when the dockerfile has changed, otherwise scheduled on the first
|
||||
# of each month
|
||||
|
||||
2
.github/workflows/docker-demo.yml
vendored
2
.github/workflows/docker-demo.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and push demo Docker image
|
||||
name: Build demo Docker image
|
||||
|
||||
# Only build when the dockerfile has changed, otherwise scheduled every two weeks
|
||||
# (on the 1st and 15th every month) since it's not so important to keep this image
|
||||
|
||||
2
.github/workflows/docker.yml
vendored
2
.github/workflows/docker.yml
vendored
@@ -1,5 +1,5 @@
|
||||
# https://docs.docker.com/build/ci/github-actions/multi-platform/
|
||||
name: Build and push production Docker images
|
||||
name: Build production Docker images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -46,7 +46,7 @@ coverage.xml
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
settings*.py
|
||||
settings/*_extra.py
|
||||
*.sqlite
|
||||
/CACHE
|
||||
|
||||
|
||||
@@ -41,15 +41,18 @@ RUN wget -O- https://deb.nodesource.com/setup_22.x | bash - \
|
||||
WORKDIR /home/wger/src
|
||||
COPY README.md pyproject.toml package.json package-lock.json package-lock.json /home/wger/src/
|
||||
COPY wger/version.py wger/__init__.py /home/wger/src/wger/
|
||||
COPY wger/core/static /root/src/wger/core/static
|
||||
COPY wger/core/static /home/wger/src/wger/core/static
|
||||
RUN npm ci --production \
|
||||
&& npm run build:css:sass \
|
||||
&& PACKAGE_NAME="@wger-project/react-components" \
|
||||
&& PACKAGE_VERSION=$(node -p "require('./package.json').devDependencies['$PACKAGE_NAME']") \
|
||||
&& TARBALL=$(npm pack ${PACKAGE_NAME}@${PACKAGE_VERSION}) \
|
||||
&& mkdir -p node_modules/${PACKAGE_NAME} \
|
||||
&& tar -xzf ${TARBALL} -C node_modules/${PACKAGE_NAME} --strip-components=1
|
||||
|
||||
&& tar -xzf ${TARBALL} -C node_modules/${PACKAGE_NAME} --strip-components=1 \
|
||||
&& pip3 wheel \
|
||||
--no-cache-dir \
|
||||
--wheel-dir /wheels \
|
||||
--group docker .
|
||||
|
||||
########
|
||||
# Final
|
||||
@@ -101,19 +104,24 @@ USER wger
|
||||
WORKDIR /home/wger/src
|
||||
RUN python3 -m venv /home/wger/venv
|
||||
|
||||
# Change permissions of some files and folders so the apache process
|
||||
# can access them.
|
||||
# Configure the application
|
||||
ENV PYTHONPATH=/home/wger/src
|
||||
ENV DJANGO_SETTINGS_MODULE=settings.main
|
||||
ENV MEDIA_ROOT=/home/wger/media
|
||||
ENV STATIC_ROOT=/home/wger/static
|
||||
ENV DJANGO_DB_DATABASE=/home/wger/db/database.sqlite
|
||||
ENV DJANGO_CACHE_BACKEND=django.core.cache.backends.locmem.LocMemCache
|
||||
|
||||
# Change permissions of some files and folders so the apache process can access them.
|
||||
RUN mkdir -p ~/static/CACHE ~/media \
|
||||
&& ln -s /home/wger/static/CACHE /home/wger/src/CACHE \
|
||||
&& chmod g+w /home/wger/static/CACHE
|
||||
|
||||
RUN --mount=type=bind,from=builder,source=/wheels,target=/wheels . /home/wger/venv/bin/activate \
|
||||
&& pip install --upgrade pip \
|
||||
&& pip install --no-cache /wheels/* \
|
||||
&& pip install -e . \
|
||||
&& wger create-settings --database-path /home/wger/db/database.sqlite \
|
||||
&& sed -i "/^MEDIA_ROOT/c\MEDIA_ROOT='\/home\/wger\/media'" settings.py \
|
||||
&& echo STATIC_ROOT=\'/home/wger/static\' >> settings.py \
|
||||
&& mkdir -p /home/wger/db \
|
||||
&& . /home/wger/venv/bin/activate \
|
||||
&& wger bootstrap --no-process-static \
|
||||
&& python3 manage.py sync-exercises \
|
||||
&& wger load-online-fixtures \
|
||||
|
||||
@@ -79,6 +79,8 @@ ARG BUILD_DATE
|
||||
ENV PATH="/home/wger/.local/bin:$PATH"
|
||||
ENV APP_BUILD_COMMIT=$BUILD_COMMIT
|
||||
ENV APP_BUILD_DATE=$BUILD_DATE
|
||||
ENV PYTHONPATH=/home/wger/src
|
||||
ENV DJANGO_SETTINGS_MODULE=settings.main
|
||||
|
||||
WORKDIR /home/wger/src
|
||||
EXPOSE 8000
|
||||
@@ -88,8 +90,6 @@ COPY --chown=wger:wger . /home/wger/src
|
||||
COPY --chown=wger:wger --from=builder /root/src/node_modules /home/wger/src/node_modules
|
||||
COPY --chown=wger:wger --from=builder /root/src/wger/core/static/bootstrap-compiled.css /home/wger/src/wger/core/static/bootstrap-compiled.css
|
||||
COPY --chown=wger:wger --from=builder /root/src/wger/core/static/bootstrap-compiled.css.map /home/wger/src/wger/core/static/bootstrap-compiled.css.map
|
||||
COPY ${DOCKER_DIR}/settings.py /home/wger/src
|
||||
COPY ${DOCKER_DIR}/settings.py /tmp/
|
||||
COPY ${DOCKER_DIR}/entrypoint.sh /home/wger/entrypoint.sh
|
||||
COPY ${DOCKER_DIR}/celery/start-beat /start-beat
|
||||
COPY ${DOCKER_DIR}/celery/start-worker /start-worker
|
||||
|
||||
@@ -65,8 +65,9 @@ fi
|
||||
# Sync ingredients
|
||||
if [[ "$SYNC_INGREDIENTS_ON_STARTUP" == "True" ]];
|
||||
then
|
||||
echo "Syncing ingredients"
|
||||
python3 manage.py sync-ingredients
|
||||
echo "The option SYNC_INGREDIENTS_ON_STARTUP is not supported anymore as it needs several hours to complete."
|
||||
echo "Please start the process manually with: docker compose exec web python3 manage.py sync-ingredients"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set the site URL
|
||||
|
||||
@@ -1,229 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Third Party
|
||||
import environ
|
||||
|
||||
# wger
|
||||
from wger.settings_global import *
|
||||
|
||||
env = environ.Env(
|
||||
# set casting, default value
|
||||
DJANGO_DEBUG=(bool, False)
|
||||
)
|
||||
|
||||
# Use 'DEBUG = True' to get more details for server errors
|
||||
DEBUG = env("DJANGO_DEBUG")
|
||||
|
||||
if os.environ.get('DJANGO_ADMINS'):
|
||||
ADMINS = [env.tuple('DJANGO_ADMINS'), ]
|
||||
MANAGERS = ADMINS
|
||||
|
||||
if os.environ.get("DJANGO_DB_ENGINE"):
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': env.str("DJANGO_DB_ENGINE"),
|
||||
'NAME': env.str("DJANGO_DB_DATABASE"),
|
||||
'USER': env.str("DJANGO_DB_USER"),
|
||||
'PASSWORD': env.str("DJANGO_DB_PASSWORD"),
|
||||
'HOST': env.str("DJANGO_DB_HOST"),
|
||||
'PORT': env.int("DJANGO_DB_PORT"),
|
||||
}
|
||||
}
|
||||
else:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': env.str('DJANGO_DB_DATABASE', '/home/wger/db/database.sqlite'),
|
||||
}
|
||||
}
|
||||
|
||||
# Timezone for this installation. Consult settings_global.py for more information
|
||||
TIME_ZONE = env.str("TIME_ZONE", 'Europe/Berlin')
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = env.str("SECRET_KEY", 'wger-docker-supersecret-key-1234567890!@#$%^&*(-_)')
|
||||
|
||||
# Your reCaptcha keys
|
||||
RECAPTCHA_PUBLIC_KEY = env.str('RECAPTCHA_PUBLIC_KEY', '')
|
||||
RECAPTCHA_PRIVATE_KEY = env.str('RECAPTCHA_PRIVATE_KEY', '')
|
||||
|
||||
# The site's URL (e.g. http://www.my-local-gym.com or http://localhost:8000)
|
||||
# This is needed for uploaded files and images (exercise images, etc.) to be
|
||||
# properly served.
|
||||
SITE_URL = env.str('SITE_URL', 'http://localhost:8000')
|
||||
|
||||
# Path to uploaded files
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
MEDIA_ROOT = env.str("DJANGO_MEDIA_ROOT", '/home/wger/media')
|
||||
STATIC_ROOT = env.str("DJANGO_STATIC_ROOT", '/home/wger/static')
|
||||
|
||||
# If you change these, adjust nginx alias definitions as well
|
||||
MEDIA_URL = env.str('MEDIA_URL', '/media/')
|
||||
STATIC_URL = env.str('STATIC_URL', '/static/')
|
||||
|
||||
LOGIN_REDIRECT_URL = env.str('LOGIN_REDIRECT_URL', '/')
|
||||
|
||||
# Allow all hosts to access the application. Change if used in production.
|
||||
ALLOWED_HOSTS = ['*', ]
|
||||
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
|
||||
# Configure a real backend in production
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
if env.bool("ENABLE_EMAIL", False):
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = env.str("EMAIL_HOST")
|
||||
EMAIL_PORT = env.int("EMAIL_PORT")
|
||||
EMAIL_HOST_USER = env.str("EMAIL_HOST_USER")
|
||||
EMAIL_HOST_PASSWORD = env.str("EMAIL_HOST_PASSWORD")
|
||||
EMAIL_USE_TLS = env.bool("EMAIL_USE_TLS", True)
|
||||
EMAIL_USE_SSL = env.bool("EMAIL_USE_SSL", False)
|
||||
EMAIL_TIMEOUT = 60
|
||||
|
||||
# Sender address used for sent emails
|
||||
DEFAULT_FROM_EMAIL = env.str("FROM_EMAIL", "wger Workout Manager <wger@example.com>")
|
||||
WGER_SETTINGS['EMAIL_FROM'] = DEFAULT_FROM_EMAIL
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
||||
EMAIL_FROM_ADDRESS = DEFAULT_FROM_EMAIL
|
||||
|
||||
# Management
|
||||
WGER_SETTINGS["ALLOW_GUEST_USERS"] = env.bool("ALLOW_GUEST_USERS", True)
|
||||
WGER_SETTINGS["ALLOW_REGISTRATION"] = env.bool("ALLOW_REGISTRATION", True)
|
||||
WGER_SETTINGS["ALLOW_UPLOAD_VIDEOS"] = env.bool("ALLOW_UPLOAD_VIDEOS", True)
|
||||
WGER_SETTINGS["DOWNLOAD_INGREDIENTS_FROM"] = env.str("DOWNLOAD_INGREDIENTS_FROM", "WGER")
|
||||
WGER_SETTINGS["EXERCISE_CACHE_TTL"] = env.int("EXERCISE_CACHE_TTL", 3600)
|
||||
WGER_SETTINGS["MIN_ACCOUNT_AGE_TO_TRUST"] = env.int("MIN_ACCOUNT_AGE_TO_TRUST", 21) # in days
|
||||
WGER_SETTINGS["SYNC_EXERCISES_CELERY"] = env.bool("SYNC_EXERCISES_CELERY", False)
|
||||
WGER_SETTINGS["SYNC_EXERCISE_IMAGES_CELERY"] = env.bool("SYNC_EXERCISE_IMAGES_CELERY", False)
|
||||
WGER_SETTINGS["SYNC_EXERCISE_VIDEOS_CELERY"] = env.bool("SYNC_EXERCISE_VIDEOS_CELERY", False)
|
||||
WGER_SETTINGS["SYNC_INGREDIENTS_CELERY"] = env.bool("SYNC_INGREDIENTS_CELERY", False)
|
||||
WGER_SETTINGS["SYNC_OFF_DAILY_DELTA_CELERY"] = env.bool("SYNC_OFF_DAILY_DELTA_CELERY", False)
|
||||
WGER_SETTINGS["USE_RECAPTCHA"] = env.bool("USE_RECAPTCHA", False)
|
||||
WGER_SETTINGS["USE_CELERY"] = env.bool("USE_CELERY", False)
|
||||
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY"] = env.bool("CACHE_API_EXERCISES_CELERY", False)
|
||||
WGER_SETTINGS["CACHE_API_EXERCISES_CELERY_FORCE_UPDATE"] = env.bool("CACHE_API_EXERCISES_CELERY_FORCE_UPDATE", False)
|
||||
|
||||
#
|
||||
# Auth Proxy Authentication
|
||||
# https://wger.readthedocs.io/en/latest/administration/auth_proxy.html
|
||||
AUTH_PROXY_HEADER = env.str("AUTH_PROXY_HEADER", '')
|
||||
AUTH_PROXY_TRUSTED_IPS = env.list("AUTH_PROXY_TRUSTED_IPS", default=[])
|
||||
AUTH_PROXY_CREATE_UNKNOWN_USER = env.bool("AUTH_PROXY_CREATE_UNKNOWN_USER", False)
|
||||
AUTH_PROXY_USER_EMAIL_HEADER = env.str("AUTH_PROXY_USER_EMAIL_HEADER", '')
|
||||
AUTH_PROXY_USER_NAME_HEADER = env.str("AUTH_PROXY_USER_NAME_HEADER", '')
|
||||
|
||||
# Cache
|
||||
if os.environ.get("DJANGO_CACHE_BACKEND"):
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': env.str("DJANGO_CACHE_BACKEND"),
|
||||
'LOCATION': env.str("DJANGO_CACHE_LOCATION"),
|
||||
'TIMEOUT': env.int("DJANGO_CACHE_TIMEOUT"),
|
||||
'OPTIONS': {
|
||||
'CLIENT_CLASS': env.str("DJANGO_CACHE_CLIENT_CLASS")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if os.environ.get('DJANGO_CACHE_CLIENT_PASSWORD'):
|
||||
CACHES['default']['OPTIONS']['PASSWORD'] = env.str('DJANGO_CACHE_CLIENT_PASSWORD')
|
||||
|
||||
CONNECTION_POOL_KWARGS = dict()
|
||||
if "DJANGO_CACHE_CLIENT_SSL_KEYFILE" in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_keyfile'] = env.str("DJANGO_CACHE_CLIENT_SSL_KEYFILE")
|
||||
|
||||
if "DJANGO_CACHE_CLIENT_SSL_CERTFILE" in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_certfile'] = env.str("DJANGO_CACHE_CLIENT_SSL_CERTFILE")
|
||||
|
||||
if "DJANGO_CACHE_CLIENT_SSL_CERT_REQS" in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_cert_reqs'] = env.str("DJANGO_CACHE_CLIENT_SSL_CERT_REQS")
|
||||
|
||||
if "DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME" in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_check_hostname'] = env.bool(
|
||||
"DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME")
|
||||
|
||||
if CONNECTION_POOL_KWARGS:
|
||||
CACHES["default"]["OPTIONS"]["CONNECTION_POOL_KWARGS"] = CONNECTION_POOL_KWARGS
|
||||
|
||||
# Folder for compressed CSS and JS files
|
||||
COMPRESS_ROOT = STATIC_ROOT
|
||||
COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', not DEBUG)
|
||||
|
||||
# The site's domain as used by the email verification workflow
|
||||
EMAIL_PAGE_DOMAIN = SITE_URL
|
||||
|
||||
#
|
||||
# Django Axes
|
||||
#
|
||||
AXES_ENABLED = env.bool('AXES_ENABLED', True)
|
||||
AXES_LOCKOUT_PARAMETERS = env.list('AXES_LOCKOUT_PARAMETERS', default=['ip_address'])
|
||||
AXES_FAILURE_LIMIT = env.int('AXES_FAILURE_LIMIT', 10)
|
||||
AXES_COOLOFF_TIME = timedelta(minutes=env.float('AXES_COOLOFF_TIME', 30))
|
||||
AXES_HANDLER = env.str('AXES_HANDLER', 'axes.handlers.cache.AxesCacheHandler')
|
||||
AXES_IPWARE_PROXY_COUNT = env.int('AXES_IPWARE_PROXY_COUNT', 0)
|
||||
AXES_IPWARE_META_PRECEDENCE_ORDER = env.list('AXES_IPWARE_META_PRECEDENCE_ORDER',
|
||||
default=['REMOTE_ADDR'])
|
||||
|
||||
#
|
||||
# Django Rest Framework SimpleJWT
|
||||
#
|
||||
SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'] = timedelta(minutes=env.int("ACCESS_TOKEN_LIFETIME", 15))
|
||||
SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'] = timedelta(hours=env.int("REFRESH_TOKEN_LIFETIME", 24))
|
||||
SIMPLE_JWT['SIGNING_KEY'] = env.str("SIGNING_KEY", SECRET_KEY)
|
||||
|
||||
#
|
||||
# https://docs.djangoproject.com/en/4.1/ref/csrf/
|
||||
#
|
||||
CSRF_TRUSTED_ORIGINS = env.list(
|
||||
"CSRF_TRUSTED_ORIGINS",
|
||||
default=['http://127.0.0.1', 'http://localhost', 'https://localhost'],
|
||||
)
|
||||
|
||||
if env.bool('X_FORWARDED_PROTO_HEADER_SET', False):
|
||||
SECURE_PROXY_SSL_HEADER = (
|
||||
env.str('SECURE_PROXY_SSL_HEADER', 'HTTP_X_FORWARDED_PROTO'),
|
||||
'https'
|
||||
)
|
||||
|
||||
REST_FRAMEWORK['NUM_PROXIES'] = env.int('NUMBER_OF_PROXIES', 1)
|
||||
|
||||
#
|
||||
# Celery message queue configuration
|
||||
#
|
||||
CELERY_BROKER_URL = env.str("CELERY_BROKER", "redis://cache:6379/2")
|
||||
CELERY_RESULT_BACKEND = env.str("CELERY_BACKEND", "redis://cache:6379/2")
|
||||
|
||||
#
|
||||
# Prometheus metrics
|
||||
#
|
||||
EXPOSE_PROMETHEUS_METRICS = env.bool('EXPOSE_PROMETHEUS_METRICS', False)
|
||||
PROMETHEUS_URL_PATH = env.str('PROMETHEUS_URL_PATH', 'super-secret-path')
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': 'level={levelname} ts={asctime} module={module} path={pathname} line={lineno} message={message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple'
|
||||
},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console'],
|
||||
'level': env.str('LOG_LEVEL_PYTHON', 'INFO').upper(),
|
||||
'propagate': True,
|
||||
},
|
||||
}
|
||||
}
|
||||
15
manage.py
15
manage.py
@@ -1,24 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Standard Library
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Django
|
||||
from django.core.management import execute_from_command_line
|
||||
|
||||
# wger
|
||||
from wger.tasks import (
|
||||
get_path,
|
||||
setup_django_environment,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# If user passed the settings flag ignore the default wger settings
|
||||
if not any('--settings' in s for s in sys.argv):
|
||||
setup_django_environment(get_path('settings.py'))
|
||||
|
||||
# Alternative to above
|
||||
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
145
package-lock.json
generated
145
package-lock.json
generated
@@ -1,17 +1,17 @@
|
||||
{
|
||||
"name": "wger",
|
||||
"version": "2.4.alpha1",
|
||||
"version": "2.4.alpha3",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "wger",
|
||||
"version": "2.4.alpha1",
|
||||
"version": "2.4.alpha3",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "5.3.8",
|
||||
"datatables.net-bs5": "^2.3.5",
|
||||
"datatables.net-bs5": "^2.3.6",
|
||||
"devbridge-autocomplete": "^1.5.0",
|
||||
"htmx.org": "^2.0.8",
|
||||
"jquery": "^3.7.1",
|
||||
@@ -52,7 +52,6 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -385,7 +384,6 @@
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -432,7 +430,6 @@
|
||||
"integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
@@ -495,6 +492,7 @@
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -512,6 +510,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -529,6 +528,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -546,6 +546,7 @@
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -563,6 +564,7 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -580,6 +582,7 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -597,6 +600,7 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -614,6 +618,7 @@
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -631,6 +636,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -648,6 +654,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -665,6 +672,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -682,6 +690,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -699,6 +708,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -716,6 +726,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -733,6 +744,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -750,6 +762,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -767,6 +780,7 @@
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -784,6 +798,7 @@
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -801,6 +816,7 @@
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -818,6 +834,7 @@
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -835,6 +852,7 @@
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -852,6 +870,7 @@
|
||||
"os": [
|
||||
"openharmony"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -869,6 +888,7 @@
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -886,6 +906,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -903,6 +924,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -920,6 +942,7 @@
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
@@ -1081,7 +1104,6 @@
|
||||
"integrity": "sha512-R4DaYF3dgCQCUAkr4wW1w26GHXcf5rCmBRHVBuuvJvaGLmZdD8EjatP80Nz5JCw0KxORAzwftnHzXVnjR8HnFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/core-downloads-tracker": "^7.3.6",
|
||||
@@ -1195,7 +1217,6 @@
|
||||
"integrity": "sha512-8fehAazkHNP1imMrdD2m2hbA9sl7Ur6jfuNweh5o4l9YPty4iaZzRXqYvBCWQNwFaSHmMEj2KPbyXGp7Bt73Rg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4",
|
||||
"@mui/private-theming": "^7.3.6",
|
||||
@@ -1437,7 +1458,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -1500,7 +1520,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1514,7 +1535,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1528,7 +1550,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1542,7 +1565,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1556,7 +1580,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1570,7 +1595,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.53.3",
|
||||
@@ -1584,7 +1610,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.53.3",
|
||||
@@ -1598,7 +1625,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1612,7 +1640,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.53.3",
|
||||
@@ -1626,7 +1655,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loong64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1640,7 +1670,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1654,7 +1685,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1668,7 +1700,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.53.3",
|
||||
@@ -1682,7 +1715,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1696,7 +1730,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1710,7 +1745,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.53.3",
|
||||
@@ -1724,7 +1760,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-openharmony-arm64": {
|
||||
"version": "4.53.3",
|
||||
@@ -1738,7 +1775,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openharmony"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.53.3",
|
||||
@@ -1752,7 +1790,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.53.3",
|
||||
@@ -1766,7 +1805,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-gnu": {
|
||||
"version": "4.53.3",
|
||||
@@ -1780,7 +1820,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.53.3",
|
||||
@@ -1794,7 +1835,8 @@
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.0.0",
|
||||
@@ -1960,7 +2002,8 @@
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/hoist-non-react-statics": {
|
||||
"version": "3.3.7",
|
||||
@@ -2163,7 +2206,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.9.0",
|
||||
"caniuse-lite": "^1.0.30001759",
|
||||
@@ -2461,21 +2503,19 @@
|
||||
}
|
||||
},
|
||||
"node_modules/datatables.net": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.5.tgz",
|
||||
"integrity": "sha512-Qrwc+vuw8GHo42u1usWTuriNAMW0VvLPSW3j8g3GxvatiD8wS/ZGW32VAYLLfmF4Hz0C/fo2KB3xZBfcpqqVTQ==",
|
||||
"license": "MIT",
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net/-/datatables.net-2.3.6.tgz",
|
||||
"integrity": "sha512-xQ/dCxrjfxM0XY70wSIzakkTZ6ghERwlLmAPyCnu8Sk5cyt9YvOVyOsFNOa/BZ/lM63Q3i2YSSvp/o7GXZGsbg==",
|
||||
"dependencies": {
|
||||
"jquery": ">=1.7"
|
||||
}
|
||||
},
|
||||
"node_modules/datatables.net-bs5": {
|
||||
"version": "2.3.5",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.3.5.tgz",
|
||||
"integrity": "sha512-2JA2WZz1tBxdVpYAspiqI8POdqEoAZZzqp7tISKaof2P5ufBJb+OLaahxwuB0sF9qcQh1azlU+JH1zsLBXVwXg==",
|
||||
"license": "MIT",
|
||||
"version": "2.3.6",
|
||||
"resolved": "https://registry.npmjs.org/datatables.net-bs5/-/datatables.net-bs5-2.3.6.tgz",
|
||||
"integrity": "sha512-oUNGjZrpNC2fY3l/6V4ijTC9kyVKU4Raons+RFmq2J7590rPn0c+5WAYKBx0evgW/CW7WfhStGBrU7+WJig6Og==",
|
||||
"dependencies": {
|
||||
"datatables.net": "2.3.5",
|
||||
"datatables.net": "2.3.6",
|
||||
"jquery": ">=1.7"
|
||||
}
|
||||
},
|
||||
@@ -2649,6 +2689,7 @@
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
@@ -2733,6 +2774,7 @@
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
@@ -2836,6 +2878,7 @@
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
@@ -3037,7 +3080,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.28.4"
|
||||
},
|
||||
@@ -3135,8 +3177,7 @@
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
|
||||
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
@@ -3228,7 +3269,6 @@
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -3305,6 +3345,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"nanoid": "bin/nanoid.cjs"
|
||||
},
|
||||
@@ -3451,6 +3492,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -3516,7 +3558,6 @@
|
||||
"integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -3598,8 +3639,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.1.tgz",
|
||||
"integrity": "sha512-L7BnWgRbMwzMAubQcS7sXdPdNLmKlucPlopgAzx7FtYbksWZgEWiuYM5x9T6UqS2Ne0rsgQTq5kY2SGqpzUkYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-redux": {
|
||||
"version": "9.2.0",
|
||||
@@ -3771,8 +3811,7 @@
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
|
||||
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/redux-thunk": {
|
||||
"version": "3.1.0",
|
||||
@@ -3835,6 +3874,7 @@
|
||||
"integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
@@ -3928,6 +3968,7 @@
|
||||
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
||||
"dev": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -3979,6 +4020,7 @@
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
@@ -4051,7 +4093,6 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "wger",
|
||||
"version": "2.4.alpha1",
|
||||
"version": "2.4.alpha3",
|
||||
"description": "Self hosted FLOSS fitness/workout and weight tracker",
|
||||
"repository": "github:wger-project/wger",
|
||||
"author": "wger team <hello@wger.de>",
|
||||
@@ -12,7 +12,7 @@
|
||||
"dependencies": {
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"bootstrap": "5.3.8",
|
||||
"datatables.net-bs5": "^2.3.5",
|
||||
"datatables.net-bs5": "^2.3.6",
|
||||
"devbridge-autocomplete": "^1.5.0",
|
||||
"htmx.org": "^2.0.8",
|
||||
"jquery": "^3.7.1",
|
||||
|
||||
@@ -29,12 +29,11 @@ classifiers = [
|
||||
|
||||
dependencies = [
|
||||
"bleach[css]~=6.3",
|
||||
"celery[redis]~=5.5.3",
|
||||
"crispy-bootstrap5==2025.6",
|
||||
"celery[redis]~=5.6.0",
|
||||
"crispy-bootstrap5~=2025.6",
|
||||
"django-activity-stream~=2.0.0",
|
||||
"django-axes[ipware]~=8.0.0",
|
||||
"django-bootstrap-breadcrumbs2==1.0.0",
|
||||
"django-compressor~=4.6.0",
|
||||
"django-cors-headers~=4.9.0",
|
||||
"django-crispy-forms~=2.5",
|
||||
"django-email-verification~=0.3.3",
|
||||
@@ -44,7 +43,7 @@ dependencies = [
|
||||
"django-prometheus~=2.4.1",
|
||||
"django-recaptcha~=4.1.0",
|
||||
"django-redis~=6.0.0",
|
||||
"django-simple-history~=3.10.1",
|
||||
"django-simple-history~=3.11.0",
|
||||
"django-sortedm2m~=4.0.0",
|
||||
"django-storages~=1.14.6",
|
||||
"djangorestframework-simplejwt[crypto]~=5.5.1",
|
||||
@@ -61,20 +60,20 @@ dependencies = [
|
||||
"openfoodfacts~=3.3.0",
|
||||
"packaging~=25.0",
|
||||
"pillow~=12.0.0",
|
||||
"psycopg~=3.2.12",
|
||||
"psycopg~=3.3.2",
|
||||
"reportlab~=4.4.5",
|
||||
"requests~=2.32.5",
|
||||
"tqdm~=4.67.1",
|
||||
"tzdata~=2025.2",
|
||||
"tzdata~=2025.3",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"coverage~=7.12.0",
|
||||
"coverage~=7.13.0",
|
||||
"django-debug-toolbar~=6.1.0",
|
||||
"django-extensions~=4.1",
|
||||
"faker~=38.2.0",
|
||||
"hatchling~=1.27.0",
|
||||
"hatchling~=1.28.0",
|
||||
"isort~=7.0.0",
|
||||
"ruff~=0.14.2",
|
||||
"tblib~=3.2.0",
|
||||
@@ -165,7 +164,7 @@ line-ending = "auto"
|
||||
src_paths = ["wger", "extras"]
|
||||
|
||||
sections = ["FUTURE", "STDLIB", "DJANGO", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"]
|
||||
skip = ["extras", "build", "dist", "node_modules", "migrations", "docs", "settings.py", "apps.py"]
|
||||
skip = ["extras", "build", "dist", "node_modules", "migrations", "docs", "settings", "apps.py"]
|
||||
# If set to true - ensures that if a star import is present, nothing else is
|
||||
# imported from that namespace.
|
||||
combine_star = false
|
||||
|
||||
15
settings/README.md
Normal file
15
settings/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Settings
|
||||
|
||||
This directory contains configuration files and settings for the project.
|
||||
|
||||
You can add your own configuration files here, e.g. for development. Set
|
||||
the `DJANGO_SETTINGS_MODULE` environment variable to point to the new settings
|
||||
file. E.g.:
|
||||
|
||||
```bash
|
||||
export DJANGO_SETTINGS_MODULE=settings.local_dev
|
||||
python manage.py runserver
|
||||
```
|
||||
|
||||
If you want to add settings that are not tracked by git, you can create a
|
||||
`local_dev_extra.py` file, this will be imported by `local_dev.py` if it exists.
|
||||
0
settings/__init__.py
Normal file
0
settings/__init__.py
Normal file
@@ -1,31 +1,48 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# This file is part of wger Workout Manager.
|
||||
#
|
||||
# wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wger Workout Manager is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
|
||||
# ruff: noqa: F405
|
||||
|
||||
# Third Party
|
||||
import environ
|
||||
|
||||
# wger
|
||||
from wger.settings_global import *
|
||||
from .settings_global import * # noqa: F403
|
||||
|
||||
env = environ.Env()
|
||||
|
||||
# Use 'DEBUG = True' to get more details for server errors
|
||||
DEBUG = True
|
||||
|
||||
# Application settings
|
||||
WGER_SETTINGS['EMAIL_FROM'] = 'wger Workout Manager <wger@example.com>'
|
||||
WGER_SETTINGS["ALLOW_REGISTRATION"] = True
|
||||
WGER_SETTINGS["ALLOW_GUEST_USERS"] = True
|
||||
WGER_SETTINGS["ALLOW_UPLOAD_VIDEOS"] = False
|
||||
WGER_SETTINGS["MIN_ACCOUNT_AGE_TO_TRUST"] = 21 # in days
|
||||
WGER_SETTINGS["EXERCISE_CACHE_TTL"] = 3600 # in seconds
|
||||
WGER_SETTINGS['ALLOW_REGISTRATION'] = True
|
||||
WGER_SETTINGS['ALLOW_GUEST_USERS'] = True
|
||||
WGER_SETTINGS['ALLOW_UPLOAD_VIDEOS'] = False
|
||||
WGER_SETTINGS['MIN_ACCOUNT_AGE_TO_TRUST'] = 21 # in days
|
||||
WGER_SETTINGS['EXERCISE_CACHE_TTL'] = 3600 # in seconds
|
||||
|
||||
DATABASES = {{
|
||||
'default': {{
|
||||
'ENGINE': 'django.db.backends.{dbengine}',
|
||||
'NAME': '{dbname}',
|
||||
'USER': '{dbuser}',
|
||||
'PASSWORD': '{dbpassword}',
|
||||
'HOST': '{dbhost}',
|
||||
'PORT': '{dbport}',
|
||||
}}
|
||||
}} # yapf: disable
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': '/Users/roland/Entwicklung/wger/server/database.sqlite',
|
||||
'USER': '',
|
||||
'PASSWORD': '',
|
||||
'HOST': '',
|
||||
'PORT': '',
|
||||
}
|
||||
} # yapf: disable
|
||||
|
||||
# List of administrations
|
||||
ADMINS = (('Your name', 'your_email@example.com'),)
|
||||
@@ -39,7 +56,7 @@ MANAGERS = ADMINS
|
||||
TIME_ZONE = 'Europe/Berlin'
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = '{default_key}'
|
||||
SECRET_KEY = '61fxc$k%9nj!be-_up9%xzm(z)9l7$h33b1!@bf9581=c-03%p'
|
||||
|
||||
# Your reCaptcha keys
|
||||
RECAPTCHA_PUBLIC_KEY = ''
|
||||
@@ -49,14 +66,14 @@ USE_RECAPTCHA = False
|
||||
# The site's URL (e.g. http://www.my-local-gym.com or http://localhost:8000)
|
||||
# This is needed for uploaded files and images (exercise images, etc.) to be
|
||||
# properly served.
|
||||
SITE_URL = '{siteurl}'
|
||||
SITE_URL = 'http://localhost:8000'
|
||||
|
||||
# Path to uploaded files
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
MEDIA_ROOT = '{media_folder_path}'
|
||||
MEDIA_ROOT = env.str('DJANGO_MEDIA_ROOT', '/tmp/')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# Allow all hosts to access the application. Change if used in production.
|
||||
# Allow all hosts to access the application.
|
||||
ALLOWED_HOSTS = [
|
||||
'*',
|
||||
]
|
||||
@@ -78,23 +95,3 @@ EMAIL_PAGE_DOMAIN = SITE_URL
|
||||
# https://django-axes.readthedocs.io/en/latest/
|
||||
#
|
||||
AXES_ENABLED = False
|
||||
# AXES_FAILURE_LIMIT = 10
|
||||
# AXES_COOLOFF_TIME = timedelta(minutes=30)
|
||||
# AXES_HANDLER = 'axes.handlers.cache.AxesCacheHandler'
|
||||
|
||||
#
|
||||
# Sometimes needed if deployed behind a proxy with HTTPS enabled:
|
||||
# https://docs.djangoproject.com/en/4.1/ref/csrf/
|
||||
#
|
||||
# CSRF_TRUSTED_ORIGINS = ['http://127.0.0.1', 'https://my.domain.example.com']
|
||||
|
||||
# Alternative to above, needs changes to the reverse proxy's config
|
||||
# https://docs.djangoproject.com/en/4.1/ref/settings/#secure-proxy-ssl-header
|
||||
#
|
||||
# SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO, 'https')
|
||||
|
||||
#
|
||||
# Celery
|
||||
# Needed if you plan to use celery for background tasks
|
||||
# CELERY_BROKER_URL = "redis://localhost:6379/2"
|
||||
# CELERY_RESULT_BACKEND = "redis://localhost:6379/2"
|
||||
125
settings/local_dev.py
Normal file
125
settings/local_dev.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""Local development settings for wger"""
|
||||
|
||||
# ruff: noqa: F405
|
||||
# ruff: noqa: F403
|
||||
|
||||
# wger
|
||||
from .settings_global import *
|
||||
|
||||
DEBUG = True
|
||||
|
||||
# List of administrators
|
||||
ADMINS = (('Your name', 'your_email@example.com'),)
|
||||
MANAGERS = ADMINS
|
||||
|
||||
# Don't use this key in production!
|
||||
SECRET_KEY = 'wger-local-development-supersecret-key-1234567890!'
|
||||
|
||||
# Allow all hosts to access the application.
|
||||
ALLOWED_HOSTS = [
|
||||
'*',
|
||||
]
|
||||
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
# WGER application
|
||||
WGER_SETTINGS['ALLOW_UPLOAD_VIDEOS'] = True
|
||||
WGER_SETTINGS['ALLOW_GUEST_USERS'] = True
|
||||
WGER_SETTINGS['ALLOW_REGISTRATION'] = True
|
||||
WGER_SETTINGS['DOWNLOAD_INGREDIENTS_FROM'] = 'WGER' # or 'None' to disable
|
||||
WGER_SETTINGS['EMAIL_FROM'] = 'wger Workout Manager <wger@example.com>'
|
||||
WGER_SETTINGS['EXERCISE_CACHE_TTL'] = 500
|
||||
WGER_SETTINGS['INGREDIENT_CACHE_TTL'] = 500
|
||||
WGER_SETTINGS['SYNC_EXERCISES_CELERY'] = False
|
||||
WGER_SETTINGS['SYNC_EXERCISE_IMAGES_CELERY'] = True
|
||||
WGER_SETTINGS['SYNC_EXERCISE_VIDEOS_CELERY'] = False
|
||||
WGER_SETTINGS['SYNC_INGREDIENTS_CELERY'] = True
|
||||
WGER_SETTINGS['USE_CELERY'] = False
|
||||
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY'] = True
|
||||
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY_FORCE_UPDATE'] = True
|
||||
WGER_SETTINGS['ROUTINE_CACHE_TTL'] = 500
|
||||
DEFAULT_FROM_EMAIL = WGER_SETTINGS['EMAIL_FROM']
|
||||
|
||||
|
||||
# CELERY_BROKER_URL = "redis://localhost:6379/2"
|
||||
# CELERY_RESULT_BACKEND = "redis://localhost:6379/2"
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = ['http://localhost:8000', 'http://127.0.0.1:8000']
|
||||
|
||||
EXPOSE_PROMETHEUS_METRICS = True
|
||||
|
||||
COMPRESS_ENABLED = False
|
||||
AXES_ENABLED = False
|
||||
|
||||
|
||||
# Does not really cache anything
|
||||
CACHES_DUMMY = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
|
||||
'TIMEOUT': 100,
|
||||
}
|
||||
}
|
||||
|
||||
# In-memory cache, resets when the server restarts
|
||||
CACHE_LOCMEM = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'LOCATION': 'wger-cache',
|
||||
'TIMEOUT': 100,
|
||||
}
|
||||
}
|
||||
|
||||
# Redis cache
|
||||
CACHE_REDIS = {
|
||||
'default': {
|
||||
'BACKEND': 'django_redis.cache.RedisCache',
|
||||
'LOCATION': 'redis://localhost:6379/1',
|
||||
'TIMEOUT': 5000,
|
||||
'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# CACHES = CACHE_REDIS
|
||||
CACHES = CACHE_LOCMEM
|
||||
# CACHES = CACHES_DUMMY
|
||||
|
||||
|
||||
# Django Debug Toolbar
|
||||
# INSTALLED_APPS += ['django_extensions', 'debug_toolbar']
|
||||
# MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware", ]
|
||||
# INTERNAL_IPS = ["127.0.0.1", ]
|
||||
|
||||
# AUTH_PROXY_HEADER = 'HTTP_X_REMOTE_USER'
|
||||
# AUTH_PROXY_USER_EMAIL_HEADER = 'HTTP_X_REMOTE_USER_EMAIL'
|
||||
# AUTH_PROXY_USER_NAME_HEADER = 'HTTP_X_REMOTE_USER_NAME'
|
||||
# AUTH_PROXY_TRUSTED_IPS = ['127.0.0.1', ]
|
||||
# AUTH_PROXY_CREATE_UNKNOWN_USER = True
|
||||
|
||||
|
||||
DBCONFIG_PG = {
|
||||
'ENGINE': 'django_prometheus.db.backends.postgresql',
|
||||
'NAME': 'wger',
|
||||
'USER': 'wger',
|
||||
'PASSWORD': 'wger',
|
||||
'HOST': 'localhost',
|
||||
'PORT': '5432',
|
||||
}
|
||||
|
||||
|
||||
DBCONFIG_SQLITE = {
|
||||
'ENGINE': 'django_prometheus.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR.parent / 'db' / 'database.sqlite',
|
||||
}
|
||||
|
||||
DATABASES = {
|
||||
# 'default': DBCONFIG_PG,
|
||||
'default': DBCONFIG_SQLITE,
|
||||
}
|
||||
|
||||
|
||||
# Import other local settings that are not in version control
|
||||
try:
|
||||
from .local_dev_extra import *
|
||||
except ImportError:
|
||||
pass
|
||||
277
settings/main.py
Normal file
277
settings/main.py
Normal file
@@ -0,0 +1,277 @@
|
||||
# This file is part of wger Workout Manager.
|
||||
#
|
||||
# wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wger Workout Manager is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
|
||||
# ruff: noqa: F405
|
||||
|
||||
# Third Party
|
||||
import environ
|
||||
|
||||
# wger
|
||||
from .settings_global import * # noqa: F403
|
||||
|
||||
"""
|
||||
Main settings file for a production deployment of wger.
|
||||
|
||||
For a more commented version of the options used here, please refer to
|
||||
https://github.com/wger-project/docker/blob/master/config/prod.env
|
||||
"""
|
||||
|
||||
env = environ.Env(
|
||||
# set casting, default value
|
||||
DJANGO_DEBUG=(bool, False)
|
||||
)
|
||||
|
||||
# Use 'DEBUG = True' to get more details for server errors
|
||||
DEBUG = env('DJANGO_DEBUG')
|
||||
|
||||
if os.environ.get('DJANGO_ADMINS'):
|
||||
ADMINS = [
|
||||
env.tuple('DJANGO_ADMINS'),
|
||||
]
|
||||
MANAGERS = ADMINS
|
||||
|
||||
if os.environ.get('DJANGO_DB_ENGINE'):
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': env.str('DJANGO_DB_ENGINE'),
|
||||
'NAME': env.str('DJANGO_DB_DATABASE'),
|
||||
'USER': env.str('DJANGO_DB_USER'),
|
||||
'PASSWORD': env.str('DJANGO_DB_PASSWORD'),
|
||||
'HOST': env.str('DJANGO_DB_HOST'),
|
||||
'PORT': env.int('DJANGO_DB_PORT'),
|
||||
}
|
||||
}
|
||||
else:
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': env.str('DJANGO_DB_DATABASE', '/home/wger/db/database.sqlite'),
|
||||
}
|
||||
}
|
||||
|
||||
# Timezone for this installation. Consult settings_global.py for more information
|
||||
TIME_ZONE = env.str('TIME_ZONE', 'Europe/Berlin')
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
# Generate e.g. with: python -c "import secrets; print(secrets.token_urlsafe(50))" or https://djecrety.ir/
|
||||
SECRET_KEY = env.str('SECRET_KEY', 'wger-docker-supersecret-key-1234567890!@#$%^&*(-_)')
|
||||
|
||||
# Your reCaptcha keys
|
||||
RECAPTCHA_PUBLIC_KEY = env.str('RECAPTCHA_PUBLIC_KEY', '')
|
||||
RECAPTCHA_PRIVATE_KEY = env.str('RECAPTCHA_PRIVATE_KEY', '')
|
||||
|
||||
# The site's URL (e.g. http://www.my-local-gym.com or http://localhost:8000)
|
||||
# This is needed for uploaded files and images (exercise images, etc.) to be
|
||||
# properly served.
|
||||
SITE_URL = env.str('SITE_URL', 'http://localhost:8000')
|
||||
|
||||
# Path to uploaded files
|
||||
# Absolute filesystem path to the directory that will hold user-uploaded files.
|
||||
MEDIA_ROOT = env.str('DJANGO_MEDIA_ROOT', '/home/wger/media')
|
||||
STATIC_ROOT = env.str('DJANGO_STATIC_ROOT', '/home/wger/static')
|
||||
|
||||
# If you change these, adjust nginx alias definitions as well
|
||||
MEDIA_URL = env.str('MEDIA_URL', '/media/')
|
||||
STATIC_URL = env.str('STATIC_URL', '/static/')
|
||||
|
||||
LOGIN_REDIRECT_URL = env.str('LOGIN_REDIRECT_URL', '/')
|
||||
|
||||
# Allow all hosts to access the application. Change if used in production.
|
||||
ALLOWED_HOSTS = [
|
||||
'*',
|
||||
]
|
||||
|
||||
SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
|
||||
|
||||
# Configure a real backend in production
|
||||
if DEBUG:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||
|
||||
if env.bool('ENABLE_EMAIL', False):
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_HOST = env.str('EMAIL_HOST')
|
||||
EMAIL_PORT = env.int('EMAIL_PORT')
|
||||
EMAIL_HOST_USER = env.str('EMAIL_HOST_USER')
|
||||
EMAIL_HOST_PASSWORD = env.str('EMAIL_HOST_PASSWORD')
|
||||
EMAIL_USE_TLS = env.bool('EMAIL_USE_TLS', True)
|
||||
EMAIL_USE_SSL = env.bool('EMAIL_USE_SSL', False)
|
||||
EMAIL_TIMEOUT = 60
|
||||
|
||||
# Sender address used for sent emails
|
||||
DEFAULT_FROM_EMAIL = env.str('FROM_EMAIL', 'wger Workout Manager <wger@example.com>')
|
||||
WGER_SETTINGS['EMAIL_FROM'] = DEFAULT_FROM_EMAIL
|
||||
SERVER_EMAIL = DEFAULT_FROM_EMAIL
|
||||
EMAIL_FROM_ADDRESS = DEFAULT_FROM_EMAIL
|
||||
|
||||
# Management
|
||||
WGER_SETTINGS['ALLOW_GUEST_USERS'] = env.bool('ALLOW_GUEST_USERS', True)
|
||||
WGER_SETTINGS['ALLOW_REGISTRATION'] = env.bool('ALLOW_REGISTRATION', True)
|
||||
WGER_SETTINGS['ALLOW_UPLOAD_VIDEOS'] = env.bool('ALLOW_UPLOAD_VIDEOS', True)
|
||||
WGER_SETTINGS['DOWNLOAD_INGREDIENTS_FROM'] = env.str('DOWNLOAD_INGREDIENTS_FROM', 'WGER')
|
||||
WGER_SETTINGS['EXERCISE_CACHE_TTL'] = env.int('EXERCISE_CACHE_TTL', 3600)
|
||||
WGER_SETTINGS['MIN_ACCOUNT_AGE_TO_TRUST'] = env.int('MIN_ACCOUNT_AGE_TO_TRUST', 21) # in days
|
||||
WGER_SETTINGS['SYNC_EXERCISES_CELERY'] = env.bool('SYNC_EXERCISES_CELERY', False)
|
||||
WGER_SETTINGS['SYNC_EXERCISE_IMAGES_CELERY'] = env.bool('SYNC_EXERCISE_IMAGES_CELERY', False)
|
||||
WGER_SETTINGS['SYNC_EXERCISE_VIDEOS_CELERY'] = env.bool('SYNC_EXERCISE_VIDEOS_CELERY', False)
|
||||
WGER_SETTINGS['SYNC_INGREDIENTS_CELERY'] = env.bool('SYNC_INGREDIENTS_CELERY', False)
|
||||
WGER_SETTINGS['SYNC_OFF_DAILY_DELTA_CELERY'] = env.bool('SYNC_OFF_DAILY_DELTA_CELERY', False)
|
||||
WGER_SETTINGS['USE_RECAPTCHA'] = env.bool('USE_RECAPTCHA', False)
|
||||
WGER_SETTINGS['USE_CELERY'] = env.bool('USE_CELERY', False)
|
||||
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY'] = env.bool('CACHE_API_EXERCISES_CELERY', False)
|
||||
WGER_SETTINGS['CACHE_API_EXERCISES_CELERY_FORCE_UPDATE'] = env.bool(
|
||||
'CACHE_API_EXERCISES_CELERY_FORCE_UPDATE', False
|
||||
)
|
||||
|
||||
#
|
||||
# Auth Proxy Authentication
|
||||
# https://wger.readthedocs.io/en/latest/administration/auth_proxy.html
|
||||
AUTH_PROXY_HEADER = env.str('AUTH_PROXY_HEADER', '')
|
||||
AUTH_PROXY_TRUSTED_IPS = env.list('AUTH_PROXY_TRUSTED_IPS', default=[])
|
||||
AUTH_PROXY_CREATE_UNKNOWN_USER = env.bool('AUTH_PROXY_CREATE_UNKNOWN_USER', False)
|
||||
AUTH_PROXY_USER_EMAIL_HEADER = env.str('AUTH_PROXY_USER_EMAIL_HEADER', '')
|
||||
AUTH_PROXY_USER_NAME_HEADER = env.str('AUTH_PROXY_USER_NAME_HEADER', '')
|
||||
|
||||
# Cache
|
||||
if os.environ.get('DJANGO_CACHE_BACKEND'):
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': env.str('DJANGO_CACHE_BACKEND'),
|
||||
'LOCATION': env.str('DJANGO_CACHE_LOCATION', ''),
|
||||
'TIMEOUT': env.int('DJANGO_CACHE_TIMEOUT', 300),
|
||||
'OPTIONS': {'CLIENT_CLASS': env.str('DJANGO_CACHE_CLIENT_CLASS', '')},
|
||||
}
|
||||
}
|
||||
|
||||
if os.environ.get('DJANGO_CACHE_CLIENT_PASSWORD'):
|
||||
CACHES['default']['OPTIONS']['PASSWORD'] = env.str('DJANGO_CACHE_CLIENT_PASSWORD')
|
||||
|
||||
CONNECTION_POOL_KWARGS = dict()
|
||||
if 'DJANGO_CACHE_CLIENT_SSL_KEYFILE' in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_keyfile'] = env.str('DJANGO_CACHE_CLIENT_SSL_KEYFILE')
|
||||
|
||||
if 'DJANGO_CACHE_CLIENT_SSL_CERTFILE' in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_certfile'] = env.str('DJANGO_CACHE_CLIENT_SSL_CERTFILE')
|
||||
|
||||
if 'DJANGO_CACHE_CLIENT_SSL_CERT_REQS' in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_cert_reqs'] = env.str('DJANGO_CACHE_CLIENT_SSL_CERT_REQS')
|
||||
|
||||
if 'DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME' in os.environ:
|
||||
CONNECTION_POOL_KWARGS['ssl_check_hostname'] = env.bool(
|
||||
'DJANGO_CACHE_CLIENT_SSL_CHECK_HOSTNAME'
|
||||
)
|
||||
|
||||
if CONNECTION_POOL_KWARGS:
|
||||
CACHES['default']['OPTIONS']['CONNECTION_POOL_KWARGS'] = CONNECTION_POOL_KWARGS
|
||||
|
||||
#
|
||||
# Django Compressor
|
||||
# Consult https://django-compressor.readthedocs.io/en/stable/ for more information
|
||||
# (specially the offline compression part)
|
||||
#
|
||||
COMPRESS_ROOT = STATIC_ROOT
|
||||
COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', not DEBUG)
|
||||
COMPRESS_OFFLINE = env.bool('COMPRESS_OFFLINE', False)
|
||||
|
||||
# The site's domain as used by the email verification workflow
|
||||
EMAIL_PAGE_DOMAIN = SITE_URL
|
||||
|
||||
#
|
||||
# Django Axes
|
||||
#
|
||||
AXES_ENABLED = env.bool('AXES_ENABLED', True)
|
||||
AXES_LOCKOUT_PARAMETERS = env.list('AXES_LOCKOUT_PARAMETERS', default=['ip_address'])
|
||||
AXES_FAILURE_LIMIT = env.int('AXES_FAILURE_LIMIT', 10)
|
||||
AXES_COOLOFF_TIME = timedelta(minutes=env.float('AXES_COOLOFF_TIME', 30))
|
||||
AXES_HANDLER = env.str('AXES_HANDLER', 'axes.handlers.cache.AxesCacheHandler')
|
||||
AXES_IPWARE_PROXY_COUNT = env.int('AXES_IPWARE_PROXY_COUNT', 0)
|
||||
AXES_IPWARE_META_PRECEDENCE_ORDER = env.list(
|
||||
'AXES_IPWARE_META_PRECEDENCE_ORDER', default=['REMOTE_ADDR']
|
||||
)
|
||||
|
||||
#
|
||||
# Django Rest Framework SimpleJWT
|
||||
#
|
||||
SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'] = timedelta(minutes=env.int('ACCESS_TOKEN_LIFETIME', 15))
|
||||
SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'] = timedelta(hours=env.int('REFRESH_TOKEN_LIFETIME', 24))
|
||||
SIMPLE_JWT['SIGNING_KEY'] = env.str('SIGNING_KEY', SECRET_KEY)
|
||||
|
||||
#
|
||||
# https://docs.djangoproject.com/en/4.1/ref/csrf/
|
||||
#
|
||||
CSRF_TRUSTED_ORIGINS = env.list(
|
||||
'CSRF_TRUSTED_ORIGINS',
|
||||
default=['http://127.0.0.1', 'http://localhost', 'https://localhost'],
|
||||
)
|
||||
|
||||
if env.bool('X_FORWARDED_PROTO_HEADER_SET', False):
|
||||
SECURE_PROXY_SSL_HEADER = (
|
||||
env.str('SECURE_PROXY_SSL_HEADER', 'HTTP_X_FORWARDED_PROTO'),
|
||||
'https',
|
||||
)
|
||||
|
||||
REST_FRAMEWORK['NUM_PROXIES'] = env.int('NUMBER_OF_PROXIES', 1)
|
||||
|
||||
#
|
||||
# Celery message queue configuration
|
||||
#
|
||||
CELERY_BROKER_URL = env.str('CELERY_BROKER', 'redis://cache:6379/2')
|
||||
CELERY_RESULT_BACKEND = env.str('CELERY_BACKEND', 'redis://cache:6379/2')
|
||||
|
||||
#
|
||||
# Prometheus metrics
|
||||
#
|
||||
EXPOSE_PROMETHEUS_METRICS = env.bool('EXPOSE_PROMETHEUS_METRICS', False)
|
||||
PROMETHEUS_URL_PATH = env.str('PROMETHEUS_URL_PATH', 'super-secret-path')
|
||||
|
||||
#
|
||||
# Logging
|
||||
#
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': 'level={levelname} ts={asctime} module={module} path={pathname} line={lineno} message={message}',
|
||||
'style': '{',
|
||||
},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple'},
|
||||
},
|
||||
'loggers': {
|
||||
'': {
|
||||
'handlers': ['console'],
|
||||
'level': env.str('LOG_LEVEL_PYTHON', 'INFO').upper(),
|
||||
'propagate': True,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
#
|
||||
# Storage options
|
||||
#
|
||||
STORAGES = {
|
||||
'default': {
|
||||
'BACKEND': env.str(
|
||||
'DJANGO_STORAGES_DEFAULT_BACKEND', 'django.core.files.storage.FileSystemStorage'
|
||||
),
|
||||
},
|
||||
# django.contrib.staticfiles.storage.StaticFilesStorage
|
||||
'staticfiles': {
|
||||
'BACKEND': env.str(
|
||||
'DJANGO_STORAGES_STATICFILES_BACKEND',
|
||||
'wger.core.storage.LenientManifestStaticFilesStorage',
|
||||
),
|
||||
},
|
||||
}
|
||||
@@ -18,20 +18,27 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from datetime import timedelta
|
||||
from pathlib import Path
|
||||
|
||||
# wger
|
||||
from wger.utils.constants import DOWNLOAD_INGREDIENT_WGER
|
||||
from wger.version import get_version
|
||||
|
||||
|
||||
"""
|
||||
This file contains the global settings that don't usually need to be changed.
|
||||
For a full list of options, visit:
|
||||
https://docs.djangoproject.com/en/dev/ref/settings/
|
||||
"""
|
||||
|
||||
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
|
||||
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent / 'wger'
|
||||
SITE_ROOT = Path(__file__).resolve().parent.parent / 'wger'
|
||||
|
||||
|
||||
# Static and media files (only during development)
|
||||
MEDIA_ROOT = BASE_DIR.parent / 'media'
|
||||
STATIC_ROOT = BASE_DIR.parent / 'static'
|
||||
MEDIA_URL = '/media/'
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
#
|
||||
# Application definition
|
||||
@@ -40,6 +47,7 @@ SITE_ID = 1
|
||||
ROOT_URLCONF = 'wger.urls'
|
||||
WSGI_APPLICATION = 'wger.wsgi.application'
|
||||
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
@@ -48,10 +56,8 @@ INSTALLED_APPS = [
|
||||
'django.contrib.sites',
|
||||
'django.contrib.staticfiles',
|
||||
'storages',
|
||||
|
||||
# Uncomment the next line to enable the admin:
|
||||
# 'django.contrib.admin',
|
||||
|
||||
# Apps from wger proper
|
||||
'wger.config',
|
||||
'wger.core',
|
||||
@@ -69,20 +75,13 @@ INSTALLED_APPS = [
|
||||
|
||||
# reCaptcha support, see https://github.com/praekelt/django-recaptcha
|
||||
'django_recaptcha',
|
||||
|
||||
# The sitemaps app
|
||||
'django.contrib.sitemaps',
|
||||
|
||||
# thumbnails
|
||||
'easy_thumbnails',
|
||||
|
||||
# CSS/JS compressor
|
||||
'compressor',
|
||||
|
||||
# Form renderer helper
|
||||
'crispy_forms',
|
||||
'crispy_bootstrap5',
|
||||
|
||||
# REST-API
|
||||
'rest_framework',
|
||||
'rest_framework.authtoken',
|
||||
@@ -90,28 +89,20 @@ INSTALLED_APPS = [
|
||||
'rest_framework_simplejwt',
|
||||
'drf_spectacular',
|
||||
'drf_spectacular_sidecar',
|
||||
|
||||
# Breadcrumbs
|
||||
'django_bootstrap_breadcrumbs',
|
||||
|
||||
# CORS
|
||||
'corsheaders',
|
||||
|
||||
# Django Axes
|
||||
'axes',
|
||||
|
||||
# History keeping
|
||||
'simple_history',
|
||||
|
||||
# Django email verification
|
||||
'django_email_verification',
|
||||
|
||||
# Activity stream
|
||||
'actstream',
|
||||
|
||||
# Fontawesome
|
||||
'fontawesomefree',
|
||||
|
||||
# Prometheus
|
||||
'django_prometheus',
|
||||
]
|
||||
@@ -119,43 +110,33 @@ INSTALLED_APPS = [
|
||||
MIDDLEWARE = [
|
||||
# Prometheus
|
||||
'django_prometheus.middleware.PrometheusBeforeMiddleware',
|
||||
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
|
||||
# Django Admin
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
|
||||
# Auth proxy middleware
|
||||
'wger.core.middleware.AuthProxyHeaderMiddleware',
|
||||
|
||||
# Javascript Header. Sends helper headers for AJAX
|
||||
'wger.utils.middleware.JavascriptAJAXRedirectionMiddleware',
|
||||
|
||||
# Custom authentication middleware. Creates users on-the-fly for certain paths
|
||||
'wger.utils.middleware.WgerAuthenticationMiddleware',
|
||||
|
||||
# Send an appropriate Header so search engines don't index pages
|
||||
'wger.utils.middleware.RobotsExclusionMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
|
||||
# History keeping
|
||||
'simple_history.middleware.HistoryRequestMiddleware',
|
||||
|
||||
# Prometheus
|
||||
'django_prometheus.middleware.PrometheusAfterMiddleware',
|
||||
|
||||
# Django Axes
|
||||
'axes.middleware.AxesMiddleware', # should be the last one in the list
|
||||
]
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'axes.backends.AxesStandaloneBackend', # should be the first one in the list
|
||||
|
||||
'wger.core.backends.AuthProxyUserBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'wger.utils.helpers.EmailAuthBackend',
|
||||
@@ -167,7 +148,6 @@ TEMPLATES = [
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'wger.utils.context_processor.processor',
|
||||
|
||||
# Django
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.template.context_processors.debug',
|
||||
@@ -176,15 +156,14 @@ TEMPLATES = [
|
||||
'django.template.context_processors.static',
|
||||
'django.template.context_processors.tz',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
|
||||
# Breadcrumbs
|
||||
'django.template.context_processors.request'
|
||||
'django.template.context_processors.request',
|
||||
],
|
||||
'loaders': [
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
],
|
||||
'debug': False
|
||||
'debug': False,
|
||||
},
|
||||
},
|
||||
]
|
||||
@@ -192,18 +171,15 @@ TEMPLATES = [
|
||||
# Store the user messages in the session
|
||||
MESSAGE_STORAGE = 'django.contrib.messages.storage.session.SessionStorage'
|
||||
|
||||
# Static files
|
||||
# https://docs.djangoproject.com/en/6.0/ref/contrib/staticfiles/
|
||||
STATICFILES_FINDERS = (
|
||||
'django.contrib.staticfiles.finders.FileSystemFinder',
|
||||
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
|
||||
|
||||
# Django compressor
|
||||
'compressor.finders.CompressorFinder',
|
||||
)
|
||||
|
||||
# Additional places to copy to static files
|
||||
STATICFILES_DIRS = (
|
||||
('node', os.path.join(BASE_DIR, '..', 'node_modules')),
|
||||
)
|
||||
STATICFILES_DIRS = (('node', os.path.join(BASE_DIR, '..', 'node_modules')),)
|
||||
|
||||
|
||||
#
|
||||
# Email
|
||||
@@ -295,23 +271,17 @@ LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
'formatters': {
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
|
||||
},
|
||||
'simple': {'format': '%(levelname)s %(asctime)s %(module)s %(message)s'},
|
||||
},
|
||||
'handlers': {
|
||||
'console': {
|
||||
'level': 'DEBUG',
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple'
|
||||
},
|
||||
'console': {'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'simple'},
|
||||
},
|
||||
'loggers': {
|
||||
'wger': {
|
||||
'handlers': ['console'],
|
||||
'level': 'INFO',
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#
|
||||
@@ -350,7 +320,7 @@ AXES_CACHE = 'default'
|
||||
#
|
||||
# Django Crispy Templates
|
||||
#
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
||||
CRISPY_ALLOWED_TEMPLATE_PACKS = 'bootstrap5'
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap5'
|
||||
|
||||
#
|
||||
@@ -358,47 +328,19 @@ CRISPY_TEMPLATE_PACK = 'bootstrap5'
|
||||
#
|
||||
THUMBNAIL_ALIASES = {
|
||||
'': {
|
||||
'micro': {
|
||||
'size': (30, 30)
|
||||
},
|
||||
'micro_cropped': {
|
||||
'size': (30, 30),
|
||||
'crop': 'smart'
|
||||
},
|
||||
'thumbnail': {
|
||||
'size': (80, 80)
|
||||
},
|
||||
'thumbnail_cropped': {
|
||||
'size': (80, 80),
|
||||
'crop': 'smart'
|
||||
},
|
||||
'small': {
|
||||
'size': (200, 200)
|
||||
},
|
||||
'small_cropped': {
|
||||
'size': (200, 200),
|
||||
'crop': 'smart'
|
||||
},
|
||||
'medium': {
|
||||
'size': (400, 400)
|
||||
},
|
||||
'medium_cropped': {
|
||||
'size': (400, 400),
|
||||
'crop': 'smart'
|
||||
},
|
||||
'large': {
|
||||
'size': (800, 800),
|
||||
'quality': 90
|
||||
},
|
||||
'large_cropped': {
|
||||
'size': (800, 800),
|
||||
'crop': 'smart',
|
||||
'quality': 90
|
||||
},
|
||||
'micro': {'size': (30, 30)},
|
||||
'micro_cropped': {'size': (30, 30), 'crop': 'smart'},
|
||||
'thumbnail': {'size': (80, 80)},
|
||||
'thumbnail_cropped': {'size': (80, 80), 'crop': 'smart'},
|
||||
'small': {'size': (200, 200)},
|
||||
'small_cropped': {'size': (200, 200), 'crop': 'smart'},
|
||||
'medium': {'size': (400, 400)},
|
||||
'medium_cropped': {'size': (400, 400), 'crop': 'smart'},
|
||||
'large': {'size': (800, 800), 'quality': 90},
|
||||
'large_cropped': {'size': (800, 800), 'crop': 'smart', 'quality': 90},
|
||||
},
|
||||
}
|
||||
|
||||
STATIC_ROOT = ''
|
||||
USE_S3 = os.getenv('USE_S3') == 'TRUE'
|
||||
|
||||
if USE_S3:
|
||||
@@ -413,49 +355,14 @@ if USE_S3:
|
||||
AWS_LOCATION = 'static'
|
||||
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
|
||||
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
COMPRESS_URL = STATIC_URL
|
||||
COMPRESS_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
|
||||
COMPRESS_OFFLINE = True
|
||||
COMPRESS_OFFLINE_CONTEXT = [
|
||||
{
|
||||
'request': {
|
||||
'user_agent': {
|
||||
'is_mobile': True
|
||||
}
|
||||
},
|
||||
'STATIC_URL': STATIC_URL
|
||||
}, {
|
||||
'request': {
|
||||
'user_agent': {
|
||||
'is_mobile': False
|
||||
}
|
||||
},
|
||||
'STATIC_URL': STATIC_URL
|
||||
}
|
||||
]
|
||||
else:
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
#
|
||||
# Django compressor
|
||||
#
|
||||
|
||||
# The default is not DEBUG, override if needed
|
||||
# COMPRESS_ENABLED = True
|
||||
COMPRESS_CSS_FILTERS = (
|
||||
'compressor.filters.css_default.CssAbsoluteFilter',
|
||||
'compressor.filters.cssmin.rCSSMinFilter',
|
||||
)
|
||||
COMPRESS_JS_FILTERS = [
|
||||
'compressor.filters.jsmin.JSMinFilter',
|
||||
'compressor.filters.template.TemplateFilter',
|
||||
]
|
||||
COMPRESS_ROOT = STATIC_ROOT
|
||||
|
||||
#
|
||||
# Django Rest Framework
|
||||
# https://www.django-rest-framework.org/
|
||||
#
|
||||
# yapf: disable
|
||||
REST_FRAMEWORK = {
|
||||
'DEFAULT_PERMISSION_CLASSES': ('wger.utils.permissions.WgerPermission',),
|
||||
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
|
||||
@@ -472,16 +379,14 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.filters.OrderingFilter',
|
||||
),
|
||||
'DEFAULT_THROTTLE_CLASSES': ['rest_framework.throttling.ScopedRateThrottle'],
|
||||
'DEFAULT_THROTTLE_RATES': {
|
||||
'login': '10/min',
|
||||
'registration': '5/min'
|
||||
},
|
||||
'DEFAULT_THROTTLE_RATES': {'login': '10/min', 'registration': '5/min'},
|
||||
'DEFAULT_SCHEMA_CLASS': 'drf_spectacular.openapi.AutoSchema',
|
||||
}
|
||||
# yapf: enable
|
||||
|
||||
# Api docs
|
||||
# yapf: disable
|
||||
#
|
||||
# API docs
|
||||
# https://drf-spectacular.readthedocs.io/en/latest/
|
||||
#
|
||||
SPECTACULAR_SETTINGS = {
|
||||
'TITLE': 'wger',
|
||||
'SERVERS': [
|
||||
@@ -495,9 +400,8 @@ SPECTACULAR_SETTINGS = {
|
||||
'SWAGGER_UI_DIST': 'SIDECAR',
|
||||
'SWAGGER_UI_FAVICON_HREF': 'SIDECAR',
|
||||
'REDOC_DIST': 'SIDECAR',
|
||||
'COMPONENT_SPLIT_REQUEST': True
|
||||
'COMPONENT_SPLIT_REQUEST': True,
|
||||
}
|
||||
# yapf: enable
|
||||
|
||||
#
|
||||
# Django Rest Framework SimpleJWT
|
||||
@@ -569,7 +473,6 @@ WGER_SETTINGS = {
|
||||
'USE_CELERY': False,
|
||||
'USE_RECAPTCHA': False,
|
||||
'WGER_INSTANCE': 'https://wger.de',
|
||||
|
||||
# Trophy system settings
|
||||
'TROPHIES_ENABLED': True, # Global toggle to enable/disable trophy system
|
||||
'TROPHIES_INACTIVE_USER_DAYS': 30, # Days of inactivity before skipping trophy evaluation
|
||||
@@ -620,3 +523,8 @@ ACTSTREAM_SETTINGS = {
|
||||
|
||||
# Whether the application is being run regularly or during tests
|
||||
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
|
||||
|
||||
# Your reCaptcha keys
|
||||
RECAPTCHA_PUBLIC_KEY = ''
|
||||
RECAPTCHA_PRIVATE_KEY = ''
|
||||
NOCAPTCHA = True
|
||||
@@ -21,7 +21,7 @@ import os
|
||||
from celery import Celery
|
||||
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
|
||||
app = Celery('wger')
|
||||
|
||||
# read config from Django settings, the CELERY namespace would make celery
|
||||
|
||||
55
wger/core/storage.py
Normal file
55
wger/core/storage.py
Normal file
@@ -0,0 +1,55 @@
|
||||
# This file is part of wger Workout Manager.
|
||||
#
|
||||
# wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wger Workout Manager is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
|
||||
# Django
|
||||
from django.contrib.staticfiles.storage import ManifestStaticFilesStorage
|
||||
|
||||
|
||||
class LenientManifestStaticFilesStorage(ManifestStaticFilesStorage):
|
||||
"""
|
||||
Like ManifestStaticFilesStorage but does not raise when an entry or target file
|
||||
referenced from CSS/JS is missing and simply returns the original name in that case.
|
||||
|
||||
This is needed for some node packages that reference files not present (e.g. source maps).
|
||||
"""
|
||||
|
||||
def stored_name(self, name):
|
||||
try:
|
||||
return super().stored_name(name)
|
||||
except Exception:
|
||||
# If the manifest lookup or hashing fails (missing .map etc.), fall back
|
||||
# to the original reference so post-processing doesn't raise.
|
||||
print(
|
||||
f"Can't find name for static file reference: {name}. "
|
||||
f'This is expected in some node packages.'
|
||||
)
|
||||
|
||||
return name
|
||||
|
||||
def url_converter(self, name, hashed_files, template=None):
|
||||
# Wrap the base converter to ignore any errors during conversion and
|
||||
# return the original matched text unchanged.
|
||||
base_converter = super().url_converter(name, hashed_files, template)
|
||||
|
||||
def converter(matchobj):
|
||||
try:
|
||||
return base_converter(matchobj)
|
||||
except Exception:
|
||||
print(
|
||||
f"Can't find name for static file reference: {matchobj.group(0)}. "
|
||||
f'This is expected in some node packages.'
|
||||
)
|
||||
return matchobj.groupdict().get('matched', matchobj.group(0))
|
||||
|
||||
return converter
|
||||
@@ -16,7 +16,7 @@
|
||||
along with Workout Manager. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
{% load i18n static wger_extras compress django_bootstrap_breadcrumbs %}
|
||||
{% load i18n static wger_extras django_bootstrap_breadcrumbs %}
|
||||
{% block breadcrumbs %}
|
||||
{% clear_breadcrumbs %}
|
||||
{% endblock %}
|
||||
@@ -27,6 +27,8 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="icon" href="{% static 'images/favicon.png' %}" type="image/png">
|
||||
|
||||
<!-- OpenGraph -->
|
||||
{% block opengraph %}
|
||||
<meta property="og:url" content="{{ request_absolute_path }}">
|
||||
@@ -44,48 +46,28 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/workout-manager.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap-compiled.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap-custom.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fontawesomefree/css/all.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'node/@wger-project/react-components/build/assets/index.css' %}" id="react-css">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/language-menu.css' %}">
|
||||
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/workout-manager.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap-compiled.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/bootstrap-custom.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fontawesomefree/css/all.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'node/@wger-project/react-components/build/assets/index.css' %}" id="react-css">
|
||||
{% endcompress %}
|
||||
|
||||
<link rel="stylesheet" href="{% static 'css/language-menu.css' %}">
|
||||
|
||||
<link rel="icon" href="{% static 'images/favicon.png' %}" type="image/png">
|
||||
|
||||
{% compress js %}
|
||||
<script src="{% static 'node/jquery/dist/jquery.js' %}"></script>
|
||||
<script src="{% static 'node/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'node/htmx.org/dist/htmx.min.js' %}"></script>
|
||||
<script src="{% static 'node/@popperjs/core/dist/umd/popper.js' %}"></script>
|
||||
<script src="{% static 'node/devbridge-autocomplete/dist/jquery.autocomplete.min.js' %}">
|
||||
</script>
|
||||
<script src="{% static 'node/masonry-layout/dist/masonry.pkgd.min.js' %}"></script>
|
||||
<script src="{% static 'js/nutrition.js' %}"></script>
|
||||
{% endcompress %}
|
||||
|
||||
{# this needs to be outside of the compress block! #}
|
||||
<script src="{% static 'node/jquery/dist/jquery.js' %}"></script>
|
||||
<script src="{% static 'node/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'node/htmx.org/dist/htmx.min.js' %}"></script>
|
||||
<script src="{% static 'node/@popperjs/core/dist/umd/popper.js' %}"></script>
|
||||
<script src="{% static 'node/devbridge-autocomplete/dist/jquery.autocomplete.min.js' %}"></script>
|
||||
<script src="{% static 'node/masonry-layout/dist/masonry.pkgd.min.js' %}"></script>
|
||||
<script src="{% static 'js/nutrition.js' %}"></script>
|
||||
<script src="{% static 'node/@wger-project/react-components/build/main.js' %}" type="module" defer="defer"></script>
|
||||
|
||||
{% block header %}{% endblock %}
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
||||
if (typeof wgerCustomPageInit !== "undefined") {
|
||||
wgerCustomPageInit();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
{# #}
|
||||
{# Navigation #}
|
||||
{# #}
|
||||
|
||||
@@ -17,14 +17,14 @@
|
||||
-->
|
||||
|
||||
|
||||
{% load i18n static compress %}
|
||||
{% load i18n static %}
|
||||
<html lang="{{ language.short_name }}">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
<meta charset="utf-8">
|
||||
<meta name="author" content="wger team">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="icon" href="{% static 'images/favicon.png' %}" type="image/png">
|
||||
|
||||
<!-- OpenGraph -->
|
||||
{% block opengraph %}
|
||||
@@ -43,19 +43,12 @@
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap-compiled.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fontawesomefree/css/all.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/landing_page.css' %}">
|
||||
|
||||
{% compress css %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'bootstrap-compiled.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'fontawesomefree/css/all.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/landing_page.css' %}">
|
||||
{% endcompress %}
|
||||
|
||||
<link rel="icon" href="{% static 'images/favicon.png' %}" type="image/png">
|
||||
|
||||
{% compress js %}
|
||||
<script src="{% static 'node/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'node/@popperjs/core/dist/umd/popper.js' %}"></script>
|
||||
{% endcompress %}
|
||||
<script src="{% static 'node/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
|
||||
<script src="{% static 'node/@popperjs/core/dist/umd/popper.js' %}"></script>
|
||||
|
||||
<title>wger Workout Manager - {% translate "Features" %}</title>
|
||||
</head>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
along with Workout Manager. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
{% load i18n static wger_extras compress django_bootstrap_breadcrumbs %}
|
||||
{% load i18n static wger_extras django_bootstrap_breadcrumbs %}
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||
|
||||
@@ -9,8 +9,8 @@ msgstr ""
|
||||
"Project-Id-Version: wger Workout Manager\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-01 16:48+0530\n"
|
||||
"PO-Revision-Date: 2023-06-24 20:53+0000\n"
|
||||
"Last-Translator: SlyBat <aw9gm3@gmail.com>\n"
|
||||
"PO-Revision-Date: 2025-12-31 21:36+0000\n"
|
||||
"Last-Translator: MR <msrhy131@gmail.com>\n"
|
||||
"Language-Team: Arabic (Saudi Arabia) <https://hosted.weblate.org/projects/"
|
||||
"wger/web/ar_SA/>\n"
|
||||
"Language: ar_SA\n"
|
||||
@@ -19,7 +19,7 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\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 ? 4 : 5;\n"
|
||||
"X-Generator: Weblate 4.18.1\n"
|
||||
"X-Generator: Weblate 5.15.1\n"
|
||||
|
||||
#: config/models/gym_config.py:46 gym/templates/gym/list.html:56
|
||||
msgid "Default gym"
|
||||
@@ -148,6 +148,8 @@ msgstr "الرمز غير صحيح"
|
||||
#: core/forms.py:271 core/forms.py:346
|
||||
msgid "The form is secured with reCAPTCHA"
|
||||
msgstr ""
|
||||
"يتم تامين النموذج مع استخدام خدمه تمييز البشر عن ريبوت في الفحص للدخول الى "
|
||||
"اي موقع او اي شيء يحتاج الى تمييزك كانسان"
|
||||
|
||||
#: core/forms.py:287 core/forms.py:309 core/templates/navigation.html:272
|
||||
#: core/templates/navigation.html:285 core/templates/template.html:150
|
||||
@@ -182,10 +184,8 @@ msgid "Language full name"
|
||||
msgstr "الاسم الكامل للغة"
|
||||
|
||||
#: core/models/language.py:45 core/templates/language/view.html:23
|
||||
#, fuzzy
|
||||
#| msgid "Language full name"
|
||||
msgid "Language full name in English"
|
||||
msgstr "الاسم الكامل للغة"
|
||||
msgstr "الاسم الكامل للغة الانكليزية"
|
||||
|
||||
#: core/models/license.py:29
|
||||
msgid "Full name"
|
||||
@@ -196,6 +196,8 @@ msgid ""
|
||||
"If a license has been localized, e.g. the Creative Commons licenses for the "
|
||||
"different countries, add them as separate entries here."
|
||||
msgstr ""
|
||||
"إذا تم توطين الترخيص، على سبيل المثال تراخيص المشاع الإبداعي لمختلف البلدان، "
|
||||
"أضفها كإدخالات منفصلة هنا."
|
||||
|
||||
#: core/models/license.py:40
|
||||
msgid "Short name, e.g. CC-BY-SA 3"
|
||||
@@ -207,7 +209,7 @@ msgstr "الرابط"
|
||||
|
||||
#: core/models/license.py:46
|
||||
msgid "Link to license text or other information"
|
||||
msgstr ""
|
||||
msgstr "رابط لنص الترخيص أو معلومات أخرى"
|
||||
|
||||
#: core/models/profile.py:57
|
||||
#, python-format
|
||||
@@ -248,7 +250,7 @@ msgstr "اظهار ملاحظات التدريب"
|
||||
|
||||
#: core/models/profile.py:120
|
||||
msgid "Check to show exercise comments on the workout view"
|
||||
msgstr ""
|
||||
msgstr "تحقق لإظهار تعليقات التمرين على عرض التمرين"
|
||||
|
||||
#: core/models/profile.py:130
|
||||
msgid "Also use ingredients in English"
|
||||
@@ -261,28 +263,39 @@ msgid ""
|
||||
"by the US Department of Agriculture. It is extremely complete, with around\n"
|
||||
"7000 entries, but can be somewhat overwhelming and make the search difficult."
|
||||
msgstr ""
|
||||
"تحقق أيضا لإظهار المكونات باللغة الإنجليزية أثناء الإنشاء\n"
|
||||
"\n"
|
||||
"خطة غذائية. يتم استخراج هذه المكونات من قائمة مقدمة\n"
|
||||
"\n"
|
||||
"من قبل وزارة الزراعة الأمريكية. إنه كامل للغاية ، مع حول\n"
|
||||
"\n"
|
||||
"7000 إدخالات، ولكن يمكن أن تكون ساحقة إلى حد ما وجعل البحث صعبا."
|
||||
|
||||
#: core/models/profile.py:141
|
||||
msgid "Activate workout reminders"
|
||||
msgstr ""
|
||||
msgstr "تفعيل تذكيرات التمرين"
|
||||
|
||||
#: core/models/profile.py:143
|
||||
msgid ""
|
||||
"Check to activate automatic reminders for workouts. You need to provide a "
|
||||
"valid email for this to work."
|
||||
msgstr ""
|
||||
"تحقق لتفعيل التذكيرات التلقائية للتدريبات. تحتاج إلى تقديم بريد إلكتروني "
|
||||
"صالح حتى يعمل هذا."
|
||||
|
||||
#: core/models/profile.py:152
|
||||
msgid "Remind before expiration"
|
||||
msgstr ""
|
||||
msgstr "تذكير قبل انتهاء الصلاحية"
|
||||
|
||||
#: core/models/profile.py:153
|
||||
msgid "The number of days you want to be reminded before a workout expires."
|
||||
msgstr ""
|
||||
msgstr "عدد الأيام التي تريد أن يتم تذكيرك بها قبل انتهاء التمرين."
|
||||
|
||||
#: core/models/profile.py:159
|
||||
msgid "Default duration of workouts"
|
||||
msgstr ""
|
||||
"المدة الافتراضية للتدريبات\n"
|
||||
"Arabic (Sudish)"
|
||||
|
||||
#: core/models/profile.py:161
|
||||
msgid ""
|
||||
@@ -302,32 +315,32 @@ msgstr ""
|
||||
|
||||
#: core/models/profile.py:214 gym/views/export.py:64
|
||||
msgid "Age"
|
||||
msgstr ""
|
||||
msgstr "العمر"
|
||||
|
||||
#: core/models/profile.py:230 nutrition/forms.py:117
|
||||
msgid "Height (cm)"
|
||||
msgstr ""
|
||||
msgstr "الطول (سنتيمتر)"
|
||||
|
||||
#: core/models/profile.py:247
|
||||
msgid "Hours of sleep"
|
||||
msgstr ""
|
||||
msgstr "ساعات النMم"
|
||||
|
||||
#: core/models/profile.py:248
|
||||
msgid "The average hours of sleep per day"
|
||||
msgstr ""
|
||||
msgstr "معدل ساعات النوم خلال اليوم"
|
||||
|
||||
#: core/models/profile.py:257
|
||||
msgid "Work"
|
||||
msgstr ""
|
||||
msgstr "العمل"
|
||||
|
||||
#: core/models/profile.py:258 core/models/profile.py:300
|
||||
msgid "Average hours per day"
|
||||
msgstr ""
|
||||
msgstr "معدل الساعات خلال اليوم"
|
||||
|
||||
#: core/models/profile.py:267 core/models/profile.py:288
|
||||
#: core/models/profile.py:309
|
||||
msgid "Physical intensity"
|
||||
msgstr ""
|
||||
msgstr "الكثافة البدنية"
|
||||
|
||||
#: core/models/profile.py:268 core/models/profile.py:289
|
||||
#: core/models/profile.py:310
|
||||
|
||||
@@ -22,15 +22,15 @@ msgstr ""
|
||||
"Project-Id-Version: wger Workout Manager\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-01 16:48+0530\n"
|
||||
"PO-Revision-Date: 2025-10-24 12:54+0000\n"
|
||||
"Last-Translator: Paul Bonneau <bonneau-paul@hotmail.fr>\n"
|
||||
"PO-Revision-Date: 2025-12-20 11:00+0000\n"
|
||||
"Last-Translator: Justin Pinheiro <justin.pinheiro.pro@gmail.com>\n"
|
||||
"Language-Team: French <https://hosted.weblate.org/projects/wger/web/fr/>\n"
|
||||
"Language: fr\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n > 1;\n"
|
||||
"X-Generator: Weblate 5.14.1-dev\n"
|
||||
"X-Generator: Weblate 5.15.1\n"
|
||||
|
||||
#: config/models/gym_config.py:46 gym/templates/gym/list.html:56
|
||||
msgid "Default gym"
|
||||
@@ -41,9 +41,9 @@ msgid ""
|
||||
"Select the default gym for this installation. This will assign all new "
|
||||
"registered users to this gym and update all existing users without a gym."
|
||||
msgstr ""
|
||||
"Sélectionner l'activité par défaut pour cette installation. Celle-ci sera "
|
||||
"assigner à chaque utilisateur non enregistré ainsi qu'aux utilisateurs sans "
|
||||
"activités."
|
||||
"Sélectionner la salle de sport par défaut pour cette installation. Celle-ci "
|
||||
"sera assignée à chaque utilisateur non enregistré ainsi qu'aux utilisateurs "
|
||||
"sans salle de sport."
|
||||
|
||||
#: config/views/gym_config.py:42 core/templates/language/view.html:64
|
||||
#: core/templates/license/list.html:17
|
||||
|
||||
@@ -15,7 +15,7 @@ msgstr ""
|
||||
"Project-Id-Version: wger Workout Manager\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-01 16:48+0530\n"
|
||||
"PO-Revision-Date: 2025-12-13 15:00+0000\n"
|
||||
"PO-Revision-Date: 2025-12-20 11:00+0000\n"
|
||||
"Last-Translator: Floris C <floris.contactmail@gmail.com>\n"
|
||||
"Language-Team: Dutch <https://hosted.weblate.org/projects/wger/web/nl/>\n"
|
||||
"Language: nl\n"
|
||||
@@ -23,7 +23,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.15-dev\n"
|
||||
"X-Generator: Weblate 5.15.1\n"
|
||||
|
||||
#: config/models/gym_config.py:46 gym/templates/gym/list.html:56
|
||||
msgid "Default gym"
|
||||
@@ -88,7 +88,7 @@ msgstr "E-mail"
|
||||
#: core/forms.py:112
|
||||
msgid "Used for password resets and, optionally, e-mail reminders."
|
||||
msgstr ""
|
||||
"Gebruikt voor het her-instellen van wachtwoorden en, optioneel, voor e-"
|
||||
"Gebruikt voor het opnieuw-instellen van wachtwoorden en, optioneel, e-"
|
||||
"mailherinneringen."
|
||||
|
||||
#: core/forms.py:116 core/models/profile.py:222
|
||||
@@ -170,7 +170,7 @@ msgstr "Contact"
|
||||
|
||||
#: core/forms.py:324
|
||||
msgid "Some way of answering you (e-mail, etc.)"
|
||||
msgstr "Een manier om je te beantwoorden (e-mail, etc.)"
|
||||
msgstr "Een manier om je van antwoorden te voorzien (e-mail, etc.)"
|
||||
|
||||
#: core/forms.py:332 exercises/models/comment.py:55 manager/models/slot.py:40
|
||||
#: nutrition/models/log.py:77
|
||||
@@ -202,13 +202,13 @@ msgid ""
|
||||
"If a license has been localized, e.g. the Creative Commons licenses for the "
|
||||
"different countries, add them as separate entries here."
|
||||
msgstr ""
|
||||
"Als een licentie werd gelokaliseerd, zoals bvb. de Creative Commons-"
|
||||
"Als een licentie werd gelokaliseerd, zoals bijv. de Creative Commons-"
|
||||
"licenties voor verschillende landen, voeg ze ze dan hier toe als aparte "
|
||||
"ingaven."
|
||||
|
||||
#: core/models/license.py:40
|
||||
msgid "Short name, e.g. CC-BY-SA 3"
|
||||
msgstr "Korte naam, bvb. CC-BY-SA 3"
|
||||
msgstr "Korte naam, bijv. CC-BY-SA 3"
|
||||
|
||||
#: core/models/license.py:45 nutrition/models/ingredient.py:223
|
||||
msgid "Link"
|
||||
@@ -258,11 +258,11 @@ msgstr "Toon opmerkingen oefening"
|
||||
#: core/models/profile.py:120
|
||||
msgid "Check to show exercise comments on the workout view"
|
||||
msgstr ""
|
||||
"Vink aan om begeleiding voor de oefeningen te tonen in de workout view."
|
||||
"Vink aan om de opmerkingen bij de oefeningen te tonen in de workout view"
|
||||
|
||||
#: core/models/profile.py:130
|
||||
msgid "Also use ingredients in English"
|
||||
msgstr "Gebruik ook engelstalige ingrediënten"
|
||||
msgstr "Gebruik ook Engelstalige ingrediënten"
|
||||
|
||||
#: core/models/profile.py:132
|
||||
msgid ""
|
||||
@@ -272,10 +272,11 @@ msgid ""
|
||||
"7000 entries, but can be somewhat overwhelming and make the search difficult."
|
||||
msgstr ""
|
||||
"Vink aan om ook ingrediënten in het Engels te tonen bij het aanmaken van\n"
|
||||
"een voedingsschema. Deze ingrediënten worden geëxtraheerd uit een lijst\n"
|
||||
"aangeboden door het Amerikaanse Departement voor Landbouw. Ze is bijzonder\n"
|
||||
"compleet, met ongeveer 7000 elementen, maar kan ook overweldigend zijn en je "
|
||||
"zoektocht bemoeilijken"
|
||||
"een voedingsschema. Deze ingrediënten worden uit een lijst gehaald die\n"
|
||||
"aangeboden wordt door het Amerikaanse Departement voor Landbouw. Deze lijst "
|
||||
"is bijzonder\n"
|
||||
"compleet, met ongeveer 7000 ingrediënten, maar kan ook overweldigend zijn en "
|
||||
"je zoektocht bemoeilijken"
|
||||
|
||||
#: core/models/profile.py:141
|
||||
msgid "Activate workout reminders"
|
||||
@@ -483,12 +484,13 @@ msgstr ""
|
||||
" "
|
||||
|
||||
#: core/templates/base_wide.html:12
|
||||
#, fuzzy, python-format
|
||||
#| msgid "Back to \"%(target)s\""
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Back to \"%(target)s\n"
|
||||
" \""
|
||||
msgstr "Terug naar \"%(target)s\""
|
||||
msgstr ""
|
||||
"Terug naar \"%(target)s\n"
|
||||
" \""
|
||||
|
||||
#: core/templates/delete.html:4
|
||||
msgid "Are you sure you want to delete this? This action cannot be undone."
|
||||
@@ -511,7 +513,7 @@ msgstr "Foutmelding, foutieve verificatie token"
|
||||
|
||||
#: core/templates/email_verification/email_body_html.tpl:6
|
||||
msgid "Email Confirmation"
|
||||
msgstr "Email bevestiging"
|
||||
msgstr "Email Bevestiging"
|
||||
|
||||
#: core/templates/email_verification/email_body_html.tpl:9
|
||||
#: core/templates/email_verification/email_body_txt.tpl:3
|
||||
@@ -577,7 +579,7 @@ msgstr "Voeg er een toe."
|
||||
#: mailer/templates/mailer/gym/overview.html:43
|
||||
#: nutrition/templates/ingredient/overview.html:60
|
||||
msgid "Add"
|
||||
msgstr "Aanmaken"
|
||||
msgstr "Toevoegen"
|
||||
|
||||
#: core/templates/language/view.html:61 core/templates/user/api_key.html:41
|
||||
#: core/templates/user/preferences.html:50 gym/models/contract.py:203
|
||||
@@ -640,7 +642,7 @@ msgstr "Training schema's"
|
||||
|
||||
#: core/templates/navigation.html:79
|
||||
msgid "Your templates"
|
||||
msgstr "Je sjablonen"
|
||||
msgstr "Je templates"
|
||||
|
||||
#: core/templates/navigation.html:88
|
||||
msgid "Public templates"
|
||||
@@ -658,7 +660,7 @@ msgstr "Administratie"
|
||||
|
||||
#: core/templates/navigation.html:106
|
||||
msgid "Exercise edit history"
|
||||
msgstr "Oefeningswijzigings geschiedenis"
|
||||
msgstr "Oefening bewerkings historie"
|
||||
|
||||
#: core/templates/navigation.html:112 exercises/models/base.py:107
|
||||
msgid "Equipment"
|
||||
@@ -1116,7 +1118,7 @@ msgstr "Opnemen in inactief overzicht"
|
||||
#: core/templates/user/overview.html:510
|
||||
#, python-format
|
||||
msgid "Admin login is only available for users in %(gym_name)s"
|
||||
msgstr "Inloggen voor beheerders is enkel beschikbaar in %(gym_name)s"
|
||||
msgstr "Admin login is alleen beschikbaar voor gebruikers %(gym_name)s"
|
||||
|
||||
#: core/templates/user/overview.html:514
|
||||
msgid "Log in as this user"
|
||||
@@ -1128,19 +1130,19 @@ msgstr "Voorkeuren"
|
||||
|
||||
#: core/templates/user/preferences.html:19
|
||||
msgid "Verified email"
|
||||
msgstr "e-mail is gevalideerd"
|
||||
msgstr "Geverifieerd email adres"
|
||||
|
||||
#: core/templates/user/preferences.html:24
|
||||
msgid "Unverified email"
|
||||
msgstr "Onbevestigde email"
|
||||
msgstr "Niet geverifieerde email"
|
||||
|
||||
#: core/templates/user/preferences.html:26
|
||||
msgid "Send verification email"
|
||||
msgstr "Verstuur bevestigingsemail"
|
||||
msgstr "Verzend verificatie email"
|
||||
|
||||
#: core/templates/user/preferences.html:31
|
||||
msgid "You need to verify your email to contribute exercises"
|
||||
msgstr "Je moet jouw email bevestigen om bij te dragen aan oefeningen"
|
||||
msgstr "Je moet jouw email nog bevestigen om oefeningen te plaatsen"
|
||||
|
||||
#: core/templates/user/preferences.html:55 core/views/user.py:598
|
||||
msgid "Change password"
|
||||
@@ -1266,7 +1268,7 @@ msgstr "Gym"
|
||||
#: core/views/user.py:647
|
||||
#, python-format
|
||||
msgid "A verification email was sent to %(email)s"
|
||||
msgstr "Een bevestigingsmail is verstuurd naar %(email)s"
|
||||
msgstr "Een verificatie email is verstuurd naar %(email)s"
|
||||
|
||||
#: exercises/models/base.py:86 nutrition/models/ingredient.py:245
|
||||
msgid "Category"
|
||||
@@ -1300,7 +1302,7 @@ msgstr "Een opmerking over hoe je deze oefening correct kan uitvoeren."
|
||||
|
||||
#: exercises/models/exercise_alias.py:56
|
||||
msgid "Alias for an exercise"
|
||||
msgstr "Andere naam voor een oefening"
|
||||
msgstr "Alias voor een oefening"
|
||||
|
||||
#: exercises/models/image.py:58
|
||||
msgid "Line"
|
||||
@@ -1312,7 +1314,7 @@ msgstr "3D"
|
||||
|
||||
#: exercises/models/image.py:60
|
||||
msgid "Low-poly"
|
||||
msgstr ""
|
||||
msgstr "Lage-poly"
|
||||
|
||||
#: exercises/models/image.py:61
|
||||
msgid "Photo"
|
||||
@@ -1341,7 +1343,7 @@ msgstr "Alternatieve naam"
|
||||
|
||||
#: exercises/models/muscle.py:48
|
||||
msgid "A more basic name for the muscle"
|
||||
msgstr "Een bekendere naam voor deze spier"
|
||||
msgstr "Een vereenvoudigde naam voor de spier"
|
||||
|
||||
#: exercises/models/translation.py:74 nutrition/models/ingredient.py:81
|
||||
#: nutrition/models/weight_unit.py:32
|
||||
@@ -1358,14 +1360,12 @@ msgid "File type is not supported"
|
||||
msgstr "Bestandstype wordt niet ondersteund"
|
||||
|
||||
#: exercises/models/video.py:72
|
||||
#, fuzzy
|
||||
msgid "File is not a valid video"
|
||||
msgstr "Bestand is geen geldige video"
|
||||
|
||||
#: exercises/models/video.py:104
|
||||
#, fuzzy
|
||||
msgid "Main video"
|
||||
msgstr "Hoofdvideo"
|
||||
msgstr "Hoofd video"
|
||||
|
||||
#: exercises/models/video.py:110
|
||||
msgid "Video"
|
||||
@@ -1401,12 +1401,11 @@ msgstr "Materiaallijst"
|
||||
|
||||
#: exercises/templates/history/overview.html:11
|
||||
msgid "Exercise admin history"
|
||||
msgstr "Oefening admin geschiedenis"
|
||||
msgstr "Admin oefening geschiedenis"
|
||||
|
||||
#: exercises/templates/history/overview.html:27
|
||||
#, fuzzy
|
||||
msgid "Action"
|
||||
msgstr "Acties"
|
||||
msgstr "Actie"
|
||||
|
||||
#: exercises/templates/history/overview.html:28 gallery/models/image.py:46
|
||||
#: gym/forms.py:58 gym/templates/gym/email_inactive_members.html:4
|
||||
@@ -1419,17 +1418,16 @@ msgid "User"
|
||||
msgstr "Gebruiker"
|
||||
|
||||
#: exercises/templates/history/overview.html:29
|
||||
#, fuzzy
|
||||
msgid "Object"
|
||||
msgstr "Onderwerp"
|
||||
|
||||
#: exercises/templates/history/overview.html:31
|
||||
msgid "Changes"
|
||||
msgstr "Veranderingen"
|
||||
msgstr "Wijzigingen"
|
||||
|
||||
#: exercises/templates/history/overview.html:32
|
||||
msgid "Revert"
|
||||
msgstr "Terug"
|
||||
msgstr "Terugzetten"
|
||||
|
||||
#: exercises/templates/history/overview.html:64
|
||||
msgid "View Object"
|
||||
@@ -1437,7 +1435,7 @@ msgstr "Bekijk Object"
|
||||
|
||||
#: exercises/templates/history/overview.html:75
|
||||
msgid "View changes"
|
||||
msgstr "Bekijk veranderingen"
|
||||
msgstr "Bekijk wijzigingen"
|
||||
|
||||
#: exercises/templates/history/overview.html:86
|
||||
msgid "Revert changes"
|
||||
@@ -1472,7 +1470,6 @@ msgid "Only PNG and JPEG formats are supported"
|
||||
msgstr "Enkel PNG en JPEG-formaten worden ondersteund"
|
||||
|
||||
#: gallery/templates/images/overview.html:72
|
||||
#, fuzzy
|
||||
msgid "Add image"
|
||||
msgstr "Voeg afbeelding toe"
|
||||
|
||||
@@ -1832,7 +1829,7 @@ msgstr "Vrije halter"
|
||||
|
||||
#: i18n.tpl:13
|
||||
msgid "Glutes"
|
||||
msgstr "Bilspieren"
|
||||
msgstr "Bil spieren"
|
||||
|
||||
#: i18n.tpl:14
|
||||
msgid "Gym mat"
|
||||
@@ -1904,7 +1901,7 @@ msgstr "Herhalingen"
|
||||
|
||||
#: i18n.tpl:31
|
||||
msgid "Resistance band"
|
||||
msgstr ""
|
||||
msgstr "Weerstandsband"
|
||||
|
||||
#: i18n.tpl:32
|
||||
msgid "SZ-Bar"
|
||||
@@ -1995,7 +1992,6 @@ msgid "Routine will expire soon"
|
||||
msgstr "Routine zal spoedig vervallen"
|
||||
|
||||
#: manager/models/day.py:52
|
||||
#, fuzzy
|
||||
msgid "Routine"
|
||||
msgstr "Routine"
|
||||
|
||||
@@ -2031,16 +2027,16 @@ msgid ""
|
||||
"Marking a workout as a template will freeze it and allow you to make copies "
|
||||
"of it"
|
||||
msgstr ""
|
||||
"Een training markeren als een sjabloon zal het bevriezen en je toelaten om "
|
||||
"er kopieën van te maken"
|
||||
"Door een workout als template te markeren zal deze niet meer aan te passen "
|
||||
"zijn en je in staat stellen om er kopieën van te maken"
|
||||
|
||||
#: manager/models/routine.py:116
|
||||
msgid "Public template"
|
||||
msgstr "Publieke sjabloon"
|
||||
msgstr "Publieke template"
|
||||
|
||||
#: manager/models/routine.py:117
|
||||
msgid "A public template is available to other users"
|
||||
msgstr "Een publieke sjabloon is beschikbaar voor andere gebruikers"
|
||||
msgstr "Een publieke template is beschikbaar voor andere gebruikers"
|
||||
|
||||
#: manager/models/routine.py:155 manager/models/session.py:147
|
||||
msgid "The start time cannot be after the end time."
|
||||
@@ -2129,11 +2125,16 @@ msgid ""
|
||||
"Your last weight entry is from %(date)s (%(days)s days ago).\n"
|
||||
"Please click the link to access the application and enter a new one."
|
||||
msgstr ""
|
||||
"Je laatste gewichtsinvoer dateert van %(date)s (%(days)s dagen geleden).\n"
|
||||
"Klik op de link om het aanvraagformulier te openen en een nieuw gewicht in "
|
||||
"te voeren."
|
||||
|
||||
#: manager/templates/workout/email_weight_reminder.tpl:8
|
||||
msgid ""
|
||||
"You will regularly receive such reminders till you enter your current weight."
|
||||
msgstr ""
|
||||
"Je zal regelmatig dergelijke herinneringen totdat je je huidige gewicht hebt "
|
||||
"ingevoerd."
|
||||
|
||||
#: manager/views/pdf.py:77 manager/views/pdf.py:146
|
||||
#, python-format
|
||||
@@ -2142,22 +2143,19 @@ msgstr "Workout voor %s"
|
||||
|
||||
#: measurements/models/measurement.py:51
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
msgstr "Waarde"
|
||||
|
||||
#: nutrition/forms.py:117
|
||||
#, fuzzy
|
||||
msgid "Height (in)"
|
||||
msgstr "Hoogte (cm)"
|
||||
msgstr "Hoogte (in)"
|
||||
|
||||
#: nutrition/forms.py:120
|
||||
#, fuzzy
|
||||
msgid "Weight (kg)"
|
||||
msgstr "Gewichtlog"
|
||||
msgstr "Gewicht (kg)"
|
||||
|
||||
#: nutrition/forms.py:120
|
||||
#, fuzzy
|
||||
msgid "Weight (lbs)"
|
||||
msgstr "Gewichtlog"
|
||||
msgstr "Gewicht (lbs)"
|
||||
|
||||
#: nutrition/forms.py:133
|
||||
msgid "Calculate"
|
||||
@@ -2216,7 +2214,7 @@ msgstr "Verzadigde vetinhoud in vetten"
|
||||
#: nutrition/models/ingredient.py:184
|
||||
#: nutrition/templates/ingredient/view.html:108 nutrition/views/plan.py:287
|
||||
msgid "Fiber"
|
||||
msgstr ""
|
||||
msgstr "Vezels"
|
||||
|
||||
#: nutrition/models/ingredient.py:194
|
||||
#: nutrition/templates/ingredient/view.html:118 nutrition/views/plan.py:293
|
||||
@@ -2225,20 +2223,21 @@ msgstr "Natrium"
|
||||
|
||||
#: nutrition/models/ingredient.py:224
|
||||
msgid "Link to product"
|
||||
msgstr ""
|
||||
msgstr "Link naar product"
|
||||
|
||||
#: nutrition/models/ingredient.py:253
|
||||
msgid "Brand name of product"
|
||||
msgstr ""
|
||||
msgstr "Merknaam van product"
|
||||
|
||||
#: nutrition/models/ingredient.py:320
|
||||
#, python-brace-format
|
||||
msgid "The total energy ({self.energy}kcal) is not the approximate sum of the "
|
||||
msgstr ""
|
||||
"De totale energie ({self.energy}kcal) is niet de benaderende som van de "
|
||||
|
||||
#: nutrition/models/ingredient_category.py:31
|
||||
msgid "Ingredient Categories"
|
||||
msgstr ""
|
||||
msgstr "Ingrediënt Categorieën"
|
||||
|
||||
#: nutrition/models/ingredient_weight_unit.py:49
|
||||
msgid "Amount in grams"
|
||||
@@ -2258,9 +2257,8 @@ msgid "Meal"
|
||||
msgstr "Maaltijd"
|
||||
|
||||
#: nutrition/models/log.py:71
|
||||
#, fuzzy
|
||||
msgid "Date and Time (Approx.)"
|
||||
msgstr "Tijd (ongeveer)"
|
||||
msgstr "Datum en Tijd (Ongeveer.)"
|
||||
|
||||
#: nutrition/models/meal.py:60
|
||||
msgid "Time (approx)"
|
||||
@@ -2271,6 +2269,8 @@ msgid ""
|
||||
"Give meals a textual description / name such as \"Breakfast\" or \"after "
|
||||
"workout\""
|
||||
msgstr ""
|
||||
"Geef maaltijden een tekstuele beschrijving / naam, zoals 'Ontbijt' of 'na de "
|
||||
"workout'."
|
||||
|
||||
#: nutrition/models/plan.py:78
|
||||
msgid ""
|
||||
@@ -2357,7 +2357,7 @@ msgstr "Macrovoedingstoffen"
|
||||
|
||||
#: nutrition/templates/ingredient/view.html:54
|
||||
msgid "kJ"
|
||||
msgstr ""
|
||||
msgstr "kJ"
|
||||
|
||||
#: nutrition/templates/ingredient/view.html:75
|
||||
#: nutrition/templates/ingredient/view.html:91
|
||||
@@ -2703,66 +2703,72 @@ msgid "exercises <br> for workout creation"
|
||||
msgstr "oefeningen <br> voor het maken van workouts"
|
||||
|
||||
#: software/templates/features.html:141
|
||||
#, fuzzy
|
||||
#| msgid "foods <br> for meal planing"
|
||||
msgid "foods <br> for meal planning"
|
||||
msgstr "voeding <br> voor maaltijdplanning"
|
||||
|
||||
#: software/templates/features.html:145
|
||||
msgid "stars <br> on GitHub"
|
||||
msgstr ""
|
||||
msgstr "sterren <br> op Github"
|
||||
|
||||
#: software/templates/features.html:173
|
||||
msgid "Craft your perfect <span>workout routines</span>"
|
||||
msgstr ""
|
||||
msgstr "Stel je perfecte <span>trainingsschema's</span> samen"
|
||||
|
||||
#: software/templates/features.html:177
|
||||
msgid "Design a weekly workout plan from more than 100 exercises"
|
||||
msgstr ""
|
||||
msgstr "Stel een wekelijks trainingsschema samen uit meer dan 100 oefeningen"
|
||||
|
||||
#: software/templates/features.html:180
|
||||
msgid "Set reps and sets, duration, or distance goals for each exercise"
|
||||
msgstr ""
|
||||
"Stel doelen vast voor het aantal herhalingen en sets, de duur of de afstand "
|
||||
"van elke oefening"
|
||||
|
||||
#: software/templates/features.html:183
|
||||
msgid "Get guided through your workouts step by step"
|
||||
msgstr ""
|
||||
msgstr "Laat je stap voor stap begeleiden tijdens je trainingen"
|
||||
|
||||
#: software/templates/features.html:186
|
||||
msgid ""
|
||||
"Keep track of the workouts you've done, including how you felt you performed"
|
||||
msgstr ""
|
||||
"Houd bij welke trainingen je hebt gedaan, inclusief hoe je je prestatie hebt "
|
||||
"ervaren"
|
||||
|
||||
#: software/templates/features.html:211
|
||||
msgid "Plan your <span>daily meals</span>"
|
||||
msgstr ""
|
||||
msgstr "Plan je <span>dagelijkse maaltijden</span>"
|
||||
|
||||
#: software/templates/features.html:215
|
||||
msgid "Fill your week with nutritious meals that help you perform better"
|
||||
msgstr ""
|
||||
msgstr "Vul je week met voedzame maaltijden die je helpen beter te presteren"
|
||||
|
||||
#: software/templates/features.html:218
|
||||
msgid "Build meals from a database of more than 2 million foods"
|
||||
msgstr ""
|
||||
"Stel maaltijden samen uit een database met meer dan 2 miljoen "
|
||||
"voedingsmiddelen"
|
||||
|
||||
#: software/templates/features.html:221
|
||||
msgid ""
|
||||
"Get automatically calculated nutritional values (energy, protein, "
|
||||
"carbs, ...) for both individual meals and your entire weekly plan"
|
||||
msgstr ""
|
||||
"Ontvang automatisch berekende voedingswaarden (energie, eiwitten, "
|
||||
"koolhydraten, ...) voor zowel individuele maaltijden als je volledige "
|
||||
"weekplan"
|
||||
|
||||
#: software/templates/features.html:251
|
||||
msgid "Keep track of <span>your progress</span>"
|
||||
msgstr ""
|
||||
msgstr "Houd je <span>voortgang</span> bij"
|
||||
|
||||
#: software/templates/features.html:256
|
||||
msgid "Track and annotate everything about your meals and workouts"
|
||||
msgstr ""
|
||||
msgstr "Houd alles bij en maak aantekeningen over je maaltijden en trainingen"
|
||||
|
||||
#: software/templates/features.html:259
|
||||
#, fuzzy
|
||||
msgid "Add custom notes"
|
||||
msgstr "Voeg dagboekitem op maat toe"
|
||||
msgstr "Voeg custom notities toe"
|
||||
|
||||
#: software/templates/features.html:262
|
||||
msgid "Record your weight"
|
||||
@@ -2774,7 +2780,7 @@ msgstr "Houd een fotologboek bij om je vooruitgang te zien"
|
||||
|
||||
#: software/templates/features.html:268
|
||||
msgid "Use the calendar view to see past entries"
|
||||
msgstr ""
|
||||
msgstr "Gebruik de kalenderweergave om eerdere berichten te bekijken"
|
||||
|
||||
#: software/templates/features.html:287
|
||||
msgid "Try wger now"
|
||||
@@ -2798,10 +2804,12 @@ msgid ""
|
||||
"We don't use ads, don't sell your personal info, and we share the code under "
|
||||
"a free and open-source license."
|
||||
msgstr ""
|
||||
"We gebruiken geen advertenties, verkopen uw persoonlijke gegevens niet en "
|
||||
"delen de code onder een gratis en open-source licentie."
|
||||
|
||||
#: software/templates/features.html:322
|
||||
msgid "Pitch in and help make wger even better!"
|
||||
msgstr ""
|
||||
msgstr "Doe mee en help wger nog beter te maken!"
|
||||
|
||||
#: software/templates/features.html:331
|
||||
msgid "Contribute"
|
||||
@@ -2809,55 +2817,55 @@ msgstr "Draag bij"
|
||||
|
||||
#: software/templates/features.html:355
|
||||
msgid "Sign Up Now For Free"
|
||||
msgstr ""
|
||||
msgstr "Meld je nu gratis aan"
|
||||
|
||||
#: software/templates/features.html:369
|
||||
msgid "Web version not enough?"
|
||||
msgstr ""
|
||||
msgstr "Is de webversie niet voldoende?"
|
||||
|
||||
#: software/templates/features.html:371
|
||||
#, fuzzy
|
||||
msgid "Get the code, too!"
|
||||
msgstr "Download de code"
|
||||
msgstr "Download de code ook!"
|
||||
|
||||
#: software/templates/features.html:382
|
||||
msgid "Set up a private server for your gym"
|
||||
msgstr ""
|
||||
msgstr "Zet een privéserver op voor je sportschool"
|
||||
|
||||
#: software/templates/features.html:385
|
||||
msgid ""
|
||||
"Don't want your data on an online service? Set up a wger instance on your "
|
||||
"own server for free!"
|
||||
msgstr ""
|
||||
"Wil je je gegevens niet op een online service opslaan? Zet dan gratis een "
|
||||
"wger-instantie op je eigen server op!"
|
||||
|
||||
#: software/templates/features.html:399
|
||||
msgid "Integrate with your own app"
|
||||
msgstr ""
|
||||
msgstr "Integreer met je eigen app"
|
||||
|
||||
#: software/templates/features.html:402
|
||||
msgid "wger offers an open API that you can use for your app."
|
||||
msgstr ""
|
||||
msgstr "wger biedt een open API die je voor je app kunt gebruiken."
|
||||
|
||||
#: software/templates/features.html:413
|
||||
#, fuzzy
|
||||
msgid "Create your own version"
|
||||
msgstr "Huidige versie"
|
||||
msgstr "Creëer je eigen versie"
|
||||
|
||||
#: software/templates/features.html:416
|
||||
msgid ""
|
||||
"The source code is available under the GNU AGPLv3 license! Feel free to fork "
|
||||
"it on GitHub."
|
||||
msgstr ""
|
||||
"De broncode is beschikbaar onder de GNU AGPLv3-licentie! Je kunt de code "
|
||||
"gerust forken op GitHub."
|
||||
|
||||
#: software/templates/features.html:437
|
||||
#, fuzzy
|
||||
msgid "Account"
|
||||
msgstr "Hoeveelheid"
|
||||
msgstr "Account"
|
||||
|
||||
#: software/templates/features.html:485
|
||||
#, fuzzy
|
||||
msgid "Workout routines"
|
||||
msgstr "Workout-herinneringen"
|
||||
msgstr "Workout routines"
|
||||
|
||||
#: software/templates/features.html:491
|
||||
msgid "Discord"
|
||||
@@ -2865,20 +2873,19 @@ msgstr "Discord"
|
||||
|
||||
#: software/templates/features.html:506
|
||||
msgid "Software"
|
||||
msgstr ""
|
||||
msgstr "Software"
|
||||
|
||||
#: software/templates/features.html:512
|
||||
msgid "Report an issue"
|
||||
msgstr ""
|
||||
msgstr "Rapporteer een probleem"
|
||||
|
||||
#: software/templates/features.html:518
|
||||
msgid "Source Code"
|
||||
msgstr ""
|
||||
msgstr "Broncode"
|
||||
|
||||
#: software/templates/features.html:524
|
||||
#, fuzzy
|
||||
msgid "Translation"
|
||||
msgstr "Vertaal"
|
||||
msgstr "Vertaling"
|
||||
|
||||
#: utils/generic_views.py:263
|
||||
msgid "Yes, delete"
|
||||
@@ -2886,31 +2893,27 @@ msgstr "Ja, verwijder"
|
||||
|
||||
#: utils/models.py:51
|
||||
msgid "The original title of this object, if available"
|
||||
msgstr ""
|
||||
msgstr "De oorspronkelijke titel van dit object, indien beschikbaar"
|
||||
|
||||
#: utils/models.py:57
|
||||
msgid "Link to original object, if available"
|
||||
msgstr ""
|
||||
msgstr "Link naar het originele object, indien beschikbaar"
|
||||
|
||||
#: utils/models.py:63
|
||||
#, fuzzy
|
||||
msgid "Author(s)"
|
||||
msgstr "Auteur"
|
||||
msgstr "Auteur(s)"
|
||||
|
||||
#: utils/models.py:67
|
||||
#, fuzzy
|
||||
msgid "If you are not the author, enter the name or source here."
|
||||
msgstr ""
|
||||
"Als je niet de auteur bent, voeg dan de naam of bron hier toe. Dit is nodig "
|
||||
"voor sommige licenties, oa. de CC-BY-SA."
|
||||
msgstr "Als je niet de auteur bent, voeg dan de naam of bron hier toe."
|
||||
|
||||
#: utils/models.py:71
|
||||
msgid "Link to author profile, if available"
|
||||
msgstr ""
|
||||
msgstr "Link naar het profiel van de auteur, indien beschikbaar"
|
||||
|
||||
#: utils/models.py:77
|
||||
msgid "Link to the original source, if this is a derivative work"
|
||||
msgstr ""
|
||||
msgstr "Link naar de originele bron als dit een afgeleid werk is"
|
||||
|
||||
#: utils/models.py:79
|
||||
msgid ""
|
||||
@@ -2918,6 +2921,9 @@ msgid ""
|
||||
"work, but which also contains sufficient new, creative content to entitle it "
|
||||
"to its own copyright."
|
||||
msgstr ""
|
||||
"Een afgeleid werk is niet alleen gebaseerd op een eerder werk, maar bevat "
|
||||
"ook voldoende nieuwe, creatieve inhoud om recht te geven op een eigen "
|
||||
"auteursrecht."
|
||||
|
||||
#: weight/forms.py:53
|
||||
msgid "Input"
|
||||
@@ -2956,6 +2962,13 @@ msgid ""
|
||||
"<strong>weight</strong>.\n"
|
||||
"All further columns are ignored. There is a 1000 row limit.\n"
|
||||
msgstr ""
|
||||
"Je kunt tekst kopiëren en plakken vanuit je spreadsheet in het tekst "
|
||||
"invoerveld.\n"
|
||||
"Het systeem zal het importformaat proberen te raden. Het enige waar je "
|
||||
"rekening mee moet houden is:\n"
|
||||
"dat de eerste kolom de <strong>datum</strong> is en de tweede het <strong>"
|
||||
"gewicht</strong>.\n"
|
||||
"Alle overige kolommen worden genegeerd. Er geldt een limiet van 1000 rijen.\n"
|
||||
|
||||
#: weight/templates/import_csv_form.html:24
|
||||
msgid ""
|
||||
|
||||
@@ -15,8 +15,8 @@ msgstr ""
|
||||
"Project-Id-Version: wger Workout Manager\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-10-01 16:48+0530\n"
|
||||
"PO-Revision-Date: 2025-09-16 20:01+0000\n"
|
||||
"Last-Translator: Иван Редун <ivanredun@gmail.com>\n"
|
||||
"PO-Revision-Date: 2026-01-03 23:01+0000\n"
|
||||
"Last-Translator: Gevorg Danielyan <forza-dgn@ya.ru>\n"
|
||||
"Language-Team: Russian <https://hosted.weblate.org/projects/wger/web/ru/>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -25,7 +25,7 @@ msgstr ""
|
||||
"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"
|
||||
"X-Generator: Weblate 5.14-dev\n"
|
||||
"X-Generator: Weblate 5.15.1\n"
|
||||
|
||||
#: config/models/gym_config.py:46 gym/templates/gym/list.html:56
|
||||
msgid "Default gym"
|
||||
@@ -1911,7 +1911,7 @@ msgstr "Повторений"
|
||||
|
||||
#: i18n.tpl:31
|
||||
msgid "Resistance band"
|
||||
msgstr ""
|
||||
msgstr "Эластичная лента"
|
||||
|
||||
#: i18n.tpl:32
|
||||
msgid "SZ-Bar"
|
||||
|
||||
185
wger/tasks.py
185
wger/tasks.py
@@ -1,5 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# This file is part of wger Workout Manager.
|
||||
#
|
||||
# wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
@@ -17,7 +15,6 @@
|
||||
# Standard Library
|
||||
import logging
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
@@ -25,9 +22,9 @@ import tempfile
|
||||
import django
|
||||
from django.core.management import (
|
||||
call_command,
|
||||
color_style,
|
||||
execute_from_command_line,
|
||||
)
|
||||
from django.utils.crypto import get_random_string
|
||||
|
||||
# Third Party
|
||||
import requests
|
||||
@@ -42,6 +39,8 @@ from tqdm import tqdm
|
||||
logger = logging.getLogger(__name__)
|
||||
FIXTURE_URL = 'https://github.com/wger-project/data/raw/master/fixtures/'
|
||||
|
||||
style = color_style()
|
||||
|
||||
|
||||
@task(
|
||||
help={
|
||||
@@ -56,8 +55,6 @@ def start(context, address='localhost', port=8000, settings_path=None, extra_arg
|
||||
"""
|
||||
Start the application using django's built in webserver
|
||||
"""
|
||||
|
||||
# Find the path to the settings and setup the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
argv = ['', 'runserver', '--noreload']
|
||||
@@ -70,22 +67,14 @@ def start(context, address='localhost', port=8000, settings_path=None, extra_arg
|
||||
|
||||
@task(
|
||||
help={
|
||||
'settings-path': 'Path to settings file (absolute path). Leave empty for default',
|
||||
'database-path': 'Path to sqlite database (absolute path). Leave empty for default',
|
||||
'settings-path': 'Path to settings file. Leave empty for default (settings.main)',
|
||||
'process-static': 'Whether to process static files (install npm packages and process css). Default: True',
|
||||
}
|
||||
)
|
||||
def bootstrap(context, settings_path=None, database_path=None, process_static=True):
|
||||
def bootstrap(context, settings_path=None, process_static=True):
|
||||
"""
|
||||
Performs all steps necessary to bootstrap the application
|
||||
"""
|
||||
|
||||
# Create settings if necessary
|
||||
if settings_path is None:
|
||||
settings_path = get_path('settings.py')
|
||||
if not os.path.exists(settings_path):
|
||||
create_settings(context, settings_path=settings_path, database_path=database_path)
|
||||
|
||||
# Find the path to the settings and setup the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
# Create Database if necessary
|
||||
@@ -101,93 +90,11 @@ def bootstrap(context, settings_path=None, database_path=None, process_static=Tr
|
||||
context.run('npm run build:css:sass')
|
||||
|
||||
|
||||
@task(
|
||||
help={
|
||||
'settings-path': 'Path to settings file (absolute path). Leave empty for default',
|
||||
'database-path': 'Path to sqlite database (absolute path). Leave empty for default',
|
||||
'database-type': 'Database type to use. Supported: sqlite3, postgresql. Default: sqlite3',
|
||||
'key-length': 'Length of the generated secret key. Default: 50',
|
||||
}
|
||||
)
|
||||
def create_settings(
|
||||
context,
|
||||
settings_path=None,
|
||||
database_path=None,
|
||||
database_type='sqlite3',
|
||||
key_length=50,
|
||||
):
|
||||
"""
|
||||
Creates a local settings file
|
||||
"""
|
||||
if settings_path is None:
|
||||
settings_path = get_path('settings.py')
|
||||
|
||||
settings_module = os.path.dirname(settings_path)
|
||||
print(f'*** Creating settings file at {settings_module}')
|
||||
|
||||
if database_path is None:
|
||||
database_path = get_path('database.sqlite').as_posix()
|
||||
dbpath_value = database_path
|
||||
|
||||
media_folder_path = get_path('media').as_posix()
|
||||
|
||||
# Use localhost with default django port if no URL given
|
||||
url = 'http://localhost:8000'
|
||||
|
||||
# Fill in the config file template
|
||||
settings_template = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'settings.tpl')
|
||||
with open(settings_template, 'r') as settings_file:
|
||||
settings_content = settings_file.read()
|
||||
|
||||
if database_type == 'postgresql':
|
||||
dbengine = 'postgresql'
|
||||
dbname = 'wger'
|
||||
dbuser = 'wger'
|
||||
dbpassword = 'wger'
|
||||
dbhost = 'localhost'
|
||||
dbport = 5432
|
||||
elif database_type == 'sqlite3':
|
||||
dbengine = 'sqlite3'
|
||||
dbname = dbpath_value
|
||||
dbuser = ''
|
||||
dbpassword = ''
|
||||
dbhost = ''
|
||||
dbport = ''
|
||||
|
||||
# Create a random SECRET_KEY to put it in the settings.
|
||||
# from django.core.management.commands.startproject
|
||||
secret_key = get_random_string(key_length, 'abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*(-_=+)')
|
||||
|
||||
settings_content = settings_content.format(
|
||||
dbname=dbname,
|
||||
dbpath=dbpath_value,
|
||||
dbengine=dbengine,
|
||||
dbuser=dbuser,
|
||||
dbpassword=dbpassword,
|
||||
dbhost=dbhost,
|
||||
dbport=dbport,
|
||||
default_key=secret_key,
|
||||
siteurl=url,
|
||||
media_folder_path=media_folder_path,
|
||||
)
|
||||
|
||||
if not os.path.exists(settings_module):
|
||||
os.makedirs(settings_module)
|
||||
|
||||
if not os.path.exists(os.path.dirname(database_path)):
|
||||
os.makedirs(os.path.dirname(database_path))
|
||||
|
||||
with open(settings_path, 'w') as settings_file:
|
||||
settings_file.write(settings_content)
|
||||
|
||||
|
||||
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
|
||||
def create_or_reset_admin(context, settings_path=None):
|
||||
def create_or_reset_admin(context, settings_path: str = None):
|
||||
"""
|
||||
Creates an admin user or resets the password for an existing one
|
||||
"""
|
||||
|
||||
# Find the path to the settings and setup the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
# can't be imported in global scope as it already requires
|
||||
@@ -207,25 +114,21 @@ def create_or_reset_admin(context, settings_path=None):
|
||||
call_command('loaddata', path + 'users.json')
|
||||
|
||||
|
||||
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
|
||||
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
|
||||
def migrate_db(context, settings_path=None):
|
||||
"""
|
||||
Run all database migrations
|
||||
"""
|
||||
|
||||
# Find the path to the settings and setup the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
call_command('migrate')
|
||||
|
||||
|
||||
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
|
||||
def load_fixtures(context, settings_path=None):
|
||||
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
|
||||
def load_fixtures(context, settings_path: str = None):
|
||||
"""
|
||||
Loads all fixtures
|
||||
"""
|
||||
|
||||
# Find the path to the settings and setup the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
# Gym
|
||||
@@ -257,13 +160,11 @@ def load_fixtures(context, settings_path=None):
|
||||
call_command('loaddata', 'gym-adminconfig.json')
|
||||
|
||||
|
||||
@task(help={'settings-path': 'Path to settings file (absolute path). Leave empty for default'})
|
||||
def load_online_fixtures(context, settings_path=None):
|
||||
@task(help={'settings-path': 'Path to settings file. Leave empty for default (settings.main)'})
|
||||
def load_online_fixtures(context, settings_path: str = None):
|
||||
"""
|
||||
Downloads fixtures from server and installs them (at the moment only ingredients)
|
||||
"""
|
||||
|
||||
# Find the path to the settings and set up the django environment
|
||||
setup_django_environment(settings_path)
|
||||
|
||||
# Prepare the download
|
||||
@@ -291,57 +192,28 @@ def load_online_fixtures(context, settings_path=None):
|
||||
os.unlink(f.name)
|
||||
|
||||
|
||||
@task
|
||||
def config_location(context):
|
||||
"""
|
||||
Returns the default location for the settings file and the data folder
|
||||
"""
|
||||
print('Default locations:')
|
||||
print(f'* settings: {get_path("settings.py")}')
|
||||
print(f'* media folder: {get_path("media")}')
|
||||
print(f'* database path: {get_path("database.sqlite")}')
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
# Helper functions
|
||||
#
|
||||
# Note: these functions were originally in wger/utils/main.py but were moved
|
||||
# here because of different import problems (the packaged pip-installed
|
||||
# packaged has a different sys path than the local one)
|
||||
#
|
||||
|
||||
|
||||
def get_path(file='settings.py') -> pathlib.Path:
|
||||
"""
|
||||
Return the path of the given file relatively to the wger source folder
|
||||
|
||||
Note: one parent is the step from e.g. some-checkout/wger/settings.py
|
||||
to some-checkout/wger, the second one to get to the source folder
|
||||
itself.
|
||||
"""
|
||||
return (pathlib.Path(__file__).parent.parent / file).resolve()
|
||||
|
||||
|
||||
def setup_django_environment(settings_path):
|
||||
def setup_django_environment(settings_path: str = None):
|
||||
"""
|
||||
Setup the django environment
|
||||
"""
|
||||
if settings_path is not None:
|
||||
print(f'*** Using settings from argument: {settings_path}')
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = settings_path
|
||||
elif os.environ.get('DJANGO_SETTINGS_MODULE') is not None:
|
||||
print(f'*** Using settings from env: {os.environ.get("DJANGO_SETTINGS_MODULE")}')
|
||||
else:
|
||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings.main'
|
||||
print('*** No settings given, using settings.main')
|
||||
|
||||
# Use default settings if the user didn't specify something else
|
||||
if settings_path is None:
|
||||
settings_path = get_path('settings.py').as_posix()
|
||||
print(f'*** No settings given, using {settings_path}')
|
||||
|
||||
# Find out file path and fine name of settings and setup django
|
||||
settings_file = os.path.basename(settings_path)
|
||||
settings_module_name = ''.join(settings_file.split('.')[:-1])
|
||||
if '.' in settings_module_name:
|
||||
print("'.' is not an allowed character in the settings-file")
|
||||
# Check if we are in the wger source folder
|
||||
if not os.path.isfile('manage.py'):
|
||||
print(style.ERROR('Error: please run this script from the wger checkout folder'))
|
||||
sys.exit(1)
|
||||
settings_module_dir = os.path.dirname(settings_path)
|
||||
sys.path.append(settings_module_dir)
|
||||
os.environ[django.conf.ENVIRONMENT_VARIABLE] = '%s' % settings_module_name
|
||||
|
||||
django.setup()
|
||||
|
||||
|
||||
@@ -356,12 +228,11 @@ def database_exists():
|
||||
from django.db import DatabaseError
|
||||
|
||||
try:
|
||||
# TODO: Use another model, the User could be deactivated
|
||||
User.objects.count()
|
||||
except DatabaseError:
|
||||
return False
|
||||
except ImproperlyConfigured:
|
||||
print('Your settings file seems broken')
|
||||
except ImproperlyConfigured as e:
|
||||
print(style.ERROR('Your settings file seems broken: '), e)
|
||||
sys.exit(0)
|
||||
else:
|
||||
return True
|
||||
@@ -369,9 +240,7 @@ def database_exists():
|
||||
|
||||
def make_program():
|
||||
ns = Collection(
|
||||
start,
|
||||
bootstrap,
|
||||
create_settings,
|
||||
create_or_reset_admin,
|
||||
migrate_db,
|
||||
load_fixtures,
|
||||
|
||||
@@ -35,7 +35,7 @@ Always use versions in the x.y.z format, without any suffixes like "beta1" or su
|
||||
MIN_SERVER_VERSION = Version('2.4.0-alpha2')
|
||||
"""Minimum version of the server required to run sync commands on this server"""
|
||||
|
||||
VERSION = Version('2.4.0-alpha2')
|
||||
VERSION = Version('2.4.0-alpha3')
|
||||
"""Current version of the app"""
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import os
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings')
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'settings.main')
|
||||
|
||||
# This application object is used by any WSGI server configured to use this
|
||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||
|
||||
Reference in New Issue
Block a user