Merged with dev and fixed conflicts

This commit is contained in:
Fmstrat
2022-07-18 09:16:17 -04:00
349 changed files with 17812 additions and 7523 deletions

View File

@@ -1,6 +1,6 @@
import logging
import os
import shutil
import re
from importlib import import_module, reload
from typing import Any
@@ -15,10 +15,12 @@ from flask_bcrypt import Bcrypt
from flask_dramatiq import Dramatiq
from flask_migrate import Migrate
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.exc import ProgrammingError
from fittrackee.emails.email import EmailService
from fittrackee.request import CustomRequest
VERSION = __version__ = '0.5.7'
VERSION = __version__ = '0.6.10'
db = SQLAlchemy()
bcrypt = Bcrypt()
migrate = Migrate()
@@ -33,9 +35,17 @@ logging.basicConfig(
appLog = logging.getLogger('fittrackee')
def create_app() -> Flask:
class CustomFlask(Flask):
# add custom Request to handle user-agent parsing
# (removed in Werkzeug 2.1)
request_class = CustomRequest
def create_app(init_email: bool = True) -> Flask:
# instantiate the app
app = Flask(__name__, static_folder='dist/static', template_folder='dist')
app = CustomFlask(
__name__, static_folder='dist/static', template_folder='dist'
)
# set config
with app.app_context():
@@ -54,8 +64,15 @@ def create_app() -> Flask:
migrate.init_app(app, db)
dramatiq.init_app(app)
# set up email
email_service.init_email(app)
# set up email if 'EMAIL_URL' is initialized
if init_email:
if app.config['EMAIL_URL']:
email_service.init_email(app)
app.config['CAN_SEND_EMAILS'] = True
else:
appLog.warning(
'EMAIL_URL is not provided, email sending is deactivated.'
)
# get configuration from database
from .application.utils import (
@@ -66,9 +83,16 @@ def create_app() -> Flask:
with app.app_context():
# Note: check if "app_config" table exist to avoid errors when
# dropping tables on dev environments
if db.engine.dialect.has_table(db.engine.connect(), 'app_config'):
db_app_config = get_or_init_config()
update_app_config_from_database(app, db_app_config)
try:
if db.engine.dialect.has_table(db.engine.connect(), 'app_config'):
db_app_config = get_or_init_config()
update_app_config_from_database(app, db_app_config)
except ProgrammingError as e:
# avoid error on AppConfig migration
if re.match(
r'psycopg2.errors.UndefinedColumn(.*)app_config.', str(e)
):
pass
from .application.app_config import config_blueprint # noqa
from .users.auth import auth_blueprint # noqa
@@ -96,7 +120,7 @@ def create_app() -> Flask:
appLog.setLevel(logging.DEBUG)
# Enable CORS
@app.after_request
@app.after_request # type: ignore
def after_request(response: Response) -> Response:
response.headers.add('Access-Control-Allow-Origin', '*')
response.headers.add(
@@ -129,17 +153,4 @@ def create_app() -> Flask:
else:
return render_template('index.html')
@app.cli.command('drop-db')
def drop_db() -> None:
"""Empty database and delete uploaded files for dev environments."""
if app_settings == 'fittrackee.config.ProductionConfig':
print('This is a production server, aborting!')
return
db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
db.drop_all()
db.session.commit()
print('Database dropped.')
shutil.rmtree(app.config['UPLOAD_FOLDER'], ignore_errors=True)
print('Uploaded files deleted.')
return app

View File

@@ -3,16 +3,23 @@
import os
from typing import Dict, Optional
import click
import gunicorn.app.base
from flask import Flask
from flask_migrate import upgrade
from fittrackee import create_app
from fittrackee.users.exceptions import UserNotFoundException
from fittrackee.users.utils.admin import UserManagerService
HOST = os.getenv('HOST', '0.0.0.0')
HOST = os.getenv('HOST', '127.0.0.1')
PORT = os.getenv('PORT', '5000')
WORKERS = os.getenv('APP_WORKERS', 1)
BASEDIR = os.path.abspath(os.path.dirname(__file__))
WARNING_MESSAGE = (
"\nThis command is deprecated, it will be removed in a next version.\n"
"Please use ftcli instead.\n"
)
app = create_app()
@@ -37,7 +44,39 @@ class StandaloneApplication(gunicorn.app.base.BaseApplication):
return self.application
# DEPRECATED COMMANDS
@click.group()
def users_cli() -> None:
pass
@users_cli.command('set_admin')
@click.argument('username')
def set_admin(username: str) -> None:
"""
[deprecated] Set admin rights for given user.
It will be removed in a next version.
"""
print(WARNING_MESSAGE)
with app.app_context():
try:
user_manager_service = UserManagerService(username)
user_manager_service.update(
is_admin=True,
)
print(f"User '{username}' updated.")
except UserNotFoundException:
print(f"User '{username}' not found.")
def upgrade_db() -> None:
"""
[deprecated] Apply migrations.
It will be removed in a next version.
"""
print(WARNING_MESSAGE)
with app.app_context():
upgrade(directory=BASEDIR + '/migrations')

View File

@@ -11,6 +11,7 @@ from fittrackee.responses import (
)
from fittrackee.users.decorators import authenticate_as_admin
from fittrackee.users.models import User
from fittrackee.users.utils.controls import is_valid_email
from .models import AppConfig
from .utils import update_app_config_from_database, verify_app_config
@@ -39,13 +40,15 @@ def get_application_config() -> Union[Dict, HttpResponse]:
{
"data": {
"admin_contact": "admin@example.com",
"gpx_limit_import": 10,
"is_email_sending_enabled": true,
"is_registration_enabled": false,
"max_single_file_size": 1048576,
"max_zip_file_size": 10485760,
"max_users": 0,
"max_zip_file_size": 10485760,
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.5.7"
"version": "0.6.10"
},
"status": "success"
}
@@ -87,20 +90,25 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
{
"data": {
"admin_contact": "admin@example.com",
"gpx_limit_import": 10,
"is_registration_enabled": true,
"is_email_sending_enabled": true,
"is_registration_enabled": false,
"max_single_file_size": 1048576,
"max_users": 10,
"max_zip_file_size": 10485760,
"max_users": 10
"map_attribution": "&copy; <a href=http://www.openstreetmap.org/copyright>OpenStreetMap</a> contributors"
"version": "0.6.10"
},
"status": "success"
}
:<json string admin_contact: email to contact the administrator
:<json integer gpx_limit_import: max number of files in zip archive
:<json boolean is_registration_enabled: is registration enabled ?
:<json integer max_single_file_size: max size of a single file
:<json integer max_zip_file_size: max size of a zip archive
:<json integer max_users: max users allowed to register on instance
:<json integer max_zip_file_size: max size of a zip archive
:reqheader Authorization: OAuth 2.0 Bearer Token
@@ -110,6 +118,7 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
- provide a valid auth token
- signature expired, please log in again
- invalid token, please log in again
- valid email must be provided for admin contact
:statuscode 403: you do not have permissions
:statuscode 500: error when updating configuration
"""
@@ -118,6 +127,9 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
return InvalidPayloadErrorResponse()
ret = verify_app_config(config_data)
admin_contact = config_data.get('admin_contact')
if admin_contact and not is_valid_email(admin_contact):
ret.append('valid email must be provided for admin contact')
if ret:
return InvalidPayloadErrorResponse(message=ret)
@@ -133,6 +145,8 @@ def update_application_config(auth_user: User) -> Union[Dict, HttpResponse]:
config.max_zip_file_size = config_data.get('max_zip_file_size')
if 'max_users' in config_data:
config.max_users = config_data.get('max_users')
if 'admin_contact' in config_data:
config.admin_contact = admin_contact if admin_contact else None
if config.max_zip_file_size < config.max_single_file_size:
return InvalidPayloadErrorResponse(

View File

@@ -23,6 +23,7 @@ class AppConfig(BaseModel):
db.Integer, default=1048576, nullable=False
)
max_zip_file_size = db.Column(db.Integer, default=10485760, nullable=False)
admin_contact = db.Column(db.String(255), nullable=True)
@property
def is_registration_enabled(self) -> bool:
@@ -43,7 +44,9 @@ class AppConfig(BaseModel):
def serialize(self) -> Dict:
return {
'admin_contact': self.admin_contact,
'gpx_limit_import': self.gpx_limit_import,
'is_email_sending_enabled': current_app.config['CAN_SEND_EMAILS'],
'is_registration_enabled': self.is_registration_enabled,
'max_single_file_size': self.max_single_file_size,
'max_zip_file_size': self.max_zip_file_size,

View File

@@ -0,0 +1,14 @@
import click
from fittrackee.migrations.commands import db_cli
from fittrackee.users.commands import users_cli
@click.group()
def cli() -> None:
"""FitTrackee Command Line Interface"""
pass
cli.add_command(db_cli)
cli.add_command(users_cli)

3
fittrackee/cli/app.py Normal file
View File

@@ -0,0 +1,3 @@
from fittrackee import create_app
app = create_app(init_email=False)

View File

@@ -12,7 +12,6 @@ else:
class BaseConfig:
"""Base configuration"""
DEBUG = False
TESTING = False
@@ -30,6 +29,7 @@ class BaseConfig:
UI_URL = os.environ.get('UI_URL')
EMAIL_URL = os.environ.get('EMAIL_URL')
SENDER_EMAIL = os.environ.get('SENDER_EMAIL')
CAN_SEND_EMAILS = False
DRAMATIQ_BROKER = broker
TILE_SERVER = {
'URL': os.environ.get(
@@ -43,40 +43,42 @@ class BaseConfig:
' contributors',
),
'DEFAULT_STATICMAP': (
os.environ.get('DEFAULT_STATICMAP', 'False') == 'True'
os.environ.get('DEFAULT_STATICMAP', 'false').lower() == 'true'
),
'STATICMAP_SUBDOMAINS': os.environ.get('STATICMAP_SUBDOMAINS', ''),
}
TRANSLATIONS_FOLDER = os.path.join(
current_app.root_path, 'emails/translations'
)
LANGUAGES = ['en', 'fr', 'de']
class DevelopmentConfig(BaseConfig):
"""Development configuration"""
DEBUG = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL')
SECRET_KEY = 'development key'
SECRET_KEY = 'development key' # nosec
BCRYPT_LOG_ROUNDS = 4
DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
class TestingConfig(BaseConfig):
"""Testing configuration"""
DEBUG = True
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_TEST_URL')
SECRET_KEY = 'test key'
SECRET_KEY = 'test key' # nosec
BCRYPT_LOG_ROUNDS = 4
TOKEN_EXPIRATION_DAYS = 0
TOKEN_EXPIRATION_SECONDS = 3
PASSWORD_TOKEN_EXPIRATION_SECONDS = 3
UPLOAD_FOLDER = '/tmp/fitTrackee/uploads'
UI_URL = 'http://0.0.0.0:5000'
SENDER_EMAIL = 'fittrackee@example.com'
class ProductionConfig(BaseConfig):
"""Production configuration"""
class End2EndTestingConfig(TestingConfig):
DRAMATIQ_BROKER_URL = os.getenv('REDIS_URL', 'redis://')
class ProductionConfig(BaseConfig):
DEBUG = False
# https://docs.sqlalchemy.org/en/13/core/pooling.html#using-connection-pools-with-multiprocessing-or-os-fork # noqa
SQLALCHEMY_ENGINE_OPTIONS = (

View File

@@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.f76e2e3d.js"></script><script defer="defer" src="/static/js/app.3c006379.js"></script><link href="/static/css/app.28b1f60f.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><!--[if IE]><link rel="icon" href="/favicon.ico"><![endif]--><link rel="stylesheet" href="/static/css/fork-awesome.min.css"/><link rel="stylesheet" href="/static/css/leaflet.css"/><title>FitTrackee</title><script defer="defer" src="/static/js/chunk-vendors.7132edc6.js"></script><script defer="defer" src="/static/js/app.bf1d4e1c.js"></script><link href="/static/css/app.32d0ced1.css" rel="stylesheet"><link rel="icon" type="image/png" sizes="32x32" href="/img/icons/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/img/icons/favicon-16x16.png"><link rel="manifest" href="/manifest.json"><meta name="theme-color" content="#4DBA87"><meta name="apple-mobile-web-app-capable" content="no"><meta name="apple-mobile-web-app-status-bar-style" content="default"><meta name="apple-mobile-web-app-title" content="fittrackee_client"><link rel="apple-touch-icon" href="/img/icons/apple-touch-icon-152x152.png"><link rel="mask-icon" href="/img/icons/safari-pinned-tab.svg" color="#4DBA87"><meta name="msapplication-TileImage" content="/img/icons/msapplication-icon-144x144.png"><meta name="msapplication-TileColor" content="#000000"></head><body><noscript><strong>We're sorry but FitTrackee doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
#account-confirmation[data-v-785df978]{display:flex;flex-direction:column;align-items:center}#account-confirmation svg[data-v-785df978]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#account-confirmation .error-message[data-v-785df978]{font-size:1.1em;text-align:center;display:flex;flex-direction:column}@media screen and (max-width:1000px){#account-confirmation .error-message[data-v-785df978]{font-size:1em}}#email-update[data-v-8c2ec9ce]{display:flex;flex-direction:column;align-items:center}#email-update svg[data-v-8c2ec9ce]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#email-update .error-message[data-v-8c2ec9ce]{font-size:1.1em;text-align:center;display:flex;flex-direction:column}@media screen and (max-width:1000px){#email-update .error-message[data-v-8c2ec9ce]{font-size:1em}}#profile[data-v-05463732],#profile[data-v-05463732] .profile-form{display:flex;flex-direction:column}#profile[data-v-05463732] .profile-form hr{border-color:var(--card-border-color);border-width:1px 0 0 0}#profile[data-v-05463732] .profile-form .form-items{display:flex;flex-direction:column}#profile[data-v-05463732] .profile-form .form-items input{margin:5px 0}#profile[data-v-05463732] .profile-form .form-items select{height:35px;padding:5px 0}#profile[data-v-05463732] .profile-form .form-items ::v-deep(.custom-textarea) textarea{padding:5px}#profile[data-v-05463732] .profile-form .form-items .form-item{display:flex;flex-direction:column;padding:10px}#profile[data-v-05463732] .profile-form .form-items .birth-date{height:20px}#profile[data-v-05463732] .profile-form .form-buttons{display:flex;margin-top:10px;padding:10px 0;gap:10px}#user[data-v-af7007f4]{margin:auto;width:700px}@media screen and (max-width:1000px){#user[data-v-af7007f4]{width:100%;margin:0 auto 50px auto}}

View File

@@ -1 +0,0 @@
#profile[data-v-163d82f7],#profile[data-v-163d82f7] .profile-form{display:flex;flex-direction:column}#profile[data-v-163d82f7] .profile-form hr{border-color:var(--card-border-color);border-width:1px 0 0 0}#profile[data-v-163d82f7] .profile-form .form-items{display:flex;flex-direction:column}#profile[data-v-163d82f7] .profile-form .form-items input{margin:5px 0}#profile[data-v-163d82f7] .profile-form .form-items select{height:35px;padding:5px 0}#profile[data-v-163d82f7] .profile-form .form-items ::v-deep(.custom-textarea) textarea{padding:5px}#profile[data-v-163d82f7] .profile-form .form-items .form-item{display:flex;flex-direction:column;padding:10px}#profile[data-v-163d82f7] .profile-form .form-items .birth-date{height:20px}#profile[data-v-163d82f7] .profile-form .form-buttons{display:flex;margin-top:10px;padding:10px 0;gap:10px}#user[data-v-10e7b479]{margin:auto;width:700px}@media screen and (max-width:1000px){#user[data-v-10e7b479]{width:100%;margin:0 auto 50px auto}}

View File

@@ -1 +0,0 @@
#password-action-done[data-v-18334f6d]{display:flex;flex-direction:column;align-items:center}#password-action-done svg[data-v-18334f6d]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#password-action-done .password-message[data-v-18334f6d]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#password-action-done .password-message[data-v-18334f6d]{font-size:1em}}#password-reset-request[data-v-68377e44] .card .card-content #user-form{width:100%}#password-reset[data-v-f5e39b60]{display:flex}#password-reset .container[data-v-f5e39b60]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#password-reset .container[data-v-f5e39b60]{width:100%;margin:0 auto 50px auto}}

View File

@@ -0,0 +1 @@
#account-confirmation-email[data-v-66aca424]{display:flex;flex-direction:column}#account-confirmation-email .email-sent[data-v-66aca424]{display:flex;flex-direction:column;align-items:center}#account-confirmation-email .email-sent svg[data-v-66aca424]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#account-confirmation-email .email-sent .email-sent-message[data-v-66aca424]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#account-confirmation-email .email-sent .email-sent-message[data-v-66aca424]{font-size:1em}}#account-confirmation-email[data-v-66aca424] .card .card-content #user-auth-form{margin-top:0}#account-confirmation-email[data-v-66aca424] .card .card-content #user-auth-form #user-form{width:100%}#account-confirmation[data-v-35aad344]{display:flex}#account-confirmation .container[data-v-35aad344]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#account-confirmation .container[data-v-35aad344]{width:100%}}#password-action-done[data-v-eac78356]{display:flex;flex-direction:column;align-items:center}#password-action-done svg[data-v-eac78356]{stroke:none;fill-rule:nonzero;fill:var(--app-color);filter:var(--svg-filter);width:100px}#password-action-done .password-message[data-v-eac78356]{font-size:1.1em;text-align:center}@media screen and (max-width:1000px){#password-action-done .password-message[data-v-eac78356]{font-size:1em}}#password-reset-request[data-v-68377e44] .card .card-content #user-form{width:100%}#password-reset[data-v-a1cc55c4]{display:flex}#password-reset .container[data-v-a1cc55c4]{display:flex;justify-content:center;width:50%}@media screen and (max-width:700px){#password-reset .container[data-v-a1cc55c4]{width:100%}}

View File

@@ -1 +0,0 @@
.chart-menu[data-v-af15954c]{display:flex}.chart-menu .chart-arrow[data-v-af15954c],.chart-menu .time-frames[data-v-af15954c]{flex-grow:1;text-align:center}.chart-menu .chart-arrow[data-v-af15954c]{cursor:pointer}.sports-menu{display:flex;flex-wrap:wrap;padding:10px}.sports-menu label{display:flex;align-items:center;font-size:.9em;font-weight:400;min-width:120px;padding:10px}@media screen and (max-width:1000px){.sports-menu label{min-width:100px}}@media screen and (max-width:500px){.sports-menu label{min-width:20px}.sports-menu label .sport-label{display:none}}.sports-menu .sport-img{padding:3px;width:20px;height:20px}#user-statistics.stats-disabled[data-v-7d54529b]{opacity:.3;pointer-events:none}#user-statistics[data-v-7d54529b] .chart-radio{justify-content:space-around;padding:30px 10px 10px 10px}#statistics[data-v-0d93da6e]{display:flex;width:100%}#statistics .container[data-v-0d93da6e]{display:flex;flex-direction:column;width:100%}

View File

@@ -0,0 +1 @@
.chart-menu[data-v-22d55de2]{display:flex}.chart-menu .chart-arrow[data-v-22d55de2],.chart-menu .time-frames[data-v-22d55de2]{flex-grow:1;text-align:center}.chart-menu .chart-arrow[data-v-22d55de2]{cursor:pointer}.sports-menu{display:flex;flex-wrap:wrap;padding:10px}.sports-menu label{display:flex;align-items:center;font-size:.9em;font-weight:400;min-width:120px;padding:10px}@media screen and (max-width:1000px){.sports-menu label{min-width:100px}}@media screen and (max-width:500px){.sports-menu label{min-width:20px}.sports-menu label .sport-label{display:none}}.sports-menu .sport-img{padding:3px;width:20px;height:20px}#user-statistics.stats-disabled[data-v-d693c7da]{opacity:.3;pointer-events:none}#user-statistics[data-v-d693c7da] .chart-radio{justify-content:space-around;padding:30px 10px 10px 10px}#statistics[data-v-2e341d4e]{display:flex;width:100%}#statistics .container[data-v-2e341d4e]{display:flex;flex-direction:column;width:100%}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[328],{6e3:function(t,e,i){i.r(e),i.d(e,{default:function(){return _}});var a=i(6252),s=i(2262),n=i(8273),c=i(8602),r=i(9917);const S=t=>((0,a.dD)("data-v-64629971"),t=t(),(0,a.Cn)(),t),l={id:"admin",class:"view"},p={key:0,class:"container"},u=S((()=>(0,a._)("div",{id:"bottom"},null,-1)));var T=(0,a.aZ)({setup(t){const e=(0,r.o)(),i=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_CONFIG])),S=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_STATS])),T=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.IS_ADMIN])),o=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.USER_LOADING]));return(0,a.wF)((()=>e.dispatch(c.SY.ACTIONS.GET_APPLICATION_STATS))),(t,e)=>{const c=(0,a.up)("router-view");return(0,a.wg)(),(0,a.iD)("div",l,[(0,s.SU)(o)?(0,a.kq)("",!0):((0,a.wg)(),(0,a.iD)("div",p,[(0,s.SU)(T)?((0,a.wg)(),(0,a.j4)(c,{key:0,appConfig:(0,s.SU)(i),appStatistics:(0,s.SU)(S)},null,8,["appConfig","appStatistics"])):((0,a.wg)(),(0,a.j4)(n.Z,{key:1})),u]))])}}}),o=i(3744);const d=(0,o.Z)(T,[["__scopeId","data-v-64629971"]]);var _=d}}]);
//# sourceMappingURL=admin.92270942.js.map

View File

@@ -1 +0,0 @@
{"version":3,"file":"static/js/admin.92270942.js","mappings":"mOAGA,MAAMA,EAAeC,KAAMC,EAAAA,EAAAA,IAAa,mBAAmBD,EAAEA,KAAIE,EAAAA,EAAAA,MAAcF,GACzEG,EAAa,CACjBC,GAAI,QACJC,MAAO,QAEHC,EAAa,CACjBC,IAAK,EACLF,MAAO,aAEHG,EAA2BT,GAAa,KAAmBU,EAAAA,EAAAA,GAAoB,MAAO,CAAEL,GAAI,UAAY,MAAO,KAUrH,OAA4BM,EAAAA,EAAAA,IAAiB,CAC3CC,MAAMC,GAEN,MAAMC,GAAQC,EAAAA,EAAAA,KAERC,GAAqCC,EAAAA,EAAAA,KACzC,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,cAEhBC,GAA6CH,EAAAA,EAAAA,KACjD,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,aAEhBE,GAAuCJ,EAAAA,EAAAA,KAC3C,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,YAEhBC,GAAoCN,EAAAA,EAAAA,KACxC,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,gBAKxB,OAFEE,EAAAA,EAAAA,KAAc,IAAMV,EAAMW,SAASN,EAAAA,GAAAA,QAAAA,yBAE9B,CAACO,EAAUC,KAChB,MAAMC,GAAyBC,EAAAA,EAAAA,IAAkB,eAEjD,OAAQC,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAO3B,EAAY,EACzD4B,EAAAA,EAAAA,IAAOT,IAWLU,EAAAA,EAAAA,IAAoB,IAAI,KAVvBH,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAOxB,EAAY,EACnDyB,EAAAA,EAAAA,IAAOX,KACHS,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaN,EAAwB,CAClDpB,IAAK,EACLQ,WAAWgB,EAAAA,EAAAA,IAAOhB,GAClBI,eAAeY,EAAAA,EAAAA,IAAOZ,IACrB,KAAM,EAAG,CAAC,YAAa,qBACzBU,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaC,EAAAA,EAAU,CAAE3B,IAAK,KACjDC,W,UCjDV,MAAM2B,GAA2B,OAAgB,EAAQ,CAAC,CAAC,YAAY,qBAEvE","sources":["webpack://fittrackee_client/./src/views/AdminView.vue?67de","webpack://fittrackee_client/./src/views/AdminView.vue"],"sourcesContent":["import { defineComponent as _defineComponent } from 'vue'\nimport { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createElementVNode as _createElementVNode, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \"vue\"\n\nconst _withScopeId = n => (_pushScopeId(\"data-v-64629971\"),n=n(),_popScopeId(),n)\nconst _hoisted_1 = {\n id: \"admin\",\n class: \"view\"\n}\nconst _hoisted_2 = {\n key: 0,\n class: \"container\"\n}\nconst _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(\"div\", { id: \"bottom\" }, null, -1))\n\nimport { computed, ComputedRef, onBeforeMount } from 'vue'\n\n import NotFound from '@/components/Common/NotFound.vue'\n import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'\n import { TAppConfig, IAppStatistics } from '@/types/application'\n import { useStore } from '@/use/useStore'\n\n \nexport default /*#__PURE__*/_defineComponent({\n setup(__props) {\n\n const store = useStore()\n\n const appConfig: ComputedRef<TAppConfig> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]\n )\n const appStatistics: ComputedRef<IAppStatistics> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_STATS]\n )\n const isAuthUserAmin: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.IS_ADMIN]\n )\n const userLoading: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]\n )\n\n onBeforeMount(() => store.dispatch(ROOT_STORE.ACTIONS.GET_APPLICATION_STATS))\n\nreturn (_ctx: any,_cache: any) => {\n const _component_router_view = _resolveComponent(\"router-view\")!\n\n return (_openBlock(), _createElementBlock(\"div\", _hoisted_1, [\n (!_unref(userLoading))\n ? (_openBlock(), _createElementBlock(\"div\", _hoisted_2, [\n (_unref(isAuthUserAmin))\n ? (_openBlock(), _createBlock(_component_router_view, {\n key: 0,\n appConfig: _unref(appConfig),\n appStatistics: _unref(appStatistics)\n }, null, 8, [\"appConfig\", \"appStatistics\"]))\n : (_openBlock(), _createBlock(NotFound, { key: 1 })),\n _hoisted_3\n ]))\n : _createCommentVNode(\"\", true)\n ]))\n}\n}\n\n})","import script from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\nexport * from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\n\nimport \"./AdminView.vue?vue&type=style&index=0&id=64629971&lang=scss&scoped=true\"\n\nimport exportComponent from \"/mnt/data-lnx/Devs/00_Perso/FitTrackee/fittrackee_client/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['__scopeId',\"data-v-64629971\"]])\n\nexport default __exports__"],"names":["_withScopeId","n","_pushScopeId","_popScopeId","_hoisted_1","id","class","_hoisted_2","key","_hoisted_3","_createElementVNode","_defineComponent","setup","__props","store","useStore","appConfig","computed","getters","ROOT_STORE","appStatistics","isAuthUserAmin","AUTH_USER_STORE","userLoading","onBeforeMount","dispatch","_ctx","_cache","_component_router_view","_resolveComponent","_openBlock","_createElementBlock","_unref","_createCommentVNode","_createBlock","NotFound","__exports__"],"sourceRoot":""}

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[328],{6e3:function(t,e,i){i.r(e),i.d(e,{default:function(){return _}});var a=i(6252),n=i(2262),s=i(8273),c=i(8602),r=i(9917);const S=t=>((0,a.dD)("data-v-64629971"),t=t(),(0,a.Cn)(),t),l={id:"admin",class:"view"},p={key:0,class:"container"},u=S((()=>(0,a._)("div",{id:"bottom"},null,-1)));var T=(0,a.aZ)({__name:"AdminView",setup(t){const e=(0,r.o)(),i=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_CONFIG])),S=(0,a.Fl)((()=>e.getters[c.SY.GETTERS.APP_STATS])),T=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.IS_ADMIN])),d=(0,a.Fl)((()=>e.getters[c.YN.GETTERS.USER_LOADING]));return(0,a.wF)((()=>e.dispatch(c.SY.ACTIONS.GET_APPLICATION_STATS))),(t,e)=>{const c=(0,a.up)("router-view");return(0,a.wg)(),(0,a.iD)("div",l,[(0,n.SU)(d)?(0,a.kq)("",!0):((0,a.wg)(),(0,a.iD)("div",p,[(0,n.SU)(T)?((0,a.wg)(),(0,a.j4)(c,{key:0,appConfig:(0,n.SU)(i),appStatistics:(0,n.SU)(S)},null,8,["appConfig","appStatistics"])):((0,a.wg)(),(0,a.j4)(s.Z,{key:1})),u]))])}}}),d=i(3744);const o=(0,d.Z)(T,[["__scopeId","data-v-64629971"]]);var _=o}}]);
//# sourceMappingURL=admin.b19d15cc.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"static/js/admin.b19d15cc.js","mappings":"mOAGA,MAAMA,EAAeC,KAAMC,EAAAA,EAAAA,IAAa,mBAAmBD,EAAEA,KAAIE,EAAAA,EAAAA,MAAcF,GACzEG,EAAa,CACjBC,GAAI,QACJC,MAAO,QAEHC,EAAa,CACjBC,IAAK,EACLF,MAAO,aAEHG,EAA2BT,GAAa,KAAmBU,EAAAA,EAAAA,GAAoB,MAAO,CAAEL,GAAI,UAAY,MAAO,KAUrH,OAA4BM,EAAAA,EAAAA,IAAiB,CAC3CC,OAAQ,YACRC,MAAMC,GAEN,MAAMC,GAAQC,EAAAA,EAAAA,KAERC,GAAqCC,EAAAA,EAAAA,KACzC,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,cAEhBC,GAA6CH,EAAAA,EAAAA,KACjD,IAAMH,EAAMI,QAAQC,EAAAA,GAAAA,QAAAA,aAEhBE,GAAuCJ,EAAAA,EAAAA,KAC3C,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,YAEhBC,GAAoCN,EAAAA,EAAAA,KACxC,IAAMH,EAAMI,QAAQI,EAAAA,GAAAA,QAAAA,gBAKxB,OAFEE,EAAAA,EAAAA,KAAc,IAAMV,EAAMW,SAASN,EAAAA,GAAAA,QAAAA,yBAE9B,CAACO,EAAUC,KAChB,MAAMC,GAAyBC,EAAAA,EAAAA,IAAkB,eAEjD,OAAQC,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAO5B,EAAY,EACzD6B,EAAAA,EAAAA,IAAOT,IAWLU,EAAAA,EAAAA,IAAoB,IAAI,KAVvBH,EAAAA,EAAAA,OAAcC,EAAAA,EAAAA,IAAoB,MAAOzB,EAAY,EACnD0B,EAAAA,EAAAA,IAAOX,KACHS,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaN,EAAwB,CAClDrB,IAAK,EACLS,WAAWgB,EAAAA,EAAAA,IAAOhB,GAClBI,eAAeY,EAAAA,EAAAA,IAAOZ,IACrB,KAAM,EAAG,CAAC,YAAa,qBACzBU,EAAAA,EAAAA,OAAcI,EAAAA,EAAAA,IAAaC,EAAAA,EAAU,CAAE5B,IAAK,KACjDC,W,UClDV,MAAM4B,GAA2B,OAAgB,EAAQ,CAAC,CAAC,YAAY,qBAEvE","sources":["webpack://fittrackee_client/./src/views/AdminView.vue?67de","webpack://fittrackee_client/./src/views/AdminView.vue"],"sourcesContent":["import { defineComponent as _defineComponent } from 'vue'\nimport { unref as _unref, resolveComponent as _resolveComponent, openBlock as _openBlock, createBlock as _createBlock, createCommentVNode as _createCommentVNode, createElementVNode as _createElementVNode, createElementBlock as _createElementBlock, pushScopeId as _pushScopeId, popScopeId as _popScopeId } from \"vue\"\n\nconst _withScopeId = n => (_pushScopeId(\"data-v-64629971\"),n=n(),_popScopeId(),n)\nconst _hoisted_1 = {\n id: \"admin\",\n class: \"view\"\n}\nconst _hoisted_2 = {\n key: 0,\n class: \"container\"\n}\nconst _hoisted_3 = /*#__PURE__*/ _withScopeId(() => /*#__PURE__*/_createElementVNode(\"div\", { id: \"bottom\" }, null, -1))\n\nimport { computed, ComputedRef, onBeforeMount } from 'vue'\n\n import NotFound from '@/components/Common/NotFound.vue'\n import { AUTH_USER_STORE, ROOT_STORE } from '@/store/constants'\n import { TAppConfig, IAppStatistics } from '@/types/application'\n import { useStore } from '@/use/useStore'\n\n \nexport default /*#__PURE__*/_defineComponent({\n __name: 'AdminView',\n setup(__props) {\n\n const store = useStore()\n\n const appConfig: ComputedRef<TAppConfig> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_CONFIG]\n )\n const appStatistics: ComputedRef<IAppStatistics> = computed(\n () => store.getters[ROOT_STORE.GETTERS.APP_STATS]\n )\n const isAuthUserAmin: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.IS_ADMIN]\n )\n const userLoading: ComputedRef<boolean> = computed(\n () => store.getters[AUTH_USER_STORE.GETTERS.USER_LOADING]\n )\n\n onBeforeMount(() => store.dispatch(ROOT_STORE.ACTIONS.GET_APPLICATION_STATS))\n\nreturn (_ctx: any,_cache: any) => {\n const _component_router_view = _resolveComponent(\"router-view\")!\n\n return (_openBlock(), _createElementBlock(\"div\", _hoisted_1, [\n (!_unref(userLoading))\n ? (_openBlock(), _createElementBlock(\"div\", _hoisted_2, [\n (_unref(isAuthUserAmin))\n ? (_openBlock(), _createBlock(_component_router_view, {\n key: 0,\n appConfig: _unref(appConfig),\n appStatistics: _unref(appStatistics)\n }, null, 8, [\"appConfig\", \"appStatistics\"]))\n : (_openBlock(), _createBlock(NotFound, { key: 1 })),\n _hoisted_3\n ]))\n : _createCommentVNode(\"\", true)\n ]))\n}\n}\n\n})","import script from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\nexport * from \"./AdminView.vue?vue&type=script&setup=true&lang=ts\"\n\nimport \"./AdminView.vue?vue&type=style&index=0&id=64629971&lang=scss&scoped=true\"\n\nimport exportComponent from \"/mnt/data-lnx/Devs/00_Perso/FitTrackee/fittrackee_client/node_modules/vue-loader/dist/exportHelper.js\"\nconst __exports__ = /*#__PURE__*/exportComponent(script, [['__scopeId',\"data-v-64629971\"]])\n\nexport default __exports__"],"names":["_withScopeId","n","_pushScopeId","_popScopeId","_hoisted_1","id","class","_hoisted_2","key","_hoisted_3","_createElementVNode","_defineComponent","__name","setup","__props","store","useStore","appConfig","computed","getters","ROOT_STORE","appStatistics","isAuthUserAmin","AUTH_USER_STORE","userLoading","onBeforeMount","dispatch","_ctx","_cache","_component_router_view","_resolveComponent","_openBlock","_createElementBlock","_unref","_createCommentVNode","_createBlock","NotFound","__exports__"],"sourceRoot":""}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[845],{4264:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(8602),c=r(9917);const l={key:0,id:"account-confirmation",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"AccountConfirmationView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),S=(0,n.Fl)((()=>t.query.token));function m(){S.value?_.dispatch(i.YN.ACTIONS.CONFIRM_ACCOUNT,{token:S.value}):r.push("/")}return(0,n.wF)((()=>m())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(e,t)=>{const r=(0,n.up)("router-link");return(0,a.SU)(d)?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n.Wm)(r,{class:"links",to:"/account-confirmation/resend"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("buttons.ACCOUNT-CONFIRMATION-RESEND"))+"? ",1)])),_:1})])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-785df978"]]);var m=S},8160:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var n=r(6252),a=r(2262),s=r(3577),u=r(2201),o=r(7167),i=r(8602),c=r(9917);const l={key:0,id:"email-update",class:"center-card with-margin"},E={class:"error-message"};var _=(0,n.aZ)({__name:"EmailUpdateView",setup(e){const t=(0,u.yj)(),r=(0,u.tv)(),_=(0,c.o)(),d=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.AUTH_USER_PROFILE])),S=(0,n.Fl)((()=>_.getters[i.YN.GETTERS.IS_AUTHENTICATED])),m=(0,n.Fl)((()=>_.getters[i.SY.GETTERS.ERROR_MESSAGES])),p=(0,n.Fl)((()=>t.query.token));function R(){p.value?_.dispatch(i.YN.ACTIONS.CONFIRM_EMAIL,{token:p.value,refreshUser:S.value}):r.push("/")}return(0,n.wF)((()=>R())),(0,n.Ah)((()=>_.commit(i.SY.MUTATIONS.EMPTY_ERROR_MESSAGES))),(0,n.YP)((()=>m.value),(e=>{d.value.username&&e&&r.push("/")})),(e,t)=>{const r=(0,n.up)("router-link"),u=(0,n.up)("i18n-t");return(0,a.SU)(m)&&!(0,a.SU)(d).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(o.Z),(0,n._)("p",E,[(0,n._)("span",null,(0,s.zw)(e.$t("error.SOMETHING_WRONG"))+".",1),(0,n._)("span",null,[(0,n.Wm)(u,{keypath:"user.PROFILE.ERRORED_EMAIL_UPDATE"},{default:(0,n.w5)((()=>[(0,n.Wm)(r,{to:"/login"},{default:(0,n.w5)((()=>[(0,n.Uk)((0,s.zw)(e.$t("user.LOG_IN")),1)])),_:1})])),_:1})])])])):(0,n.kq)("",!0)}}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-8c2ec9ce"]]);var m=S},6266:function(e,t,r){r.r(t),r.d(t,{default:function(){return d}});var n=r(6252),a=r(2262),s=r(8602),u=r(9917);const o=e=>((0,n.dD)("data-v-05463732"),e=e(),(0,n.Cn)(),e),i={key:0,id:"profile",class:"container view"},c=o((()=>(0,n._)("div",{id:"bottom"},null,-1)));var l=(0,n.aZ)({__name:"ProfileView",setup(e){const t=(0,u.o)(),r=(0,n.Fl)((()=>t.getters[s.YN.GETTERS.AUTH_USER_PROFILE]));return(e,t)=>{const s=(0,n.up)("router-view");return(0,a.SU)(r).username?((0,n.wg)(),(0,n.iD)("div",i,[(0,n.Wm)(s,{user:(0,a.SU)(r)},null,8,["user"]),c])):(0,n.kq)("",!0)}}}),E=r(3744);const _=(0,E.Z)(l,[["__scopeId","data-v-05463732"]]);var d=_},9453:function(e,t,r){r.r(t),r.d(t,{default:function(){return m}});var n=r(6252),a=r(2262),s=r(2201),u=r(2179),o=r(7408),i=r(8602),c=r(9917);const l={key:0,id:"user",class:"view"},E={class:"box"};var _=(0,n.aZ)({__name:"UserView",props:{fromAdmin:{type:Boolean}},setup(e){const t=e,{fromAdmin:r}=(0,a.BK)(t),_=(0,s.yj)(),d=(0,c.o)(),S=(0,n.Fl)((()=>d.getters[i.RT.GETTERS.USER]));return(0,n.wF)((()=>{_.params.username&&"string"===typeof _.params.username&&d.dispatch(i.RT.ACTIONS.GET_USER,_.params.username)})),(0,n.Jd)((()=>{d.dispatch(i.RT.ACTIONS.EMPTY_USER)})),(e,t)=>(0,a.SU)(S).username?((0,n.wg)(),(0,n.iD)("div",l,[(0,n.Wm)(u.Z,{user:(0,a.SU)(S)},null,8,["user"]),(0,n._)("div",E,[(0,n.Wm)(o.Z,{user:(0,a.SU)(S),"from-admin":(0,a.SU)(r)},null,8,["user","from-admin"])])])):(0,n.kq)("",!0)}}),d=r(3744);const S=(0,d.Z)(_,[["__scopeId","data-v-af7007f4"]]);var m=S}}]);
//# sourceMappingURL=profile.12bdb140.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[845],{2023:function(e,r,t){t.r(r),t.d(r,{default:function(){return f}});var s=t(6252),a=t(2262),n=t(8602),u=t(9917);const i=e=>((0,s.dD)("data-v-163d82f7"),e=e(),(0,s.Cn)(),e),c={key:0,id:"profile",class:"container view"},d=i((()=>(0,s._)("div",{id:"bottom"},null,-1)));var o=(0,s.aZ)({setup(e){const r=(0,u.o)(),t=(0,s.Fl)((()=>r.getters[n.YN.GETTERS.AUTH_USER_PROFILE]));return(e,r)=>{const n=(0,s.up)("router-view");return(0,a.SU)(t).username?((0,s.wg)(),(0,s.iD)("div",c,[(0,s.Wm)(n,{user:(0,a.SU)(t)},null,8,["user"]),d])):(0,s.kq)("",!0)}}}),l=t(3744);const v=(0,l.Z)(o,[["__scopeId","data-v-163d82f7"]]);var f=v},8368:function(e,r,t){t.r(r),t.d(r,{default:function(){return m}});var s=t(6252),a=t(2262),n=t(2119),u=t(5160),i=t(2165),c=t(8602),d=t(9917);const o={key:0,id:"user",class:"view"},l={class:"box"};var v=(0,s.aZ)({setup(e){const r=(0,n.yj)(),t=(0,d.o)(),v=(0,s.Fl)((()=>t.getters[c.RT.GETTERS.USER]));return(0,s.wF)((()=>{r.params.username&&"string"===typeof r.params.username&&t.dispatch(c.RT.ACTIONS.GET_USER,r.params.username)})),(0,s.Jd)((()=>{t.dispatch(c.RT.ACTIONS.EMPTY_USER)})),(e,r)=>(0,a.SU)(v).username?((0,s.wg)(),(0,s.iD)("div",o,[(0,s.Wm)(u.Z,{user:(0,a.SU)(v)},null,8,["user"]),(0,s._)("div",l,[(0,s.Wm)(i.Z,{user:(0,a.SU)(v),"from-admin":!0},null,8,["user"])])])):(0,s.kq)("",!0)}}),f=t(3744);const p=(0,f.Z)(v,[["__scopeId","data-v-10e7b479"]]);var m=p}}]);
//# sourceMappingURL=profile.97ac14b7.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[193],{9161:function(e,s,t){t.r(s),t.d(s,{default:function(){return A}});t(6699);var a=t(6252),r=t(2262),l=t(3577),o=t(3324),n=t(9996);const c={class:"chart-menu"},i={class:"chart-arrow"},u={class:"time-frames custom-checkboxes-group"},d={class:"time-frames-checkboxes custom-checkboxes"},p=["id","name","checked","onInput"],m={class:"chart-arrow"};var v=(0,a.aZ)({__name:"StatsMenu",emits:["arrowClick","timeFrameUpdate"],setup(e,{emit:s}){const t=(0,r.iH)("month"),o=["week","month","year"];function n(e){t.value=e,s("timeFrameUpdate",e)}return(e,r)=>((0,a.wg)(),(0,a.iD)("div",c,[(0,a._)("div",i,[(0,a._)("i",{class:"fa fa-chevron-left","aria-hidden":"true",onClick:r[0]||(r[0]=e=>s("arrowClick",!0))})]),(0,a._)("div",u,[(0,a._)("div",d,[((0,a.wg)(),(0,a.iD)(a.HY,null,(0,a.Ko)(o,(s=>(0,a._)("div",{class:"time-frame custom-checkbox",key:s},[(0,a._)("label",null,[(0,a._)("input",{type:"radio",id:s,name:s,checked:t.value===s,onInput:e=>n(s)},null,40,p),(0,a._)("span",null,(0,l.zw)(e.$t(`statistics.TIME_FRAMES.${s}`)),1)])]))),64))])]),(0,a._)("div",m,[(0,a._)("i",{class:"fa fa-chevron-right","aria-hidden":"true",onClick:r[1]||(r[1]=e=>s("arrowClick",!1))})])]))}}),k=t(3744);const _=(0,k.Z)(v,[["__scopeId","data-v-22d55de2"]]);var S=_,w=t(631);const f={class:"sports-menu"},h=["id","name","checked","onInput"],U={class:"sport-label"};var b=(0,a.aZ)({__name:"StatsSportsMenu",props:{userSports:null,selectedSportIds:{default:()=>[]}},emits:["selectedSportIdsUpdate"],setup(e,{emit:s}){const t=e,{t:n}=(0,o.QT)(),c=(0,a.f3)("sportColors"),{selectedSportIds:i}=(0,r.BK)(t),u=(0,a.Fl)((()=>(0,w.xH)(t.userSports,n)));function d(e){s("selectedSportIdsUpdate",e)}return(e,s)=>{const t=(0,a.up)("SportImage");return(0,a.wg)(),(0,a.iD)("div",f,[((0,a.wg)(!0),(0,a.iD)(a.HY,null,(0,a.Ko)((0,r.SU)(u),(e=>((0,a.wg)(),(0,a.iD)("label",{type:"checkbox",key:e.id,style:(0,l.j5)({color:e.color?e.color:(0,r.SU)(c)[e.label]})},[(0,a._)("input",{type:"checkbox",id:e.id,name:e.label,checked:(0,r.SU)(i).includes(e.id),onInput:s=>d(e.id)},null,40,h),(0,a.Wm)(t,{"sport-label":e.label,color:e.color},null,8,["sport-label","color"]),(0,a._)("span",U,(0,l.zw)(e.translatedLabel),1)],4)))),128))])}}});const I=b;var g=I,T=t(9318);const y={key:0,id:"user-statistics"};var C=(0,a.aZ)({__name:"index",props:{sports:null,user:null},setup(e){const s=e,{t:t}=(0,o.QT)(),{sports:l,user:c}=(0,r.BK)(s),i=(0,r.iH)("month"),u=(0,r.iH)(v(i.value)),d=(0,a.Fl)((()=>(0,w.xH)(s.sports,t))),p=(0,r.iH)(_(s.sports));function m(e){i.value=e,u.value=v(i.value)}function v(e){return(0,T.aZ)(new Date,e,s.user.weekm)}function k(e){u.value=(0,T.FN)(u.value,e,s.user.weekm)}function _(e){return e.map((e=>e.id))}function f(e){p.value.includes(e)?p.value=p.value.filter((s=>s!==e)):p.value.push(e)}return(0,a.YP)((()=>s.sports),(e=>{p.value=_(e)})),(e,s)=>(0,r.SU)(d)?((0,a.wg)(),(0,a.iD)("div",y,[(0,a.Wm)(S,{onTimeFrameUpdate:m,onArrowClick:k}),(0,a.Wm)(n.Z,{sports:(0,r.SU)(l),user:(0,r.SU)(c),chartParams:u.value,"displayed-sport-ids":p.value,fullStats:!0},null,8,["sports","user","chartParams","displayed-sport-ids"]),(0,a.Wm)(g,{"selected-sport-ids":p.value,"user-sports":(0,r.SU)(l),onSelectedSportIdsUpdate:f},null,8,["selected-sport-ids","user-sports"])])):(0,a.kq)("",!0)}});const F=(0,k.Z)(C,[["__scopeId","data-v-d693c7da"]]);var Z=F,x=t(5630),D=t(8602),H=t(9917);const E={id:"statistics",class:"view"},R={key:0,class:"container"};var W=(0,a.aZ)({__name:"StatisticsView",setup(e){const s=(0,H.o)(),t=(0,a.Fl)((()=>s.getters[D.YN.GETTERS.AUTH_USER_PROFILE])),o=(0,a.Fl)((()=>s.getters[D.O8.GETTERS.SPORTS].filter((e=>t.value.sports_list.includes(e.id)))));return(e,s)=>{const n=(0,a.up)("Card");return(0,a.wg)(),(0,a.iD)("div",E,[(0,r.SU)(t).username?((0,a.wg)(),(0,a.iD)("div",R,[(0,a.Wm)(n,null,{title:(0,a.w5)((()=>[(0,a.Uk)((0,l.zw)(e.$t("statistics.STATISTICS")),1)])),content:(0,a.w5)((()=>[(0,a.Wm)(Z,{class:(0,l.C_)({"stats-disabled":0===(0,r.SU)(t).nb_workouts}),user:(0,r.SU)(t),sports:(0,r.SU)(o)},null,8,["class","user","sports"])])),_:1}),0===(0,r.SU)(t).nb_workouts?((0,a.wg)(),(0,a.j4)(x.Z,{key:0})):(0,a.kq)("",!0)])):(0,a.kq)("",!0)])}}});const P=(0,k.Z)(W,[["__scopeId","data-v-2e341d4e"]]);var A=P}}]);
//# sourceMappingURL=statistics.1ad194e3.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +0,0 @@
"use strict";(self["webpackChunkfittrackee_client"]=self["webpackChunkfittrackee_client"]||[]).push([[193],{2319:function(e,s,t){t.r(s),t.d(s,{default:function(){return A}});var r=t(6252),a=t(2262),l=t(3577),o=t(3324),c=t(9472);const n={class:"chart-menu"},i={class:"chart-arrow"},u={class:"time-frames custom-checkboxes-group"},d={class:"time-frames-checkboxes custom-checkboxes"},p=["id","name","checked","onInput"],m={class:"chart-arrow"};var v=(0,r.aZ)({emits:["arrowClick","timeFrameUpdate"],setup(e,{emit:s}){let t=(0,a.iH)("month");const o=["week","month","year"];function c(e){t.value=e,s("timeFrameUpdate",e)}return(e,v)=>((0,r.wg)(),(0,r.iD)("div",n,[(0,r._)("div",i,[(0,r._)("i",{class:"fa fa-chevron-left","aria-hidden":"true",onClick:v[0]||(v[0]=e=>s("arrowClick",!0))})]),(0,r._)("div",u,[(0,r._)("div",d,[((0,r.wg)(),(0,r.iD)(r.HY,null,(0,r.Ko)(o,(s=>(0,r._)("div",{class:"time-frame custom-checkbox",key:s},[(0,r._)("label",null,[(0,r._)("input",{type:"radio",id:s,name:s,checked:(0,a.SU)(t)===s,onInput:e=>c(s)},null,40,p),(0,r._)("span",null,(0,l.zw)(e.$t(`statistics.TIME_FRAMES.${s}`)),1)])]))),64))])]),(0,r._)("div",m,[(0,r._)("i",{class:"fa fa-chevron-right","aria-hidden":"true",onClick:v[1]||(v[1]=e=>s("arrowClick",!1))})])]))}}),k=t(3744);const S=(0,k.Z)(v,[["__scopeId","data-v-af15954c"]]);var w=S,f=t(631);const _={class:"sports-menu"},h=["id","name","checked","onInput"],U={class:"sport-label"};var b=(0,r.aZ)({props:{userSports:null,selectedSportIds:{default:()=>[]}},emits:["selectedSportIdsUpdate"],setup(e,{emit:s}){const t=e,{t:c}=(0,o.QT)(),n=(0,r.f3)("sportColors"),{selectedSportIds:i}=(0,a.BK)(t),u=(0,r.Fl)((()=>(0,f.xH)(t.userSports,c)));function d(e){s("selectedSportIdsUpdate",e)}return(e,s)=>{const t=(0,r.up)("SportImage");return(0,r.wg)(),(0,r.iD)("div",_,[((0,r.wg)(!0),(0,r.iD)(r.HY,null,(0,r.Ko)((0,a.SU)(u),(e=>((0,r.wg)(),(0,r.iD)("label",{type:"checkbox",key:e.id,style:(0,l.j5)({color:e.color?e.color:(0,a.SU)(n)[e.label]})},[(0,r._)("input",{type:"checkbox",id:e.id,name:e.label,checked:(0,a.SU)(i).includes(e.id),onInput:s=>d(e.id)},null,40,h),(0,r.Wm)(t,{"sport-label":e.label,color:e.color},null,8,["sport-label","color"]),(0,r._)("span",U,(0,l.zw)(e.translatedLabel),1)],4)))),128))])}}});const I=b;var g=I,T=t(9318);const y={key:0,id:"user-statistics"};var C=(0,r.aZ)({props:{sports:null,user:null},setup(e){const s=e,{t:t}=(0,o.QT)(),{sports:l,user:n}=(0,a.BK)(s);let i=(0,a.iH)("month");const u=(0,a.iH)(v(i.value)),d=(0,r.Fl)((()=>(0,f.xH)(s.sports,t))),p=(0,a.iH)(S(s.sports));function m(e){i.value=e,u.value=v(i.value)}function v(e){return(0,T.aZ)(new Date,e,s.user.weekm)}function k(e){u.value=(0,T.FN)(u.value,e,s.user.weekm)}function S(e){return e.map((e=>e.id))}function _(e){p.value.includes(e)?p.value=p.value.filter((s=>s!==e)):p.value.push(e)}return(0,r.YP)((()=>s.sports),(e=>{p.value=S(e)})),(e,s)=>(0,a.SU)(d)?((0,r.wg)(),(0,r.iD)("div",y,[(0,r.Wm)(w,{onTimeFrameUpdate:m,onArrowClick:k}),(0,r.Wm)(c.Z,{sports:(0,a.SU)(l),user:(0,a.SU)(n),chartParams:u.value,"displayed-sport-ids":p.value,fullStats:!0},null,8,["sports","user","chartParams","displayed-sport-ids"]),(0,r.Wm)(g,{"selected-sport-ids":p.value,"user-sports":(0,a.SU)(l),onSelectedSportIdsUpdate:_},null,8,["selected-sport-ids","user-sports"])])):(0,r.kq)("",!0)}});const F=(0,k.Z)(C,[["__scopeId","data-v-7d54529b"]]);var Z=F,D=t(5630),H=t(8602),x=t(9917);const E={id:"statistics",class:"view"},R={key:0,class:"container"};var W=(0,r.aZ)({setup(e){const s=(0,x.o)(),t=(0,r.Fl)((()=>s.getters[H.YN.GETTERS.AUTH_USER_PROFILE])),o=(0,r.Fl)((()=>s.getters[H.O8.GETTERS.SPORTS].filter((e=>t.value.sports_list.includes(e.id)))));return(e,s)=>{const c=(0,r.up)("Card");return(0,r.wg)(),(0,r.iD)("div",E,[(0,a.SU)(t).username?((0,r.wg)(),(0,r.iD)("div",R,[(0,r.Wm)(c,null,{title:(0,r.w5)((()=>[(0,r.Uk)((0,l.zw)(e.$t("statistics.STATISTICS")),1)])),content:(0,r.w5)((()=>[(0,r.Wm)(Z,{class:(0,l.C_)({"stats-disabled":0===(0,a.SU)(t).nb_workouts}),user:(0,a.SU)(t),sports:(0,a.SU)(o)},null,8,["class","user","sports"])])),_:1}),0===(0,a.SU)(t).nb_workouts?((0,r.wg)(),(0,r.j4)(D.Z,{key:0})):(0,r.kq)("",!0)])):(0,r.kq)("",!0)])}}});const P=(0,k.Z)(W,[["__scopeId","data-v-0d93da6e"]]);var A=P}}]);
//# sourceMappingURL=statistics.221180ef.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,8 +3,9 @@ import smtplib
import ssl
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from typing import Dict, Optional, Type, Union
from typing import Dict, List, Optional, Type, Union
from babel.support import Translations
from flask import Flask
from jinja2 import Environment, FileSystemLoader, select_autoescape
from urllib3.util import parse_url
@@ -38,16 +39,43 @@ class EmailMessage:
class EmailTemplate:
def __init__(self, template_directory: str) -> None:
def __init__(
self,
template_directory: str,
translations_directory: str,
languages: List[str],
) -> None:
self._translations = self._get_translations(
translations_directory, languages
)
self._env = Environment(
autoescape=select_autoescape(['html', 'htm', 'xml']),
loader=FileSystemLoader(template_directory),
extensions=['jinja2.ext.i18n'],
)
@staticmethod
def _get_translations(
translations_directory: str, languages: List[str]
) -> Dict:
translations = {}
for language in languages:
translations[language] = Translations.load(
dirname=translations_directory, locales=[language]
)
return translations
def _load_translation(self, lang: str) -> None:
self._env.install_gettext_translations( # type: ignore
self._translations[lang],
newstyle=True,
)
def get_content(
self, template_name: str, lang: str, part: str, data: Dict
) -> str:
template = self._env.get_template(f'{template_name}/{lang}/{part}')
self._load_translation(lang)
template = self._env.get_template(f'{template_name}/{part}')
return template.render(data)
def get_all_contents(self, template: str, lang: str, data: Dict) -> Dict:
@@ -92,7 +120,11 @@ class EmailService:
self.username = parsed_url['username']
self.password = parsed_url['password']
self.sender_email = app.config['SENDER_EMAIL']
self.email_template = EmailTemplate(app.config['TEMPLATES_FOLDER'])
self.email_template = EmailTemplate(
app.config['TEMPLATES_FOLDER'],
app.config['TRANSLATIONS_FOLDER'],
app.config['LANGUAGES'],
)
@staticmethod
def parse_email_url(email_url: str) -> Dict:
@@ -131,9 +163,11 @@ class EmailService:
with self.smtp(
self.host, self.port, **connection_params # type: ignore
) as smtp:
if self.use_tls:
smtp.ehlo()
smtp.starttls(context=context)
smtp.ehlo()
if self.username and self.password:
smtp.login(self.username, self.password) # type: ignore
if self.use_tls:
smtp.starttls(context=context)
smtp.sendmail(self.sender_email, recipient, message.as_string())
smtp.quit()

View File

@@ -11,3 +11,43 @@ def reset_password_email(user: Dict, email_data: Dict) -> None:
recipient=user['email'],
data=email_data,
)
@dramatiq.actor(queue_name='fittrackee_emails')
def email_updated_to_current_address(user: Dict, email_data: Dict) -> None:
email_service.send(
template='email_update_to_current_email',
lang=user['language'],
recipient=user['email'],
data=email_data,
)
@dramatiq.actor(queue_name='fittrackee_emails')
def email_updated_to_new_address(user: Dict, email_data: Dict) -> None:
email_service.send(
template='email_update_to_new_email',
lang=user['language'],
recipient=user['email'],
data=email_data,
)
@dramatiq.actor(queue_name='fittrackee_emails')
def password_change_email(user: Dict, email_data: Dict) -> None:
email_service.send(
template='password_change',
lang=user['language'],
recipient=user['email'],
data=email_data,
)
@dramatiq.actor(queue_name='fittrackee_emails')
def account_confirmation_email(user: Dict, email_data: Dict) -> None:
email_service.send(
template='account_confirmation',
lang=user['language'],
recipient=user['email'],
data=email_data,
)

View File

@@ -0,0 +1,26 @@
{% extends "layout.html" %}
{% block title %}{{ _('Confirm your account') }}{% endblock %}
{% block preheader %}{{ _('Use this link to confirm your account.') }}{% endblock %}
{% block content %}<p>{{ _('You have created an account on FitTrackee.') }} {{ _('Use the button below to confirm your address email.') }}</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
<a href="{{account_confirmation_url}}" class="f-fallback button button--green" target="_blank">{{ _('Verify your email') }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>{% endblock %}
{% block not_initiated %}{{ _("If this account creation wasn't initiated by you, please ignore this email.") }}{% endblock %}
{% block url_to_paste %}<table class="body-sub" role="presentation">
<tr>
<td>
<p class="f-fallback sub">{{ _("If you're having trouble with the button above, copy and paste the URL below into your web browser.") }}</p>
<p class="f-fallback sub">{{account_confirmation_url}}</p>
</td>
</tr>
</table>{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends "layout.txt" %}{% block content %}{{ _('You have created an account on FitTrackee.') }}
{{ _('Use the link below to confirm your address email.') }}
{{ _('Verify your email') }}: {{ account_confirmation_url }}
{% if operating_system and browser_name %}{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{% endif %}{{ _("If this account creation wasn't initiated by you, please ignore this email.") }}{% endblock %}

View File

@@ -0,0 +1 @@
FitTrackee - {{ _('Confirm your account') }}

View File

@@ -0,0 +1,18 @@
{% extends "layout.html" %}
{% block title %}{{ _('Email changed') }}{% endblock %}
{% block preheader %}{{ _('Your email is being updated.') }}{% endblock %}
{% block content %}<p>{{ _('You recently requested to change your email address for your FitTrackee account to:') }}</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
{{new_email_address}}
</td>
</tr>
</table>
</td>
</tr>
</table>{% endblock %}
{% block not_initiated %}{{ _("If this email change wasn't initiated by you, please change your password immediately or contact your administrator if your account is locked.") }}{% endblock %}

View File

@@ -0,0 +1,4 @@
{% extends "layout.txt" %}{% block content %}{{ _('You recently requested to change your email address for your FitTrackee account to:') }} {{ new_email_address }}
{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{{ _("If this email change wasn't initiated by you, please change your password immediately or contact your administrator if your account is locked.") }}{% endblock %}

View File

@@ -0,0 +1 @@
FitTrackee - {{ _('Email changed') }}

View File

@@ -0,0 +1,26 @@
{% extends "layout.html" %}
{% block title %}{{ _('Confirm email change') }}{% endblock %}
{% block preheader %}{{ _('Use this link to confirm email change.') }}{% endblock %}
{% block content %}<p>{{ _('You recently requested to change your email address for your FitTrackee account.') }} {{ _('Use the button below to confirm this address.') }}</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
<a href="{{email_confirmation_url}}" class="f-fallback button button--green" target="_blank">{{ _('Verify your email') }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>{% endblock %}
{% block not_initiated %}{{ _("If this email change wasn't initiated by you, please ignore this email.") }}{% endblock %}
{% block url_to_paste %}<table class="body-sub" role="presentation">
<tr>
<td>
<p class="f-fallback sub">{{ _("If you're having trouble with the button above, copy and paste the URL below into your web browser.") }}</p>
<p class="f-fallback sub">{{email_confirmation_url}}</p>
</td>
</tr>
</table>{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends "layout.txt" %}{% block content %}{{ _('You recently requested to change your email address for your FitTrackee account.') }}
{{ _('Use the link below to confirm this address.') }}
{{ _('Verify your email') }}: {{ email_confirmation_url }}
{% if operating_system and browser_name %}{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{% endif %}{{ _("If this email change wasn't initiated by you, please ignore this email.") }}{% endblock %}

View File

@@ -0,0 +1 @@
FitTrackee - {{ _('Confirm email change') }}

View File

@@ -4,7 +4,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" content=""/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Fittrackee - Password reset request</title>
<title>FitTrackee - {% block title %}{% endblock %}</title>
<style type="text/css" rel="stylesheet" media="all">
body {
@@ -194,14 +194,14 @@
<![endif]-->
</head>
<body>
<span class="preheader">Use this link to reset your password. The link is only valid for {{ expiration_delay }}.</span>
<span class="preheader">{% block preheader %}{% endblock %}</span>
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="email-masthead">
<a href="https://example.com" class="f-fallback email-masthead-name">
<a href="{{fittrackee_url}}" class="f-fallback email-masthead-name">
FitTrackee
</a>
</td>
@@ -212,37 +212,15 @@
<tr>
<td class="content-cell">
<div class="f-fallback">
<h1>Hi {{username}},</h1>
<p>You recently requested to reset your password for your account. Use the button below to reset it.
<strong>This password reset link is only valid for {{ expiration_delay }}.</strong>
</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
<a href="{{password_reset_url}}" class="f-fallback button button--green" target="_blank">Reset your password</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<h1>{{ _('Hi %(username)s,', username=username) }}</h1>
{% block content %}{% endblock %}
<p>
For security, this request was received from a {{operating_system}} device using {{browser_name}}.
If you did not request a password reset, please ignore this email.
{% if operating_system and browser_name %}{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{% endif %}{% block not_initiated %}{% endblock %}
</p>
<p>Thanks,
<br>The FitTrackee Team</p>
<table class="body-sub" role="presentation">
<tr>
<td>
<p class="f-fallback sub">If youre having trouble with the button above, copy and paste the URL below into your web browser.</p>
<p class="f-fallback sub">{{password_reset_url}}</p>
</td>
</tr>
</table>
<p>{{ _('Thanks,') }}
<br>{{ _('The FitTrackee Team') }}</p>
{% block url_to_paste %}{% endblock %}
</div>
</td>
</tr>

View File

@@ -0,0 +1,7 @@
{{ _('Hi %(username)s,', username=username) }}
{% block content %}{% endblock %}
{{ _('Thanks,') }}
{{ _('The FitTrackee Team') }}
{{fittrackee_url}}

View File

@@ -0,0 +1,5 @@
{% extends "layout.html" %}
{% block title %}{{ _('Password changed') }}{% endblock %}
{% block preheader %}{{ _('Your password has been changed.') }}{% endblock %}
{% block content %}<p>{{ _('The password for your FitTrackee account has been changed.') }}</p>{% endblock %}
{% block not_initiated %}{{ _("If this password change wasn't initiated by you, please change your password immediately or contact your administrator if your account is locked.") }}{% endblock %}

View File

@@ -0,0 +1,4 @@
{% extends "layout.txt" %}{% block content %}{{ _('The password for your FitTrackee account has been changed.') }}
{% if operating_system and browser_name %}{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{% endif %}{{ _("If this password change wasn't initiated by you, please change your password immediately or contact your administrator if your account is locked.") }}{% endblock %}

View File

@@ -0,0 +1 @@
FitTrackee - {{ _('Password changed') }}

View File

@@ -0,0 +1,28 @@
{% extends "layout.html" %}
{% block title %}{{ _('Password reset request') }}{% endblock %}
{% block preheader %}{{ _('Use this link to reset your password. The link is only valid for %(expiration_delay)s.', expiration_delay=expiration_delay) }}{% endblock %}
{% block content %}<p>{{ _('You recently requested to reset your password for your FitTrackee account.') }} {{ _('Use the button below to reset it.') }}
<strong>{{ _('This password reset link is only valid for %(expiration_delay)s.', expiration_delay=expiration_delay) }}</strong>
</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
<a href="{{password_reset_url}}" class="f-fallback button button--green" target="_blank">{{ _('Reset your password') }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>{% endblock %}
{% block not_initiated %}{{ _('If you did not request a password reset, please ignore this email.') }}{% endblock %}
{% block url_to_paste %}<table class="body-sub" role="presentation">
<tr>
<td>
<p class="f-fallback sub">{{ _("If you're having trouble with the button above, copy and paste the URL below into your web browser.") }}</p>
<p class="f-fallback sub">{{password_reset_url}}</p>
</td>
</tr>
</table>{% endblock %}

View File

@@ -0,0 +1,7 @@
{% extends "layout.txt" %}{% block content %}{{ _('You recently requested to reset your password for your FitTrackee account.') }} {{ _('Use the link below to reset it.') }}
{{ _('This password reset link is only valid for %(expiration_delay)s.', expiration_delay=expiration_delay) }}
{{ _('Reset your password') }}: {{ password_reset_url }}
{% if operating_system and browser_name %}{{ _('For security, this request was received from a %(operating_system)s device using %(browser_name)s.', operating_system=operating_system, browser_name=browser_name) }}
{% endif %}{{ _('If you did not request a password reset, please ignore this email.') }}{% endblock %}

View File

@@ -1,10 +0,0 @@
Hi {{username}},
You recently requested to reset your password for your FitTrackee account. Use the button below to reset it. This password reset link is only valid for {{ expiration_delay }}.
Reset your password ( {{ password_reset_url }} )
For security, this request was received from a {{operating_system}} device using {{browser_name}}. If you did not request a password reset, please ignore this email.
Thanks,
The FitTrackee Team

View File

@@ -1 +0,0 @@
FitTrackee - Password reset request

View File

@@ -1,269 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="x-apple-disable-message-reformatting" content=""/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>FitTrackee - Réinitialiser le mot de passe</title>
<style type="text/css" rel="stylesheet" media="all">
body {
background-color: #F4F4F7;
color: #51545E;
width: 100% !important;
height: 100%;
margin: 0;
-webkit-text-size-adjust: none;
}
a {
color: #3869D4;
}
a img {
border: none;
}
td {
word-break: break-word;
}
.preheader {
display: none !important;
visibility: hidden;
mso-hide: all;
font-size: 1px;
line-height: 1px;
max-height: 0;
max-width: 0;
opacity: 0;
overflow: hidden;
}
body,
td,
th {
font-family: Helvetica, Arial, sans-serif;
}
h1 {
margin-top: 0;
color: #333333;
font-size: 22px;
font-weight: bold;
text-align: left;
}
td,
th {
font-size: 16px;
}
p {
color: #51545E;
margin: .4em 0 1.1875em;
font-size: 16px;
line-height: 1.625;
}
p.sub {
color: #6B6E76;
font-size: 13px;
}
.button {
background-color: #3869D4;
border-top: 10px solid #3869D4;
border-right: 18px solid #3869D4;
border-bottom: 10px solid #3869D4;
border-left: 18px solid #3869D4;
display: inline-block;
color: #FFF;
text-decoration: none;
border-radius: 3px;
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16);
-webkit-text-size-adjust: none;
box-sizing: border-box;
}
.button--green {
background-color: #22BC66;
border-top: 10px solid #22BC66;
border-right: 18px solid #22BC66;
border-bottom: 10px solid #22BC66;
border-left: 18px solid #22BC66;
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
text-align: center !important;
}
}
.email-wrapper {
width: 100%;
margin: 0;
padding: 0;
background-color: #F4F4F7;
}
.email-content {
width: 100%;
margin: 0;
padding: 0;
}
.email-masthead {
padding: 25px 0;
text-align: center;
}
.email-masthead-name {
font-size: 16px;
font-weight: bold;
color: #A8AAAF;
text-decoration: none;
text-shadow: 0 1px 0 white;
}
.email-body {
width: 100%;
margin: 0;
padding: 0;
background-color: #FFFFFF;
}
.email-body-inner {
width: 570px;
margin: 0 auto;
padding: 0;
background-color: #FFFFFF;
}
.body-action {
width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
}
.body-sub {
margin-top: 25px;
padding-top: 25px;
border-top: 1px solid #EAEAEC;
}
.content-cell {
padding: 35px;
}
@media only screen and (max-width: 600px) {
.email-body-inner,
.email-footer {
width: 100% !important;
}
}
@media (prefers-color-scheme: dark) {
body,
.email-body,
.email-body-inner,
.email-content,
.email-wrapper,
.email-masthead,
.email-footer {
background-color: #333333 !important;
color: #FFF !important;
}
p,
h1 {
color: #FFF !important;
}
.email-masthead-name {
text-shadow: none !important;
}
}
</style>
<!--[if mso]>
<style type="text/css">
.f-fallback {
font-family: Arial, sans-serif;
}
</style>
<![endif]-->
</head>
<body>
<span class="preheader">Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide que pendant {{ expiration_delay }}.</span>
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="email-masthead">
<a href="https://example.com" class="f-fallback email-masthead-name">
FitTrackee
</a>
</td>
</tr>
<tr>
<td class="email-body" width="100%" cellpadding="0" cellspacing="0">
<table class="email-body-inner" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell">
<div class="f-fallback">
<h1>Bonjour {{username}},</h1>
<p>Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
Cliquez sur le bouton ci-dessous pour le réinitialiser.
<strong>Cette réinitialisation n'est valide que pendant {{ expiration_delay }}.</strong>
</p>
<table class="body-action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellspacing="0" cellpadding="0" role="presentation">
<tr>
<td align="center">
<a href="{{password_reset_url}}" class="f-fallback button button--green" target="_blank">Réinitialiser le mot de passe</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
<p>
Pour vérification, cette demande a été reçue à partir d'un appareil sous {{operating_system}}, utilisant le navigateur {{browser_name}}.
Si vous n'avez pas demandé de réinitalisation, vous pouvez ignorer cet e-mail.
</p>
<p>Merci,
<br>L'équipe FitTrackee</p>
<table class="body-sub" role="presentation">
<tr>
<td>
<p class="f-fallback sub">Si vous avez des problèmes avec le bouton, vous pouvez copier et coller le lien suivant dans votre navigateur</p>
<p class="f-fallback sub">{{password_reset_url}}</p>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<table class="email-footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
<p class="f-fallback sub align-center">&copy; FitTrackee.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@@ -1,12 +0,0 @@
Bonjour {{username}},
Vous avez récemment demander la réinitilisation du mot de passe de votre compte sur FitTrackee.
Cliquez sur le lien ci-dessous pour le réinitialiser. Ce lien n'est valide que pendant {{ expiration_delay }}.
Réinitialiser le mot de passe: ( {{ password_reset_url }} )
Pour vérification, cette demande a été reçue à partir d'un appareil sous {{operating_system}}, utilisant le navigateur {{browser_name}}.
Si vous n'avez pas demandé de réinitalisation, vous pouvez ignorer cet e-mail.
Merci,
L'équipe FitTrackee

View File

@@ -1 +0,0 @@
FitTrackee - Réinitialiser votre mot de passe

View File

@@ -0,0 +1 @@
FitTrackee - {{ _('Password reset request') }}

View File

@@ -0,0 +1,231 @@
# German translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-07-03 07:58+0200\n"
"PO-Revision-Date: 2022-07-02 18:25+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: de\n"
"Language-Team: de <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
#: fittrackee/emails/templates/layout.html:215
#: fittrackee/emails/templates/layout.txt:1
#, python-format
msgid "Hi %(username)s,"
msgstr "Hallo %(username)s,"
#: fittrackee/emails/templates/account_confirmation/body.txt:6
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:3
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:6
#: fittrackee/emails/templates/layout.html:218
#: fittrackee/emails/templates/password_change/body.txt:3
#: fittrackee/emails/templates/password_reset_request/body.txt:6
#, python-format
msgid ""
"For security, this request was received from a %(operating_system)s "
"device using %(browser_name)s."
msgstr ""
"Zur Sicherheit: Diese Anfrage wurde von einem %(operating_system)s Gerät "
"mit %(browser_name)s ausgelöst."
#: fittrackee/emails/templates/layout.html:221
#: fittrackee/emails/templates/layout.txt:5
msgid "Thanks,"
msgstr "Danke"
#: fittrackee/emails/templates/layout.html:222
#: fittrackee/emails/templates/layout.txt:6
msgid "The FitTrackee Team"
msgstr "Dein FitTrackee Team"
#: fittrackee/emails/templates/account_confirmation/body.html:2
#: fittrackee/emails/templates/account_confirmation/subject.txt:1
msgid "Confirm your account"
msgstr "Bestätige Dein Konto"
#: fittrackee/emails/templates/account_confirmation/body.html:3
msgid "Use this link to confirm your account."
msgstr "Verwendet diesen Link um Dein Konto zu bestätigen."
#: fittrackee/emails/templates/account_confirmation/body.html:4
#: fittrackee/emails/templates/account_confirmation/body.txt:1
msgid "You have created an account on FitTrackee."
msgstr "Du hast ein Konto bei FitTrackee angelegt."
#: fittrackee/emails/templates/account_confirmation/body.html:4
msgid "Use the button below to confirm your address email."
msgstr "Verwende den unteren Button um Deine E-Mail Adresse zu bestätigen."
#: fittrackee/emails/templates/account_confirmation/body.html:11
#: fittrackee/emails/templates/account_confirmation/body.txt:4
#: fittrackee/emails/templates/email_update_to_new_email/body.html:11
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:4
msgid "Verify your email"
msgstr "Bestätige Deine E-Mail"
#: fittrackee/emails/templates/account_confirmation/body.html:18
#: fittrackee/emails/templates/account_confirmation/body.txt:7
msgid ""
"If this account creation wasn't initiated by you, please ignore this "
"email."
msgstr ""
"Falls die Kontoerstellung nicht von Dir initiiert wurde, ignoriere diese "
"E-Mail bitte."
#: fittrackee/emails/templates/account_confirmation/body.html:22
#: fittrackee/emails/templates/email_update_to_new_email/body.html:22
#: fittrackee/emails/templates/password_reset_request/body.html:24
msgid ""
"If you're having trouble with the button above, copy and paste the URL "
"below into your web browser."
msgstr ""
"Falls Du Probleme mit dem oberen Button hast, kopiere diese URL und gebe "
"sie in Deinen Webbrowser ein."
#: fittrackee/emails/templates/account_confirmation/body.txt:2
msgid "Use the link below to confirm your address email."
msgstr "Verwende den unteren Link um Deine E-Mail Adresse zu bestätigen."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:2
#: fittrackee/emails/templates/email_update_to_current_email/subject.txt:1
msgid "Email changed"
msgstr "E-Mail Adresse geändert"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:3
msgid "Your email is being updated."
msgstr "Deine E-Mail Adresse wure aktualisiert."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:4
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account to:"
msgstr ""
"Du hast kürzlich beantragt, die E-Mail Adresse Deines FitTrackee Kontos "
"zu ändern. Neue Adresse:"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:18
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:4
msgid ""
"If this email change wasn't initiated by you, please change your password"
" immediately or contact your administrator if your account is locked."
msgstr ""
"Falls die Änderung der E-Mail Adresse nicht von Dir initiiert wurde, "
"ändere bitte sofort Dein Passwort oder kontaktiere den Administrator, "
"falls Dein Konto gesperrt ist."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:2
#: fittrackee/emails/templates/email_update_to_new_email/subject.txt:1
msgid "Confirm email change"
msgstr "Bestätige E-Mail Änderung"
#: fittrackee/emails/templates/email_update_to_new_email/body.html:3
msgid "Use this link to confirm email change."
msgstr "Verwende den unteren Link um Deine Adresse zu bestätigen."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account."
msgstr ""
"Du hast kürzlich beantragt, die E-Mail Adresse Deines FitTrackee Kontos "
"zu ändern."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
msgid "Use the button below to confirm this address."
msgstr "Verwende den unteren Button um Deine Adresse zu bestätigen."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:18
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:7
msgid "If this email change wasn't initiated by you, please ignore this email."
msgstr ""
"Falls die Änderung der E-Mail Adresse nicht von Dir initiiert wurde, "
"ignoriere diese E-Mail bitte."
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:2
msgid "Use the link below to confirm this address."
msgstr "Verwende den unteren Link um Deine Adresse zu bestätigen."
#: fittrackee/emails/templates/password_change/body.html:2
#: fittrackee/emails/templates/password_change/subject.txt:1
msgid "Password changed"
msgstr "Passwort geändert"
#: fittrackee/emails/templates/password_change/body.html:3
msgid "Your password has been changed."
msgstr "Dein Passwort wurde geändert."
#: fittrackee/emails/templates/password_change/body.html:4
#: fittrackee/emails/templates/password_change/body.txt:1
msgid "The password for your FitTrackee account has been changed."
msgstr "Das Passwort Deines FitTrackee Kontos wurde geändert."
#: fittrackee/emails/templates/password_change/body.html:5
#: fittrackee/emails/templates/password_change/body.txt:4
msgid ""
"If this password change wasn't initiated by you, please change your "
"password immediately or contact your administrator if your account is "
"locked."
msgstr ""
"Falls die Änderung des Passworts nicht von Dir initiiert wurde, ändere "
"bitte sofort Dein Passwort oder kontaktiere den Administrator, falls Dein"
" Konto gesperrt ist."
#: fittrackee/emails/templates/password_reset_request/body.html:2
#: fittrackee/emails/templates/password_reset_request/subject.txt:1
msgid "Password reset request"
msgstr "Anfrage zum Zurücksetzen des Passworts"
#: fittrackee/emails/templates/password_reset_request/body.html:3
#, python-format
msgid ""
"Use this link to reset your password. The link is only valid for "
"%(expiration_delay)s."
msgstr ""
"Verwende den unteren Link um Dein Passwort zurückzusetzen. Der Link ist "
"nur für %(expiration_delay)s gültig."
#: fittrackee/emails/templates/password_reset_request/body.html:4
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "You recently requested to reset your password for your FitTrackee account."
msgstr ""
"Du hast kürzlich beantragt, das Passwort Deines FitTrackee Kontos "
"zurückzusetzen."
#: fittrackee/emails/templates/password_reset_request/body.html:4
msgid "Use the button below to reset it."
msgstr "Verwende den unteren Button um es zurückzusetzen."
#: fittrackee/emails/templates/password_reset_request/body.html:5
#: fittrackee/emails/templates/password_reset_request/body.txt:2
#, python-format
msgid "This password reset link is only valid for %(expiration_delay)s."
msgstr "Der Link ist nur für %(expiration_delay)s gültig."
#: fittrackee/emails/templates/password_reset_request/body.html:13
#: fittrackee/emails/templates/password_reset_request/body.txt:4
msgid "Reset your password"
msgstr "Setze Dein Passwort zurück"
#: fittrackee/emails/templates/password_reset_request/body.html:20
#: fittrackee/emails/templates/password_reset_request/body.txt:7
msgid "If you did not request a password reset, please ignore this email."
msgstr ""
"Falls Du das Zurücksetzen des Passworts nicht angefordert hast, igoniere "
"diese E-Mail bitte."
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "Use the link below to reset it."
msgstr "Verwende den unteren Link um es zurückzusetzen."

View File

@@ -0,0 +1,224 @@
# English translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-07-03 07:58+0200\n"
"PO-Revision-Date: 2022-07-02 18:25+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: en\n"
"Language-Team: en <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
#: fittrackee/emails/templates/layout.html:215
#: fittrackee/emails/templates/layout.txt:1
#, python-format
msgid "Hi %(username)s,"
msgstr "Hi %(username)s,"
#: fittrackee/emails/templates/account_confirmation/body.txt:6
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:3
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:6
#: fittrackee/emails/templates/layout.html:218
#: fittrackee/emails/templates/password_change/body.txt:3
#: fittrackee/emails/templates/password_reset_request/body.txt:6
#, python-format
msgid ""
"For security, this request was received from a %(operating_system)s "
"device using %(browser_name)s."
msgstr ""
"For security, this request was received from a %(operating_system)s "
"device using %(browser_name)s."
#: fittrackee/emails/templates/layout.html:221
#: fittrackee/emails/templates/layout.txt:5
msgid "Thanks,"
msgstr "Thanks,"
#: fittrackee/emails/templates/layout.html:222
#: fittrackee/emails/templates/layout.txt:6
msgid "The FitTrackee Team"
msgstr "The FitTrackee Team"
#: fittrackee/emails/templates/account_confirmation/body.html:2
#: fittrackee/emails/templates/account_confirmation/subject.txt:1
msgid "Confirm your account"
msgstr "Confirm your account"
#: fittrackee/emails/templates/account_confirmation/body.html:3
msgid "Use this link to confirm your account."
msgstr "Use this link to confirm your account."
#: fittrackee/emails/templates/account_confirmation/body.html:4
#: fittrackee/emails/templates/account_confirmation/body.txt:1
msgid "You have created an account on FitTrackee."
msgstr "You have created an account on FitTrackee."
#: fittrackee/emails/templates/account_confirmation/body.html:4
msgid "Use the button below to confirm your address email."
msgstr "Use the button below to confirm your address email."
#: fittrackee/emails/templates/account_confirmation/body.html:11
#: fittrackee/emails/templates/account_confirmation/body.txt:4
#: fittrackee/emails/templates/email_update_to_new_email/body.html:11
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:4
msgid "Verify your email"
msgstr "Verify your email"
#: fittrackee/emails/templates/account_confirmation/body.html:18
#: fittrackee/emails/templates/account_confirmation/body.txt:7
msgid ""
"If this account creation wasn't initiated by you, please ignore this "
"email."
msgstr ""
"If this account creation wasn't initiated by you, please ignore this "
"email."
#: fittrackee/emails/templates/account_confirmation/body.html:22
#: fittrackee/emails/templates/email_update_to_new_email/body.html:22
#: fittrackee/emails/templates/password_reset_request/body.html:24
msgid ""
"If you're having trouble with the button above, copy and paste the URL "
"below into your web browser."
msgstr ""
"If you're having trouble with the button above, copy and paste the URL "
"below into your web browser."
#: fittrackee/emails/templates/account_confirmation/body.txt:2
msgid "Use the link below to confirm your address email."
msgstr "Use the link below to confirm your address email."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:2
#: fittrackee/emails/templates/email_update_to_current_email/subject.txt:1
msgid "Email changed"
msgstr "Email changed"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:3
msgid "Your email is being updated."
msgstr "Your email is being updated."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:4
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account to:"
msgstr ""
"You recently requested to change your email address for your FitTrackee "
"account to:"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:18
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:4
msgid ""
"If this email change wasn't initiated by you, please change your password"
" immediately or contact your administrator if your account is locked."
msgstr ""
"If this email change wasn't initiated by you, please change your password"
" immediately or contact your administrator if your account is locked."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:2
#: fittrackee/emails/templates/email_update_to_new_email/subject.txt:1
msgid "Confirm email change"
msgstr "Confirm email change"
#: fittrackee/emails/templates/email_update_to_new_email/body.html:3
msgid "Use this link to confirm email change."
msgstr "Use this link to confirm email change."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account."
msgstr ""
"You recently requested to change your email address for your FitTrackee "
"account."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
msgid "Use the button below to confirm this address."
msgstr "Use the button below to confirm this address."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:18
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:7
msgid "If this email change wasn't initiated by you, please ignore this email."
msgstr "If this email change wasn't initiated by you, please ignore this email."
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:2
msgid "Use the link below to confirm this address."
msgstr "Use the link below to confirm this address."
#: fittrackee/emails/templates/password_change/body.html:2
#: fittrackee/emails/templates/password_change/subject.txt:1
msgid "Password changed"
msgstr "Password changed"
#: fittrackee/emails/templates/password_change/body.html:3
msgid "Your password has been changed."
msgstr "Your password has been changed."
#: fittrackee/emails/templates/password_change/body.html:4
#: fittrackee/emails/templates/password_change/body.txt:1
msgid "The password for your FitTrackee account has been changed."
msgstr "The password for your FitTrackee account has been changed."
#: fittrackee/emails/templates/password_change/body.html:5
#: fittrackee/emails/templates/password_change/body.txt:4
msgid ""
"If this password change wasn't initiated by you, please change your "
"password immediately or contact your administrator if your account is "
"locked."
msgstr ""
"If this password change wasn't initiated by you, please change your "
"password immediately or contact your administrator if your account is "
"locked."
#: fittrackee/emails/templates/password_reset_request/body.html:2
#: fittrackee/emails/templates/password_reset_request/subject.txt:1
msgid "Password reset request"
msgstr "Password reset request"
#: fittrackee/emails/templates/password_reset_request/body.html:3
#, python-format
msgid ""
"Use this link to reset your password. The link is only valid for "
"%(expiration_delay)s."
msgstr ""
"Use this link to reset your password. The link is only valid for "
"%(expiration_delay)s."
#: fittrackee/emails/templates/password_reset_request/body.html:4
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "You recently requested to reset your password for your FitTrackee account."
msgstr "You recently requested to reset your password for your FitTrackee account."
#: fittrackee/emails/templates/password_reset_request/body.html:4
msgid "Use the button below to reset it."
msgstr "Use the button below to reset it."
#: fittrackee/emails/templates/password_reset_request/body.html:5
#: fittrackee/emails/templates/password_reset_request/body.txt:2
#, python-format
msgid "This password reset link is only valid for %(expiration_delay)s."
msgstr "This password reset link is only valid for %(expiration_delay)s."
#: fittrackee/emails/templates/password_reset_request/body.html:13
#: fittrackee/emails/templates/password_reset_request/body.txt:4
msgid "Reset your password"
msgstr "Reset your password"
#: fittrackee/emails/templates/password_reset_request/body.html:20
#: fittrackee/emails/templates/password_reset_request/body.txt:7
msgid "If you did not request a password reset, please ignore this email."
msgstr "If you did not request a password reset, please ignore this email."
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "Use the link below to reset it."
msgstr "Use the link below to reset it."

View File

@@ -0,0 +1,231 @@
# French translations for PROJECT.
# Copyright (C) 2022 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2022.
#
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2022-07-03 07:58+0200\n"
"PO-Revision-Date: 2022-07-02 17:27+0200\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.10.3\n"
#: fittrackee/emails/templates/layout.html:215
#: fittrackee/emails/templates/layout.txt:1
#, python-format
msgid "Hi %(username)s,"
msgstr "Bonjour %(username)s,"
#: fittrackee/emails/templates/account_confirmation/body.txt:6
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:3
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:6
#: fittrackee/emails/templates/layout.html:218
#: fittrackee/emails/templates/password_change/body.txt:3
#: fittrackee/emails/templates/password_reset_request/body.txt:6
#, python-format
msgid ""
"For security, this request was received from a %(operating_system)s "
"device using %(browser_name)s."
msgstr ""
"Pour vérification, cette demande a été reçue à partir d'un appareil sous "
"%(operating_system)s, utilisant le navigateur %(browser_name)s."
#: fittrackee/emails/templates/layout.html:221
#: fittrackee/emails/templates/layout.txt:5
msgid "Thanks,"
msgstr "Merci,"
#: fittrackee/emails/templates/layout.html:222
#: fittrackee/emails/templates/layout.txt:6
msgid "The FitTrackee Team"
msgstr "L'équipe FitTrackee"
#: fittrackee/emails/templates/account_confirmation/body.html:2
#: fittrackee/emails/templates/account_confirmation/subject.txt:1
msgid "Confirm your account"
msgstr "Confirmer votre inscription"
#: fittrackee/emails/templates/account_confirmation/body.html:3
msgid "Use this link to confirm your account."
msgstr "Utiliser ce lien pour confirmer votre inscription."
#: fittrackee/emails/templates/account_confirmation/body.html:4
#: fittrackee/emails/templates/account_confirmation/body.txt:1
msgid "You have created an account on FitTrackee."
msgstr "Vous avez créé un compte sur FitTrackee."
#: fittrackee/emails/templates/account_confirmation/body.html:4
msgid "Use the button below to confirm your address email."
msgstr "Cliquez sur le bouton pour confirmer votre adresse email."
#: fittrackee/emails/templates/account_confirmation/body.html:11
#: fittrackee/emails/templates/account_confirmation/body.txt:4
#: fittrackee/emails/templates/email_update_to_new_email/body.html:11
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:4
msgid "Verify your email"
msgstr "Vérifier l'adresse email"
#: fittrackee/emails/templates/account_confirmation/body.html:18
#: fittrackee/emails/templates/account_confirmation/body.txt:7
msgid ""
"If this account creation wasn't initiated by you, please ignore this "
"email."
msgstr ""
"Si vous n'êtes pas à l'origine de la création de ce compte, vous pouvez "
"ignorer cet e-mail."
#: fittrackee/emails/templates/account_confirmation/body.html:22
#: fittrackee/emails/templates/email_update_to_new_email/body.html:22
#: fittrackee/emails/templates/password_reset_request/body.html:24
msgid ""
"If you're having trouble with the button above, copy and paste the URL "
"below into your web browser."
msgstr ""
"Si vous avez des problèmes avec le bouton, vous pouvez copier et coller "
"le lien suivant dans votre navigateur."
#: fittrackee/emails/templates/account_confirmation/body.txt:2
msgid "Use the link below to confirm your address email."
msgstr "Cliquez sur le lien ci-dessous pour confirmer votre adresse email."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:2
#: fittrackee/emails/templates/email_update_to_current_email/subject.txt:1
msgid "Email changed"
msgstr "Adresse email modifiée"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:3
msgid "Your email is being updated."
msgstr "Votre adresse email est en cours de mise à jour."
#: fittrackee/emails/templates/email_update_to_current_email/body.html:4
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account to:"
msgstr ""
"Vous avez récemment demandé la modification de l'adresse email associée à"
" votre compte sur FitTrackee vers :"
#: fittrackee/emails/templates/email_update_to_current_email/body.html:18
#: fittrackee/emails/templates/email_update_to_current_email/body.txt:4
msgid ""
"If this email change wasn't initiated by you, please change your password"
" immediately or contact your administrator if your account is locked."
msgstr ""
"Si vous n'êtes pas à l'origine de cette modification, veuillez changer "
"votre mot de passe immédiatement ou contacter l'administrateur si votre "
"compte est bloqué."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:2
#: fittrackee/emails/templates/email_update_to_new_email/subject.txt:1
msgid "Confirm email change"
msgstr "Confirmer le changement d'adresse email"
#: fittrackee/emails/templates/email_update_to_new_email/body.html:3
msgid "Use this link to confirm email change."
msgstr "Confirmer le changement d'adresse email."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:1
msgid ""
"You recently requested to change your email address for your FitTrackee "
"account."
msgstr ""
"Vous avez récemment demandé la modification de l'adresse email associée à"
" votre compte sur FitTrackee."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:4
msgid "Use the button below to confirm this address."
msgstr "Cliquez sur le bouton ci-dessous pour confirmer cette adresse email."
#: fittrackee/emails/templates/email_update_to_new_email/body.html:18
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:7
msgid "If this email change wasn't initiated by you, please ignore this email."
msgstr ""
"Si vous n'êtes pas à l'origine de cette modification, vous pouvez ignorer"
" cet e-mail."
#: fittrackee/emails/templates/email_update_to_new_email/body.txt:2
msgid "Use the link below to confirm this address."
msgstr "Cliquez sur le lien ci-dessous pour confirmer cette adresse email."
#: fittrackee/emails/templates/password_change/body.html:2
#: fittrackee/emails/templates/password_change/subject.txt:1
msgid "Password changed"
msgstr "Mot de passe modifié"
#: fittrackee/emails/templates/password_change/body.html:3
msgid "Your password has been changed."
msgstr "Votre mot de passe a été modifié."
#: fittrackee/emails/templates/password_change/body.html:4
#: fittrackee/emails/templates/password_change/body.txt:1
msgid "The password for your FitTrackee account has been changed."
msgstr "Le mot de passe de votre compte FitTrackee a été modifié."
#: fittrackee/emails/templates/password_change/body.html:5
#: fittrackee/emails/templates/password_change/body.txt:4
msgid ""
"If this password change wasn't initiated by you, please change your "
"password immediately or contact your administrator if your account is "
"locked."
msgstr ""
"Si vous n'êtes pas à l'origine de cette modification, veuillez changer "
"votre mot de passe immédiatement ou contacter l'administrateur si votre "
"compte est bloqué."
#: fittrackee/emails/templates/password_reset_request/body.html:2
#: fittrackee/emails/templates/password_reset_request/subject.txt:1
msgid "Password reset request"
msgstr "Réinitialiser votre mot de passe"
#: fittrackee/emails/templates/password_reset_request/body.html:3
#, python-format
msgid ""
"Use this link to reset your password. The link is only valid for "
"%(expiration_delay)s."
msgstr ""
"Utiliser ce lien pour réinitialiser le mot de passe. Ce lien n'est valide"
" que pendant %(expiration_delay)s."
#: fittrackee/emails/templates/password_reset_request/body.html:4
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "You recently requested to reset your password for your FitTrackee account."
msgstr ""
"Vous avez récemment demandé la réinitialisation du mot de passe de votre "
"compte sur FitTrackee."
#: fittrackee/emails/templates/password_reset_request/body.html:4
msgid "Use the button below to reset it."
msgstr "Cliquez sur le bouton pour le réinitialiser."
#: fittrackee/emails/templates/password_reset_request/body.html:5
#: fittrackee/emails/templates/password_reset_request/body.txt:2
#, python-format
msgid "This password reset link is only valid for %(expiration_delay)s."
msgstr "Ce lien n'est valide que pendant %(expiration_delay)s."
#: fittrackee/emails/templates/password_reset_request/body.html:13
#: fittrackee/emails/templates/password_reset_request/body.txt:4
msgid "Reset your password"
msgstr "Réinitialiser le mot de passe"
#: fittrackee/emails/templates/password_reset_request/body.html:20
#: fittrackee/emails/templates/password_reset_request/body.txt:7
msgid "If you did not request a password reset, please ignore this email."
msgstr ""
"Si vous n'avez pas demandé de réinitialisation, vous pouvez ignorer cet "
"e-mail."
#: fittrackee/emails/templates/password_reset_request/body.txt:1
msgid "Use the link below to reset it."
msgstr "Cliquez sur le lien ci-dessous pour le réinitialiser."

View File

@@ -0,0 +1,44 @@
import os
import shutil
import click
from flask_migrate import upgrade
from fittrackee import db
from fittrackee.cli.app import app
BASEDIR = os.path.abspath(os.path.dirname(__file__))
app_settings = os.getenv('APP_SETTINGS', 'fittrackee.config.ProductionConfig')
@click.group(name='db')
def db_cli() -> None:
"""Manage database."""
pass
@db_cli.command('upgrade')
def upgrade_db() -> None:
"""Apply migrations."""
with app.app_context():
upgrade(directory=BASEDIR)
@db_cli.command('drop')
def drop_db() -> None:
"""Empty database and delete uploaded files for dev environments."""
with app.app_context():
if app_settings == 'fittrackee.config.ProductionConfig':
click.echo(
click.style(
'This is a production server, aborting!', bold=True
),
err=True,
)
return
db.engine.execute("DROP TABLE IF EXISTS alembic_version;")
db.drop_all()
db.session.commit()
click.echo('Database dropped.')
shutil.rmtree(app.config['UPLOAD_FOLDER'], ignore_errors=True)
click.echo('Uploaded files deleted.')

View File

@@ -0,0 +1,59 @@
"""update User and AppConfig tables
Revision ID: 5e3a3a31c432
Revises: e30007d681cb
Create Date: 2022-02-23 11:05:24.223304
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5e3a3a31c432'
down_revision = 'e30007d681cb'
branch_labels = None
depends_on = None
def upgrade():
op.alter_column(
'users', 'username', existing_type=sa.String(length=20),
type_=sa.String(length=255), existing_nullable=False
)
op.alter_column(
'users', 'email', existing_type=sa.String(length=120),
type_=sa.String(length=255), existing_nullable=False
)
op.add_column(
'users',
sa.Column('is_active', sa.Boolean(), default=False, nullable=True))
op.execute("UPDATE users SET is_active = true")
op.alter_column('users', 'is_active', nullable=False)
op.add_column(
'users',
sa.Column('email_to_confirm', sa.String(length=255), nullable=True))
op.add_column(
'users',
sa.Column('confirmation_token', sa.String(length=255), nullable=True))
op.add_column(
'app_config',
sa.Column('admin_contact', sa.String(length=255), nullable=True)
)
def downgrade():
op.drop_column('app_config', 'admin_contact')
op.drop_column('users', 'confirmation_token')
op.drop_column('users', 'email_to_confirm')
op.drop_column('users', 'is_active')
op.alter_column(
'users', 'email', existing_type=sa.String(length=255),
type_=sa.String(length=120), existing_nullable=False
)
op.alter_column(
'users', 'username', existing_type=sa.String(length=255),
type_=sa.String(length=20), existing_nullable=False
)

24
fittrackee/request.py Normal file
View File

@@ -0,0 +1,24 @@
from typing import Optional, Tuple
from flask import Request
from ua_parser import user_agent_parser
from werkzeug.user_agent import UserAgent as IUserAgent
class UserAgent(IUserAgent):
def __init__(self, string: str):
super().__init__(string)
self.platform, self.browser = self._parse_user_agent(self.string)
@staticmethod
def _parse_user_agent(
user_agent: str,
) -> Tuple[Optional[str], Optional[str]]:
parsed_string = user_agent_parser.Parse(user_agent)
platform = parsed_string.get('os', {}).get('family')
browser = parsed_string.get('user_agent', {}).get('family')
return platform, browser
class CustomRequest(Request):
user_agent_class = UserAgent

View File

@@ -1,14 +1,30 @@
import json
from typing import Optional
import pytest
from flask import Flask
import fittrackee
from fittrackee.application.models import AppConfig
from fittrackee.users.models import User
from ..api_test_case import ApiTestCaseMixin
from ..mixins import ApiTestCaseMixin
from ..utils import jsonify_dict
class TestGetConfig(ApiTestCaseMixin):
def test_it_gets_application_config_for_unauthenticated_user(
self, app: Flask
) -> None:
app_config = AppConfig.query.first()
client = app.test_client()
response = client.get('/api/config')
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert data['data'] == jsonify_dict(app_config.serialize())
def test_it_gets_application_config(
self, app: Flask, user_1: User
) -> None:
@@ -24,17 +40,6 @@ class TestGetConfig(ApiTestCaseMixin):
data = json.loads(response.data.decode())
assert response.status_code == 200
assert 'success' in data['status']
assert data['data']['gpx_limit_import'] == 10
assert data['data']['is_registration_enabled'] is True
assert data['data']['max_single_file_size'] == 1048576
assert data['data']['max_zip_file_size'] == 10485760
assert data['data']['max_users'] == 100
assert data['data']['map_attribution'] == (
'&copy; <a href="http://www.openstreetmap.org/copyright" '
'target="_blank" rel="noopener noreferrer">OpenStreetMap</a> '
'contributors'
)
assert data['data']['version'] == fittrackee.__version__
def test_it_returns_error_if_application_has_no_config(
self, app_no_config: Flask, user_1_admin: User
@@ -96,12 +101,14 @@ class TestUpdateConfig(ApiTestCaseMixin):
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
admin_email = self.random_email()
response = client.patch(
'/api/config',
content_type='application/json',
data=json.dumps(
dict(
admin_contact=admin_email,
gpx_limit_import=20,
max_single_file_size=10000,
max_zip_file_size=25000,
@@ -111,9 +118,10 @@ class TestUpdateConfig(ApiTestCaseMixin):
headers=dict(Authorization=f'Bearer {auth_token}'),
)
data = json.loads(response.data.decode())
assert response.status_code == 200
data = json.loads(response.data.decode())
assert 'success' in data['status']
assert data['data']['admin_contact'] == admin_email
assert data['data']['gpx_limit_import'] == 20
assert data['data']['is_registration_enabled'] is True
assert data['data']['max_single_file_size'] == 10000
@@ -262,3 +270,57 @@ class TestUpdateConfig(ApiTestCaseMixin):
self.assert_400(
response, 'Max. files in a zip archive must be greater than 0'
)
def test_it_raises_error_if_admin_contact_is_invalid(
self, app: Flask, user_1_admin: User
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
response = client.patch(
'/api/config',
content_type='application/json',
data=json.dumps(
dict(
admin_contact=self.random_string(),
)
),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
self.assert_400(
response, 'valid email must be provided for admin contact'
)
@pytest.mark.parametrize(
'input_description,input_email', [('input string', ''), ('None', None)]
)
def test_it_empties_error_if_admin_contact_is_an_empty(
self,
app: Flask,
user_1_admin: User,
input_description: str,
input_email: Optional[str],
) -> None:
client, auth_token = self.get_test_client_and_auth_token(
app, user_1_admin.email
)
app_config = AppConfig.query.first()
app_config.admin_contact = self.random_email()
response = client.patch(
'/api/config',
content_type='application/json',
data=json.dumps(
dict(
admin_contact=input_email,
)
),
headers=dict(Authorization=f'Bearer {auth_token}'),
)
assert response.status_code == 200
data = json.loads(response.data.decode())
assert 'success' in data['status']
assert data['data']['admin_contact'] is None

View File

@@ -1,21 +1,60 @@
from flask import Flask
from fittrackee import VERSION
from fittrackee.application.models import AppConfig
from fittrackee.users.models import User
class TestConfigModel:
def test_application_config(self, app: Flask) -> None:
app_config = AppConfig.query.first()
assert 1 == app_config.id
app_config.admin_contact = 'admin@example.com'
assert app_config.is_registration_enabled is True
assert (
app_config.map_attribution
== app.config['TILE_SERVER']['ATTRIBUTION']
)
serialized_app_config = app_config.serialize()
assert serialized_app_config['gpx_limit_import'] == 10
assert serialized_app_config['is_registration_enabled'] is True
assert serialized_app_config['max_single_file_size'] == 1048576
assert serialized_app_config['max_zip_file_size'] == 10485760
assert serialized_app_config['max_users'] == 100
assert serialized_app_config['map_attribution'] == (
'&copy; <a href="http://www.openstreetmap.org/copyright" '
'target="_blank" rel="noopener noreferrer">OpenStreetMap</a> '
'contributors'
assert (
serialized_app_config['admin_contact'] == app_config.admin_contact
)
assert (
serialized_app_config['gpx_limit_import']
== app_config.gpx_limit_import
)
assert serialized_app_config['is_email_sending_enabled'] is True
assert serialized_app_config['is_registration_enabled'] is True
assert (
serialized_app_config['max_single_file_size']
== app_config.max_single_file_size
)
assert (
serialized_app_config['max_zip_file_size']
== app_config.max_zip_file_size
)
assert serialized_app_config['max_users'] == app_config.max_users
assert (
serialized_app_config['map_attribution']
== app_config.map_attribution
)
assert serialized_app_config['version'] == VERSION
def test_it_returns_registration_disabled_when_users_count_exceeds_limit(
self, app: Flask, user_1: User, user_2: User
) -> None:
app_config = AppConfig.query.first()
app_config.max_users = 2
serialized_app_config = app_config.serialize()
assert app_config.is_registration_enabled is False
assert serialized_app_config['is_registration_enabled'] is False
def test_it_returns_email_sending_disabled_when_no_email_url_provided(
self, app_wo_email_activation: Flask, user_1: User, user_2: User
) -> None:
app_config = AppConfig.query.first()
serialized_app_config = app_config.serialize()
assert serialized_app_config['is_email_sending_enabled'] is False

Some files were not shown because too many files have changed in this diff Show More