mirror of https://github.com/snachodog/mybuddy.git
Merge remote-tracking branch 'origin/master' into tags
This commit is contained in:
commit
23e7c84547
|
@ -31,3 +31,6 @@ Pipfile.lock
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
/site
|
/site
|
||||||
|
|
||||||
|
# macOS files
|
||||||
|
.DS_Store
|
||||||
|
|
|
@ -6,7 +6,7 @@ tasks:
|
||||||
gulp migrate &&
|
gulp migrate &&
|
||||||
gulp createcachetable
|
gulp createcachetable
|
||||||
env:
|
env:
|
||||||
DJANGO_SETTINGS_MODULE: babybuddy.settings.development
|
DJANGO_SETTINGS_MODULE: babybuddy.settings.gitpod
|
||||||
command: gulp
|
command: gulp
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
|
|
63
CHANGELOG.md
63
CHANGELOG.md
|
@ -1,5 +1,68 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## [v1.10.0](https://github.com/babybuddy/babybuddy/tree/v1.10.0) (2022-02-21)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/babybuddy/babybuddy/compare/v1.10.0...v1.10.1)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Add Chinese \(simplified\) translations [\#399](https://github.com/babybuddy/babybuddy/pull/399) ([cdubz](https://github.com/cdubz))
|
||||||
|
- Use "secret" generator for `DISABLE_COLLECTSTATIC` in Heroku [\#398](https://github.com/babybuddy/babybuddy/pull/398) ([cdubz](https://github.com/cdubz))
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- CSRF Error \(403\) When Adding Entry \(v1.10.0\) [\#393](https://github.com/babybuddy/babybuddy/issues/393)
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Heroku Server Error \(500\) [\#395](https://github.com/babybuddy/babybuddy/issues/395)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Additional Dutch translations [\#397](https://github.com/babybuddy/babybuddy/pull/397) ([Gitoffomalawn](https://github.com/Gitoffomalawn))
|
||||||
|
|
||||||
|
## [v1.10.0](https://github.com/babybuddy/babybuddy/tree/v1.10.0) (2022-02-16)
|
||||||
|
|
||||||
|
[Full Changelog](https://github.com/babybuddy/babybuddy/compare/v1.9.3...v1.10.0)
|
||||||
|
|
||||||
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
- Feature Request: Track baby height [\#191](https://github.com/babybuddy/babybuddy/issues/191)
|
||||||
|
- Hardcoded English strings on timeline [\#352](https://github.com/babybuddy/babybuddy/issues/352)
|
||||||
|
- breakout feeding types [\#384](https://github.com/babybuddy/babybuddy/pull/384) ([alzyee](https://github.com/alzyee))
|
||||||
|
- Rearrange dashboard cards to set timer card first [\#382](https://github.com/babybuddy/babybuddy/pull/382) ([adamaze](https://github.com/adamaze))
|
||||||
|
- Update to Django 4.x [\#378](https://github.com/babybuddy/babybuddy/pull/378) ([cdubz](https://github.com/cdubz))
|
||||||
|
- Updated Italian translation [\#376](https://github.com/babybuddy/babybuddy/pull/376) ([nos86](https://github.com/nos86))
|
||||||
|
- Fix ordering of start/stop items when start and stop times are the same [\#372](https://github.com/babybuddy/babybuddy/pull/372) ([MrApplejuice](https://github.com/MrApplejuice))
|
||||||
|
- Fix German translation mistake [\#368](https://github.com/babybuddy/babybuddy/pull/368) ([MrApplejuice](https://github.com/MrApplejuice))
|
||||||
|
- Add timer restart and stop triggers to REST-API [\#367](https://github.com/babybuddy/babybuddy/pull/367) ([MrApplejuice](https://github.com/MrApplejuice))
|
||||||
|
- Optional last name [\#361](https://github.com/babybuddy/babybuddy/pull/361) ([Alberdi](https://github.com/Alberdi))
|
||||||
|
- Add Height, Head Circumference, and BMI [\#360](https://github.com/babybuddy/babybuddy/pull/360) ([Daegalus](https://github.com/Daegalus))
|
||||||
|
- Improve iOS webapp/clip/pwa experience [\#359](https://github.com/babybuddy/babybuddy/pull/359) ([cdubz](https://github.com/cdubz))
|
||||||
|
- Convert envir variables that are supposed to be boolean to boolean [\#356](https://github.com/babybuddy/babybuddy/pull/356) ([MagiX13](https://github.com/MagiX13))
|
||||||
|
|
||||||
|
**Fixed bugs:**
|
||||||
|
|
||||||
|
- iOS 15 web app experience is degraded [\#357](https://github.com/babybuddy/babybuddy/issues/357)
|
||||||
|
- Boolean environmental variables [\#354](https://github.com/babybuddy/babybuddy/issues/354)
|
||||||
|
- Sleep graph has incorrect positioning when there is a gap in days. [\#286](https://github.com/babybuddy/babybuddy/issues/286)
|
||||||
|
- Sleep graph issues [\#283](https://github.com/babybuddy/babybuddy/issues/283)
|
||||||
|
- KeyError at /children/XXX/reports/sleep/pattern/ [\#211](https://github.com/babybuddy/babybuddy/issues/211)
|
||||||
|
- fix\(sleep-reports\): \#286 Init all days in the period to remove gaps [\#341](https://github.com/babybuddy/babybuddy/pull/341) ([codisart](https://github.com/codisart))
|
||||||
|
|
||||||
|
**Closed issues:**
|
||||||
|
|
||||||
|
- Show type breakdown on feeding\_amounts report [\#383](https://github.com/babybuddy/babybuddy/issues/383)
|
||||||
|
- Error during add/edit action for fed and diaper [\#374](https://github.com/babybuddy/babybuddy/issues/374)
|
||||||
|
- Evaluate replacements for Easy Thumbnails [\#373](https://github.com/babybuddy/babybuddy/issues/373)
|
||||||
|
- Issue with timer API for Feeding, Sleep, and Tummy-Time [\#363](https://github.com/babybuddy/babybuddy/issues/363)
|
||||||
|
|
||||||
|
**Merged pull requests:**
|
||||||
|
|
||||||
|
- Hide feeding\_day card when no information present [\#386](https://github.com/babybuddy/babybuddy/pull/386) ([BenjaminHae](https://github.com/BenjaminHae))
|
||||||
|
- add HA Addon link [\#375](https://github.com/babybuddy/babybuddy/pull/375) ([OttPeterR](https://github.com/OttPeterR))
|
||||||
|
- Minor changes to run into subdir [\#358](https://github.com/babybuddy/babybuddy/pull/358) ([MagiX13](https://github.com/MagiX13))
|
||||||
|
|
||||||
## [v1.9.3](https://github.com/babybuddy/babybuddy/tree/v1.9.3) (2021-12-14)
|
## [v1.9.3](https://github.com/babybuddy/babybuddy/tree/v1.9.3) (2021-12-14)
|
||||||
|
|
||||||
[Full Changelog](https://github.com/babybuddy/babybuddy/compare/v1.9.2...v1.9.3)
|
[Full Changelog](https://github.com/babybuddy/babybuddy/compare/v1.9.2...v1.9.3)
|
||||||
|
|
|
@ -44,6 +44,8 @@ for information about how to create/update translations.
|
||||||
|
|
||||||
### Available languages
|
### Available languages
|
||||||
|
|
||||||
|
:cn: Chinese (simplified)
|
||||||
|
|
||||||
:netherlands: Dutch
|
:netherlands: Dutch
|
||||||
|
|
||||||
:uk: English (U.K.)
|
:uk: English (U.K.)
|
||||||
|
|
21
app.json
21
app.json
|
@ -12,13 +12,17 @@
|
||||||
"self-host"
|
"self-host"
|
||||||
],
|
],
|
||||||
"repository": "https://github.com/babybuddy/babybuddy",
|
"repository": "https://github.com/babybuddy/babybuddy",
|
||||||
"website": "http://www.baby-buddy.net",
|
"website": "https://docs.baby-buddy.net",
|
||||||
"buildpacks": [
|
"buildpacks": [
|
||||||
{
|
{
|
||||||
"url": "heroku/python"
|
"url": "heroku/python"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
|
"DISABLE_COLLECTSTATIC": {
|
||||||
|
"description": "Prevent static asset collection.",
|
||||||
|
"generator": "secret"
|
||||||
|
},
|
||||||
"DJANGO_SETTINGS_MODULE": {
|
"DJANGO_SETTINGS_MODULE": {
|
||||||
"description": "A prebuilt configuration for Heroku.",
|
"description": "A prebuilt configuration for Heroku.",
|
||||||
"value": "babybuddy.settings.heroku"
|
"value": "babybuddy.settings.heroku"
|
||||||
|
@ -26,16 +30,15 @@
|
||||||
"SECRET_KEY": {
|
"SECRET_KEY": {
|
||||||
"description": "Used for the auth system.",
|
"description": "Used for the auth system.",
|
||||||
"generator": "secret"
|
"generator": "secret"
|
||||||
},
|
|
||||||
"DISABLE_COLLECTSTATIC": {
|
|
||||||
"description": "Prevent static asset collection.",
|
|
||||||
"value": "1"
|
|
||||||
},
|
|
||||||
"TIME_ZONE": {
|
|
||||||
"description": "Sets the instance default time zone.",
|
|
||||||
"value": "UTC"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"environments": {
|
||||||
|
"review": {
|
||||||
|
"scripts": {
|
||||||
|
"postdeploy": "python manage.py migrate && python manage.py createcachetable && python manage.py reset --no-input"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postdeploy": "python manage.py migrate && python manage.py createcachetable"
|
"postdeploy": "python manage.py migrate && python manage.py createcachetable"
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
""" # noqa
|
""" # noqa
|
||||||
|
|
||||||
__title__ = "Baby Buddy"
|
__title__ = "Baby Buddy"
|
||||||
__version__ = "1.9.3"
|
__version__ = "1.10.1"
|
||||||
__license__ = "BSD 2-Clause"
|
__license__ = "BSD 2-Clause"
|
||||||
|
|
||||||
VERSION = __version__
|
VERSION = __version__
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
!base.py
|
!base.py
|
||||||
!ci.py
|
!ci.py
|
||||||
!development.py
|
!development.py
|
||||||
|
!gitpod.py
|
||||||
!heroku.py
|
!heroku.py
|
||||||
!production.example.py
|
!production.example.py
|
||||||
!test.py
|
!test.py
|
||||||
|
|
|
@ -20,7 +20,7 @@ DEBUG = bool(strtobool(os.environ.get("DEBUG") or "False"))
|
||||||
|
|
||||||
|
|
||||||
# Applications
|
# Applications
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/applications/
|
# https://docs.djangoproject.com/en/4.0/ref/applications/
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"api",
|
"api",
|
||||||
|
@ -47,7 +47,7 @@ INSTALLED_APPS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
# Middleware
|
# Middleware
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/middleware/
|
# https://docs.djangoproject.com/en/4.0/ref/middleware/
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.security.SecurityMiddleware",
|
"django.middleware.security.SecurityMiddleware",
|
||||||
|
@ -67,18 +67,18 @@ MIDDLEWARE = [
|
||||||
|
|
||||||
|
|
||||||
# URL dispatcher
|
# URL dispatcher
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/http/urls/
|
# https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
||||||
|
|
||||||
ROOT_URLCONF = "babybuddy.urls"
|
ROOT_URLCONF = "babybuddy.urls"
|
||||||
|
|
||||||
|
|
||||||
# Templates
|
# Templates
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#std:setting-TEMPLATES
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-TEMPLATES
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||||
"DIRS": [],
|
"DIRS": ["babybuddy/templates/error"],
|
||||||
"APP_DIRS": True,
|
"APP_DIRS": True,
|
||||||
"OPTIONS": {
|
"OPTIONS": {
|
||||||
"context_processors": [
|
"context_processors": [
|
||||||
|
@ -93,7 +93,7 @@ TEMPLATES = [
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
"ENGINE": os.getenv("DB_ENGINE") or "django.db.backends.sqlite3",
|
"ENGINE": os.getenv("DB_ENGINE") or "django.db.backends.sqlite3",
|
||||||
|
@ -114,7 +114,7 @@ DATABASES = {"default": config}
|
||||||
|
|
||||||
|
|
||||||
# Cache
|
# Cache
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/cache/
|
# https://docs.djangoproject.com/en/4.0/topics/cache/
|
||||||
|
|
||||||
CACHES = {
|
CACHES = {
|
||||||
"default": {
|
"default": {
|
||||||
|
@ -125,13 +125,13 @@ CACHES = {
|
||||||
|
|
||||||
|
|
||||||
# WGSI
|
# WGSI
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
|
# https://docs.djangoproject.com/en/4.0/howto/deployment/wsgi/
|
||||||
|
|
||||||
WSGI_APPLICATION = "babybuddy.wsgi.application"
|
WSGI_APPLICATION = "babybuddy.wsgi.application"
|
||||||
|
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/auth/default/
|
# https://docs.djangoproject.com/en/4.0/topics/auth/default/
|
||||||
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
"axes.backends.AxesBackend",
|
"axes.backends.AxesBackend",
|
||||||
|
@ -146,14 +146,14 @@ LOGOUT_REDIRECT_URL = "babybuddy:login"
|
||||||
|
|
||||||
|
|
||||||
# Timezone
|
# Timezone
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/timezones/
|
# https://docs.djangoproject.com/en/4.0/topics/i18n/timezones/
|
||||||
|
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
TIME_ZONE = os.environ.get("TIME_ZONE") or "UTC"
|
TIME_ZONE = os.environ.get("TIME_ZONE") or "UTC"
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
# https://docs.djangoproject.com/en/4.0/topics/i18n/
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
|
@ -164,9 +164,10 @@ LOCALE_PATHS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
LANGUAGES = [
|
LANGUAGES = [
|
||||||
|
("zh-hans", _("Chinese (simplified)")),
|
||||||
|
("nl", _("Dutch")),
|
||||||
("en-US", _("English (US)")),
|
("en-US", _("English (US)")),
|
||||||
("en-GB", _("English (UK)")),
|
("en-GB", _("English (UK)")),
|
||||||
("nl", _("Dutch")),
|
|
||||||
("fr", _("French")),
|
("fr", _("French")),
|
||||||
("fi", _("Finnish")),
|
("fi", _("Finnish")),
|
||||||
("de", _("German")),
|
("de", _("German")),
|
||||||
|
@ -180,7 +181,7 @@ LANGUAGES = [
|
||||||
|
|
||||||
|
|
||||||
# Format localization
|
# Format localization
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/formatting/
|
# https://docs.djangoproject.com/en/4.0/topics/i18n/formatting/
|
||||||
|
|
||||||
USE_L10N = True
|
USE_L10N = True
|
||||||
|
|
||||||
|
@ -196,7 +197,7 @@ USE_24_HOUR_TIME_FORMAT = bool(
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||||
# http://whitenoise.evans.io/en/stable/django.html
|
# http://whitenoise.evans.io/en/stable/django.html
|
||||||
|
|
||||||
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
STATICFILES_STORAGE = "whitenoise.storage.CompressedManifestStaticFilesStorage"
|
||||||
|
@ -206,7 +207,7 @@ STATICFILES_FINDERS = [
|
||||||
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
|
||||||
]
|
]
|
||||||
|
|
||||||
STATIC_URL = "static/"
|
STATIC_URL = os.path.join(os.environ.get("SUB_PATH") or "", "static/")
|
||||||
|
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
STATIC_ROOT = os.path.join(BASE_DIR, "static")
|
||||||
|
|
||||||
|
@ -214,7 +215,7 @@ WHITENOISE_ROOT = os.path.join(BASE_DIR, "static", "babybuddy", "root")
|
||||||
|
|
||||||
|
|
||||||
# Media files (User uploaded content)
|
# Media files (User uploaded content)
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/files/
|
# https://docs.djangoproject.com/en/4.0/topics/files/
|
||||||
|
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||||
|
|
||||||
|
@ -232,19 +233,24 @@ if AWS_STORAGE_BUCKET_NAME:
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
|
|
||||||
# https://docs.djangoproject.com/en/3.2/ref/settings/#secure-proxy-ssl-header
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#secure-proxy-ssl-header
|
||||||
if os.environ.get("SECURE_PROXY_SSL_HEADER"):
|
if os.environ.get("SECURE_PROXY_SSL_HEADER"):
|
||||||
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
|
||||||
|
|
||||||
# https://docs.djangoproject.com/en/3.2/topics/http/sessions/#settings
|
# https://docs.djangoproject.com/en/4.0/topics/http/sessions/#settings
|
||||||
SESSION_COOKIE_HTTPONLY = True
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
# SESSION_COOKIE_SECURE = True
|
# SESSION_COOKIE_SECURE = True
|
||||||
|
|
||||||
# https://docs.djangoproject.com/en/3.2/ref/csrf/#settings
|
# https://docs.djangoproject.com/en/4.0/ref/csrf/#settings
|
||||||
CSRF_COOKIE_HTTPONLY = True
|
CSRF_COOKIE_HTTPONLY = True
|
||||||
# CSRF_COOKIE_SECURE = True
|
# CSRF_COOKIE_SECURE = True
|
||||||
|
CSRF_FAILURE_VIEW = "babybuddy.views.csrf_failure"
|
||||||
|
CSRF_TRUSTED_ORIGINS = list(
|
||||||
|
filter(None, os.environ.get("CSRF_TRUSTED_ORIGINS", "").split(","))
|
||||||
|
)
|
||||||
|
|
||||||
# https://docs.djangoproject.com/en/3.2/topics/auth/passwords/
|
|
||||||
|
# https://docs.djangoproject.com/en/4.0/topics/auth/passwords/
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||||
|
@ -301,12 +307,12 @@ AXES_FAILURE_LIMIT = 5
|
||||||
|
|
||||||
# Session configuration
|
# Session configuration
|
||||||
# Used by RollingSessionMiddleware to determine how often to reset the session.
|
# Used by RollingSessionMiddleware to determine how often to reset the session.
|
||||||
# See https://docs.djangoproject.com/en/3.0/topics/http/sessions/
|
# See https://docs.djangoproject.com/en/4.0/topics/http/sessions/
|
||||||
|
|
||||||
ROLLING_SESSION_REFRESH = 86400
|
ROLLING_SESSION_REFRESH = 86400
|
||||||
|
|
||||||
# Set default auto field for models.
|
# Set default auto field for models.
|
||||||
# See https://docs.djangoproject.com/en/3.2/releases/3.2/#customizing-type-of-auto-created-primary-keys
|
# See https://docs.djangoproject.com/en/4.0/releases/3.2/#customizing-type-of-auto-created-primary-keys
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,6 @@ SECRET_KEY = "CISECRETKEYIGUESS"
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||||
|
|
||||||
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
|
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.StaticFilesStorage"
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
from .base import *
|
from .base import *
|
||||||
|
|
||||||
# Quick-start development settings - unsuitable for production
|
# Quick-start development settings - unsuitable for production
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
|
# https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/
|
||||||
|
|
||||||
SECRET_KEY = "CHANGE ME"
|
SECRET_KEY = os.environ.get("SECRET_KEY") or "DEVELOPMENT!!"
|
||||||
DEBUG = True
|
DEBUG = bool(strtobool(os.environ.get("DEBUG") or "True"))
|
||||||
|
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
# https://docs.djangoproject.com/en/4.0/howto/static-files/
|
||||||
#
|
#
|
||||||
# Comment out STATICFILES_STORAGE and uncomment DEBUG = False to test with
|
# Comment out STATICFILES_STORAGE and uncomment DEBUG = False to test with
|
||||||
# production static files.
|
# production static files.
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
from .development import *
|
||||||
|
|
||||||
|
# CSRF configuration
|
||||||
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS
|
||||||
|
# https://www.gitpod.io/docs/environment-variables/#default-environment-variables
|
||||||
|
|
||||||
|
CSRF_TRUSTED_ORIGINS = [
|
||||||
|
os.environ.get("GITPOD_WORKSPACE_URL").replace("https://", "https://8000-")
|
||||||
|
]
|
|
@ -11,13 +11,13 @@ BABY_BUDDY["ALLOW_UPLOADS"] = bool(
|
||||||
|
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {"default": dj_database_url.config(conn_max_age=500)}
|
DATABASES = {"default": dj_database_url.config(conn_max_age=500)}
|
||||||
|
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/email/
|
# https://docs.djangoproject.com/en/4.0/topics/email/
|
||||||
# https://devcenter.heroku.com/articles/sendgrid#python
|
# https://devcenter.heroku.com/articles/sendgrid#python
|
||||||
|
|
||||||
SENDGRID_USERNAME = os.environ.get("SENDGRID_USERNAME", None) # noqa: F405
|
SENDGRID_USERNAME = os.environ.get("SENDGRID_USERNAME", None) # noqa: F405
|
||||||
|
|
|
@ -8,7 +8,7 @@ SECRET_KEY = ""
|
||||||
ALLOWED_HOSTS = [""]
|
ALLOWED_HOSTS = [""]
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases
|
||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": {
|
||||||
|
@ -18,7 +18,7 @@ DATABASES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Media files
|
# Media files
|
||||||
# https://docs.djangoproject.com/en/3.0/topics/files/
|
# https://docs.djangoproject.com/en/4.0/topics/files/
|
||||||
|
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media")
|
MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media")
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ MEDIA_ROOT = os.path.join(BASE_DIR, "../data/media")
|
||||||
# After setting up SSL, uncomment the settings below for enhanced security of
|
# After setting up SSL, uncomment the settings below for enhanced security of
|
||||||
# application cookies.
|
# application cookies.
|
||||||
#
|
#
|
||||||
# See https://docs.djangoproject.com/en/3.2/topics/http/sessions/#settings
|
# See https://docs.djangoproject.com/en/4.0/topics/http/sessions/#settings
|
||||||
# See https://docs.djangoproject.com/en/3.2/ref/csrf/#settings
|
# See https://docs.djangoproject.com/en/4.0/ref/csrf/#settings
|
||||||
|
|
||||||
# SESSION_COOKIE_SECURE = True
|
# SESSION_COOKIE_SECURE = True
|
||||||
# CSRF_COOKIE_SECURE = True
|
# CSRF_COOKIE_SECURE = True
|
||||||
|
|
|
@ -3,8 +3,8 @@ from .base import *
|
||||||
SECRET_KEY = "TESTS"
|
SECRET_KEY = "TESTS"
|
||||||
|
|
||||||
# Password hasher configuration
|
# Password hasher configuration
|
||||||
# See https://docs.djangoproject.com/en/3.2/ref/settings/#password-hashers
|
# See https://docs.djangoproject.com/en/4.0/ref/settings/#password-hashers
|
||||||
# See https://docs.djangoproject.com/en/3.2/topics/testing/overview/#password-hashing
|
# See https://docs.djangoproject.com/en/4.0/topics/testing/overview/#password-hashing
|
||||||
|
|
||||||
PASSWORD_HASHERS = [
|
PASSWORD_HASHERS = [
|
||||||
"django.contrib.auth.hashers.MD5PasswordHasher",
|
"django.contrib.auth.hashers.MD5PasswordHasher",
|
||||||
|
|
|
@ -29,6 +29,7 @@ BabyBuddy.DatetimePicker = function ($, moment) {
|
||||||
var defaultOptions = {
|
var defaultOptions = {
|
||||||
buttons: { showToday: true, showClose: true },
|
buttons: { showToday: true, showClose: true },
|
||||||
defaultDate: 'now',
|
defaultDate: 'now',
|
||||||
|
focusOnShow: false,
|
||||||
format: 'L LT',
|
format: 'L LT',
|
||||||
ignoreReadonly: true,
|
ignoreReadonly: true,
|
||||||
locale: moment.locale(),
|
locale: moment.locale(),
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 7.7 KiB |
|
@ -0,0 +1,26 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 26.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 334.8 305.4" style="enable-background:new 0 0 334.8 305.4;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#37ABE9;}
|
||||||
|
</style>
|
||||||
|
<g transform="matrix(1, 0, 0, 1, -910.945007, -2957.187256)">
|
||||||
|
<path class="st0" d="M1036.6,3076.7c0,6.4-3.8,11.5-8.4,11.5c-4.6,0-8.4-5.2-8.4-11.5c0-6.4,3.8-11.5,8.4-11.5
|
||||||
|
C1032.8,3065.2,1036.6,3070.4,1036.6,3076.7z"/>
|
||||||
|
<path class="st0" d="M1136.8,3076.7c0,6.4-3.8,11.5-8.4,11.5c-4.6,0-8.4-5.2-8.4-11.5c0-6.4,3.8-11.5,8.4-11.5
|
||||||
|
C1133.1,3065.2,1136.8,3070.4,1136.8,3076.7z"/>
|
||||||
|
</g>
|
||||||
|
<path class="st0" d="M305.9,138c-1.1,0-2.9,0.4-3.3-1.1c-10.7-49.5-48-87.6-95.1-103.1c-5-25.3-34.8-47.7-55.8-23.9
|
||||||
|
c-14.2,15.3,5.3,42.2,20.6,25.9c3.3-4.6,0-11.9-6-11.9c-3.3,0-4.6,3.3-6,5.3c-2.7,3.3-5.3-2.7-5.3-5.3c0-3.3,0.7-7.3,3.3-10
|
||||||
|
c27.3-21.4,48.7,24.4,17.2,37.8c-15.3,6-31.2-3.3-39.5-15.4c-2-3-3.3-3.2-4.4-2.9C82.3,45.5,43,86.9,32.1,136.9
|
||||||
|
c-0.3,1.5-2.1,1.1-3.3,1.1c-38.9,0.8-37.4,59.2,1.7,57.7C47,256.5,98.9,306,167.4,305.4c68.4,0.6,120.4-48.9,136.8-109.7
|
||||||
|
C343.1,197.2,345,138.9,305.9,138z M217.5,87.5c30.3,0,30.3,64,0,64C187.1,151.5,187.1,87.6,217.5,87.5z M107.2,185.9
|
||||||
|
c-1.2,11.6-19.1,11.3-19.9-0.4c-0.4-4.4,1.2-8,3.5-12.8c2.8-5.7,4.6-10.9,6-16.7C93.8,171.1,107.6,174.7,107.2,185.9z M94,119.5
|
||||||
|
c0.6-42.1,45.9-42.1,46.5,0C139.9,161.6,94.6,161.6,94,119.5z M202.7,254.2l-71.5,0.2c-0.9,0-1.5-0.7-1.5-1.6
|
||||||
|
c1.9-44.8,72.9-45.8,74.5-0.2C204.2,253.4,203.5,254.2,202.7,254.2z M254.5,190.8c-0.7,1.2-0.6,8.2-3.2,7.4c0,0-69.3-15.9-69.3-15.9
|
||||||
|
c-0.8-0.3-6,3.8-5.8,4.8c0,0,0.6,13.2,0.6,13.2c0,0.9-0.6,1.6-1.5,1.6h-16.6c-0.9,0-1.5-0.7-1.5-1.6c0,0,0.6-13.2,0.6-13.2
|
||||||
|
c-15.7-14.9,0.2-10.4-22.6-15.5c-0.8-0.2-1.3-1-1.1-1.9l4.4-16c0.2-0.8,1.1-1.3,1.9-1l12.3,4c0.4,0.6,6.9-3.9,6.5-4.6
|
||||||
|
c0,0,3-69.7,3-69.7c0-0.8,0.7-1.4,1.5-1.4c1.4,0.4,8-1.1,8,1.4c0.8,1.3,1.6,70.5,3.8,71c5,2.6,8.6,7.3,9.8,12.9
|
||||||
|
c-0.1,2.3,67.2,21.7,68.2,22.8C254.3,189.3,254.7,190.1,254.5,190.8z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends 'error/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}400 {% trans "Bad Request" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>400 {% trans "Bad Request" %}</h1>
|
||||||
|
{% endblock %}
|
|
@ -1,13 +1,10 @@
|
||||||
{% extends 'babybuddy/page.html' %}
|
{% extends 'error/base.html' %}
|
||||||
{% load i18n widget_tweaks %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}403 {% trans "Permission Denied" %}{% endblock %}
|
{% block title %}403 {% trans "Permission Denied" %}{% endblock %}
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
|
||||||
<li class="breadcrumb-item active" aria-current="page">{% trans "Permission Denied" %}</li>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<h1>403 {% trans "Permission Denied" %}</h1>
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
{% blocktrans trimmed %}
|
{% blocktrans trimmed %}
|
||||||
You do not have permission to access this resource. Contact a site
|
You do not have permission to access this resource. Contact a site
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% extends 'error/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}403 {{ title }}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumb_nav %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>403: {{ title }}</h1>
|
||||||
|
<div class="alert alert-danger" role="alert">
|
||||||
|
{{ main }} {{ reason }}
|
||||||
|
</div>
|
||||||
|
<div class="jumbotron">
|
||||||
|
<h2>{% trans "How to Fix" %}</h2>
|
||||||
|
{% blocktrans trimmed with origin=origin %}
|
||||||
|
Add <samp>{{ origin }}</samp> to the <code>CSRF_TRUSTED_ORIGINS</code>
|
||||||
|
environment variable. If multiple origins are required separate
|
||||||
|
with commas.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{% extends 'error/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}404 {% trans "Page Not Found" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>404 {% trans "Page Not Found" %}</h1>
|
||||||
|
<div>
|
||||||
|
{% blocktrans trimmed with request_path=request_path %}
|
||||||
|
The path <code>{{ request_path }}</code> does not exist.
|
||||||
|
{% endblocktrans %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% extends 'error/base.html' %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block title %}500 {% trans "Server Error" %}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>500 {% trans "Server Error" %}</h1>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{% extends 'babybuddy/base.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
{% block breadcrumb_nav %}{% endblock %}
|
||||||
|
|
||||||
|
{% block page %}
|
||||||
|
<div class="container mt-2 mt-lg-4">
|
||||||
|
<div class="row justify-content-md-center">
|
||||||
|
<div class="col-lg-12 mb-4">
|
||||||
|
<div class="d-sm-flex">
|
||||||
|
<img class="d-inline-block align-self-top mr-2 mt-2 text-center" src="{% static "babybuddy/logo/logo-sad.png" %}" width="65" height="65">
|
||||||
|
<div class="p-2 flex-grow-1">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
<a href="{% url "babybuddy:root-router" %}" class="btn btn-outline-primary mt-3">{% trans "Return to Baby Buddy" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -186,3 +186,21 @@ class FormsTestCase(TestCase):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self.user.settings.dashboard_hide_age, datetime.timedelta(days=1)
|
self.user.settings.dashboard_hide_age, datetime.timedelta(days=1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_csrf_error_handling(self):
|
||||||
|
c = HttpClient(enforce_csrf_checks=True)
|
||||||
|
c.login(**self.credentials)
|
||||||
|
|
||||||
|
# Add a CSRF cookie to the client by making a request with the logout form.
|
||||||
|
c.get("/", follow=True)
|
||||||
|
|
||||||
|
# Send POST request with an invalid Origin.
|
||||||
|
headers = {"HTTP_ORIGIN": "https://www.example.com"}
|
||||||
|
data = {"csrfmiddlewaretoken": c.cookies["csrftoken"].value}
|
||||||
|
response = c.post("/logout/", data=data, follow=True, **headers)
|
||||||
|
|
||||||
|
# Assert response contains Baby Buddy's custom 403 handler text.
|
||||||
|
self.assertContains(response, "How to Fix", status_code=403)
|
||||||
|
|
||||||
|
response = c.post("/logout/", data=data, follow=True)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
|
@ -5,12 +5,16 @@ from django.contrib.auth.forms import PasswordChangeForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.views import LogoutView as LogoutViewBase
|
from django.contrib.auth.views import LogoutView as LogoutViewBase
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
|
from django.http import HttpResponseForbidden
|
||||||
|
from django.middleware.csrf import REASON_BAD_ORIGIN
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
|
from django.template import loader
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import translation
|
from django.utils import translation
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.text import format_lazy
|
from django.utils.text import format_lazy
|
||||||
from django.utils.translation import gettext as _, gettext_lazy
|
from django.utils.translation import gettext as _, gettext_lazy
|
||||||
|
from django.views import csrf
|
||||||
from django.views.decorators.cache import never_cache
|
from django.views.decorators.cache import never_cache
|
||||||
from django.views.decorators.csrf import csrf_protect
|
from django.views.decorators.csrf import csrf_protect
|
||||||
from django.views.decorators.http import require_POST
|
from django.views.decorators.http import require_POST
|
||||||
|
@ -25,6 +29,28 @@ from babybuddy import forms
|
||||||
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin, StaffOnlyMixin
|
from babybuddy.mixins import LoginRequiredMixin, PermissionRequiredMixin, StaffOnlyMixin
|
||||||
|
|
||||||
|
|
||||||
|
def csrf_failure(request, reason=""):
|
||||||
|
"""
|
||||||
|
Overrides the 403 CSRF failure template for bad origins in order to provide more
|
||||||
|
userful information about how to resolve the issue.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (
|
||||||
|
"HTTP_ORIGIN" in request.META
|
||||||
|
and reason == REASON_BAD_ORIGIN % request.META["HTTP_ORIGIN"]
|
||||||
|
):
|
||||||
|
context = {
|
||||||
|
"title": _("Forbidden"),
|
||||||
|
"main": _("CSRF verification failed. Request aborted."),
|
||||||
|
"reason": reason,
|
||||||
|
"origin": request.META["HTTP_ORIGIN"],
|
||||||
|
}
|
||||||
|
template = loader.get_template("error/403_csrf_bad_origin.html")
|
||||||
|
return HttpResponseForbidden(template.render(context), content_type="text/html")
|
||||||
|
|
||||||
|
return csrf.csrf_failure(request, reason, "403_csrf.html")
|
||||||
|
|
||||||
|
|
||||||
class RootRouter(LoginRequiredMixin, RedirectView):
|
class RootRouter(LoginRequiredMixin, RedirectView):
|
||||||
"""
|
"""
|
||||||
Redirects to the site dashboard.
|
Redirects to the site dashboard.
|
||||||
|
|
|
@ -9,32 +9,11 @@
|
||||||
<a href="{% url 'core:child' object.slug %}" class="btn" title="{% trans "Timeline" %}">
|
<a href="{% url 'core:child' object.slug %}" class="btn" title="{% trans "Timeline" %}">
|
||||||
<i class="icon-timeline" aria-hidden="true"></i>
|
<i class="icon-timeline" aria-hidden="true"></i>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="{% url 'reports:report-list' object.slug %}" class="btn" title="{% trans "Reports" %}">
|
||||||
|
<i class="icon-graph" aria-hidden="true"></i>
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="btn-group" role="group">
|
|
||||||
<button id="reports-dropdown"
|
|
||||||
class="btn dropdown-toggle"
|
|
||||||
title="{% trans "Reports" %}"
|
|
||||||
type="button"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false"><i class="icon-graph" aria-hidden="true"></i></button>
|
|
||||||
<div class="dropdown-menu" aria-labelledby="reports-dropdown">
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-amounts-child' object.slug %}">{% trans "Diaper Change Amounts" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-types-child' object.slug %}">{% trans "Diaper Change Types" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}">{% trans "Diaper Lifetimes" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-feeding-amounts-child' object.slug %}">{% trans "Feeding Amounts" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-feeding-duration-child' object.slug %}">{% trans "Feeding Durations (Average)" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-sleep-pattern-child' object.slug %}">{% trans "Sleep Pattern" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-sleep-totals-child' object.slug %}">{% trans "Sleep Totals" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-tummytime-duration-child' object.slug %}">{% trans "Tummy Time Durations (Sum)" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-weight-weight-child' object.slug %}">{% trans "Weight" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-height-height-child' object.slug %}">{% trans "Height" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-head-circumference-head-circumference-child' object.slug %}">{% trans "Head Circumference" %}</a>
|
|
||||||
<a class="dropdown-item" href="{% url 'reports:report-bmi-bmi-child' object.slug %}">{% trans "BMI" %}</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% if perms.core.change_child %}
|
{% if perms.core.change_child %}
|
||||||
<a class="btn d-none d-md-inline-block"
|
<a class="btn d-none d-md-inline-block"
|
||||||
href="{% url 'core:child-update' object.slug %}"
|
href="{% url 'core:child-update' object.slug %}"
|
||||||
|
|
|
@ -44,9 +44,9 @@ If no `Authorization` header set, or the key is not valid the API will return
|
||||||
## Schema
|
## Schema
|
||||||
|
|
||||||
The API schema in [OpenAPI format](https://swagger.io/specification/) can be
|
The API schema in [OpenAPI format](https://swagger.io/specification/) can be
|
||||||
found in the [`openapi-schema.yml`](/openapi-schema.yml) file in the project
|
found in the [`openapi-schema.yml`](https://github.com/babybuddy/babybuddy/tree/master/openapi-schema.yml)
|
||||||
root. A live version is also available at the `/api/schema` path of a running
|
file in the project root. A live version is also available at the `/api/schema` path of
|
||||||
instance.
|
a running instance.
|
||||||
|
|
||||||
## `GET` Method
|
## `GET` Method
|
||||||
|
|
||||||
|
|
|
@ -1,325 +0,0 @@
|
||||||
# Contributing
|
|
||||||
|
|
||||||
Baby Buddy's maintainers accept and encourage contributions via GitHub [Issues](https://github.com/babybuddy/babybuddy/issues)
|
|
||||||
and [Pull Requests](https://github.com/babybuddy/babybuddy/pulls). Maintainers
|
|
||||||
and users may also be found at [babybuddy/Lobby](https://gitter.im/babybuddy/Lobby)
|
|
||||||
on Gitter.
|
|
||||||
|
|
||||||
## Pull request process
|
|
||||||
|
|
||||||
1. Fork this repository and commit your changes.
|
|
||||||
1. Make and commit tests for any new features or major functionality changes.
|
|
||||||
1. Run `gulp lint` and `gulp test` (see [Gulp Commands](#gulp-commands)) and
|
|
||||||
ensure that all tests pass.
|
|
||||||
1. Updated static assets if necessary and commit the `/static` folder (see [`gulp updatestatic`](#updatestatic)).
|
|
||||||
1. Open a [new pull request](https://github.com/babybuddy/babybuddy/compare) against
|
|
||||||
the `master` branch.
|
|
||||||
|
|
||||||
Maintainers will review new pull requests will as soon as possible, and we will
|
|
||||||
do our best to provide feedback and support potential contributors.
|
|
||||||
|
|
||||||
## Translation
|
|
||||||
|
|
||||||
### POEditor
|
|
||||||
|
|
||||||
Baby Buddy uses [POEditor](https://poeditor.com/) for translation contributions.
|
|
||||||
Interested contributors can [join translation of Baby Buddy](https://poeditor.com/join/project/QwQqrpTIzn)
|
|
||||||
for access to a simple, web-based frontend for adding/editing translation files
|
|
||||||
to the project.
|
|
||||||
|
|
||||||
### Manual
|
|
||||||
|
|
||||||
Baby Buddy has support for translation/localization. A manual translation
|
|
||||||
process will look something like this:
|
|
||||||
|
|
||||||
1. Set up a development environment (see [Development](#development) below).
|
|
||||||
|
|
||||||
1. Run `gulp makemessages -l xx` where `xx` is a specific locale code in the
|
|
||||||
[ISO 639-1 format](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g.,
|
|
||||||
"il" for Italian or "es" for Spanish). This creates a new translation file at
|
|
||||||
`locale/xx/LC_MESSAGES/django.po`, or updates one if it exists.
|
|
||||||
|
|
||||||
1. Open the created/updated `django.po` file and update the header template
|
|
||||||
with license and contact info.
|
|
||||||
|
|
||||||
1. Start translating! Each translatable string will have a `msgid` value with
|
|
||||||
the string in English and a corresponding (empty) `msgstr` value where a
|
|
||||||
translated string can be filled in.
|
|
||||||
|
|
||||||
1. Once all strings have been translated, run `gulp compilemessages -l xx` to
|
|
||||||
compile an optimized translation file (`locale/xx/LC_MESSAGES/django.mo`).
|
|
||||||
|
|
||||||
1. To expose the new translation as a user setting, add the locale code to the
|
|
||||||
`LANGUAGES` array in the base settings file (`babybuddy/settings/base.py`).
|
|
||||||
|
|
||||||
1. Check if Plotly offers a translation (in `node_modules/plotly.js/dist/`) for
|
|
||||||
the language. If it does:
|
|
||||||
|
|
||||||
1. Add the Plotly translation file path to [`gulpfile.config.js`](/gulpfile.config.js) in
|
|
||||||
`scriptsConfig.graph`.
|
|
||||||
|
|
||||||
1. Build, collect, and commit the `/static` folder (see
|
|
||||||
[`gulp updatestatic`](#updatestatic)).
|
|
||||||
|
|
||||||
1. Check if Moment offers a translation (in `node_modules/moment/locale/`) for
|
|
||||||
the language. If it does:
|
|
||||||
|
|
||||||
1. Add the Moment translation file path to [`gulpfile.config.js`](/gulpfile.config.js) in
|
|
||||||
`scriptsConfig.vendor`.
|
|
||||||
|
|
||||||
1. Build, collect, and commit the `/static` folder (see
|
|
||||||
[`gulp updatestatic`](#updatestatic)).
|
|
||||||
|
|
||||||
1. Run the development server, log in, and update the user language to test the
|
|
||||||
newly translated strings.
|
|
||||||
|
|
||||||
Once the translation is complete, commit the new files and changes to a fork
|
|
||||||
and [create a pull request](#pull-request-process) for review.
|
|
||||||
|
|
||||||
For more information on the Django translation process, see Django's
|
|
||||||
documentation section: [Translation](https://docs.djangoproject.com/en/3.0/topics/i18n/translation/).
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Requirements
|
|
||||||
|
|
||||||
- Python 3.6+, pip, pipenv
|
|
||||||
- NVM (NodeJS 14.x and NPM 7.x)
|
|
||||||
- Gulp
|
|
||||||
- Possibly `libpq-dev`
|
|
||||||
- This is necessary if `psycopg2` can't find an appropriate prebuilt binary.
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
1. Install pipenv per [Installing pipenv](https://pipenv.pypa.io/en/latest/install/#installing-pipenv)
|
|
||||||
|
|
||||||
1. Install required Python packages, including dev packages
|
|
||||||
|
|
||||||
pipenv install --three --dev
|
|
||||||
|
|
||||||
- If this fails, install `libpq-dev` (e.g. `sudo apt install libpq-dev`) and try again.
|
|
||||||
|
|
||||||
1. Installed Node 14.x (if necessary)
|
|
||||||
|
|
||||||
nvm install 14
|
|
||||||
|
|
||||||
1. Activate Node 14.x
|
|
||||||
|
|
||||||
nvm use
|
|
||||||
|
|
||||||
1. Install Gulp CLI
|
|
||||||
|
|
||||||
npm install -g gulp-cli
|
|
||||||
|
|
||||||
1. Install required Node packages
|
|
||||||
|
|
||||||
npm install
|
|
||||||
|
|
||||||
1. Set, at least, the `DJANGO_SETTINGS_MODULE` environment variable
|
|
||||||
|
|
||||||
export DJANGO_SETTINGS_MODULE=babybuddy.settings.development
|
|
||||||
|
|
||||||
This process will differ based on the host OS. The above example is for
|
|
||||||
Linux-based systems. See [Configuration](/docs/setup/configuration.md) for other
|
|
||||||
settings and methods for defining them.
|
|
||||||
|
|
||||||
1. Migrate the database
|
|
||||||
|
|
||||||
gulp migrate
|
|
||||||
|
|
||||||
1. Create cache table
|
|
||||||
|
|
||||||
gulp createcachetable
|
|
||||||
|
|
||||||
1. Build assets and run the server
|
|
||||||
|
|
||||||
gulp
|
|
||||||
|
|
||||||
This command will also watch for file system changes to rebuild assets and
|
|
||||||
restart the server as needed.
|
|
||||||
|
|
||||||
Open [http://127.0.0.1:8000](http://127.0.0.1:8000) and log in with the default
|
|
||||||
username and password (`admin`/`admin`).
|
|
||||||
|
|
||||||
### Gulp commands
|
|
||||||
|
|
||||||
[`gulpfile.js`](/gulpfile.js) defines Baby Buddy's Gulp commands.
|
|
||||||
|
|
||||||
[`babybuddy/management/commands`](/babybuddy/management/commands) defines Baby Buddy's
|
|
||||||
management commands.
|
|
||||||
|
|
||||||
- [`gulp`](#gulp)
|
|
||||||
- [`gulp build`](#build)
|
|
||||||
- [`gulp clean`](#clean)
|
|
||||||
- [`gulp collectstatic`](#collectstatic)
|
|
||||||
- [`gulp compilemessages`](#compilemessages)
|
|
||||||
- [`gulp coverage`](#coverage)
|
|
||||||
- [`gulp createcachetable`](#createcachetable)
|
|
||||||
- [`gulp docs:build`](#docsbuild)
|
|
||||||
- [`gulp docs:deploy`](#docsdeploy)
|
|
||||||
- [`gulp docs:watch`](#docswatch)
|
|
||||||
- [`gulp extras`](#extras)
|
|
||||||
- [`gulp fake`](#fake)
|
|
||||||
- [`gulp generatescheme`](#generatescheme)
|
|
||||||
- [`gulp lint`](#lint)
|
|
||||||
- [`gulp makemessages`](#makemessages)
|
|
||||||
- [`gulp makemigrations`](#makemigrations)
|
|
||||||
- [`gulp migrate`](#migrate)
|
|
||||||
- [`gulp reset`](#reset)
|
|
||||||
- [`gulp runserver`](#runserver)
|
|
||||||
- [`gulp scripts`](#scripts)
|
|
||||||
- [`gulp styles`](#styles)
|
|
||||||
- [`gulp test`](#test)
|
|
||||||
- [`gulp updatestatic`](#updatestatic)
|
|
||||||
|
|
||||||
#### `gulp`
|
|
||||||
|
|
||||||
Executes the `build` and `watch` commands and runs Django's development server.
|
|
||||||
This command also accepts the special parameter `--ip` for setting the
|
|
||||||
server IP address. See [`gulp runserver`](#runserver) for details.
|
|
||||||
|
|
||||||
#### `build`
|
|
||||||
|
|
||||||
Creates all script, style and "extra" assets and places them in the
|
|
||||||
`babybuddy/static` folder.
|
|
||||||
|
|
||||||
#### `docs:build`
|
|
||||||
|
|
||||||
Builds the documentation site in a local directory (`site` by default).
|
|
||||||
|
|
||||||
#### `docs:deploy`
|
|
||||||
|
|
||||||
Deploys the documentation site to GitHub Pages.
|
|
||||||
|
|
||||||
#### `docs:watch`
|
|
||||||
|
|
||||||
Runs a local server for the documentation site reloading whenever documentation
|
|
||||||
sites files (in the `docs` directory) as modified.
|
|
||||||
|
|
||||||
#### `clean`
|
|
||||||
|
|
||||||
Deletes all build folders and the root `static` folder. Generally this should
|
|
||||||
be run before a `gulp build` to remove previous build files and the generated
|
|
||||||
static assets.
|
|
||||||
|
|
||||||
#### `collectstatic`
|
|
||||||
|
|
||||||
Executes Django's `collectstatic` management task. This task relies on files in
|
|
||||||
the `babybuddy/static` folder, so generally `gulp build` should be run before
|
|
||||||
this command for production deployments. Gulp also passes along
|
|
||||||
non-overlapping arguments for this command, e.g. `--no-input`.
|
|
||||||
|
|
||||||
Note: a `SECRET_KEY` value must be set for this command to work.
|
|
||||||
|
|
||||||
#### `compilemessages`
|
|
||||||
|
|
||||||
Executes Django's `compilemessages` management task. See [django-admin compilemessages](https://docs.djangoproject.com/en/3.0/ref/django-admin/#compilemessages)
|
|
||||||
for more details about other options and functionality of this command.
|
|
||||||
|
|
||||||
#### `coverage`
|
|
||||||
|
|
||||||
Create a test coverage report. See [`.coveragerc`](/.coveragerc) for default
|
|
||||||
settings information.
|
|
||||||
|
|
||||||
#### `createcachetable`
|
|
||||||
|
|
||||||
Executes Django's `createcachetable` management task. See [django-admin createcachetable](https://docs.djangoproject.com/en/3.0/ref/django-admin/#createcachetable)
|
|
||||||
for more details about other options and functionality of this command.
|
|
||||||
|
|
||||||
#### `extras`
|
|
||||||
|
|
||||||
Copies "extra" files (fonts, images and server root contents) to the build
|
|
||||||
folder.
|
|
||||||
|
|
||||||
#### `fake`
|
|
||||||
|
|
||||||
Adds some fake data to the database. By default, ``fake`` creates one child and
|
|
||||||
31 days of random data. Use the `--children` and `--days` flags to change the
|
|
||||||
default values, e.g. `gulp fake --children 5 --days 7` to generate five fake
|
|
||||||
children and seven days of data for each.
|
|
||||||
|
|
||||||
#### `generateschema`
|
|
||||||
|
|
||||||
Updates the [`openapi-schema.yml`](/openapi-schema.yml) file in the project root
|
|
||||||
based on current API functionality.
|
|
||||||
|
|
||||||
#### `lint`
|
|
||||||
|
|
||||||
Executes Python and SASS linting for all relevant source files.
|
|
||||||
|
|
||||||
#### `makemessages`
|
|
||||||
|
|
||||||
Executes Django's `makemessages` management task. See [django-admin makemessages](https://docs.djangoproject.com/en/3.0/ref/django-admin/#makemessages)
|
|
||||||
for more details about other options and functionality of this command. When
|
|
||||||
working on a single translation, the `-l` flag is useful to make message for
|
|
||||||
only that language, e.g. `gulp makemessages -l fr` to make only a French
|
|
||||||
language translation file.
|
|
||||||
|
|
||||||
#### `makemigrations`
|
|
||||||
|
|
||||||
Executes Django's `makemigrations` management task. Gulp also passes along
|
|
||||||
non-overlapping arguments for this command.
|
|
||||||
|
|
||||||
#### `migrate`
|
|
||||||
|
|
||||||
Executes Django's `migrate` management task. In addition to migrating the
|
|
||||||
database, this command creates the default `admin` user. Gulp also passes along
|
|
||||||
non-overlapping arguments for this command.
|
|
||||||
|
|
||||||
#### `reset`
|
|
||||||
|
|
||||||
Resets the database to a default state *with* one fake child and 31 days of
|
|
||||||
fake data.
|
|
||||||
|
|
||||||
#### `runserver`
|
|
||||||
|
|
||||||
Executes Django's `runserver` management task. Gulp passes non-overlapping
|
|
||||||
arguments for this command.
|
|
||||||
|
|
||||||
A special parameter, `--ip`, is also available to set the IP for the server.
|
|
||||||
E.g., execute `gulp runserver --ip 0.0.0.0:8000` to make the server available to
|
|
||||||
other devices on your local network.
|
|
||||||
|
|
||||||
#### `scripts`
|
|
||||||
|
|
||||||
Builds and combines relevant application scripts. Generally this command does
|
|
||||||
not need to be executed independently - see the `build` command.
|
|
||||||
|
|
||||||
#### `styles`
|
|
||||||
|
|
||||||
Builds and combines SASS styles in to CSS files. Generally this command does
|
|
||||||
not need to be executed independently - see the `build` command.
|
|
||||||
|
|
||||||
#### `test`
|
|
||||||
|
|
||||||
Executes Baby Buddy's suite of tests.
|
|
||||||
|
|
||||||
Gulp also passes along non-overlapping arguments for this command, however
|
|
||||||
individual tests cannot be run with this command. `python manage.py test` can be
|
|
||||||
used for individual test execution.
|
|
||||||
|
|
||||||
#### `updateglyphs`
|
|
||||||
|
|
||||||
Downloads generated glyph font files data from [Fonttello](https://fontello.com/)
|
|
||||||
based on [`config.json` file](/babybuddy/static_src/fontello/config.json). This
|
|
||||||
only needs to be run after making changes to the config file.
|
|
||||||
|
|
||||||
#### `updatestatic`
|
|
||||||
|
|
||||||
Rebuilds Baby Buddy's `/static` folder by running the following commands in
|
|
||||||
order:
|
|
||||||
|
|
||||||
- [`lint`](#lint)
|
|
||||||
- [`clean`](#clean)
|
|
||||||
- [`build`](#build)
|
|
||||||
- [`collectstatic`](#collectstatic)
|
|
||||||
|
|
||||||
Execute and commit changes made by this command whenever changing Baby Buddy's
|
|
||||||
frontend code (SASS, JS, etc.).
|
|
||||||
|
|
||||||
### Icon Font
|
|
||||||
|
|
||||||
Baby Buddy uses [Fontello](https://fontello.com/) to build a custom icon font
|
|
||||||
for icons used throughout the application. See [`babybuddy/static_src/fontello`](/babybuddy/static_src/fontello)
|
|
||||||
for further documentation about using the config file with Fontello and license
|
|
||||||
information for fonts used by this application.
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
# Development environment
|
||||||
|
|
||||||
|
[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/babybuddy/babybuddy)
|
||||||
|
|
||||||
|
Click the Gitpod badge to open a new development environment in Gitpod or use the
|
||||||
|
information and steps below to set up a local development environment for Baby Buddy.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
- Python 3.6+, pip, pipenv
|
||||||
|
- NVM (NodeJS 14.x and NPM 7.x)
|
||||||
|
- Gulp
|
||||||
|
- Possibly `libpq-dev`
|
||||||
|
- This is necessary if `psycopg2` can't find an appropriate prebuilt binary.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install pipenv per [Installing pipenv](https://pipenv.pypa.io/en/latest/install/#installing-pipenv)
|
||||||
|
|
||||||
|
1. Install required Python packages, including dev packages
|
||||||
|
|
||||||
|
pipenv install --three --dev
|
||||||
|
|
||||||
|
- If this fails, install `libpq-dev` (e.g. `sudo apt install libpq-dev`) and try again.
|
||||||
|
|
||||||
|
1. Installed Node 14.x (if necessary)
|
||||||
|
|
||||||
|
nvm install 14
|
||||||
|
|
||||||
|
1. Activate Node 14.x
|
||||||
|
|
||||||
|
nvm use
|
||||||
|
|
||||||
|
1. Install Gulp CLI
|
||||||
|
|
||||||
|
npm install -g gulp-cli
|
||||||
|
|
||||||
|
1. Install required Node packages
|
||||||
|
|
||||||
|
npm install
|
||||||
|
|
||||||
|
1. Set, at least, the `DJANGO_SETTINGS_MODULE` environment variable
|
||||||
|
|
||||||
|
export DJANGO_SETTINGS_MODULE=babybuddy.settings.development
|
||||||
|
|
||||||
|
This process will differ based on the host OS. The above example is for
|
||||||
|
Linux-based systems. See [Configuration](../setup/configuration.md) for other
|
||||||
|
settings and methods for defining them.
|
||||||
|
|
||||||
|
1. Migrate the database
|
||||||
|
|
||||||
|
gulp migrate
|
||||||
|
|
||||||
|
1. Create cache table
|
||||||
|
|
||||||
|
gulp createcachetable
|
||||||
|
|
||||||
|
1. Build assets and run the server
|
||||||
|
|
||||||
|
gulp
|
||||||
|
|
||||||
|
This command will also watch for file system changes to rebuild assets and
|
||||||
|
restart the server as needed.
|
||||||
|
|
||||||
|
Open [http://127.0.0.1:8000](http://127.0.0.1:8000) and log in with the default
|
||||||
|
username and password (`admin`/`admin`).
|
|
@ -0,0 +1,159 @@
|
||||||
|
# Gulp command reference
|
||||||
|
|
||||||
|
## Definitions
|
||||||
|
|
||||||
|
Baby Buddy's Gulp commands are defined in [`gulpfile.js`](https://github.com/babybuddy/babybuddy/tree/master/gulpfile.js).
|
||||||
|
|
||||||
|
Baby Buddy's management commands are defined in [`babybuddy/management/commands`](https://github.com/babybuddy/babybuddy/tree/master/babybuddy/management/commands).
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
### `gulp`
|
||||||
|
|
||||||
|
Executes the `build` and `watch` commands and runs Django's development server.
|
||||||
|
This command also accepts the special parameter `--ip` for setting the
|
||||||
|
server IP address. See [`gulp runserver`](#runserver) for details.
|
||||||
|
|
||||||
|
### `build`
|
||||||
|
|
||||||
|
Creates all script, style and "extra" assets and places them in the
|
||||||
|
`babybuddy/static` folder.
|
||||||
|
|
||||||
|
### `docs:build`
|
||||||
|
|
||||||
|
Builds the documentation site in a local directory (`site` by default).
|
||||||
|
|
||||||
|
### `docs:deploy`
|
||||||
|
|
||||||
|
Deploys the documentation site to GitHub Pages.
|
||||||
|
|
||||||
|
### `docs:watch`
|
||||||
|
|
||||||
|
Runs a local server for the documentation site reloading whenever documentation
|
||||||
|
sites files (in the `docs` directory) as modified.
|
||||||
|
|
||||||
|
### `clean`
|
||||||
|
|
||||||
|
Deletes all build folders and the root `static` folder. Generally this should
|
||||||
|
be run before a `gulp build` to remove previous build files and the generated
|
||||||
|
static assets.
|
||||||
|
|
||||||
|
### `collectstatic`
|
||||||
|
|
||||||
|
Executes Django's `collectstatic` management task. This task relies on files in
|
||||||
|
the `babybuddy/static` folder, so generally `gulp build` should be run before
|
||||||
|
this command for production deployments. Gulp also passes along
|
||||||
|
non-overlapping arguments for this command, e.g. `--no-input`.
|
||||||
|
|
||||||
|
Note: a `SECRET_KEY` value must be set for this command to work.
|
||||||
|
|
||||||
|
### `compilemessages`
|
||||||
|
|
||||||
|
Executes Django's `compilemessages` management task. See [django-admin compilemessages](https://docs.djangoproject.com/en/4.0/ref/django-admin/#compilemessages)
|
||||||
|
for more details about other options and functionality of this command.
|
||||||
|
|
||||||
|
### `coverage`
|
||||||
|
|
||||||
|
Create a test coverage report. See [`.coveragerc`](https://github.com/babybuddy/babybuddy/tree/master/.coveragerc)
|
||||||
|
for default settings information.
|
||||||
|
|
||||||
|
### `createcachetable`
|
||||||
|
|
||||||
|
Executes Django's `createcachetable` management task. See [django-admin createcachetable](https://docs.djangoproject.com/en/4.0/ref/django-admin/#createcachetable)
|
||||||
|
for more details about other options and functionality of this command.
|
||||||
|
|
||||||
|
### `extras`
|
||||||
|
|
||||||
|
Copies "extra" files (fonts, images and server root contents) to the build
|
||||||
|
folder.
|
||||||
|
|
||||||
|
### `fake`
|
||||||
|
|
||||||
|
Adds some fake data to the database. By default, ``fake`` creates one child and
|
||||||
|
31 days of random data. Use the `--children` and `--days` flags to change the
|
||||||
|
default values, e.g. `gulp fake --children 5 --days 7` to generate five fake
|
||||||
|
children and seven days of data for each.
|
||||||
|
|
||||||
|
### `format`
|
||||||
|
|
||||||
|
Formats Baby Buddy's code base using [black](https://github.com/psf/black). Run this
|
||||||
|
before commits to ensure linting will pass!
|
||||||
|
|
||||||
|
### `generateschema`
|
||||||
|
|
||||||
|
Updates the [`openapi-schema.yml`](https://github.com/babybuddy/babybuddy/tree/master/openapi-schema.yml)
|
||||||
|
file in the project root based on current API functionality.
|
||||||
|
|
||||||
|
### `lint`
|
||||||
|
|
||||||
|
Executes Python and SASS linting for all relevant source files.
|
||||||
|
|
||||||
|
### `makemessages`
|
||||||
|
|
||||||
|
Executes Django's `makemessages` management task. See [django-admin makemessages](https://docs.djangoproject.com/en/4.0/ref/django-admin/#makemessages)
|
||||||
|
for more details about other options and functionality of this command. When
|
||||||
|
working on a single translation, the `-l` flag is useful to make message for
|
||||||
|
only that language, e.g. `gulp makemessages -l fr` to make only a French
|
||||||
|
language translation file.
|
||||||
|
|
||||||
|
### `makemigrations`
|
||||||
|
|
||||||
|
Executes Django's `makemigrations` management task. Gulp also passes along
|
||||||
|
non-overlapping arguments for this command.
|
||||||
|
|
||||||
|
### `migrate`
|
||||||
|
|
||||||
|
Executes Django's `migrate` management task. In addition to migrating the
|
||||||
|
database, this command creates the default `admin` user. Gulp also passes along
|
||||||
|
non-overlapping arguments for this command.
|
||||||
|
|
||||||
|
### `reset`
|
||||||
|
|
||||||
|
Resets the database to a default state *with* one fake child and 31 days of
|
||||||
|
fake data.
|
||||||
|
|
||||||
|
### `runserver`
|
||||||
|
|
||||||
|
Executes Django's `runserver` management task. Gulp passes non-overlapping
|
||||||
|
arguments for this command.
|
||||||
|
|
||||||
|
A special parameter, `--ip`, is also available to set the IP for the server.
|
||||||
|
E.g., execute `gulp runserver --ip 0.0.0.0:8000` to make the server available to
|
||||||
|
other devices on your local network.
|
||||||
|
|
||||||
|
### `scripts`
|
||||||
|
|
||||||
|
Builds and combines relevant application scripts. Generally this command does
|
||||||
|
not need to be executed independently - see the `build` command.
|
||||||
|
|
||||||
|
### `styles`
|
||||||
|
|
||||||
|
Builds and combines SASS styles in to CSS files. Generally this command does
|
||||||
|
not need to be executed independently - see the `build` command.
|
||||||
|
|
||||||
|
### `test`
|
||||||
|
|
||||||
|
Executes Baby Buddy's suite of tests.
|
||||||
|
|
||||||
|
Gulp also passes along non-overlapping arguments for this command, however
|
||||||
|
individual tests cannot be run with this command. `python manage.py test` can be
|
||||||
|
used for individual test execution.
|
||||||
|
|
||||||
|
### `updateglyphs`
|
||||||
|
|
||||||
|
Downloads generated glyph font files data from [Fonttello](https://fontello.com/)
|
||||||
|
based on [`config.json` file](https://github.com/babybuddy/babybuddy/tree/master/babybuddy/static_src/fontello/config.json). This
|
||||||
|
only needs to be run after making changes to the config file.
|
||||||
|
|
||||||
|
### `updatestatic`
|
||||||
|
|
||||||
|
Rebuilds Baby Buddy's `/static` folder by running the following commands in
|
||||||
|
order:
|
||||||
|
|
||||||
|
- [`lint`](#lint)
|
||||||
|
- [`clean`](#clean)
|
||||||
|
- [`build`](#build)
|
||||||
|
- [`collectstatic`](#collectstatic)
|
||||||
|
|
||||||
|
Execute and commit changes made by this command whenever changing Baby Buddy's
|
||||||
|
frontend code (SASS, JS, etc.).
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Pull requests
|
||||||
|
|
||||||
|
Baby Buddy's maintainers accept and encourage contributions via GitHub [Issues](https://github.com/babybuddy/babybuddy/issues)
|
||||||
|
and [Pull Requests](https://github.com/babybuddy/babybuddy/pulls). Maintainers
|
||||||
|
and users may also be found at [babybuddy/Lobby](https://gitter.im/babybuddy/Lobby)
|
||||||
|
on Gitter.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
### Icon font
|
||||||
|
|
||||||
|
Baby Buddy uses [Fontello](https://fontello.com/) to build a custom icon font
|
||||||
|
for icons used throughout the application. See [`babybuddy/static_src/fontello`](https://github.com/babybuddy/babybuddy/tree/master/babybuddy/static_src/fontello)
|
||||||
|
for further documentation about using the config file with Fontello and license
|
||||||
|
information for fonts used by this application.
|
||||||
|
|
||||||
|
### Pipfile
|
||||||
|
|
||||||
|
If the [Pipfile](https://github.com/babybuddy/babybuddy/tree/master/Pipfile) is changed
|
||||||
|
the [requirements.txt](https://github.com/babybuddy/babybuddy/tree/master/requirements.txt)
|
||||||
|
must also be updated to reflect the change. This is necessary because hosting environments
|
||||||
|
do not provide adequate support for pipenv. To update the `requirements.txt` file to be in
|
||||||
|
sync with the `Pipenv` file run:
|
||||||
|
|
||||||
|
pipenv lock -r > requirements.txt
|
||||||
|
|
||||||
|
Add and commit the changes to the `requirements.txt` file.
|
||||||
|
|
||||||
|
### Static files
|
||||||
|
|
||||||
|
If static file assets (files in a `static_src` directory) are updated the production
|
||||||
|
static files (in the [`static` directory](https://github.com/babybuddy/babybuddy/tree/master/static))
|
||||||
|
must also be updated *and committed*. This is done because it prevents the need for Node
|
||||||
|
and related tooling in deployment environments. See [`gulp updatestatic`](gulp-command-reference.md#updatestatic)
|
||||||
|
for more information on how to update the static files.
|
||||||
|
|
||||||
|
### Translations
|
||||||
|
|
||||||
|
Modifying [locale files](https://github.com/babybuddy/babybuddy/tree/master/locale)
|
||||||
|
requires some extra steps. See [Translation](translation.md) for details.
|
||||||
|
|
||||||
|
## Pull request process
|
||||||
|
|
||||||
|
1. Fork this repository and commit your changes.
|
||||||
|
2. Make and commit tests for any new features or major functionality changes.
|
||||||
|
3. Run `gulp lint` and `gulp test` (see [Gulp command reference](gulp-command-reference.md))
|
||||||
|
and ensure that all tests pass.
|
||||||
|
4. Updated static assets if necessary and commit the `/static` folder (see [`gulp updatestatic`](gulp-command-reference.md#updatestatic)).
|
||||||
|
5. Open a [new pull request](https://github.com/babybuddy/babybuddy/compare) against
|
||||||
|
the `master` branch.
|
||||||
|
|
||||||
|
Maintainers will review new pull requests will as soon as possible, and we will
|
||||||
|
do our best to provide feedback and support potential contributors.
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Translation
|
||||||
|
|
||||||
|
## POEditor
|
||||||
|
|
||||||
|
Baby Buddy uses [POEditor](https://poeditor.com/) for translation contributions.
|
||||||
|
Interested contributors can [join translation of Baby Buddy](https://poeditor.com/join/project/QwQqrpTIzn)
|
||||||
|
for access to a simple, web-based frontend for adding/editing translation files
|
||||||
|
to the project.
|
||||||
|
|
||||||
|
## Manual
|
||||||
|
|
||||||
|
Baby Buddy has support for translation/localization. A manual translation
|
||||||
|
process will look something like this:
|
||||||
|
|
||||||
|
1. Set up a development environment (see [Development environment](development-environment.md)).
|
||||||
|
|
||||||
|
1. Run `gulp makemessages -l xx` where `xx` is a specific locale code in the
|
||||||
|
[ISO 639-1 format](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) (e.g.,
|
||||||
|
"il" for Italian or "es" for Spanish). This creates a new translation file at
|
||||||
|
`locale/xx/LC_MESSAGES/django.po`, or updates one if it exists.
|
||||||
|
|
||||||
|
1. Open the created/updated `django.po` file and update the header template
|
||||||
|
with license and contact info.
|
||||||
|
|
||||||
|
1. Start translating! Each translatable string will have a `msgid` value with
|
||||||
|
the string in English and a corresponding (empty) `msgstr` value where a
|
||||||
|
translated string can be filled in.
|
||||||
|
|
||||||
|
1. Once all strings have been translated, run `gulp compilemessages -l xx` to
|
||||||
|
compile an optimized translation file (`locale/xx/LC_MESSAGES/django.mo`).
|
||||||
|
|
||||||
|
1. To expose the new translation as a user setting, add the locale code to the
|
||||||
|
`LANGUAGES` array in the base settings file (`babybuddy/settings/base.py`).
|
||||||
|
|
||||||
|
1. Check if Plotly offers a translation (in `node_modules/plotly.js/dist/`) for
|
||||||
|
the language. If it does:
|
||||||
|
|
||||||
|
1. Add the Plotly translation file path to [`gulpfile.config.js`](https://github.com/babybuddy/babybuddy/tree/master/gulpfile.config.js)
|
||||||
|
in `scriptsConfig.graph`.
|
||||||
|
|
||||||
|
2. Build, collect, and commit the `/static` folder (see [`gulp updatestatic`](gulp-command-reference.md#updatestatic)).
|
||||||
|
|
||||||
|
1. Check if Moment offers a translation (in `node_modules/moment/locale/`) for
|
||||||
|
the language. If it does:
|
||||||
|
|
||||||
|
1. Add the Moment translation file path to [`gulpfile.config.js`](https://github.com/babybuddy/babybuddy/tree/master/gulpfile.config.js)
|
||||||
|
in `scriptsConfig.vendor`.
|
||||||
|
|
||||||
|
2. Build, collect, and commit the `/static` folder (see
|
||||||
|
[`gulp updatestatic`](gulp-command-reference.md#updatestatic)).
|
||||||
|
|
||||||
|
1. Run the development server, log in, and update the user language to test the
|
||||||
|
newly translated strings.
|
||||||
|
|
||||||
|
Once the translation is complete, commit the new files and changes to a fork
|
||||||
|
and [create a pull request](pull-requests.md) for review.
|
||||||
|
|
||||||
|
For more information on the Django translation process, see Django's
|
||||||
|
documentation section: [Translation](https://docs.djangoproject.com/en/4.0/topics/i18n/translation/).
|
|
@ -39,5 +39,5 @@ and formats. All rows will be checked for errors on import and any issues will
|
||||||
be reported on screen and will need to be resolved before the import can be
|
be reported on screen and will need to be resolved before the import can be
|
||||||
performed.
|
performed.
|
||||||
|
|
||||||
See the [example import files](/core/tests/import) used for tests to get an idea
|
See the [example import files](https://github.com/babybuddy/babybuddy/tree/master/core/tests/import)
|
||||||
of the expected data format.
|
used for tests to get an idea of the expected data format.
|
||||||
|
|
|
@ -5,44 +5,35 @@ Baby Buddy will check the application directory structure for an `.env` file or
|
||||||
take these variables from the system environment. **System environment variables
|
take these variables from the system environment. **System environment variables
|
||||||
take precedence over the contents of an `.env` file.**
|
take precedence over the contents of an `.env` file.**
|
||||||
|
|
||||||
- [`ALLOWED_HOSTS`](#allowed_hosts)
|
|
||||||
- [`ALLOW_UPLOADS`](#allow_uploads)
|
|
||||||
- [`AWS_ACCESS_KEY_ID`](#aws_access_key_id)
|
|
||||||
- [`AWS_SECRET_ACCESS_KEY`](#aws_secret_access_key)
|
|
||||||
- [`AWS_STORAGE_BUCKET_NAME`](#aws_storage_bucket_name)
|
|
||||||
- [`DEBUG`](#debug)
|
|
||||||
- [`NAP_START_MAX`](#nap_start_max)
|
|
||||||
- [`NAP_START_MIN`](#nap_start_min)
|
|
||||||
- [`DB_ENGINE`](#db_engine)
|
|
||||||
- [`DB_HOST`](#db_host)
|
|
||||||
- [`DB_NAME`](#db_name)
|
|
||||||
- [`DB_PASSWORD`](#db_password)
|
|
||||||
- [`DB_PORT`](#db_port)
|
|
||||||
- [`DB_USER`](#db_user)
|
|
||||||
- [`SECRET_KEY`](#secret_key)
|
|
||||||
- [`SECURE_PROXY_SSL_HEADER`](#secure_proxy_ssl_header)
|
|
||||||
- [`TIME_ZONE`](#time_zone)
|
|
||||||
- [`USE_24_HOUR_TIME_FORMAT`](#use_24_hour_time_format)
|
|
||||||
|
|
||||||
## `ALLOWED_HOSTS`
|
## `ALLOWED_HOSTS`
|
||||||
|
|
||||||
*Default: * (any)*
|
*Default:* `*` (any host)
|
||||||
|
|
||||||
Set this variable to a single host or comma-separated list of hosts without spaces.
|
Set this variable to a single host or comma-separated list of hosts without spaces.
|
||||||
This should *always* be set to a specific host or hosts in production deployments.
|
This should *always* be set to a specific host or hosts in production deployments.
|
||||||
|
|
||||||
See also: [Django's documentation on the ALLOWED_HOSTS setting](https://docs.djangoproject.com/en/3.0/ref/settings/#allowed-hosts)
|
Do not include schemes ("http" or "https") with this setting.
|
||||||
|
|
||||||
|
**Example value**
|
||||||
|
|
||||||
|
baby.example.test,baby.example2.test
|
||||||
|
|
||||||
|
**See also**
|
||||||
|
|
||||||
|
- [Django's documentation on the ALLOWED_HOSTS setting](https://docs.djangoproject.com/en/4.0/ref/settings/#allowed-hosts)
|
||||||
|
- [`CSRF_TRUSTED_ORIGINS`](#csrf_trusted_origins)
|
||||||
|
- [`SECURE_PROXY_SSL_HEADER`](#secure_proxy_ssl_header)
|
||||||
|
|
||||||
## `ALLOW_UPLOADS`
|
## `ALLOW_UPLOADS`
|
||||||
|
|
||||||
*Default: True*
|
*Default:* `True`
|
||||||
|
|
||||||
Whether to allow uploads (e.g., of Child photos). For some deployments (Heroku)
|
Whether to allow uploads (e.g., of Child photos). For some deployments (Heroku)
|
||||||
this setting will default to False due to the lack of available persistent storage.
|
this setting will default to False due to the lack of available persistent storage.
|
||||||
|
|
||||||
## `AWS_ACCESS_KEY_ID`
|
## `AWS_ACCESS_KEY_ID`
|
||||||
|
|
||||||
*Default: None*
|
*Default:* `None`
|
||||||
|
|
||||||
Required to access your AWS S3 bucket, should be uniquely generated per bucket
|
Required to access your AWS S3 bucket, should be uniquely generated per bucket
|
||||||
for security.
|
for security.
|
||||||
|
@ -51,7 +42,7 @@ See also: [`AWS_STORAGE_BUCKET_NAME`](#aws_storage_bucket_name)
|
||||||
|
|
||||||
## `AWS_SECRET_ACCESS_KEY`
|
## `AWS_SECRET_ACCESS_KEY`
|
||||||
|
|
||||||
*Default: None*
|
*Default:* `None`
|
||||||
|
|
||||||
Required to access your AWS S3 bucket, should be uniquely generated per bucket
|
Required to access your AWS S3 bucket, should be uniquely generated per bucket
|
||||||
for security.
|
for security.
|
||||||
|
@ -60,116 +51,148 @@ See also: [`AWS_STORAGE_BUCKET_NAME`](#aws_storage_bucket_name)
|
||||||
|
|
||||||
## `AWS_STORAGE_BUCKET_NAME`
|
## `AWS_STORAGE_BUCKET_NAME`
|
||||||
|
|
||||||
*Default: None*
|
*Default:* `None`
|
||||||
|
|
||||||
If you would like to use AWS S3 for storage on ephemeral storage platforms like
|
If you would like to use AWS S3 for storage on ephemeral storage platforms like
|
||||||
Heroku you will need to create a bucket and add its name. See django-storages'
|
Heroku you will need to create a bucket and add its name. See django-storages'
|
||||||
[Amazon S3 documentation](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html).
|
[Amazon S3 documentation](https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html).
|
||||||
|
|
||||||
## `DEBUG`
|
## `CSRF_TRUSTED_ORIGINS`
|
||||||
|
|
||||||
*Default: False*
|
*Default:* `None`
|
||||||
|
|
||||||
When in debug mode, Baby Buddy will print much more detailed error information
|
If Baby Buddy is behind a proxy, you may need add all possible origins to this setting
|
||||||
for exceptions. This setting should be *False* in production deployments.
|
for form submission to work correctly. Separate multiple origins with commas.
|
||||||
|
|
||||||
See also [Django's documentation on the DEBUG setting](https://docs.djangoproject.com/en/3.0/ref/settings/#debug).
|
Each entry must contain both the scheme (http, https) and fully-qualified domain name.
|
||||||
|
|
||||||
## `NAP_START_MAX`
|
**Example value**
|
||||||
|
|
||||||
*Default: 18:00*
|
https://baby.example.test,http://baby.example2.test,http://babybudy
|
||||||
|
|
||||||
The maximum nap *start* time (in the instance's time zone). Expects the 24-hour
|
**See also**
|
||||||
format %H:%M.
|
|
||||||
|
|
||||||
## `NAP_START_MIN`
|
- [Django's documentation on the `CSRF_TRUSTED_ORIGINS` setting](https://docs.djangoproject.com/en/4.0/ref/settings/#std:setting-CSRF_TRUSTED_ORIGINS)
|
||||||
|
- [`ALLOWED_HOSTS`](#allowed_hosts)
|
||||||
*Default: 06:00*
|
- [`SECURE_PROXY_SSL_HEADER`](#secure_proxy_ssl_header)
|
||||||
|
|
||||||
The minimum nap *start* time (in the instance's time zone). Expects the 24-hour
|
|
||||||
format %H:%M.
|
|
||||||
|
|
||||||
## `DB_ENGINE`
|
## `DB_ENGINE`
|
||||||
|
|
||||||
*Default: django.db.backends.postgresql*
|
*Default:* `django.db.backends.postgresql`
|
||||||
|
|
||||||
The database engine utilized for the deployment.
|
The database engine utilized for the deployment.
|
||||||
|
|
||||||
See also [Django's documentation on the ENGINE setting](https://docs.djangoproject.com/en/3.0/ref/settings/#engine).
|
See also [Django's documentation on the ENGINE setting](https://docs.djangoproject.com/en/4.0/ref/settings/#engine).
|
||||||
|
|
||||||
## `DB_HOST`
|
## `DB_HOST`
|
||||||
|
|
||||||
*Default: db*
|
*Default:* `db`
|
||||||
|
|
||||||
The name of the database host for the deployment.
|
The name of the database host for the deployment.
|
||||||
|
|
||||||
## `DB_NAME`
|
## `DB_NAME`
|
||||||
|
|
||||||
*Default: postgres*
|
*Default:* `postgres`
|
||||||
|
|
||||||
The name of the database table utilized for the deployment.
|
The name of the database table utilized for the deployment.
|
||||||
|
|
||||||
## `DB_PASSWORD`
|
## `DB_PASSWORD`
|
||||||
|
|
||||||
*No Default*
|
*Default:* `None`
|
||||||
|
|
||||||
The password for the database user for the deployment. In the default example,
|
The password for the database user for the deployment. In the default example,
|
||||||
this is the root PostgreSQL password.
|
this is the root PostgreSQL password.
|
||||||
|
|
||||||
## `DB_PORT`
|
## `DB_PORT`
|
||||||
|
|
||||||
*Default: 5432*
|
*Default:* `5432`
|
||||||
|
|
||||||
The listening port for the database. The default port is 5432 for PostgreSQL.
|
The listening port for the database. The default port is 5432 for PostgreSQL.
|
||||||
|
|
||||||
## `DB_USER`
|
## `DB_USER`
|
||||||
|
|
||||||
*Default: postgres*
|
*Default:* `postgres`
|
||||||
|
|
||||||
The database username utilized for the deployment.
|
The database username utilized for the deployment.
|
||||||
|
|
||||||
|
## `DEBUG`
|
||||||
|
|
||||||
|
*Default:* `False`
|
||||||
|
|
||||||
|
When in debug mode, Baby Buddy will print much more detailed error information
|
||||||
|
for exceptions. This setting should be *False* in production deployments.
|
||||||
|
|
||||||
|
See also [Django's documentation on the DEBUG setting](https://docs.djangoproject.com/en/4.0/ref/settings/#debug).
|
||||||
|
|
||||||
|
## `NAP_START_MAX`
|
||||||
|
|
||||||
|
*Default:* `18:00`
|
||||||
|
|
||||||
|
The maximum nap *start* time (in the instance's time zone). Expects the 24-hour
|
||||||
|
format %H:%M.
|
||||||
|
|
||||||
|
## `NAP_START_MIN`
|
||||||
|
|
||||||
|
*Default:* `06:00`
|
||||||
|
|
||||||
|
The minimum nap *start* time (in the instance's time zone). Expects the 24-hour
|
||||||
|
format %H:%M.
|
||||||
|
|
||||||
## `SECRET_KEY`
|
## `SECRET_KEY`
|
||||||
|
|
||||||
*Default: None*
|
*Default:* `None`
|
||||||
|
|
||||||
A random, unique string must be set as the "secret key" before Baby Buddy can
|
A random, unique string must be set as the "secret key" before Baby Buddy can
|
||||||
be deployed and run.
|
be deployed and run.
|
||||||
|
|
||||||
See also [Django's documentation on the SECRET_KEY setting](https://docs.djangoproject.com/en/3.0/ref/settings/#secret-key).
|
See also [Django's documentation on the SECRET_KEY setting](https://docs.djangoproject.com/en/4.0/ref/settings/#secret-key).
|
||||||
|
|
||||||
## `SECURE_PROXY_SSL_HEADER`
|
## `SECURE_PROXY_SSL_HEADER`
|
||||||
|
|
||||||
*Default: None*
|
*Default:* `None`
|
||||||
|
|
||||||
If Baby Buddy is behind a proxy, you may need to set this to `True` in order to
|
If Baby Buddy is behind a proxy, you may need to set this to `True` in order to
|
||||||
trust the `X-Forwarded-Proto` header that comes from your proxy, and any time
|
trust the `X-Forwarded-Proto` header that comes from your proxy, and any time
|
||||||
its value is "https". This guarantees the request is secure (i.e., it originally
|
its value is "https". This guarantees the request is secure (i.e., it originally
|
||||||
came in via HTTPS).
|
came in via HTTPS).
|
||||||
|
|
||||||
:warning: Modifying this setting can compromise Baby Buddy’s security. Ensure
|
**See also**
|
||||||
you fully understand your setup before changing it.
|
|
||||||
|
|
||||||
See also [Django's documentation on the SECURE_PROXY_SSL_HEADER setting](https://docs.djangoproject.com/en/3.0/ref/settings/#secure-proxy-ssl-header).
|
- [Django's documentation on the SECURE_PROXY_SSL_HEADER setting](https://docs.djangoproject.com/en/4.0/ref/settings/#secure-proxy-ssl-header)
|
||||||
|
- [`ALLOWED_HOSTS`](#allowed_hosts)
|
||||||
|
- [`CSRF_TRUSTED_ORIGINS`](#csrf_trusted_origins)
|
||||||
|
|
||||||
|
## `SUB_PATH`
|
||||||
|
|
||||||
|
*Default:* `None`
|
||||||
|
|
||||||
|
If Baby Buddy is hosted in a subdirectory of another server (e.g., `http://www.example.com/babybuddy`)
|
||||||
|
this must be set to the subdirectory path (e.g., `/babybuddy`) for correct handling of
|
||||||
|
application configuration.
|
||||||
|
|
||||||
|
Additional steps are required! See [Subdirectory configuration](subdirectory.md) for
|
||||||
|
details.
|
||||||
|
|
||||||
## `TIME_ZONE`
|
## `TIME_ZONE`
|
||||||
|
|
||||||
*Default: UTC*
|
*Default:* `UTC`
|
||||||
|
|
||||||
The default time zone to use for the instance. See [List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
|
The default time zone to use for the instance. This value can be overridden per use from
|
||||||
for all possible values. This value can be overridden per use from the user
|
the user settings form.
|
||||||
settings form.
|
|
||||||
|
**Example value**
|
||||||
|
|
||||||
|
America/Los_Angeles
|
||||||
|
|
||||||
|
**See also**
|
||||||
|
|
||||||
|
[List of tz database time zones](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)
|
||||||
|
|
||||||
## `USE_24_HOUR_TIME_FORMAT`
|
## `USE_24_HOUR_TIME_FORMAT`
|
||||||
|
|
||||||
*Default: False*
|
*Default:* `False`
|
||||||
|
|
||||||
Whether to force 24-hour time format for locales that do not ordinarily use it
|
Whether to force 24-hour time format for locales that do not ordinarily use it
|
||||||
(e.g. `en`). Support for this feature must be implemented on a per-locale basis.
|
(e.g. `en`). Support for this feature must be implemented on a per-locale basis.
|
||||||
See format files under [`babybuddy/formats`](/babybuddy/formats) for supported
|
See format files under [`babybuddy/formats`](https://github.com/babybuddy/babybuddy/tree/master/babybuddy/formats)
|
||||||
locales.
|
for supported locales.
|
||||||
|
|
||||||
Note: Baby Buddy interprets this value as a boolean from a string
|
|
||||||
using Python's built-in [`strtobool`](https://docs.python.org/3/distutils/apiref.html#distutils.util.strtobool)
|
|
||||||
tool. Only certain strings will work (e.g., "True" for `True` and "False" for
|
|
||||||
`False`), other unrecognized strings will cause a `ValueError` and prevent Baby
|
|
||||||
Buddy from loading.
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ The default username and password for Baby Buddy is `admin`/`admin`. For any
|
||||||
deployment, **log in and change the default password immediately**.
|
deployment, **log in and change the default password immediately**.
|
||||||
|
|
||||||
Many of Baby Buddy's configuration settings can be controlled using environment
|
Many of Baby Buddy's configuration settings can be controlled using environment
|
||||||
variables - see [Configuration](/docs/setup/configuration.md) for detailed information.
|
variables - see [Configuration](configuration.md) for detailed information.
|
||||||
|
|
||||||
## Docker
|
## Docker
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ configuration as a template to get started quickly:
|
||||||
version: "2.1"
|
version: "2.1"
|
||||||
services:
|
services:
|
||||||
babybuddy:
|
babybuddy:
|
||||||
image: ghcr.io/linuxserver/babybuddy
|
image: lscr.io/linuxserver/babybuddy
|
||||||
container_name: babybuddy
|
container_name: babybuddy
|
||||||
environment:
|
environment:
|
||||||
- TZ=UTC
|
- TZ=UTC
|
||||||
|
@ -29,9 +29,7 @@ services:
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
```
|
```
|
||||||
|
|
||||||
:warning: Baby Buddy v1.7.0 was the final version deployed to
|
See [HTTPS/SSL configuration](ssl.md) for information on how to secure Baby Buddy.
|
||||||
[babybuddy/babybuddy](https://hub.docker.com/r/babybuddy/babybuddy) on Docker Hub
|
|
||||||
Future versions of Baby Buddy will use the LSIO container.
|
|
||||||
|
|
||||||
For doing administrative work within the LSIO container, setting an environment variable may be necessary.
|
For doing administrative work within the LSIO container, setting an environment variable may be necessary.
|
||||||
For example:
|
For example:
|
||||||
|
@ -49,12 +47,12 @@ python3 /app/babybuddy/manage.py clearsessions
|
||||||
For manual deployments to Heroku without using the "deploy" button, make sure to
|
For manual deployments to Heroku without using the "deploy" button, make sure to
|
||||||
create the following settings before pushing:
|
create the following settings before pushing:
|
||||||
|
|
||||||
|
heroku config:set DISABLE_COLLECTSTATIC=1
|
||||||
heroku config:set DJANGO_SETTINGS_MODULE=babybuddy.settings.heroku
|
heroku config:set DJANGO_SETTINGS_MODULE=babybuddy.settings.heroku
|
||||||
heroku config:set SECRET_KEY=<CHANGE TO SOMETHING RANDOM>
|
heroku config:set SECRET_KEY=<CHANGE TO SOMETHING RANDOM>
|
||||||
heroku config:set DISABLE_COLLECTSTATIC=1
|
|
||||||
heroku config:set TIME_ZONE=<DESIRED DEFAULT TIMEZONE>
|
heroku config:set TIME_ZONE=<DESIRED DEFAULT TIMEZONE>
|
||||||
|
|
||||||
See [Configuration](/docs/setup/configuration.md) for other settings that can be controlled
|
See [Configuration](configuration.md) for other settings that can be controlled
|
||||||
by `heroku config:set`.
|
by `heroku config:set`.
|
||||||
|
|
||||||
After an initial push, execute the following commands:
|
After an initial push, execute the following commands:
|
||||||
|
@ -76,115 +74,124 @@ requirements are Python, a web server, an application server, and a database.
|
||||||
|
|
||||||
### Example deployment
|
### Example deployment
|
||||||
|
|
||||||
*This example assumes a 512 MB VPS instance with Ubuntu 18.04.* It uses Python 3.6+,
|
*This example assumes a 1 GB VPS instance with Ubuntu 20.04.* It uses Python 3.8,
|
||||||
nginx, uwsgi and sqlite. It should be sufficient for a few users(e.g., two parents
|
nginx, uwsgi and sqlite. It should be sufficient for a few users (e.g., two parents
|
||||||
and 1+ child).
|
and any number of children).
|
||||||
|
|
||||||
1. Install system packages
|
1. Install system packages
|
||||||
|
|
||||||
sudo apt-get install python3 python3-pip nginx uwsgi uwsgi-plugin-python3 git libopenjp2-7-dev libpq-dev
|
sudo apt-get install python3 python3-pip nginx uwsgi uwsgi-plugin-python3 git libopenjp2-7-dev libpq-dev
|
||||||
|
|
||||||
1. Default python3 to python for this session
|
2. Default python3 to python for this session
|
||||||
|
|
||||||
alias python=python3
|
alias python=python3
|
||||||
|
|
||||||
1. Install pipenv
|
3. Install pipenv
|
||||||
|
|
||||||
sudo -H pip3 install pipenv
|
sudo -H pip3 install pipenv
|
||||||
|
|
||||||
1. Set up directories and files
|
4. Set up directories and files
|
||||||
|
|
||||||
sudo mkdir /var/www/babybuddy
|
sudo mkdir /var/www/babybuddy
|
||||||
sudo chown $USER:$(id -gn $USER) /var/www/babybuddy
|
sudo chown $USER:$(id -gn $USER) /var/www/babybuddy
|
||||||
mkdir -p /var/www/babybuddy/data/media
|
mkdir -p /var/www/babybuddy/data/media
|
||||||
git clone https://github.com/babybuddy/babybuddy.git /var/www/babybuddy/public
|
git clone https://github.com/babybuddy/babybuddy.git /var/www/babybuddy/public
|
||||||
|
|
||||||
1. Move in to the application folder
|
5. Move in to the application folder
|
||||||
|
|
||||||
cd /var/www/babybuddy/public
|
cd /var/www/babybuddy/public
|
||||||
|
|
||||||
1. Set pipenv to install locally.
|
6. Initiate and enter a Python environment with Pipenv locally.
|
||||||
|
|
||||||
export PIPENV_VENV_IN_PROJECT=1
|
export PIPENV_VENV_IN_PROJECT=1
|
||||||
|
|
||||||
1. Initiate and enter the Python environment
|
|
||||||
|
|
||||||
pipenv install --three
|
pipenv install --three
|
||||||
pipenv shell
|
pipenv shell
|
||||||
|
|
||||||
1. Create a production settings file and set the ``SECRET_KEY`` and ``ALLOWED_HOSTS`` values
|
7. Create a production settings file and set the ``SECRET_KEY`` and ``ALLOWED_HOSTS`` values
|
||||||
|
|
||||||
cp babybuddy/settings/production.example.py babybuddy/settings/production.py
|
cp babybuddy/settings/production.example.py babybuddy/settings/production.py
|
||||||
editor babybuddy/settings/production.py
|
editor babybuddy/settings/production.py
|
||||||
|
|
||||||
1. Initiate the application
|
8. Initiate the application
|
||||||
|
|
||||||
export DJANGO_SETTINGS_MODULE=babybuddy.settings.production
|
export DJANGO_SETTINGS_MODULE=babybuddy.settings.production
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
python manage.py createcachetable
|
python manage.py createcachetable
|
||||||
|
|
||||||
1. Set appropriate permissions on the database and data folder
|
9. Set appropriate permissions on the database and data folder
|
||||||
|
|
||||||
sudo chown -R www-data:www-data /var/www/babybuddy/data
|
sudo chown -R www-data:www-data /var/www/babybuddy/data
|
||||||
sudo chmod 640 /var/www/babybuddy/data/db.sqlite3
|
sudo chmod 640 /var/www/babybuddy/data/db.sqlite3
|
||||||
sudo chmod 750 /var/www/babybuddy/data
|
sudo chmod 750 /var/www/babybuddy/data
|
||||||
|
|
||||||
1. Create and configure the uwsgi app
|
10. Create and configure the uwsgi app
|
||||||
|
|
||||||
sudo editor /etc/uwsgi/apps-available/babybuddy.ini
|
sudo editor /etc/uwsgi/apps-available/babybuddy.ini
|
||||||
|
|
||||||
Example config:
|
Example config:
|
||||||
|
|
||||||
[uwsgi]
|
```ini
|
||||||
plugins = python3
|
[uwsgi]
|
||||||
project = babybuddy
|
plugins = python3
|
||||||
base_dir = /var/www/babybuddy
|
project = babybuddy
|
||||||
|
base_dir = /var/www/babybuddy
|
||||||
|
|
||||||
chdir = %(base_dir)/public
|
chdir = %(base_dir)/public
|
||||||
virtualenv = %(chdir)/.venv
|
virtualenv = %(chdir)/.venv
|
||||||
module = %(project).wsgi:application
|
module = %(project).wsgi:application
|
||||||
env = DJANGO_SETTINGS_MODULE=%(project).settings.production
|
env = DJANGO_SETTINGS_MODULE=%(project).settings.production
|
||||||
master = True
|
master = True
|
||||||
vacuum = True
|
vacuum = True
|
||||||
|
```
|
||||||
|
|
||||||
See the [uWSGI documentation](http://uwsgi-docs.readthedocs.io/en/latest/)
|
See the [uWSGI documentation](http://uwsgi-docs.readthedocs.io/en/latest/)
|
||||||
for more advanced configuration details.
|
for more advanced configuration details.
|
||||||
|
|
||||||
1. Symlink config and restart uWSGI:
|
See [Subdirectory configuration](subdirectory.md) for additional configuration
|
||||||
|
required if Baby Buddy will be hosted in a subdirectory of another server.
|
||||||
|
|
||||||
sudo ln -s /etc/uwsgi/apps-available/babybuddy.ini /etc/uwsgi/apps-enabled/babybuddy.ini
|
11. Symlink config and restart uWSGI:
|
||||||
sudo service uwsgi restart
|
|
||||||
|
|
||||||
1. Create and configure the nginx server
|
sudo ln -s /etc/uwsgi/apps-available/babybuddy.ini /etc/uwsgi/apps-enabled/babybuddy.ini
|
||||||
|
sudo service uwsgi restart
|
||||||
|
|
||||||
sudo editor /etc/nginx/sites-available/babybuddy
|
12. Create and configure the nginx server
|
||||||
|
|
||||||
Example config:
|
sudo editor /etc/nginx/sites-available/babybuddy
|
||||||
|
|
||||||
upstream babybuddy {
|
Example config:
|
||||||
server unix:///var/run/uwsgi/app/babybuddy/socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
server {
|
```nginx
|
||||||
listen 80;
|
upstream babybuddy {
|
||||||
server_name babybuddy.example.com;
|
server unix:///var/run/uwsgi/app/babybuddy/socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
uwsgi_pass babybuddy;
|
||||||
|
include uwsgi_params;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /media {
|
||||||
|
alias /var/www/babybuddy/data/media;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
location / {
|
See the [nginx documentation](https://nginx.org/en/docs/) for more advanced
|
||||||
uwsgi_pass babybuddy;
|
configuration details.
|
||||||
include uwsgi_params;
|
|
||||||
}
|
|
||||||
|
|
||||||
location /media {
|
|
||||||
alias /var/www/babybuddy/data/media;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
See the [nginx documentation](https://nginx.org/en/docs/) for more advanced
|
See [Subdirectory configuration](subdirectory.md) for additional configuration
|
||||||
configuration details.
|
required if Baby Buddy will be hosted in a subdirectory of another server.
|
||||||
|
|
||||||
1. Symlink config and restart NGINX:
|
14. Symlink config and restart NGINX:
|
||||||
|
|
||||||
sudo ln -s /etc/nginx/sites-available/babybuddy /etc/nginx/sites-enabled/babybuddy
|
sudo ln -s /etc/nginx/sites-available/babybuddy /etc/nginx/sites-enabled/babybuddy
|
||||||
sudo service nginx restart
|
sudo service nginx restart
|
||||||
|
|
||||||
1. That's it (hopefully)! :tada:
|
15. That's it (hopefully)!
|
||||||
|
|
||||||
|
See [HTTPS/SSL configuration](ssl.md) for information on how to secure Baby Buddy.
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Proxy configuration
|
||||||
|
|
||||||
|
Configuring Baby Buddy to run behind a proxy may require some additional configuration
|
||||||
|
depending on the individual proxy configuration. Baby Buddy's environment variables for
|
||||||
|
configuration should allow most proxy setups to work, but it may require some testing
|
||||||
|
and tweaking of settings.
|
||||||
|
|
||||||
|
## Important configuration
|
||||||
|
|
||||||
|
### [`CSRF_TRUSTED_ORIGINS`](../configuration#csrf_trusted_origins)
|
||||||
|
|
||||||
|
[Cross Site Request Forgery](https://owasp.org/www-community/attacks/csrf) protection is
|
||||||
|
an important way to prevent malicious users from sening fake requests to Baby Buddy to
|
||||||
|
read, alter, or destroy data.
|
||||||
|
|
||||||
|
To protect against this threat Baby Buddy checks the `Origin` header of certain requests
|
||||||
|
to ensure that it matches a "trusted" origin for the application. If the origin and host
|
||||||
|
are the same CSRF will pass without any extra configuration but if the two are different
|
||||||
|
the origin must be in `CSRF_TRUSTED_ORIGINS` to pass.
|
||||||
|
|
||||||
|
For example if Baby Buddy is configured in a container with a private network and a host
|
||||||
|
`babybuddy` that is exposed publicly by a proxy (e.g., nginx) at the address
|
||||||
|
`https://baby.example.com` then form submissions from browsers will have an `Origin` of
|
||||||
|
`https://baby.example.com` that *does not match* the host `babybudy`. This will cause a
|
||||||
|
CSRF error and the request will be rejected with a `403 Forbidden` error. To support
|
||||||
|
this example configuration the environment variable `CSRF_TRUSTED_ORIGINS` should be set
|
||||||
|
to the full public address (including the scheme): `https://baby.example.com` for CSRF
|
||||||
|
protected requests to succeed.
|
||||||
|
|
||||||
|
Note: multiple origins can be added by separating origins with commas. E.g.:
|
||||||
|
|
||||||
|
CSRF_TRUSTED_ORIGINS=https://baby.example.com,https://baby.example.org
|
||||||
|
|
||||||
|
### [`SECURE_PROXY_SSL_HEADER`](../configuration#secure_proxy_ssl_header)
|
||||||
|
|
||||||
|
If Baby Buddy is configured behind a standard HTTP proxy requests will always been seen
|
||||||
|
as insecure even if the exposed public connection uses HTTPS between the client and
|
||||||
|
proxy.
|
||||||
|
|
||||||
|
To address this most proxies can be configured to pass a special header to Baby Buddy
|
||||||
|
indicating the scheme used by the original request. [`X-Forwarded-Proto`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto)
|
||||||
|
is a common standard header for this feature and it is currently the only header
|
||||||
|
supported by Baby Buddy. To use this feature the `SECURE_PROXY_SSL_HEADER` environment
|
||||||
|
variable to `True` and Baby Buddy will consider the scheme indicated by the
|
||||||
|
`X-Forwarded-Proto` header to be the scheme used for the request.
|
||||||
|
|
||||||
|
**Additional Resources**
|
||||||
|
|
||||||
|
- [Caddy Reverse Proxy Defaults](https://caddyserver.com/docs/caddyfile/directives/reverse_proxy#defaults)
|
||||||
|
- [NGINX - Using the `Forwarded` Header](https://www.nginx.com/resources/wiki/start/topics/examples/forwarded/)
|
||||||
|
(Note: NGINX treats `X-Forwarded-Proto` as legacy. See the bottom of this resource for relevant information.)
|
||||||
|
- [Redirect HTTP to HTTPS with HAProxy](https://www.haproxy.com/blog/redirect-http-to-https-with-haproxy/)
|
||||||
|
- [Traefik Routing - EntryPoints - Forwarded Headers](https://doc.traefik.io/traefik/v2.3/routing/entrypoints/#forwarded-headers)
|
|
@ -0,0 +1,194 @@
|
||||||
|
# HTTPS/SSL configuration
|
||||||
|
|
||||||
|
The example Docker and manual deployment methods do not include HTTPS/SSL by default.
|
||||||
|
Additional tools and configuration are required to add HTTPS support.
|
||||||
|
|
||||||
|
## Configuration requirements
|
||||||
|
|
||||||
|
For either approach (host- or container-based) Baby Buddy's configuration will need to
|
||||||
|
be updated to account for the proxy. For details on these settings see [Proxy configuration](proxy.md).
|
||||||
|
|
||||||
|
After configuring the proxy set the following two environment variables and then restart
|
||||||
|
necessary services:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
CSRF_TRUSTED_ORIGINS=https://babybuddy.example.com
|
||||||
|
SECURE_PROXY_SSL_HEADER=True
|
||||||
|
```
|
||||||
|
|
||||||
|
## Host-based proxy
|
||||||
|
|
||||||
|
This guide assumes Baby Buddy has been deployed to a Debian-like system with
|
||||||
|
[snapd installed](https://snapcraft.io/docs/installing-snapd) using the [example deployment](deployment.md#example-deployment)
|
||||||
|
however this approach can also be used with a Docker deployment if having the proxy
|
||||||
|
in the host is desired (otherwise see [Container-based proxy](#container-based-proxy)).
|
||||||
|
|
||||||
|
If the example deployment with uWSGI and NGINX is already used skip to [Install Certbot](#install-certbot)
|
||||||
|
and [Obtain and install certificate](#obtain-and-install-certificate).
|
||||||
|
|
||||||
|
### Install NGINX
|
||||||
|
|
||||||
|
If NGINX is not already installed on the host system install it with a package manager.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
apt-get -y install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
NGINX will be used to proxy HTTPS traffic to Baby Buddy. There are many other proxies
|
||||||
|
available for this (often with Let's Encrypt support, as well) so a different one can
|
||||||
|
be used if desired.
|
||||||
|
|
||||||
|
#### Configure NGINX
|
||||||
|
|
||||||
|
If Baby Buddy is running from Docker a new NGINX site will need to be created to send
|
||||||
|
traffic to Docker. The configuration below uses the example domain `babybuddy.example.com`
|
||||||
|
and assumes Docker has exposed Baby Buddy on port `8000` (the default configuration).
|
||||||
|
|
||||||
|
```shell
|
||||||
|
editor /etc/nginx/sites-available/babybuddy
|
||||||
|
```
|
||||||
|
|
||||||
|
Initial configuration:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server_tokens off;
|
||||||
|
access_log /var/log/nginx/babybuddy.access.log;
|
||||||
|
error_log /var/log/nginx/babybuddy.error.log;
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable the new site:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
ln -s /etc/nginx/sites-available/babybuddy /etc/nginx/sites-enabled/babybuddy
|
||||||
|
service nginx restart
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirm the site is not accessible at `http://babybuddy.example.com`. Note: Attempting
|
||||||
|
to log in will result in a CSRF error! This will be addressed after HTTPS has been
|
||||||
|
established.
|
||||||
|
|
||||||
|
### Install Certbot
|
||||||
|
|
||||||
|
This example uses [Let's Encrypt's](https://letsencrypt.org/) free service for obtaining
|
||||||
|
SSL certificates. Other methods can be used to obtain and install a certificate as
|
||||||
|
desired.
|
||||||
|
|
||||||
|
[Certbot](https://certbot.eff.org/instructions) is used to obtain free SSL certificates
|
||||||
|
from Let's Encrypt.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
snap install core && snap refresh core
|
||||||
|
snap install --classic certbot
|
||||||
|
ln -s /snap/bin/certbot /usr/bin/certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
### Obtain and install certificate
|
||||||
|
|
||||||
|
The following command will ask for an email address to register with Let's Encrypt and
|
||||||
|
then prompt a service agreement and which NGINX host to obtain a certificate for. The
|
||||||
|
certificate will be installed and activated automatically.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
certbot --nginx
|
||||||
|
[answers prompts as required]
|
||||||
|
service nginx restart
|
||||||
|
```
|
||||||
|
|
||||||
|
Certbot should have updated the NGINX site configuration (`/etc/nginx/sites-available/babybuddy`)
|
||||||
|
to look something like this:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server_tokens off;
|
||||||
|
access_log /var/log/nginx/babybuddy.access.log;
|
||||||
|
error_log /var/log/nginx/babybuddy.error.log;
|
||||||
|
|
||||||
|
server {
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://localhost:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
|
||||||
|
listen 443 ssl; # managed by Certbot
|
||||||
|
ssl_certificate /etc/letsencrypt/live/babybuddy.example.com/fullchain.pem; # managed by Certbot
|
||||||
|
ssl_certificate_key /etc/letsencrypt/live/babybuddy.example.com/privkey.pem; # managed by Certbot
|
||||||
|
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
|
||||||
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
if ($host = babybuddy.example.com) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
} # managed by Certbot
|
||||||
|
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
listen 80;
|
||||||
|
return 404; # managed by Certbot
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the certificate was obtained by some other means the configuration about should be
|
||||||
|
instructive for how to add it to the NGINX site configuration.
|
||||||
|
|
||||||
|
## Container-based proxy
|
||||||
|
|
||||||
|
If Baby Buddy is already hosted in a Docker container the proxy (NGINX) can be hosted
|
||||||
|
there as well. The configuration provided here assumes the `docker-compose.yml` example
|
||||||
|
from the [Docker deployment method](deployment.md#docker) is used.
|
||||||
|
|
||||||
|
### Add NGINX service
|
||||||
|
|
||||||
|
Add the following `services` entry to `docker-compose.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
babybuddy-nginx:
|
||||||
|
image: nginx
|
||||||
|
container_name: babybuddy-nginx
|
||||||
|
volumes:
|
||||||
|
- /path/to/appdata/nginx.conf:/etc/nginx/conf.d/default.conf
|
||||||
|
- /path/to/appdata/logs:/var/log/nginx
|
||||||
|
- /path/to/appdata/certs:/certs
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 443:443
|
||||||
|
depends_on:
|
||||||
|
- babybuddy
|
||||||
|
```
|
||||||
|
|
||||||
|
Set the contents of `/path/to/appdata/nginx.conf` to:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
listen 443 ssl;
|
||||||
|
ssl_certificate /certs/babybuddy.example.com.crt;
|
||||||
|
ssl_certificate_key /certs/babybuddy.example.com.key;
|
||||||
|
location / {
|
||||||
|
proxy_pass http://babybuddy:8000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
if ($host = babybuddy.example.com) {
|
||||||
|
return 301 https://$host$request_uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_name babybuddy.example.com;
|
||||||
|
listen 80;
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add certificates
|
||||||
|
|
||||||
|
Place certificates in `/path/to/appdata/certs` using the files name of `ssl_certificate`
|
||||||
|
and `ssl_ceritifcate_key` in the NGINX configuration.
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Subdirectory configuration
|
||||||
|
|
||||||
|
Baby Buddy's default configuration assumes deployment in to the root of a web server.
|
||||||
|
Some additional configuration is required to install Baby Buddy in a subdirectory of a
|
||||||
|
server instead (e.g., to `http://www.example.com/babybuddy`).
|
||||||
|
|
||||||
|
## Minimum version
|
||||||
|
|
||||||
|
Baby Buddy added full support for subdirectory installing in version **1.10.2**. While
|
||||||
|
it is still possible to do a subdirectory installation in older versions of Baby Buddy
|
||||||
|
it is not recommended.
|
||||||
|
|
||||||
|
## [`SUB_PATH`](../configuration#sub_path)
|
||||||
|
|
||||||
|
Set this environment variable to the subdirectory of the Baby Buddy installation. E.g.,
|
||||||
|
`SUB_PATH=/babybuddy` if the desired URL is `http://www.example.com/babybuddy`).
|
||||||
|
|
||||||
|
## uWSGI + NGINX configuration
|
||||||
|
|
||||||
|
When using uWSGI and NGINX (as in the [example deployment](deployment.md#example-deployment))
|
||||||
|
the following configurations are required.
|
||||||
|
|
||||||
|
*Assume the subdirectory `babybuddy` for configuration change examples below but this
|
||||||
|
can be anything includes multiple subdirectories (e.g., `/my/apps/babybuddy`). Other
|
||||||
|
paths used in these examples also assume a configuration based on the
|
||||||
|
[example deployment](deployment.md#example-deployment).*
|
||||||
|
|
||||||
|
### uWSGI
|
||||||
|
|
||||||
|
In the app configuration replace the `module` declaration with a `mount` declaration and
|
||||||
|
add the `manage-script-name` declaration and [`SUB_PATH`](../configuration#sub_path)
|
||||||
|
environment variable to the `[uwsgi]` configuration block.
|
||||||
|
|
||||||
|
``` diff
|
||||||
|
- module = %(project).wsgi:application
|
||||||
|
+ mount = /babybuddy=%(project).wsgi:application
|
||||||
|
+ manage-script-name = true
|
||||||
|
+ env = SUB_PATH=/babybuddy
|
||||||
|
```
|
||||||
|
|
||||||
|
### NGINX
|
||||||
|
|
||||||
|
Alter the NGINX `server` configuration to include the desired subdirectory path in the
|
||||||
|
app (Baby Buddy root) and media `location` declarations *and* add a new declaration for
|
||||||
|
the static `location`.
|
||||||
|
|
||||||
|
``` diff
|
||||||
|
- location / {
|
||||||
|
+ location /babybuddy {
|
||||||
|
```
|
||||||
|
|
||||||
|
``` diff
|
||||||
|
+ location /babybuddy/static {
|
||||||
|
+ alias /var/www/babybuddy/public/static;
|
||||||
|
+ }
|
||||||
|
```
|
||||||
|
|
||||||
|
``` diff
|
||||||
|
- location /media {
|
||||||
|
+ location /babybuddy/media {
|
||||||
|
```
|
|
@ -1,4 +1,4 @@
|
||||||
var basePath = 'babybuddy/static/babybuddy/';
|
const basePath = 'babybuddy/static/babybuddy/';
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
basePath: basePath,
|
basePath: basePath,
|
||||||
|
@ -43,6 +43,7 @@ module.exports = {
|
||||||
'node_modules/moment/locale/pt.js',
|
'node_modules/moment/locale/pt.js',
|
||||||
'node_modules/moment/locale/sv.js',
|
'node_modules/moment/locale/sv.js',
|
||||||
'node_modules/moment/locale/tr.js',
|
'node_modules/moment/locale/tr.js',
|
||||||
|
'node_modules/moment/locale/zh-cn.js',
|
||||||
'node_modules/moment-timezone/builds/moment-timezone-with-data-10-year-range.js',
|
'node_modules/moment-timezone/builds/moment-timezone-with-data-10-year-range.js',
|
||||||
'node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.js'
|
'node_modules/tempusdominus-bootstrap-4/build/js/tempusdominus-bootstrap-4.js'
|
||||||
],
|
],
|
||||||
|
@ -59,7 +60,8 @@ module.exports = {
|
||||||
'node_modules/plotly.js/dist/plotly-locale-pt-pt.js',
|
'node_modules/plotly.js/dist/plotly-locale-pt-pt.js',
|
||||||
'node_modules/plotly.js/dist/plotly-locale-sv.js',
|
'node_modules/plotly.js/dist/plotly-locale-sv.js',
|
||||||
'node_modules/plotly.js/dist/plotly-locale-tr.js',
|
'node_modules/plotly.js/dist/plotly-locale-tr.js',
|
||||||
'node_modules/plotly.js/dist/plotly-locale-uk.js'
|
'node_modules/plotly.js/dist/plotly-locale-uk.js',
|
||||||
|
'node_modules/plotly.js/dist/plotly-locale-zh-cn.js',
|
||||||
],
|
],
|
||||||
app: [
|
app: [
|
||||||
'babybuddy/static_src/js/babybuddy.js',
|
'babybuddy/static_src/js/babybuddy.js',
|
||||||
|
|
44
gulpfile.js
44
gulpfile.js
|
@ -1,17 +1,18 @@
|
||||||
var gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
|
|
||||||
var concat = require('gulp-concat');
|
const concat = require('gulp-concat');
|
||||||
var del = require('del');
|
const del = require('del');
|
||||||
var es = require('child_process').execSync;
|
const es = require('child_process').execSync;
|
||||||
var flatten = require('gulp-flatten');
|
const flatten = require('gulp-flatten');
|
||||||
var fontello = require('gulp-fontello');
|
const fontello = require('gulp-fontello');
|
||||||
var pump = require('pump');
|
const pump = require('pump');
|
||||||
var sass = require('gulp-sass')(require('sass'));
|
const removeSourcemaps = require('gulp-remove-sourcemaps');
|
||||||
var sassGlob = require('gulp-sass-glob');
|
const sass = require('gulp-sass')(require('sass'));
|
||||||
var styleLint = require('gulp-stylelint');
|
const sassGlob = require('gulp-sass-glob');
|
||||||
var spawn = require('child_process').spawn;
|
const styleLint = require('gulp-stylelint');
|
||||||
|
const spawn = require('child_process').spawn;
|
||||||
|
|
||||||
var config = require('./gulpfile.config.js');
|
const config = require('./gulpfile.config.js');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Support functions for Gulp tasks.
|
* Support functions for Gulp tasks.
|
||||||
|
@ -180,18 +181,21 @@ function lint(cb) {
|
||||||
function scripts(cb) {
|
function scripts(cb) {
|
||||||
pump([
|
pump([
|
||||||
gulp.src(config.scriptsConfig.vendor),
|
gulp.src(config.scriptsConfig.vendor),
|
||||||
|
removeSourcemaps(),
|
||||||
concat('vendor.js'),
|
concat('vendor.js'),
|
||||||
gulp.dest(config.scriptsConfig.dest)
|
gulp.dest(config.scriptsConfig.dest)
|
||||||
], cb);
|
], cb);
|
||||||
|
|
||||||
pump([
|
pump([
|
||||||
gulp.src(config.scriptsConfig.graph),
|
gulp.src(config.scriptsConfig.graph),
|
||||||
|
removeSourcemaps(),
|
||||||
concat('graph.js'),
|
concat('graph.js'),
|
||||||
gulp.dest(config.scriptsConfig.dest)
|
gulp.dest(config.scriptsConfig.dest)
|
||||||
], cb);
|
], cb);
|
||||||
|
|
||||||
pump([
|
pump([
|
||||||
gulp.src(config.scriptsConfig.app),
|
gulp.src(config.scriptsConfig.app),
|
||||||
|
removeSourcemaps(),
|
||||||
concat('app.js'),
|
concat('app.js'),
|
||||||
gulp.dest(config.scriptsConfig.dest)
|
gulp.dest(config.scriptsConfig.dest)
|
||||||
], cb);
|
], cb);
|
||||||
|
@ -224,7 +228,7 @@ function styles(cb) {
|
||||||
* @param cb
|
* @param cb
|
||||||
*/
|
*/
|
||||||
function test(cb) {
|
function test(cb) {
|
||||||
var command = [
|
let command = [
|
||||||
'run',
|
'run',
|
||||||
'python',
|
'python',
|
||||||
'manage.py',
|
'manage.py',
|
||||||
|
@ -277,12 +281,12 @@ function watch() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
gulp.task('collectstatic', function(cb) {
|
gulp.task('collectstatic', function(cb) {
|
||||||
var command = ['run', 'python', 'manage.py', 'collectstatic'];
|
let command = ['run', 'python', 'manage.py', 'collectstatic'];
|
||||||
|
|
||||||
/* Use base settings if no settings parameter is supplied. */
|
/* Use base settings if no settings parameter is supplied. */
|
||||||
var parameters = process.argv.splice(3);
|
const parameters = process.argv.splice(3);
|
||||||
var noSettings = true;
|
let noSettings = true;
|
||||||
for (var i = 0; i < parameters.length; i++) {
|
for (let i = 0; i < parameters.length; i++) {
|
||||||
if (parameters[i].substring(0, 10) === '--settings') {
|
if (parameters[i].substring(0, 10) === '--settings') {
|
||||||
noSettings = false;
|
noSettings = false;
|
||||||
break;
|
break;
|
||||||
|
@ -325,15 +329,15 @@ gulp.task('reset', function(cb) {
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('runserver', function(cb) {
|
gulp.task('runserver', function(cb) {
|
||||||
var command = ['run', 'python', 'manage.py', 'runserver'];
|
let command = ['run', 'python', 'manage.py', 'runserver'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process any parameters. Any arguments found here will be removed from
|
* Process any parameters. Any arguments found here will be removed from
|
||||||
* the parameters list so other parameters continue to be passed to the
|
* the parameters list so other parameters continue to be passed to the
|
||||||
* command.
|
* command.
|
||||||
**/
|
**/
|
||||||
var parameters = process.argv.splice(2);
|
const parameters = process.argv.splice(2);
|
||||||
for (var i = 0; i < parameters.length; i++) {
|
for (let i = 0; i < parameters.length; i++) {
|
||||||
/* May be included because this is the default gulp command. */
|
/* May be included because this is the default gulp command. */
|
||||||
if (parameters[i] === 'runserver') {
|
if (parameters[i] === 'runserver') {
|
||||||
delete parameters[i];
|
delete parameters[i];
|
||||||
|
|
Binary file not shown.
|
@ -121,22 +121,26 @@ msgstr "Zeitzone"
|
||||||
msgid "{user}'s Settings"
|
msgid "{user}'s Settings"
|
||||||
msgstr "{user} Einstellungen"
|
msgstr "{user} Einstellungen"
|
||||||
|
|
||||||
|
#: babybuddy/settings/base.py:166
|
||||||
|
msgid "Chinese (simplified)"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#: babybuddy/settings/base.py:167
|
#: babybuddy/settings/base.py:167
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/settings/base.py:168
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "English"
|
#| msgid "English"
|
||||||
msgid "English (US)"
|
msgid "English (US)"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
#: babybuddy/settings/base.py:168
|
#: babybuddy/settings/base.py:169
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "English"
|
#| msgid "English"
|
||||||
msgid "English (UK)"
|
msgid "English (UK)"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
#: babybuddy/settings/base.py:169
|
|
||||||
msgid "Dutch"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: babybuddy/settings/base.py:170
|
#: babybuddy/settings/base.py:170
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr "Französisch"
|
msgstr "Französisch"
|
||||||
|
@ -173,18 +177,6 @@ msgstr "Schwedisch"
|
||||||
msgid "Turkish"
|
msgid "Turkish"
|
||||||
msgstr "Türkisch"
|
msgstr "Türkisch"
|
||||||
|
|
||||||
#: babybuddy/templates/403.html:4 babybuddy/templates/403.html:7
|
|
||||||
msgid "Permission Denied"
|
|
||||||
msgstr "Zugriff verweigert"
|
|
||||||
|
|
||||||
#: babybuddy/templates/403.html:12
|
|
||||||
msgid ""
|
|
||||||
"You do not have permission to access this resource. Contact a site "
|
|
||||||
"administrator for assistance."
|
|
||||||
msgstr ""
|
|
||||||
"Du hast keine Berechtigung auf diese Ressource zuzugreifen. Für "
|
|
||||||
"Unterstützung kontaktiere bitte den Administrator."
|
|
||||||
|
|
||||||
#: babybuddy/templates/admin/base_site.html:4
|
#: babybuddy/templates/admin/base_site.html:4
|
||||||
#: babybuddy/templates/admin/base_site.html:7
|
#: babybuddy/templates/admin/base_site.html:7
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:338
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:338
|
||||||
|
@ -230,27 +222,27 @@ msgstr ""
|
||||||
msgid "Quick Start Timer"
|
msgid "Quick Start Timer"
|
||||||
msgstr "Quick-Start Timer"
|
msgstr "Quick-Start Timer"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:51 core/models.py:216
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:51 core/models.py:158
|
||||||
#: core/models.py:220
|
#: core/models.py:162
|
||||||
msgid "Diaper Change"
|
msgid "Diaper Change"
|
||||||
msgstr "Windeln wechseln"
|
msgstr "Windeln wechseln"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:57
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:57
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:274 core/models.py:285
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:274 core/models.py:227
|
||||||
#: core/models.py:289 core/templates/core/timer_detail.html:43
|
#: core/models.py:231 core/templates/core/timer_detail.html:43
|
||||||
msgid "Feeding"
|
msgid "Feeding"
|
||||||
msgstr "Mahlzeit"
|
msgstr "Mahlzeit"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:63
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:63
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:150 core/models.py:307
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:150 core/models.py:250
|
||||||
#: core/models.py:318 core/models.py:322 core/templates/core/note_list.html:29
|
#: core/models.py:260 core/models.py:264 core/templates/core/note_list.html:29
|
||||||
msgid "Note"
|
msgid "Note"
|
||||||
msgstr "Notiz"
|
msgstr "Notiz"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:69
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:69
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:281
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:281
|
||||||
#: babybuddy/templates/babybuddy/welcome.html:42 core/models.py:350
|
#: babybuddy/templates/babybuddy/welcome.html:42 core/models.py:292
|
||||||
#: core/models.py:351 core/models.py:354
|
#: core/models.py:293 core/models.py:296
|
||||||
#: core/templates/core/sleep_confirm_delete.html:7
|
#: core/templates/core/sleep_confirm_delete.html:7
|
||||||
#: core/templates/core/sleep_form.html:13 core/templates/core/sleep_list.html:4
|
#: core/templates/core/sleep_form.html:13 core/templates/core/sleep_list.html:4
|
||||||
#: core/templates/core/sleep_list.html:7 core/templates/core/sleep_list.html:12
|
#: core/templates/core/sleep_list.html:7 core/templates/core/sleep_list.html:12
|
||||||
|
@ -259,8 +251,8 @@ msgid "Sleep"
|
||||||
msgstr "Schlafen"
|
msgstr "Schlafen"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:75
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:75
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:172 core/models.py:389
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:172 core/models.py:331
|
||||||
#: core/models.py:399 core/models.py:400 core/models.py:403
|
#: core/models.py:341 core/models.py:342 core/models.py:345
|
||||||
#: core/templates/core/temperature_confirm_delete.html:7
|
#: core/templates/core/temperature_confirm_delete.html:7
|
||||||
#: core/templates/core/temperature_form.html:13
|
#: core/templates/core/temperature_form.html:13
|
||||||
#: core/templates/core/temperature_list.html:4
|
#: core/templates/core/temperature_list.html:4
|
||||||
|
@ -272,8 +264,8 @@ msgstr "Temperatur"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:81
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:81
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:294
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:294
|
||||||
#: babybuddy/templates/babybuddy/welcome.html:50 core/models.py:526
|
#: babybuddy/templates/babybuddy/welcome.html:50 core/models.py:468
|
||||||
#: core/models.py:527 core/models.py:530
|
#: core/models.py:469 core/models.py:472
|
||||||
#: core/templates/core/timer_detail.html:59
|
#: core/templates/core/timer_detail.html:59
|
||||||
#: core/templates/core/tummytime_confirm_delete.html:7
|
#: core/templates/core/tummytime_confirm_delete.html:7
|
||||||
#: core/templates/core/tummytime_form.html:13
|
#: core/templates/core/tummytime_form.html:13
|
||||||
|
@ -284,8 +276,8 @@ msgid "Tummy Time"
|
||||||
msgstr "Bauchzeit"
|
msgstr "Bauchzeit"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:87
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:87
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:186 core/models.py:552
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:186 core/models.py:494
|
||||||
#: core/models.py:561 core/models.py:562 core/models.py:565
|
#: core/models.py:503 core/models.py:504 core/models.py:507
|
||||||
#: core/templates/core/weight_confirm_delete.html:7
|
#: core/templates/core/weight_confirm_delete.html:7
|
||||||
#: core/templates/core/weight_form.html:13
|
#: core/templates/core/weight_form.html:13
|
||||||
#: core/templates/core/weight_list.html:4
|
#: core/templates/core/weight_list.html:4
|
||||||
|
@ -307,7 +299,7 @@ msgid "Timeline"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:122
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:122
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:130 core/models.py:159
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:130 core/models.py:101
|
||||||
#: core/templates/core/child_confirm_delete.html:7
|
#: core/templates/core/child_confirm_delete.html:7
|
||||||
#: core/templates/core/child_detail.html:7
|
#: core/templates/core/child_detail.html:7
|
||||||
#: core/templates/core/child_form.html:13 core/templates/core/child_list.html:4
|
#: core/templates/core/child_form.html:13 core/templates/core/child_list.html:4
|
||||||
|
@ -317,10 +309,10 @@ msgstr ""
|
||||||
msgid "Children"
|
msgid "Children"
|
||||||
msgstr "Kinder"
|
msgstr "Kinder"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:136 core/models.py:158
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:136 core/models.py:100
|
||||||
#: core/models.py:192 core/models.py:248 core/models.py:305 core/models.py:334
|
#: core/models.py:134 core/models.py:190 core/models.py:248 core/models.py:276
|
||||||
#: core/models.py:386 core/models.py:417 core/models.py:510 core/models.py:550
|
#: core/models.py:328 core/models.py:359 core/models.py:452 core/models.py:492
|
||||||
#: core/models.py:577 core/models.py:604 core/models.py:630
|
#: core/models.py:519 core/models.py:546 core/models.py:572
|
||||||
#: core/templates/core/bmi_list.html:27
|
#: core/templates/core/bmi_list.html:27
|
||||||
#: core/templates/core/diaperchange_list.html:27
|
#: core/templates/core/diaperchange_list.html:27
|
||||||
#: core/templates/core/feeding_list.html:27
|
#: core/templates/core/feeding_list.html:27
|
||||||
|
@ -334,9 +326,9 @@ msgstr "Kinder"
|
||||||
msgid "Child"
|
msgid "Child"
|
||||||
msgstr "Kind"
|
msgstr "Kind"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:144 core/models.py:209
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:144 core/models.py:151
|
||||||
#: core/models.py:278 core/models.py:319 core/models.py:342 core/models.py:392
|
#: core/models.py:220 core/models.py:261 core/models.py:284 core/models.py:334
|
||||||
#: core/models.py:554 core/models.py:581 core/models.py:610 core/models.py:634
|
#: core/models.py:496 core/models.py:523 core/models.py:552 core/models.py:576
|
||||||
#: core/templates/core/note_confirm_delete.html:7
|
#: core/templates/core/note_confirm_delete.html:7
|
||||||
#: core/templates/core/note_form.html:13 core/templates/core/note_list.html:4
|
#: core/templates/core/note_form.html:13 core/templates/core/note_list.html:4
|
||||||
#: core/templates/core/note_list.html:7 core/templates/core/note_list.html:12
|
#: core/templates/core/note_list.html:7 core/templates/core/note_list.html:12
|
||||||
|
@ -345,7 +337,7 @@ msgstr "Notizen"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:164
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:164
|
||||||
msgid "Measurements"
|
msgid "Measurements"
|
||||||
msgstr "Messungen"
|
msgstr ""
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:178
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:178
|
||||||
msgid "Temperature reading"
|
msgid "Temperature reading"
|
||||||
|
@ -355,8 +347,8 @@ msgstr "Temperatur Messung"
|
||||||
msgid "Weight entry"
|
msgid "Weight entry"
|
||||||
msgstr "Gewichtseintrag"
|
msgstr "Gewichtseintrag"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:200 core/models.py:579
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:200 core/models.py:521
|
||||||
#: core/models.py:588 core/models.py:589 core/models.py:592
|
#: core/models.py:530 core/models.py:531 core/models.py:534
|
||||||
#: core/templates/core/height_confirm_delete.html:7
|
#: core/templates/core/height_confirm_delete.html:7
|
||||||
#: core/templates/core/height_form.html:13
|
#: core/templates/core/height_form.html:13
|
||||||
#: core/templates/core/height_list.html:4
|
#: core/templates/core/height_list.html:4
|
||||||
|
@ -378,8 +370,8 @@ msgstr "Gewicht"
|
||||||
msgid "Height entry"
|
msgid "Height entry"
|
||||||
msgstr "Gewichtseintrag"
|
msgstr "Gewichtseintrag"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:214 core/models.py:607
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:214 core/models.py:549
|
||||||
#: core/models.py:617 core/models.py:618 core/models.py:621
|
#: core/models.py:559 core/models.py:560 core/models.py:563
|
||||||
#: core/templates/core/head_circumference_confirm_delete.html:7
|
#: core/templates/core/head_circumference_confirm_delete.html:7
|
||||||
#: core/templates/core/head_circumference_form.html:13
|
#: core/templates/core/head_circumference_form.html:13
|
||||||
#: core/templates/core/head_circumference_list.html:4
|
#: core/templates/core/head_circumference_list.html:4
|
||||||
|
@ -398,8 +390,8 @@ msgstr ""
|
||||||
msgid "Head Circumference entry"
|
msgid "Head Circumference entry"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:228 core/models.py:632
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:228 core/models.py:574
|
||||||
#: core/models.py:641 core/models.py:642 core/models.py:645
|
#: core/models.py:583 core/models.py:584 core/models.py:587
|
||||||
#: core/templates/core/bmi_confirm_delete.html:7
|
#: core/templates/core/bmi_confirm_delete.html:7
|
||||||
#: core/templates/core/bmi_form.html:13 core/templates/core/bmi_list.html:4
|
#: core/templates/core/bmi_form.html:13 core/templates/core/bmi_list.html:4
|
||||||
#: core/templates/core/bmi_list.html:7 core/templates/core/bmi_list.html:12
|
#: core/templates/core/bmi_list.html:7 core/templates/core/bmi_list.html:12
|
||||||
|
@ -431,7 +423,7 @@ msgid "Change"
|
||||||
msgstr "Wechsel"
|
msgstr "Wechsel"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:268
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:268
|
||||||
#: babybuddy/templates/babybuddy/welcome.html:34 core/models.py:286
|
#: babybuddy/templates/babybuddy/welcome.html:34 core/models.py:228
|
||||||
#: core/templates/core/feeding_confirm_delete.html:7
|
#: core/templates/core/feeding_confirm_delete.html:7
|
||||||
#: core/templates/core/feeding_form.html:13
|
#: core/templates/core/feeding_form.html:13
|
||||||
#: core/templates/core/feeding_list.html:4
|
#: core/templates/core/feeding_list.html:4
|
||||||
|
@ -451,7 +443,7 @@ msgstr "Bauchzeit-Eintrag"
|
||||||
#: babybuddy/templates/babybuddy/nav-dropdown.html:325
|
#: babybuddy/templates/babybuddy/nav-dropdown.html:325
|
||||||
#: babybuddy/templates/babybuddy/user_list.html:17
|
#: babybuddy/templates/babybuddy/user_list.html:17
|
||||||
#: babybuddy/templates/babybuddy/user_password_form.html:7
|
#: babybuddy/templates/babybuddy/user_password_form.html:7
|
||||||
#: babybuddy/templates/babybuddy/user_settings_form.html:7 core/models.py:436
|
#: babybuddy/templates/babybuddy/user_settings_form.html:7 core/models.py:378
|
||||||
#: core/templates/core/timer_list.html:32
|
#: core/templates/core/timer_list.html:32
|
||||||
msgid "User"
|
msgid "User"
|
||||||
msgstr "Benutzer"
|
msgstr "Benutzer"
|
||||||
|
@ -639,7 +631,7 @@ msgstr "E-Mail"
|
||||||
msgid "Staff"
|
msgid "Staff"
|
||||||
msgstr "Angestellte"
|
msgstr "Angestellte"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/user_list.html:22 core/models.py:431
|
#: babybuddy/templates/babybuddy/user_list.html:22 core/models.py:373
|
||||||
#: core/templates/core/timer_list.html:31
|
#: core/templates/core/timer_list.html:31
|
||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr "Aktiv"
|
msgstr "Aktiv"
|
||||||
|
@ -716,7 +708,9 @@ msgstr ""
|
||||||
"Lerne und sehe die Bedürfnisse deines Babys voraus, ohne (<em>allzu viel</"
|
"Lerne und sehe die Bedürfnisse deines Babys voraus, ohne (<em>allzu viel</"
|
||||||
"em>)Spekulation indem du Baby Buddy verwendest —"
|
"em>)Spekulation indem du Baby Buddy verwendest —"
|
||||||
|
|
||||||
#: babybuddy/templates/babybuddy/welcome.html:26 core/models.py:217
|
==== BASE ====
|
||||||
|
#: babybuddy/templates/babybuddy/welcome.html:26 core/models.py:166
|
||||||
|
==== BASE ====
|
||||||
#: core/templates/core/diaperchange_confirm_delete.html:7
|
#: core/templates/core/diaperchange_confirm_delete.html:7
|
||||||
#: core/templates/core/diaperchange_form.html:13
|
#: core/templates/core/diaperchange_form.html:13
|
||||||
#: core/templates/core/diaperchange_list.html:4
|
#: core/templates/core/diaperchange_list.html:4
|
||||||
|
@ -748,6 +742,52 @@ msgstr ""
|
||||||
msgid "Add a Child"
|
msgid "Add a Child"
|
||||||
msgstr "Kind hinzufügen"
|
msgstr "Kind hinzufügen"
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/400.html:4 babybuddy/templates/error/400.html:7
|
||||||
|
msgid "Bad Request"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/403.html:4 babybuddy/templates/error/403.html:7
|
||||||
|
msgid "Permission Denied"
|
||||||
|
msgstr "Zugriff verweigert"
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/403.html:9
|
||||||
|
msgid ""
|
||||||
|
"You do not have permission to access this resource. Contact a site "
|
||||||
|
"administrator for assistance."
|
||||||
|
msgstr ""
|
||||||
|
"Du hast keine Berechtigung auf diese Ressource zuzugreifen. Für "
|
||||||
|
"Unterstützung kontaktiere bitte den Administrator."
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/403_csrf_bad_origin.html:14
|
||||||
|
msgid "How to Fix"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/403_csrf_bad_origin.html:15
|
||||||
|
#, python-format
|
||||||
|
msgid ""
|
||||||
|
"Add <samp>%(origin)s</samp> to the <code>CSRF_TRUSTED_ORIGINS</code> "
|
||||||
|
"environment variable. If multiple origins are required separate with commas."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/404.html:4 babybuddy/templates/error/404.html:7
|
||||||
|
msgid "Page Not Found"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/404.html:9
|
||||||
|
#, python-format
|
||||||
|
msgid "The path <code>%(request_path)s</code> does not exist."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/500.html:4 babybuddy/templates/error/500.html:7
|
||||||
|
msgid "Server Error"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/templates/error/base.html:14
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "Welcome to Baby Buddy!"
|
||||||
|
msgid "Return to Baby Buddy"
|
||||||
|
msgstr "Willkommen bei Baby Buddy!"
|
||||||
|
|
||||||
#: babybuddy/templates/registration/login.html:32
|
#: babybuddy/templates/registration/login.html:32
|
||||||
msgid "Login"
|
msgid "Login"
|
||||||
msgstr "Login"
|
msgstr "Login"
|
||||||
|
@ -821,34 +861,42 @@ msgstr ""
|
||||||
"Bitte gib deine Account E-Mail-Adresse ins folgende Formular ein. Wenn die "
|
"Bitte gib deine Account E-Mail-Adresse ins folgende Formular ein. Wenn die "
|
||||||
"Adresse gültig ist, erhältst du Anweisungen um das Passwort zurückzusetzen."
|
"Adresse gültig ist, erhältst du Anweisungen um das Passwort zurückzusetzen."
|
||||||
|
|
||||||
#: babybuddy/views.py:76
|
#: babybuddy/views.py:43
|
||||||
|
msgid "Forbidden"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/views.py:44
|
||||||
|
msgid "CSRF verification failed. Request aborted."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: babybuddy/views.py:102
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User %(username)s added!"
|
msgid "User %(username)s added!"
|
||||||
msgstr "User %(username)s hinzugefügt!"
|
msgstr "User %(username)s hinzugefügt!"
|
||||||
|
|
||||||
#: babybuddy/views.py:87
|
#: babybuddy/views.py:113
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "User %(username)s updated."
|
msgid "User %(username)s updated."
|
||||||
msgstr "User %(username)s geändert!"
|
msgstr "User %(username)s geändert!"
|
||||||
|
|
||||||
#: babybuddy/views.py:99
|
#: babybuddy/views.py:125
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "User {user} deleted."
|
msgid "User {user} deleted."
|
||||||
msgstr "User {user} gelöscht."
|
msgstr "User {user} gelöscht."
|
||||||
|
|
||||||
#: babybuddy/views.py:120
|
#: babybuddy/views.py:146
|
||||||
msgid "Password updated."
|
msgid "Password updated."
|
||||||
msgstr "Passwort geändert."
|
msgstr "Passwort geändert."
|
||||||
|
|
||||||
#: babybuddy/views.py:149
|
#: babybuddy/views.py:175
|
||||||
msgid "User API key regenerated."
|
msgid "User API key regenerated."
|
||||||
msgstr "User API-Key neu generiert."
|
msgstr "User API-Key neu generiert."
|
||||||
|
|
||||||
#: babybuddy/views.py:162
|
#: babybuddy/views.py:188
|
||||||
msgid "Settings saved!"
|
msgid "Settings saved!"
|
||||||
msgstr "Einstellungen gespeichert!"
|
msgstr "Einstellungen gespeichert!"
|
||||||
|
|
||||||
#: core/forms.py:116
|
#: core/forms.py:115
|
||||||
msgid "Name does not match child name."
|
msgid "Name does not match child name."
|
||||||
msgstr "Name entspricht nicht dem Kindernamen."
|
msgstr "Name entspricht nicht dem Kindernamen."
|
||||||
|
|
||||||
|
@ -868,23 +916,15 @@ msgstr "Dauer zu lange."
|
||||||
msgid "Another entry intersects the specified time period."
|
msgid "Another entry intersects the specified time period."
|
||||||
msgstr "Ein anderer Eintrag schneidet sich mit der angegebenen Zeitperiode."
|
msgstr "Ein anderer Eintrag schneidet sich mit der angegebenen Zeitperiode."
|
||||||
|
|
||||||
#: core/models.py:81
|
#: core/models.py:70
|
||||||
msgid "Date/time can not be in the future."
|
msgid "Date/time can not be in the future."
|
||||||
msgstr "Datum/Zeit darf nicht in der Zukunft liegen."
|
msgstr "Datum/Zeit darf nicht in der Zukunft liegen."
|
||||||
|
|
||||||
#: core/models.py:95
|
#: core/models.py:76
|
||||||
msgid "Tag"
|
|
||||||
msgstr "Tag"
|
|
||||||
|
|
||||||
#: core/models.py:96
|
|
||||||
msgid "Tags"
|
|
||||||
msgstr "Tags"
|
|
||||||
|
|
||||||
#: core/models.py:134
|
|
||||||
msgid "First name"
|
msgid "First name"
|
||||||
msgstr "Vorname"
|
msgstr "Vorname"
|
||||||
|
|
||||||
#: core/models.py:136
|
#: core/models.py:78
|
||||||
msgid "Last name"
|
msgid "Last name"
|
||||||
msgstr "Nachname"
|
msgstr "Nachname"
|
||||||
|
|
||||||
|
@ -892,69 +932,69 @@ msgstr "Nachname"
|
||||||
msgid "Birth date"
|
msgid "Birth date"
|
||||||
msgstr "Geburtsdatum"
|
msgstr "Geburtsdatum"
|
||||||
|
|
||||||
#: core/models.py:145
|
#: core/models.py:87
|
||||||
msgid "Slug"
|
msgid "Slug"
|
||||||
msgstr "Slug"
|
msgstr "Slug"
|
||||||
|
|
||||||
#: core/models.py:148
|
#: core/models.py:90
|
||||||
msgid "Picture"
|
msgid "Picture"
|
||||||
msgstr "Bild"
|
msgstr "Bild"
|
||||||
|
|
||||||
#: core/models.py:194 core/models.py:309 core/models.py:391
|
#: core/models.py:136 core/models.py:252 core/models.py:333
|
||||||
#: core/templates/core/diaperchange_list.html:25
|
#: core/templates/core/diaperchange_list.html:25
|
||||||
#: core/templates/core/note_list.html:25
|
#: core/templates/core/note_list.html:25
|
||||||
#: core/templates/core/temperature_list.html:25
|
#: core/templates/core/temperature_list.html:25
|
||||||
msgid "Time"
|
msgid "Time"
|
||||||
msgstr "Zeit"
|
msgstr "Zeit"
|
||||||
|
|
||||||
#: core/models.py:195 core/templates/core/diaperchange_list.html:60
|
#: core/models.py:137 core/templates/core/diaperchange_list.html:60
|
||||||
#: reports/graphs/diaperchange_types.py:36
|
#: reports/graphs/diaperchange_types.py:36
|
||||||
msgid "Wet"
|
msgid "Wet"
|
||||||
msgstr "Nass"
|
msgstr "Nass"
|
||||||
|
|
||||||
#: core/models.py:196 core/templates/core/diaperchange_list.html:61
|
#: core/models.py:138 core/templates/core/diaperchange_list.html:61
|
||||||
#: reports/graphs/diaperchange_types.py:30
|
#: reports/graphs/diaperchange_types.py:30
|
||||||
msgid "Solid"
|
msgid "Solid"
|
||||||
msgstr "Fest"
|
msgstr "Fest"
|
||||||
|
|
||||||
#: core/models.py:200
|
#: core/models.py:142
|
||||||
msgid "Black"
|
msgid "Black"
|
||||||
msgstr "Schwarz"
|
msgstr "Schwarz"
|
||||||
|
|
||||||
#: core/models.py:201
|
#: core/models.py:143
|
||||||
msgid "Brown"
|
msgid "Brown"
|
||||||
msgstr "Braun"
|
msgstr "Braun"
|
||||||
|
|
||||||
#: core/models.py:202
|
#: core/models.py:144
|
||||||
msgid "Green"
|
msgid "Green"
|
||||||
msgstr "Grün"
|
msgstr "Grün"
|
||||||
|
|
||||||
#: core/models.py:203
|
#: core/models.py:145
|
||||||
msgid "Yellow"
|
msgid "Yellow"
|
||||||
msgstr "Gelb"
|
msgstr "Gelb"
|
||||||
|
|
||||||
#: core/models.py:206 core/templates/core/diaperchange_list.html:30
|
#: core/models.py:148 core/templates/core/diaperchange_list.html:30
|
||||||
msgid "Color"
|
msgid "Color"
|
||||||
msgstr "Farbe"
|
msgstr "Farbe"
|
||||||
|
|
||||||
#: core/models.py:208 core/models.py:277
|
#: core/models.py:150 core/models.py:219
|
||||||
#: core/templates/core/diaperchange_list.html:31
|
#: core/templates/core/diaperchange_list.html:31
|
||||||
msgid "Amount"
|
msgid "Amount"
|
||||||
msgstr "Menge"
|
msgstr "Menge"
|
||||||
|
|
||||||
#: core/models.py:238
|
#: core/models.py:180
|
||||||
msgid "Wet and/or solid is required."
|
msgid "Wet and/or solid is required."
|
||||||
msgstr "Nass und/oder fest wird benötigt."
|
msgstr "Nass und/oder fest wird benötigt."
|
||||||
|
|
||||||
#: core/models.py:250 core/models.py:337 core/models.py:423 core/models.py:512
|
#: core/models.py:192 core/models.py:279 core/models.py:365 core/models.py:454
|
||||||
msgid "Start time"
|
msgid "Start time"
|
||||||
msgstr "Startzeit"
|
msgstr "Startzeit"
|
||||||
|
|
||||||
#: core/models.py:251 core/models.py:338 core/models.py:426 core/models.py:513
|
#: core/models.py:193 core/models.py:280 core/models.py:368 core/models.py:455
|
||||||
msgid "End time"
|
msgid "End time"
|
||||||
msgstr "Endzeit"
|
msgstr "Endzeit"
|
||||||
|
|
||||||
#: core/models.py:253 core/models.py:340 core/models.py:429 core/models.py:515
|
#: core/models.py:195 core/models.py:282 core/models.py:371 core/models.py:457
|
||||||
#: core/templates/core/feeding_list.html:34
|
#: core/templates/core/feeding_list.html:34
|
||||||
#: core/templates/core/sleep_list.html:30
|
#: core/templates/core/sleep_list.html:30
|
||||||
#: core/templates/core/timer_list.html:29
|
#: core/templates/core/timer_list.html:29
|
||||||
|
@ -962,67 +1002,67 @@ msgstr "Endzeit"
|
||||||
msgid "Duration"
|
msgid "Duration"
|
||||||
msgstr "Dauer"
|
msgstr "Dauer"
|
||||||
|
|
||||||
#: core/models.py:257
|
#: core/models.py:199
|
||||||
msgid "Breast milk"
|
msgid "Breast milk"
|
||||||
msgstr "Brustmilch"
|
msgstr "Brustmilch"
|
||||||
|
|
||||||
#: core/models.py:258
|
#: core/models.py:200
|
||||||
msgid "Formula"
|
msgid "Formula"
|
||||||
msgstr "Formel"
|
msgstr "Formel"
|
||||||
|
|
||||||
#: core/models.py:259
|
#: core/models.py:201
|
||||||
msgid "Fortified breast milk"
|
msgid "Fortified breast milk"
|
||||||
msgstr "Angereicherte Brustmilch"
|
msgstr "Angereicherte Brustmilch"
|
||||||
|
|
||||||
#: core/models.py:260
|
#: core/models.py:202
|
||||||
msgid "Solid food"
|
msgid "Solid food"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core/models.py:263 core/templates/core/feeding_list.html:30
|
#: core/models.py:205 core/templates/core/feeding_list.html:30
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Typ"
|
msgstr "Typ"
|
||||||
|
|
||||||
#: core/models.py:267
|
#: core/models.py:209
|
||||||
msgid "Bottle"
|
msgid "Bottle"
|
||||||
msgstr "Fläschchen"
|
msgstr "Fläschchen"
|
||||||
|
|
||||||
#: core/models.py:268
|
#: core/models.py:210
|
||||||
msgid "Left breast"
|
msgid "Left breast"
|
||||||
msgstr "Linke Brust"
|
msgstr "Linke Brust"
|
||||||
|
|
||||||
#: core/models.py:269
|
#: core/models.py:211
|
||||||
msgid "Right breast"
|
msgid "Right breast"
|
||||||
msgstr "Rechte Brust"
|
msgstr "Rechte Brust"
|
||||||
|
|
||||||
#: core/models.py:270
|
#: core/models.py:212
|
||||||
msgid "Both breasts"
|
msgid "Both breasts"
|
||||||
msgstr "Beide Brüste"
|
msgstr "Beide Brüste"
|
||||||
|
|
||||||
#: core/models.py:271
|
#: core/models.py:213
|
||||||
msgid "Parent fed"
|
msgid "Parent fed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core/models.py:272
|
#: core/models.py:214
|
||||||
msgid "Self fed"
|
msgid "Self fed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core/models.py:275 core/templates/core/feeding_list.html:29
|
#: core/models.py:217 core/templates/core/feeding_list.html:29
|
||||||
msgid "Method"
|
msgid "Method"
|
||||||
msgstr "Methode"
|
msgstr "Methode"
|
||||||
|
|
||||||
#: core/models.py:336
|
#: core/models.py:278
|
||||||
msgid "Napping"
|
msgid "Napping"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: core/models.py:420 core/templates/core/timer_list.html:25
|
#: core/models.py:362 core/templates/core/timer_list.html:25
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
#: core/models.py:444 core/templates/core/timer_form.html:4
|
#: core/models.py:386 core/templates/core/timer_form.html:4
|
||||||
msgid "Timer"
|
msgid "Timer"
|
||||||
msgstr "Timer"
|
msgstr "Timer"
|
||||||
|
|
||||||
#: core/models.py:445 core/templates/core/timer_confirm_delete.html:9
|
#: core/models.py:387 core/templates/core/timer_confirm_delete.html:9
|
||||||
#: core/templates/core/timer_confirm_delete_inactive.html:9
|
#: core/templates/core/timer_confirm_delete_inactive.html:9
|
||||||
#: core/templates/core/timer_detail.html:8
|
#: core/templates/core/timer_detail.html:8
|
||||||
#: core/templates/core/timer_form.html:7 core/templates/core/timer_list.html:4
|
#: core/templates/core/timer_form.html:7 core/templates/core/timer_list.html:4
|
||||||
|
@ -1031,16 +1071,16 @@ msgstr "Timer"
|
||||||
msgid "Timers"
|
msgid "Timers"
|
||||||
msgstr "Timer"
|
msgstr "Timer"
|
||||||
|
|
||||||
#: core/models.py:448
|
#: core/models.py:390
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Timer #{id}"
|
msgid "Timer #{id}"
|
||||||
msgstr "Timer #{id}"
|
msgstr "Timer #{id}"
|
||||||
|
|
||||||
#: core/models.py:518 core/templates/core/tummytime_list.html:30
|
#: core/models.py:460 core/templates/core/tummytime_list.html:30
|
||||||
msgid "Milestone"
|
msgid "Milestone"
|
||||||
msgstr "Meilenstein"
|
msgstr "Meilenstein"
|
||||||
|
|
||||||
#: core/models.py:553 core/models.py:580 core/models.py:609 core/models.py:633
|
#: core/models.py:495 core/models.py:522 core/models.py:551 core/models.py:575
|
||||||
#: core/templates/core/bmi_list.html:25
|
#: core/templates/core/bmi_list.html:25
|
||||||
#: core/templates/core/feeding_list.html:25
|
#: core/templates/core/feeding_list.html:25
|
||||||
#: core/templates/core/head_circumference_list.html:25
|
#: core/templates/core/head_circumference_list.html:25
|
||||||
|
@ -1959,3 +1999,6 @@ msgstr "Es gibt nicht genügend Daten um diesen Report zu generieren."
|
||||||
#: reports/templates/reports/tummytime_duration.html:8
|
#: reports/templates/reports/tummytime_duration.html:8
|
||||||
msgid "Total Tummy Time Durations"
|
msgid "Total Tummy Time Durations"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#~ msgid "Total feeding amount"
|
||||||
|
#~ msgstr "Total Mahlzeiten"
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
14
mkdocs.yml
14
mkdocs.yml
|
@ -1,18 +1,30 @@
|
||||||
extra_css:
|
extra_css:
|
||||||
- css/extras.css
|
- css/extras.css
|
||||||
|
markdown_extensions:
|
||||||
|
- pymdownx.highlight
|
||||||
|
- pymdownx.inlinehilite
|
||||||
|
- pymdownx.snippets
|
||||||
|
- pymdownx.superfences
|
||||||
nav:
|
nav:
|
||||||
- 'index.md'
|
- 'index.md'
|
||||||
- 'Setup':
|
- 'Setup':
|
||||||
- 'setup/deployment.md'
|
- 'setup/deployment.md'
|
||||||
- 'setup/configuration.md'
|
- 'setup/configuration.md'
|
||||||
|
- 'setup/ssl.md'
|
||||||
|
- 'setup/subdirectory.md'
|
||||||
|
- 'setup/proxy.md'
|
||||||
- 'User Guide':
|
- 'User Guide':
|
||||||
- 'user-guide/getting-started.md'
|
- 'user-guide/getting-started.md'
|
||||||
- 'user-guide/managing-users.md'
|
- 'user-guide/managing-users.md'
|
||||||
- 'user-guide/adding-entries.md'
|
- 'user-guide/adding-entries.md'
|
||||||
- 'user-guide/using-timers.md'
|
- 'user-guide/using-timers.md'
|
||||||
|
- 'Contributing':
|
||||||
|
- 'contributing/development-environment.md'
|
||||||
|
- 'contributing/translation.md'
|
||||||
|
- 'contributing/pull-requests.md'
|
||||||
|
- 'contributing/gulp-command-reference.md'
|
||||||
- 'import-export.md'
|
- 'import-export.md'
|
||||||
- 'api.md'
|
- 'api.md'
|
||||||
- 'contributing.md'
|
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: material
|
||||||
favicon: assets/images/favicon.svg
|
favicon: assets/images/favicon.svg
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"gulp-csso": "^4.0.1",
|
"gulp-csso": "^4.0.1",
|
||||||
"gulp-flatten": "^0.4.0",
|
"gulp-flatten": "^0.4.0",
|
||||||
"gulp-fontello": "^0.5.2",
|
"gulp-fontello": "^0.5.2",
|
||||||
|
"gulp-remove-sourcemaps": "^1.0.4",
|
||||||
"gulp-sass": "^5.0.0",
|
"gulp-sass": "^5.0.0",
|
||||||
"gulp-sass-glob": "^1.1.0",
|
"gulp-sass-glob": "^1.1.0",
|
||||||
"gulp-spawn": "^1.0.0",
|
"gulp-spawn": "^1.0.0",
|
||||||
|
@ -3858,6 +3859,12 @@
|
||||||
"y18n": "^3.2.0"
|
"y18n": "^3.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/gulp-remove-sourcemaps": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/gulp-remove-sourcemaps/-/gulp-remove-sourcemaps-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-zW+YRzgbJIDDqAlIa/EtFJbOV1GSzQe2qV2zy+PjI1TJ27JCykQ5eYC+ajogEKZUOzoosgBnXzUGQlVW3YQUIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/gulp-sass": {
|
"node_modules/gulp-sass": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.0.0.tgz",
|
||||||
|
@ -13127,6 +13134,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"gulp-remove-sourcemaps": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/gulp-remove-sourcemaps/-/gulp-remove-sourcemaps-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-zW+YRzgbJIDDqAlIa/EtFJbOV1GSzQe2qV2zy+PjI1TJ27JCykQ5eYC+ajogEKZUOzoosgBnXzUGQlVW3YQUIw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"gulp-sass": {
|
"gulp-sass": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/gulp-sass/-/gulp-sass-5.0.0.tgz",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"gulp-csso": "^4.0.1",
|
"gulp-csso": "^4.0.1",
|
||||||
"gulp-flatten": "^0.4.0",
|
"gulp-flatten": "^0.4.0",
|
||||||
"gulp-fontello": "^0.5.2",
|
"gulp-fontello": "^0.5.2",
|
||||||
|
"gulp-remove-sourcemaps": "^1.0.4",
|
||||||
"gulp-sass": "^5.0.0",
|
"gulp-sass": "^5.0.0",
|
||||||
"gulp-sass-glob": "^1.1.0",
|
"gulp-sass-glob": "^1.1.0",
|
||||||
"gulp-spawn": "^1.0.0",
|
"gulp-spawn": "^1.0.0",
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{% extends 'babybuddy/page.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
{% block title %}{% endblock %}
|
||||||
|
|
||||||
|
{% block breadcrumbs %}
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
||||||
|
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
|
||||||
|
<li class="breadcrumb-item"><a href="{% url 'reports:report-list' object.slug %}">{% trans "Reports" %}</a></li>
|
||||||
|
{% endblock %}
|
|
@ -1,12 +1,10 @@
|
||||||
{% extends 'babybuddy/page.html' %}
|
{% extends 'reports/base.html' %}
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block title %}{% endblock %}
|
{% block title %}{% endblock %}
|
||||||
|
|
||||||
{% block breadcrumbs %}
|
{% block breadcrumbs %}
|
||||||
<li class="breadcrumb-item"><a href="{% url 'core:child-list' %}">{% trans "Children" %}</a></li>
|
{{ block.super }}
|
||||||
<li class="breadcrumb-item font-weight-bold"><a href="{% url 'core:child' object.slug %}">{{ object }}</a></li>
|
|
||||||
<li class="breadcrumb-item">{% trans "Reports" %}</li>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends 'reports/base.html' %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
{% block title %}{% trans "Reports" %} - {{ object }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="container-fluid">
|
||||||
|
<h1>Reports</h1>
|
||||||
|
<div class="list-group">
|
||||||
|
<a href="{% url 'reports:report-bmi-bmi-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Body Mass Index (BMI)" %}</a>
|
||||||
|
<a href="{% url 'reports:report-diaperchange-amounts-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Diaper Change Amounts" %}</a>
|
||||||
|
<a href="{% url 'reports:report-diaperchange-types-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Diaper Change Types" %}</a>
|
||||||
|
<a href="{% url 'reports:report-diaperchange-lifetimes-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Diaper Lifetimes" %}</a>
|
||||||
|
<a href="{% url 'reports:report-feeding-amounts-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Feeding Amounts" %}</a>
|
||||||
|
<a href="{% url 'reports:report-feeding-duration-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Feeding Durations (Average)" %}</a>
|
||||||
|
<a href="{% url 'reports:report-head-circumference-head-circumference-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Head Circumference" %}</a>
|
||||||
|
<a href="{% url 'reports:report-height-height-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Height" %}</a>
|
||||||
|
<a href="{% url 'reports:report-sleep-pattern-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Sleep Pattern" %}</a>
|
||||||
|
<a href="{% url 'reports:report-sleep-totals-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Sleep Totals" %}</a>
|
||||||
|
<a href="{% url 'reports:report-tummytime-duration-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Tummy Time Durations (Sum)" %}</a>
|
||||||
|
<a href="{% url 'reports:report-weight-weight-child' object.slug %}" class="list-group-item list-group-item-action">{% trans "Weight" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -32,6 +32,9 @@ class ViewsTestCase(TestCase):
|
||||||
child = models.Child.objects.first()
|
child = models.Child.objects.first()
|
||||||
base_url = "/children/{}/reports".format(child.slug)
|
base_url = "/children/{}/reports".format(child.slug)
|
||||||
|
|
||||||
|
page = self.c.get(base_url)
|
||||||
|
self.assertEqual(page.status_code, 200)
|
||||||
|
|
||||||
page = self.c.get("{}/changes/amounts/".format(base_url))
|
page = self.c.get("{}/changes/amounts/".format(base_url))
|
||||||
self.assertEqual(page.status_code, 200)
|
self.assertEqual(page.status_code, 200)
|
||||||
page = self.c.get("{}/changes/lifetimes/".format(base_url))
|
page = self.c.get("{}/changes/lifetimes/".format(base_url))
|
||||||
|
|
|
@ -6,6 +6,11 @@ from . import views
|
||||||
app_name = "reports"
|
app_name = "reports"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path(
|
||||||
|
"children/<str:slug>/reports",
|
||||||
|
views.ChildReportList.as_view(),
|
||||||
|
name="report-list",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"children/<str:slug>/reports/changes/amounts/",
|
"children/<str:slug>/reports/changes/amounts/",
|
||||||
views.DiaperChangeAmounts.as_view(),
|
views.DiaperChangeAmounts.as_view(),
|
||||||
|
|
|
@ -7,6 +7,16 @@ from core import models
|
||||||
from . import graphs
|
from . import graphs
|
||||||
|
|
||||||
|
|
||||||
|
class ChildReportList(PermissionRequiredMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Listing of available reports for a child.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = models.Child
|
||||||
|
permission_required = ("core.view_child",)
|
||||||
|
template_name = "reports/report_list.html"
|
||||||
|
|
||||||
|
|
||||||
class DiaperChangeAmounts(PermissionRequiredMixin, DetailView):
|
class DiaperChangeAmounts(PermissionRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Graph of diaper "amounts" - measurements of urine output.
|
Graph of diaper "amounts" - measurements of urine output.
|
||||||
|
@ -109,29 +119,6 @@ class FeedingDurationChildReport(PermissionRequiredMixin, DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class TummyTimeDurationChildReport(PermissionRequiredMixin, DetailView):
|
|
||||||
"""
|
|
||||||
Graph of tummy time durations over time.
|
|
||||||
"""
|
|
||||||
|
|
||||||
model = models.Child
|
|
||||||
permission_required = ("core.view_child",)
|
|
||||||
template_name = "reports/tummytime_duration.html"
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super(TummyTimeDurationChildReport, self).__init__()
|
|
||||||
self.html = ""
|
|
||||||
self.js = ""
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super(TummyTimeDurationChildReport, self).get_context_data(**kwargs)
|
|
||||||
child = context["object"]
|
|
||||||
instances = models.TummyTime.objects.filter(child=child)
|
|
||||||
if instances:
|
|
||||||
context["html"], context["js"] = graphs.tummytime_duration(instances)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class SleepPatternChildReport(PermissionRequiredMixin, DetailView):
|
class SleepPatternChildReport(PermissionRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Graph of sleep pattern comparing sleep to wake times by day.
|
Graph of sleep pattern comparing sleep to wake times by day.
|
||||||
|
@ -178,6 +165,29 @@ class SleepTotalsChildReport(PermissionRequiredMixin, DetailView):
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class TummyTimeDurationChildReport(PermissionRequiredMixin, DetailView):
|
||||||
|
"""
|
||||||
|
Graph of tummy time durations over time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
model = models.Child
|
||||||
|
permission_required = ("core.view_child",)
|
||||||
|
template_name = "reports/tummytime_duration.html"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(TummyTimeDurationChildReport, self).__init__()
|
||||||
|
self.html = ""
|
||||||
|
self.js = ""
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super(TummyTimeDurationChildReport, self).get_context_data(**kwargs)
|
||||||
|
child = context["object"]
|
||||||
|
instances = models.TummyTime.objects.filter(child=child)
|
||||||
|
if instances:
|
||||||
|
context["html"], context["js"] = graphs.tummytime_duration(instances)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
class WeightWeightChildReport(PermissionRequiredMixin, DetailView):
|
class WeightWeightChildReport(PermissionRequiredMixin, DetailView):
|
||||||
"""
|
"""
|
||||||
Graph of weight change over time.
|
Graph of weight change over time.
|
||||||
|
|
Binary file not shown.
|
@ -125,6 +125,10 @@
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#changelist-search .help {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
/* FILTER COLUMN */
|
/* FILTER COLUMN */
|
||||||
|
|
||||||
#changelist-filter {
|
#changelist-filter {
|
Binary file not shown.
|
@ -125,6 +125,10 @@
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#changelist-search .help {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
/* FILTER COLUMN */
|
/* FILTER COLUMN */
|
||||||
|
|
||||||
#changelist-filter {
|
#changelist-filter {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -37,16 +37,19 @@ label {
|
||||||
|
|
||||||
/* RADIO BUTTONS */
|
/* RADIO BUTTONS */
|
||||||
|
|
||||||
form ul.radiolist li {
|
form div.radiolist div {
|
||||||
list-style-type: none;
|
padding-right: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form ul.radiolist label {
|
form div.radiolist.inline div {
|
||||||
float: none;
|
display: inline-block;
|
||||||
display: inline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form ul.radiolist input[type="radio"] {
|
form div.radiolist label {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.radiolist input[type="radio"] {
|
||||||
margin: -2px 4px 0 0;
|
margin: -2px 4px 0 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +87,7 @@ form ul.inline li {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-left: 170px;
|
margin-left: 170px;
|
||||||
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aligned ul label {
|
.aligned ul label {
|
||||||
|
@ -105,7 +109,7 @@ form .aligned ul {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
Binary file not shown.
|
@ -37,16 +37,19 @@ label {
|
||||||
|
|
||||||
/* RADIO BUTTONS */
|
/* RADIO BUTTONS */
|
||||||
|
|
||||||
form ul.radiolist li {
|
form div.radiolist div {
|
||||||
list-style-type: none;
|
padding-right: 7px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form ul.radiolist label {
|
form div.radiolist.inline div {
|
||||||
float: none;
|
display: inline-block;
|
||||||
display: inline;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form ul.radiolist input[type="radio"] {
|
form div.radiolist label {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form div.radiolist input[type="radio"] {
|
||||||
margin: -2px 4px 0 0;
|
margin: -2px 4px 0 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
@ -84,6 +87,7 @@ form ul.inline li {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-left: 170px;
|
margin-left: 170px;
|
||||||
|
overflow-wrap: break-word;
|
||||||
}
|
}
|
||||||
|
|
||||||
.aligned ul label {
|
.aligned ul label {
|
||||||
|
@ -105,7 +109,7 @@ form .aligned ul {
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
Binary file not shown.
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
.login #header h1 {
|
.login #header h1 {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login #header h1 a {
|
.login #header h1 a {
|
Binary file not shown.
Binary file not shown.
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
.login #header h1 {
|
.login #header h1 {
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login #header h1 a {
|
.login #header h1 a {
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -60,13 +60,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.main.shifted > #nav-sidebar {
|
.main.shifted > #nav-sidebar {
|
||||||
left: 24px;
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[dir="rtl"] .main.shifted > #nav-sidebar {
|
[dir="rtl"] .main.shifted > #nav-sidebar {
|
||||||
left: 0;
|
|
||||||
right: 24px;
|
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,3 +115,25 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-filter {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2px 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter:focus {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter.no-results {
|
||||||
|
background: var(--message-error-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
Binary file not shown.
|
@ -60,13 +60,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.main.shifted > #nav-sidebar {
|
.main.shifted > #nav-sidebar {
|
||||||
left: 24px;
|
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[dir="rtl"] .main.shifted > #nav-sidebar {
|
[dir="rtl"] .main.shifted > #nav-sidebar {
|
||||||
left: 0;
|
|
||||||
right: 24px;
|
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,3 +115,25 @@
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nav-filter {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 2px 5px;
|
||||||
|
margin: 5px 0;
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
background-color: var(--darkened-bg);
|
||||||
|
color: var(--body-fg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter:focus {
|
||||||
|
border-color: var(--body-quiet-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-filter.no-results {
|
||||||
|
background: var(--message-error-bg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-sidebar table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
|
@ -232,7 +232,7 @@ input[type="submit"], button {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,12 +650,13 @@ input[type="submit"], button {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
|
margin-top: 5px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
margin-bottom: -3px;
|
margin-bottom: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist:not(.inline) li + li {
|
form .aligned div.radiolist:not(.inline) div + div {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
|
@ -232,7 +232,7 @@ input[type="submit"], button {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,12 +650,13 @@ input[type="submit"], button {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist {
|
form .aligned div.radiolist {
|
||||||
|
margin-top: 5px;
|
||||||
margin-right: 15px;
|
margin-right: 15px;
|
||||||
margin-bottom: -3px;
|
margin-bottom: -3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form .aligned ul.radiolist:not(.inline) li + li {
|
form .aligned div.radiolist:not(.inline) div + div {
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -156,7 +156,7 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelector('#changelist-form button[name=index]').addEventListener('click', function() {
|
document.querySelector('#changelist-form button[name=index]').addEventListener('click', function(event) {
|
||||||
if (list_editable_changed) {
|
if (list_editable_changed) {
|
||||||
const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
|
const confirmed = confirm(gettext("You have unsaved changes on individual editable fields. If you run an action, your unsaved changes will be lost."));
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue