Merge branch 'master' into feature/flexible-routines

# Conflicts:
#	wger/core/templates/tags/render_day.html
#	wger/manager/tests/test_day.py
#	wger/manager/urls.py
#	wger/manager/views/day.py
#	wger/manager/views/set.py
This commit is contained in:
Roland Geider
2024-11-14 19:47:51 +01:00
111 changed files with 12870 additions and 15810 deletions

View File

@@ -41,4 +41,4 @@ jobs:
push: true
file: extras/docker/demo/Dockerfile
platforms: linux/amd64,linux/arm64
tags: ${{ vars.REGISTRY_REPO }}/demo:latest,${{ vars.REGISTRY_REPO }}/demo:2.3-dev,${{ vars.REGISTRY_REPO }}/apache:latest,${{ vars.REGISTRY_REPO }}/apache:2.3-dev
tags: ${{ vars.REGISTRY_REPO }}/demo:latest,${{ vars.REGISTRY_REPO }}/demo:2.3-dev

View File

@@ -79,6 +79,7 @@ Developers
* Ethan Winters - https://github.com/ebwinters
* Dieter Plaetinck - https://github.com/Dieterbe
* Jonathan La Field - https://github.com/JLaField
* Kevin Moy - https://github.com/kmoy1
* Taylor Fuller - https://github.com/taylor-fuller

View File

@@ -0,0 +1,27 @@
#
# Installs some additional packages needed for development
#
# Note that this dockerfile is built from the corresponding docker-compose file
#
FROM wger/server:latest
USER root
WORKDIR /home/wger/src
RUN apt-get update && \
apt-get install -y \
git \
vim \
yarnpkg \
sassc
COPY ../../../requirements.txt /tmp/requirements.txt
COPY ../../../requirements_dev.txt /tmp/requirements_dev.txt
RUN ln -s /usr/bin/yarnpkg /usr/bin/yarn \
&& ln -s /usr/bin/sassc /usr/bin/sass
USER wger
RUN pip3 install --break-system-packages --user -r /tmp/requirements.txt \
&& pip3 install --break-system-packages --user -r /tmp/requirements_dev.txt

View File

@@ -0,0 +1,8 @@
# Development image for wger
## Usage
Clone the docker repository, there are two development configuration that use
this image:
<https://github.com/wger-project/docker>

View File

@@ -1,79 +1,16 @@
# Development image for wger
# Production image for wger
wger (ˈɡɐ) Workout Manager is a free, open source web application that help
you manage your personal workouts, weight and diet plans and can also be used
as a simple gym management utility. It offers a REST API as well, for easy
integration with other projects and tools.
If you want to host your own instance, take a look at the provided docker compose file:
<https://github.com/wger-project/docker>
## Usage
This docker image is meant to provide a quick development environment using
django's development server and an sqlite database from your current code
checkout (if you don't want or need a local checkout, use the wger/demo image,
it is self-contained).
It is recommended to use this image with the provided docker compose, which has all the different
services configured:
A more comfortable development version is provided in the compose folder.
### 1 - Installing docker
Install docker, and the docker buildx tool (if they are separate packages on your system, e.g. on
Arch Linux)
### 2 - Obtaining/building the docker image
We will run the `wger/server:latest` image in the next step.
You can either download it from [dockerhub](https://hub.docker.com/r/wger/server); docker will do
this automatically if you have no such image with that tag yet.
You can also run `docker pull wger/server` to get the latest version. (you can use `docker images`
to see if your image is old)
Alternatively, you can build it yourself from your wger code checkout.
To do this, you **must** build from the project root!
```docker build -f extras/docker/production/Dockerfile --tag wger/server .```
### 3 - Start the container
docker run -ti \
-v /path/to/your/wger/checkout:/home/wger/src \
--name wger.devel \
--publish 8000:8000 wger/server
When developing with windows, you might have problems with the `--volume` option,
use the more verbose mount instead:
--mount type=bind,source='"C:\your\path\to your\checkout"',target=/home/wger/src
You might also want to download the exercise images and the ingredients
(will take some time):
docker exec wger.devel python3 manage.py sync-exercises
docker exec wger.devel python3 manage.py download-exercise-images
docker exec wger.devel wger load-online-fixtures
### 4 - Open the Application
Just open <http://localhost:8000> and log in as: **admin**, password **adminadmin**
To stop the container:
```sudo docker container stop wger.devel```
To start developing again:
```sudo docker container start --attach wger.devel```
### 5 - Other commands
If you need to update the CSS/JS libraries or just issue some other command:
docker exec -ti wger.devel yarn
docker exec -ti wger.devel /bin/bash
<https://github.com/wger-project/docker>
## Contact

View File

@@ -137,6 +137,7 @@ if os.environ.get("DJANGO_CACHE_BACKEND"):
# Folder for compressed CSS and JS files
COMPRESS_ROOT = STATIC_ROOT
COMPRESS_ENABLED = env.bool('COMPRESS_ENABLED', not DEBUG)
# The site's domain as used by the email verification workflow
EMAIL_PAGE_DOMAIN = SITE_URL

View File

@@ -10,11 +10,10 @@
},
"homepage": "https://github.com/wger-project/wger",
"dependencies": {
"Sortable": "RubaXa/Sortable#1.15.2",
"htmx.org": "^2.0.3",
"bootstrap": "5.3.3",
"components-font-awesome": "5.9.0",
"d3": "^7.9.0",
"datatables": "^1.10.18",
"datatables.net-bs5": "^2.1.8",
"devbridge-autocomplete": "^1.4.11",
"jquery": "^3.7.1",
"masonry-layout": "^4.2.2",

View File

@@ -23,20 +23,20 @@ drf-spectacular[sidecar]==0.27.2
easy-thumbnails==2.10
flower==2.0.1
fontawesomefree~=6.6.0
icalendar==6.0.0
icalendar==6.0.1
invoke==2.2.0
openfoodfacts==1.1.3
openfoodfacts==2.2.0
pillow==10.4.0
reportlab==4.2.2
reportlab==4.2.5
requests==2.32.3
tqdm==4.66.4
tqdm==4.66.5
tzdata==2024.2
# AWS
#boto3
# REST API
django-cors-headers==4.4.0
django-cors-headers==4.5.0
django-filter==24.3
djangorestframework==3.15.2
djangorestframework-simplejwt[crypto]==5.3.1

View File

@@ -6,7 +6,7 @@
-r requirements.txt
# Building/installing
wheel==0.44.0
wheel==0.45.0
# for ingredient import script from OFF
pymongo==4.10.1
@@ -16,7 +16,7 @@ faker==26.0.0
# Development packages
django-extensions~=3.2
coverage==7.6.1
coverage==7.6.3
django-debug-toolbar==4.4.5
isort==5.13.2
ruff==0.6.8

View File

@@ -6,5 +6,5 @@
-r requirements.txt
django-redis==5.4.0
gunicorn==22.0.0
psycopg2==2.9.9
gunicorn==23.0.0
psycopg[binary]==3.2.3

View File

@@ -5,5 +5,5 @@
# Regular packages
-r requirements.txt
psycopg2-binary==2.9.9
psycopg[binary]==3.2.3
django-redis==5.4.0

View File

@@ -8,7 +8,6 @@
# Local
from .celery_configuration import app
MIN_APP_VERSION = (1, 7, 4, 'final', 1)
VERSION = (2, 3, 0, 'alpha', 3)

View File

@@ -304,8 +304,9 @@ class UserAPIRegistrationViewSet(viewsets.ViewSet):
API endpoint
"""
permission_classes = (AllowRegisterUser,)
# permission_classes = (AllowRegisterUser,)
serializer_class = UserRegistrationSerializer
throttle_scope = 'registration'
def get_queryset(self):
"""
@@ -327,7 +328,7 @@ class UserAPIRegistrationViewSet(viewsets.ViewSet):
serializer = self.serializer_class(data=data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
user.userprofile.added_by = request.user
# user.userprofile.added_by = request.user
user.userprofile.save()
token = create_token(user)

View File

@@ -137,6 +137,7 @@ class UserPreferencesForm(forms.ModelForm):
'ro_access',
'num_days_weight_reminder',
'birthdate',
'height',
)
def __init__(self, *args, **kwargs):
@@ -153,6 +154,7 @@ class UserPreferencesForm(forms.ModelForm):
css_class='form-row',
),
'birthdate',
'height',
HTML('<hr>'),
),
Fieldset(

View File

@@ -27,55 +27,52 @@
/* font-face kit by Fonts2u - http://www.fonts2u.com */
@font-face {
font-family: "Open Sans Light";
src:
url("/static/fonts/OpenSans-Light.eot?") format("eot"),
font-family: "Open Sans Light";
src: url("/static/fonts/OpenSans-Light.eot?") format("eot"),
url("/static/fonts/OpenSans-Light.woff") format("woff"),
url("/static/fonts/OpenSans-Light.ttf") format("truetype"),
url("/static/fonts/OpenSans-Light.svg#OpenSans-Light") format("svg");
font-weight: normal;
font-style: normal;
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Open Sans";
src:
url("/static/fonts/OpenSans-Regular.eot?") format("eot"),
font-family: "Open Sans";
src: url("/static/fonts/OpenSans-Regular.eot?") format("eot"),
url("/static/fonts/OpenSans-Regular.woff") format("woff"),
url("/static/fonts/OpenSans-Regular.ttf") format("truetype"),
url("/static/fonts/OpenSans-Regular.svg#OpenSans") format("svg");
font-weight: normal;
font-style: normal;
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "Open Sans Bold";
src:
url("/static/fonts/OpenSans-Bold.eot?") format("eot"),
font-family: "Open Sans Bold";
src: url("/static/fonts/OpenSans-Bold.eot?") format("eot"),
url("/static/fonts/OpenSans-Bold.woff") format("woff"),
url("/static/fonts/OpenSans-Bold.ttf") format("truetype"),
url("/static/fonts/OpenSans-Bold.svg#OpenSans-Bold") format("svg");
font-weight: normal;
font-style: normal;
font-weight: normal;
font-style: normal;
}
/* sticky footer */
html {
position: relative;
min-height: 100%;
position: relative;
min-height: 100%;
}
body {
margin-bottom: 5rem !important; /* Margin bottom by footer height */
margin-bottom: 5rem !important; /* Margin bottom by footer height */
}
footer {
position: absolute;
bottom: 0;
width: 100%;
height: 4rem; /* Set the fixed height of the footer here */
position: absolute;
bottom: 0;
width: 100%;
height: 4rem; /* Set the fixed height of the footer here */
}
@@ -83,54 +80,36 @@ footer {
* Images in links are vertically aligned
*/
a img {
vertical-align: middle;
vertical-align: middle;
}
/*
* "invisible" elements, simply hidden (they still take all the space)
*/
.invisible {
display: none;
display: none;
}
.align-right {
text-align: right;
text-align: right;
}
.sortable-settings {
border: 1px solid black;
}
.sortable-settings-drag {
padding-left: 1em;
padding-right: 1em;
background-color: #babdb6;
border-bottom: 1px solid black;
}
/*
* Set a more appropriate cursor to the drag'n'drop handle on the
* workout view
*/
.dragndrop-handle {
cursor: move;
}
/*
* Set the sizes for the background where the muscles are shown on the exercise view page
*/
div.muscle-background {
width: 150px;
height: 276px;
background-size: 150px;
background-repeat: no-repeat;
width: 150px;
height: 276px;
background-size: 150px;
background-repeat: no-repeat;
}
/*
* Set an additional padding to the generated DIV on the edit set page
*/
#exercise-search-log {
padding-left: 1em;
padding-left: 1em;
}
@@ -142,13 +121,13 @@ div.muscle-background {
* Hide the edit options (edit, delete) on tables, only show them when the mouse is over the row
*/
.editoptions {
visibility: hidden;
visibility: hidden;
}
ul:hover .editoptions,
tr:hover .editoptions,
tbody tr:hover td .editoptions {
visibility: visible;
visibility: visible;
}
/*
@@ -160,7 +139,7 @@ tbody tr:hover td .editoptions {
.ajax-exercise-select a:hover,
.no-hover a:hover,
.no-hover:hover {
background-color: transparent;
background-color: transparent;
}
@@ -168,7 +147,7 @@ tbody tr:hover td .editoptions {
* Make more space for the page title
*/
#page-title {
margin-top: 0.5em;
margin-top: 0.5em;
}
/*
@@ -184,9 +163,9 @@ div.warning a:hover,
div.warning a:active,
div.info a:hover,
div.info a:active {
color: black;
text-decoration: underline;
background-color: transparent;
color: black;
text-decoration: underline;
background-color: transparent;
}
/**************************************************************
@@ -197,30 +176,30 @@ div.info a:active {
* Category heading for the ajax autocompleter
*/
.ui-autocomplete-category {
margin: 0;
text-align: center;
zoom: 1;
width: 100%;
font-weight: bold;
background-color: #d3d7cf;
border-bottom: 1px solid #888a85;
margin: 0;
text-align: center;
zoom: 1;
width: 100%;
font-weight: bold;
background-color: #d3d7cf;
border-bottom: 1px solid #888a85;
}
/*
* Give the result list from the autocompleter a maximum size
*/
.ui-autocomplete {
max-height: 500px;
max-width: 500px;
overflow-y: auto;
overflow-x: hidden; /* prevent horizontal scrollbar */
max-height: 500px;
max-width: 500px;
overflow-y: auto;
overflow-x: hidden; /* prevent horizontal scrollbar */
}
/*
* Give odd child's a different background colour (thanks CSS3!)
*/
.ui-autocomplete li.ui-menu-item:nth-child(odd) {
background: #eeeeec;
background: #eeeeec;
}
/**************************************************************
@@ -228,197 +207,196 @@ div.info a:active {
**************************************************************/
.extra-bold {
font-family: "Open Sans Bold", Arial, Helvetica, sans-serif;
font-weight: 300;
font-family: "Open Sans Bold", Arial, Helvetica, sans-serif;
font-weight: 300;
}
/**************************************************************
* Styling for the SVG elements on the weight chart
**************************************************************/
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.area {
fill: lightsteelblue;
fill: lightsteelblue;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.dot {
fill: white;
stroke: steelblue;
stroke-width: 1.5px;
fill: white;
stroke: steelblue;
stroke-width: 1.5px;
}
circle {
cursor: pointer;
cursor: pointer;
}
circle:hover {
fill: #204a87;
stroke: #204a87;
fill: #204a87;
stroke: #204a87;
}
.brush .extent {
stroke: #fff;
fill-opacity: 0.125;
shape-rendering: crispEdges;
stroke: #fff;
fill-opacity: 0.125;
shape-rendering: crispEdges;
}
/* Color series from d3.scale.category10() */
circle.color-1f77b4:hover {
fill: #1f77b4;
fill: #1f77b4;
}
circle.color-ff7f0e:hover {
fill: #ff7f0e;
fill: #ff7f0e;
}
circle.color-2ca02c:hover {
fill: #2ca02c;
fill: #2ca02c;
}
circle.color-d62728:hover {
fill: #d62728;
fill: #d62728;
}
circle.color-9467bd:hover {
fill: #9467bd;
fill: #9467bd;
}
circle.color-8c564b:hover {
fill: #8c564b;
fill: #8c564b;
}
circle.color-e377c2:hover {
fill: #e377c2;
fill: #e377c2;
}
circle.color-7f7f7f:hover {
fill: #7f7f7f;
fill: #7f7f7f;
}
circle.color-bcbd22:hover {
fill: #bcbd22;
fill: #bcbd22;
}
circle.color-17becf:hover {
fill: #17becf;
fill: #17becf;
}
/**************************************************************
* Weight log calendar
**************************************************************/
table.month td {
width: 14.285%;
height: 4em;
text-align: center;
width: 14.285%;
height: 4em;
text-align: center;
}
table.month th.month {
text-align: center;
background-color: #eeeeec;
text-align: center;
background-color: #eeeeec;
}
table.month a.calendar-link {
height: 100%;
line-height: 1;
height: 100%;
line-height: 1;
}
table.month td.session-bad {
background-color: #f2dede;
background-color: #f2dede;
}
table.month td.session-bad:hover {
background-color: #c9302c;
background-color: #c9302c;
}
table.month td.session-neutral {
background-color: #f5f5f5;
background-color: #f5f5f5;
}
table.month td.session-neutral:hover {
background-color: #babdb6;
background-color: #babdb6;
}
table.month td.session-good {
background-color: #dff0d8;
background-color: #dff0d8;
}
table.month td.session-good:hover {
background-color: #449d44;
background-color: #449d44;
}
/*
* Autocompleter
*/
.autocomplete-wrapper {
margin: 44px auto 44px;
max-width: 600px;
margin: 44px auto 44px;
max-width: 600px;
}
.autocomplete-wrapper label {
display: block;
margin-bottom: 0.75em;
color: #3f4e5e;
font-size: 1.25em;
display: block;
margin-bottom: 0.75em;
color: #3f4e5e;
font-size: 1.25em;
}
.autocomplete-wrapper .text-field {
padding: 0 15px;
width: 100%;
height: 40px;
border: 1px solid #cbd3dd;
font-size: 1.125em;
padding: 0 15px;
width: 100%;
height: 40px;
border: 1px solid #cbd3dd;
font-size: 1.125em;
}
.autocomplete-wrapper ::-webkit-input-placeholder,
.autocomplete-wrapper ::-moz-placeholder,
.autocomplete-wrapper :-moz-placeholder,
.autocomplete-wrapper :-ms-input-placeholder {
color: #cbd3dd;
font-style: italic;
font-size: 18px;
color: #cbd3dd;
font-style: italic;
font-size: 18px;
}
.autocomplete-suggestions {
overflow: auto;
border: 1px solid #cbd3dd;
background: #fff;
overflow: auto;
border: 1px solid #cbd3dd;
background: #fff;
}
.autocomplete-suggestion {
overflow: hidden;
padding: 5px 15px 5px 22px;
white-space: nowrap;
overflow: hidden;
padding: 5px 15px 5px 22px;
white-space: nowrap;
}
.autocomplete-selected {
background: #f0f0f0;
background: #f0f0f0;
}
.autocomplete-suggestions strong {
color: #3465a4;
font-weight: bold;
color: #3465a4;
font-weight: bold;
}
.autocomplete-group {
background-color: #d3d7cf;
padding-left: 13px;
background-color: #d3d7cf;
padding-left: 13px;
}
.autocomplete-group strong {
color: black;
font-weight: bold;
color: black;
font-weight: bold;
}

View File

@@ -15,270 +15,12 @@
*/
'use strict';
/*
AJAX related functions
See https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax for
more information
*/
function getCookie(name) {
var cookie;
var cookies;
var cookieValue = null;
var loopCounter;
if (document.cookie && document.cookie !== '') {
cookies = document.cookie.split(';');
for (loopCounter = 0; loopCounter < cookies.length; loopCounter++) {
cookie = jQuery.trim(cookies[loopCounter]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function csrfSafeMethod(method) {
// These HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
crossDomain: false, // obviates need for sameOrigin test
beforeSend: function (xhr, settings) {
if (!csrfSafeMethod(settings.type)) {
xhr.setRequestHeader('X-CSRFToken', getCookie('csrftoken'));
}
}
});
function getCurrentLanguage() {
// Returns a short name, like 'en' or 'de'
return $('#current-language').data('currentLanguage');
}
/*
Setup sortable to make the sets sortable
*/
function wgerSetupSortable() {
var elements = document.getElementsByTagName('tbody');
$.each(elements, function (index, element) {
Sortable.create(element, {
handle: '.dragndrop-handle',
animation: 150,
onUpdate: function (event) {
var dayId;
dayId = $(event.target).parents('table').data('id');
$.each(($(event.from).children('tr')), function (eventIndex, eventElement) {
var trElement;
var setId;
trElement = $(eventElement);
// The last table element has no ID attribute (has only the 'add exercise' link
if (trElement.data('id')) {
setId = trElement.data('id');
$.ajax({
url: '/api/v2/set/' + setId + '/',
type: 'PATCH',
data: {order: eventIndex + 1}
});
}
});
// Replace the content of the table with a fresh version that has
// correct indexes.
$.get('/' + getCurrentLanguage() + '/routine/day/' + dayId + '/view/');
$('#div-day-' + dayId)
.load('/' + getCurrentLanguage() + '/routine/day/' + dayId + '/view/');
}
});
});
}
/*
Functions related to the user's preferences
*/
/*
Updates a single field in the user profile
*/
function setProfileField(field, newValue) {
var dataDict = {};
dataDict[field] = newValue;
$.post({
url: '/api/v2/userprofile/',
data: dataDict
});
}
/*
Get a single field from the user's profile.
Synchronous request, use sparingly!
*/
function getProfileField(field) {
var result;
result = null;
$.ajax({
url: '/api/v2/userprofile/',
type: 'GET',
async: false,
success: function (userprofile) {
result = userprofile.results[field];
}
});
return result;
}
/*
Open a modal dialog for form editing
*/
function modalDialogFormEdit() {
var $submit;
var $form;
$form = $('#ajax-info-content').find('form');
$submit = $($form).find('#form-save');
$submit.click(function (e) {
var formData;
var formAction;
e.preventDefault();
formAction = $form.attr('action');
formData = $form.serialize();
// Unbind all click elements, so the form doesn't get submitted twice
// if the user clicks 2 times on the button (while there is already a request
// happening in the background)
$submit.off();
// Show a loader while we fetch the real page
$form.html('<div style="text-align:center;">' +
'<img src="/static/images/loader.svg" ' +
'width="48" ' +
'height="48"> ' +
'</div>');
$('#ajax-info-title').html('Processing'); // TODO: translate this
// OK, we did the POST, what do we do with the result?
$.ajax({
type: 'POST',
url: formAction,
data: formData,
beforeSend: function (jqXHR) {
// Send a custom header so django's messages are not displayed in the next
// request which will be not be displayed to the user, but on the next one
// that will
jqXHR.setRequestHeader('X-wger-no-messages', '1');
},
success: function (data, textStatus, jqXHR) {
var url = jqXHR.getResponseHeader('X-wger-redirect');
if (url) {
window.location.href = url;
/*
if(document.URL.indexOf(url)) {
history.pushState({}, "", url);
}
*/
} else if ($(data).find('form .has-error').length > 0) {
// we must do the same with the new form as before, binding the click-event,
// checking for errors etc, so it calls itself here again.
$form.html($(data).find('form').html());
$('#ajax-info-title').html($(data).find('#page-title').html());
modalDialogFormEdit();
} else {
console.log('No X-wger-redirect found but also no .has-error!');
$('#wger-ajax-info').modal('hide');
$form.html(data);
}
// Call other custom initialisation functions
// (e.g. if the form as an autocompleter, it has to be initialised again)
if (typeof wgerCustomModalInit !== 'undefined') {
wgerCustomModalInit(); // eslint-disable-line no-undef
}
if (typeof wgerCustomPageInit !== 'undefined') {
wgerCustomPageInit(); // eslint-disable-line no-undef
}
},
error: function (jqXHR) {
// console.log(errorThrown); // INTERNAL SERVER ERROR
$('#ajax-info-content').html(jqXHR.responseText);
}
});
});
}
function wgerFormModalDialog() {
var $wgerModalDialog;
$wgerModalDialog = $('.wger-modal-dialog');
// Unbind all other click events so we don't do this more than once
$wgerModalDialog.off();
// Load the edit dialog when the user clicks on an edit link
$wgerModalDialog.click(function (e) {
var $ajaxInfoContent;
var targetUrl;
e.preventDefault();
targetUrl = $(this).attr('href');
// It's not possible to have more than one modal open at any time, so close them
$('.modal').modal('hide');
// Show a loader while we fetch the real page
$ajaxInfoContent = $('#ajax-info-content');
$ajaxInfoContent.html('<div style="text-align:center;">' +
'<img src="/static/images/loader.svg" ' +
'width="48" ' +
'height="48"> ' +
'</div>');
$('#ajax-info-title').html('Loading...');
$('#wger-ajax-info').modal('show');
$ajaxInfoContent.load(targetUrl + ' .wger-form',
function (responseText, textStatus, XMLHttpRequest) {
var $ajaxInfoTitle;
var modalTitle;
$ajaxInfoTitle = $('#ajax-info-title');
if (textStatus === 'error') {
$ajaxInfoTitle.html('Sorry but an error occured');
$('#ajax-info-content').html(XMLHttpRequest.status + ' ' + XMLHttpRequest.statusText);
}
// Call other custom initialisation functions
// (e.g. if the form as an autocompleter, it has to be initialised again)
if (typeof wgerCustomModalInit !== 'undefined') {
// Function is defined in templates. Eslint doesn't check the templates resulting in a
// un-def error message.
wgerCustomModalInit(); // eslint-disable-line no-undef
}
// Set the new title
modalTitle = '';
if ($(responseText).find('#page-title').length > 0) {
// Complete HTML page
modalTitle = $(responseText).find('#page-title').html();
} else {
// Page fragment
modalTitle = $(responseText).filter('#page-title').html();
}
$ajaxInfoTitle.html(modalTitle);
// If there is a form in the modal dialog (there usually is) prevent the submit
// button from submitting it and do it here with an AJAX request. If there
// are errors (there is an element with the class 'ym-error' in the result)
// reload the content back into the dialog so the user can correct the entries.
// If there isn't assume all was saved correctly and load that result into the
// page's main DIV (#main-content). All this must be done like this because there
// doesn't seem to be any reliable and easy way to detect redirects with AJAX.
if ($(responseText).find('.wger-form').length > 0) {
modalDialogFormEdit();
}
});
});
}
/*
Returns a random hex string. This is useful, e.g. to add a unique ID to generated
@@ -295,8 +37,8 @@ function getRandomHex() {
Template-like function that adds form elements to the ajax exercise selection in the edit set page
*/
function addExercise(exercise) {
var $exerciseSearchLog;
var resultDiv;
let $exerciseSearchLog;
let resultDiv;
resultDiv = '<div id="DIV-ID" class="ajax-exercise-select">\n' +
' <a href="#" ' +
'data-role="button" ' +
@@ -320,15 +62,15 @@ function addExercise(exercise) {
}
function getExerciseFormset(baseId) {
var formsetUrl;
var setValue;
let formsetUrl;
let setValue;
setValue = $('#id_sets').val();
if (setValue && parseInt(setValue, 10) && baseId && parseInt(baseId, 10)) {
formsetUrl = '/' + getCurrentLanguage() +
'/routine/set/get-formset/' + baseId + '/' + setValue;
$.get(formsetUrl, function (data) {
var $formsets;
let $formsets;
$formsets = $('#formsets');
$formsets.append(data);
$('#exercise-search-log').scrollTop(0);
@@ -341,13 +83,13 @@ function getExerciseFormset(baseId) {
Updates all exercise formsets, e.g. when the number of sets changed
*/
function updateAllExerciseFormset() {
var setValue;
let setValue;
setValue = $('#id_sets').val();
if (setValue && parseInt(setValue, 10)) {
$.each($('#exercise-search-log').find('input'), function (index, value) {
var promise;
var exerciseId;
var formsetUrl;
let promise;
let exerciseId;
let formsetUrl;
exerciseId = value.value;
promise = $().promise();
if (exerciseId && parseInt(exerciseId, 10)) {
@@ -356,7 +98,7 @@ function updateAllExerciseFormset() {
'get-formset/' + exerciseId + '/' + setValue;
promise.done(function () {
promise = $.get(formsetUrl, function (data) {
var $formsets;
let $formsets;
$('#formset-base-' + exerciseId).remove();
$formsets = $('#formsets');
$formsets.append(data);
@@ -375,7 +117,7 @@ function updateAllExerciseFormset() {
*/
function initRemoveExerciseFormset() {
$('.ajax-exercise-select a').click(function (e) {
var baseId;
let baseId;
e.preventDefault();
baseId = $(this).parent('div').find('input').val();
$('#formset-base-' + baseId).remove();
@@ -424,9 +166,9 @@ function wgerInitEditSet() {
// Mobile select box
$('#id_exercise_list').change(function () {
var $idExerciseList;
var baseId;
var exerciseName;
let $idExerciseList;
let baseId;
let exerciseName;
$idExerciseList = $('#id_exercise_list');
baseId = $idExerciseList.val();
exerciseName = $idExerciseList.find(':selected').text();
@@ -457,10 +199,10 @@ function wgerInitEditSet() {
$('#id_categories_list').on('change', function () {
// Remember to filter by exercise language
$.get('/api/v2/language/?short_name=' + getCurrentLanguage(), function (data) {
var filter;
var baseUrl;
var categoryPk;
var languagePk;
let filter;
let baseUrl;
let categoryPk;
let languagePk;
languagePk = data.results[0].id;
categoryPk = $('#id_categories_list').val();
baseUrl = '/api/v2/exercise/';
@@ -473,7 +215,7 @@ function wgerInitEditSet() {
$.get(baseUrl + filter, function (exerciseData) {
// Sort the results by name, at the moment it's not possible
// to search and sort the API at the same time
var $idExerciseList;
let $idExerciseList;
exerciseData.results.sort(function (a, b) {
if (a.name < b.name) {
return -1;
@@ -500,28 +242,6 @@ function wgerInitEditSet() {
});
}
/*
Helper function to load the target of a link into the main-content DIV (the main left colum)
*/
function wgerLoadMaincontent() {
$('.load-maincontent').click(function (e) {
var targetUrl;
e.preventDefault();
targetUrl = $(this).attr('href');
$.get(targetUrl, function (data) {
var currentUrl;
// Load the data
$('#main-content').html($(data).find('#main-content').html());
// Update the browser's history
currentUrl = $(data).find('#current-url').data('currentUrl');
history.pushState({}, '', currentUrl);
wgerLoadMaincontent();
});
});
}
/*
Helper function used in the workout log dialog to fetch existing workout sessions through the
@@ -529,7 +249,7 @@ function wgerLoadMaincontent() {
*/
function wgerGetWorkoutSession() {
$('#id_date').on('change', function () {
var date = $('#id_date').val();
let date = $('#id_date').val();
if (date) {
$.get('/api/v2/workoutsession/?date=' + date, function (data) {
if (data.results.length === 1) {
@@ -551,14 +271,14 @@ function wgerGetWorkoutSession() {
$(document).ready(function () {
// Handle the workout PDF download options for workouts
$('#download-pdf-button').click(function (e) {
var targetUrl;
var token;
var uid;
var workoutId;
var downloadComments;
var downloadImages;
var downloadType;
var downloadInfo;
let targetUrl;
let token;
let uid;
let workoutId;
let downloadComments;
let downloadImages;
let downloadType;
let downloadInfo;
e.preventDefault();
downloadInfo = $('#pdf-download-info');
@@ -583,14 +303,14 @@ $(document).ready(function () {
// Handle the workout PDF download options for schedules
$('#download-pdf-button-schedule').click(function (e) {
var targetUrl;
var token;
var uid;
var scheduleId;
var downloadComments;
var downloadImages;
var downloadType;
var downloadInfo;
let targetUrl;
let token;
let uid;
let scheduleId;
let downloadComments;
let downloadImages;
let downloadType;
let downloadInfo;
e.preventDefault();
downloadInfo = $('#pdf-download-info');

View File

@@ -58,7 +58,13 @@
"replacementsInfoText": "Optional können Sie auch eine Übung auswählen, die diese ersetzen soll (z. B. weil sie zweimal eingereicht wurde oder ähnliches). Dadurch wird die Übung in Routinen sowie Trainingsprotokollen ersetzt, anstatt sie einfach zu löschen. Diese Änderungen werden auch auf alle Instanzen übertragen, die Übungen von dieser übernehmen.",
"replacementsSearch": "Suche nach Übungen oder kopiere eine Bekannte ID in das Feld und drücke den \"Laden\" Button.",
"noReplacementSelected": " Keine Übung zum ersätzen ausgewählt",
"deleteExerciseReplace": "Löschen und ersetzen"
"deleteExerciseReplace": "Löschen und ersetzen",
"imageStylePhoto": "Foto",
"imageStyle3D": "3D",
"imageStyleLine": "Linie",
"imageStyleLowPoly": "Niedrig-Poly",
"imageStyleOther": "andere",
"imageDetails": "Bilddetails"
},
"noResults": "Keine Ergebnisse",
"noResultsDescription": "Keine Ergebnisse für diese Suche gefunden, bitte reduziere die Anzahl der Filter.",
@@ -100,7 +106,8 @@
"kilometers_per_hour": "Kilometer pro Stunde",
"body_weight": "Körpergewicht",
"miles_per_hour": "Meilen pro Stunde",
"max_reps": "Max. Wdh."
"max_reps": "Max. Wdh.",
"plates": "Gewichtsscheiben"
},
"submit": "Abschicken",
"weight": "Gewicht",
@@ -127,7 +134,9 @@
"routine": "Routine",
"routines": "Routinen",
"addWeightLog": "Trainingslog hinzufügen",
"logsFilterNote": "Beachte, dass nur Einträge mit einer Gewichtseinheit (kg oder lb) und Wiederholungen gezeichnet werden, andere Kombinationen wie Zeit oder bis zum Ausfall werden hier ignoriert"
"logsFilterNote": "Beachte, dass nur Einträge mit einer Gewichtseinheit (kg oder lb) und Wiederholungen gezeichnet werden, andere Kombinationen wie Zeit oder bis zum Ausfall werden hier ignoriert",
"logsHeader": "Trainingsprotokoll für das Workout",
"addLogToDay": "Protokoll zu diesem Tag hinzufügen"
},
"name": "Name",
"cancel": "Abbrechen",
@@ -197,11 +206,21 @@
"addMealItem": "Zutat zu Mahlzeit hinzufügen",
"searchIngredientName": "Suche nach Zutatenname",
"saturatedFat": "Gesättigte Fette",
"valueTooMany": "zu viele"
"valueTooMany": "zu viele",
"logThisMealItem": "Diese Zutat unverändert in das Ernährungstagebuch eintragen",
"onlyLoggingHelpText": "Nur Kalorien verfolgen. Markiere das Kästchen, wenn du nur deine Kalorien protokollieren möchtest und keinen detaillierten Ernährungsplan mit spezifischen Mahlzeiten einrichten willst",
"goalFiber": "Ballaststoffziel",
"logThisMeal": "Dieses Gericht unverändert in das Ernährungstagebuch eintragen"
},
"downloadAsPdf": "Als PDF runterladen",
"total": "Summe",
"licenses": {
"authors": "Author(en)"
}
"authors": "Author(en)",
"derivativeSourceUrl": "Link zur Originalquelle, falls es sich um ein abgeleitetes Werk handelt",
"originalObjectUrl": "Link zur Quell-Website, falls verfügbar",
"originalTitle": "Titel",
"authorProfile": "Link zur Website oder zum Profil des Autors, falls verfügbar",
"derivativeSourceUrlHelper": "Beachte, dass ein abgeleitetes Werk nicht nur auf einem vorherigen Werk basiert, sondern auch genügend neue, kreative Inhalte enthält, um ein eigenes Urheberrecht zu beanspruchen."
},
"filters": "Filter"
}

View File

@@ -1,5 +1,7 @@
{
"weight": "Weight",
"height": "Height",
"cm": "cm",
"date": "Date",
"timeOfDay": "Time of day",
"submit": "Submit",
@@ -10,6 +12,11 @@
"close": "Close",
"difference": "Difference",
"days": "Days",
"all": "All",
"lastYear": "Last Year",
"lastHalfYear": "Last 6 Months",
"lastMonth": "Last Month",
"lastWeek": "Last Week",
"licenses": {
"authors": "Author(s)",
"authorProfile": "Link to author website or profile, if available",
@@ -135,6 +142,14 @@
"valueRemaining": "remaining",
"valueTooMany": "too many"
},
"bmi": {
"calculator": "BMI calculator",
"overweight": "Overweight",
"obese": "Obese",
"normal": "Normal weight",
"underweight": "Underweight",
"result": "Your BMI is {{value}}"
},
"downloadAsPdf": "Download as PDF",
"total": "Total",
"description": "Description",

View File

@@ -130,7 +130,7 @@
"preferences": "Preferencias",
"cancel": "Cancelar",
"noResults": "Sin resultados",
"success": "¡Éxitoso!",
"success": "¡Éxito!",
"routines": {
"rir": "Repeticiones",
"addDay": "Añadir un día de entrenamiento",
@@ -219,5 +219,10 @@
"derivativeSourceUrlHelper": "Tenga en cuenta que una obra derivada es aquella que no sólo se basa en una obra anterior, sino que además contiene suficiente contenido nuevo y creativo como para tener derecho a sus propios derechos de autor.",
"authorProfile": "Enlace al sitio web o perfil del autor, si está disponible"
},
"filters": "Filtros"
"filters": "Filtros",
"lastYear": "El año pasado",
"lastHalfYear": "Últimos 6 meses",
"lastMonth": "Mes pasado",
"all": "Todo",
"lastWeek": "Mes semana"
}

View File

@@ -5,7 +5,7 @@
"routines": "Rotinas",
"addDay": "Adicionar dia de treinamento",
"addWeightLog": "Adicionar registro de peso",
"logsHeader": "Registro de peso para exercícios",
"logsHeader": "Registro de exercícios para treino",
"logsFilterNote": "Observe que somente as entradas com uma unidade de peso de kg ou lb e repetições são registradas; outras combinações, como tempo ou até a falha, são ignoradas aqui",
"addLogToDay": "Adicionar registro a este dia"
},
@@ -68,7 +68,7 @@
"deleteInfo": "Isso excluirá a categoria, bem como todas as suas entradas"
},
"category": "Categoria",
"success": "Sucesso!",
"success": "Sucesso",
"English": "Inglês",
"save": "Salvar",
"videos": "Vídeos",
@@ -117,7 +117,18 @@
"changeExerciseLanguage": "Alterar o idioma deste exercício",
"secondaryMuscles": "Músculos secundários",
"compatibleImagesCC": "As imagens devem ser compatíveis com a licença CC BY SA. Em caso de dúvida, faça o upload apenas de fotos tiradas por você mesmo.",
"exerciseNotTranslatedBody": "Este exercício não está disponível no idioma atualmente selecionado. Você deseja contribuir com uma tradução?"
"exerciseNotTranslatedBody": "Este exercício não está disponível no idioma atualmente selecionado. Você deseja contribuir com uma tradução?",
"replacements": "Substituições",
"imageStylePhoto": "Foto",
"replacementsSearch": "Procure por um exercício ou copie e cole um ID conhecido no campo e e client no botão \"carregar\".",
"noReplacementSelected": " .Nenhum exercício selecionado para substituição",
"imageStyle3D": "3D",
"imageStyleLine": "Linha",
"imageStyleLowPoly": "Polia Baixa",
"imageStyleOther": "Outros",
"imageDetails": "Detalhes da Imagem",
"replacementsInfoText": "Opcionalmente, você pode também selecionar um exercício que pode substituir esse (ex: porque foi substituído duas vezes, or similar). Isso irá substituir o exercício em rotinas e também logs de treinos, ao invés de apenas excluí-lo. Essas mudanças irão também ser propagadas para qualquer instancia que sincroniza os exercícios a partir desse.",
"deleteExerciseReplace": "Delete e substitua."
},
"notes": "Notas",
"value": "Valor",
@@ -145,6 +156,67 @@
"timeOfDay": "Hora do dia",
"licenses": {
"authors": "Autor(es)",
"authorProfile": "Link para o website ou profile do autor, se disponível"
}
"authorProfile": "Link para o website ou profile do autor, se disponível",
"originalTitle": "Título",
"derivativeSourceUrl": "Link para o conteúdo original, se isso é um trabalho derivativo",
"originalObjectUrl": "Link ao website de origem, se disponível",
"derivativeSourceUrlHelper": "Observe que um trabalho derivativo é um em que não é somente baseado em um trabalho anterior, mas que também contém suficientemente novos, conteúdos criativos para dar títulos aos próprios copyrights"
},
"filters": "Filtros",
"nothingHereYet": "Nada aqui ainda...",
"nothingHereYetAction": "Pressione o botão de ação para iniciar",
"copyToClipboard": "Copiar para área de transferência",
"nutrition": {
"goalFat": "Objetivo de gordura",
"addMeal": "Adicionar alimentação",
"addNutritionalDiary": "Adicione entrada diaria nutricional",
"valueEnergyKcalKj": "{{kcal}} kcal / {{kj}} kJ",
"plan": "Plano nutricional",
"addMealItem": "Adicione ingrediente ou alimentação",
"valueEnergyKcal": "{{value}} kcal",
"searchIngredientName": "Procure pelo nome do ingrediente",
"macronutrient": "Macronutriente",
"loggedToday": "Logado hoje",
"difference": "Diferença",
"today": "Hoje",
"sugar": "Açúcar",
"ofWhichSugars": "do qual açúcares",
"fat": "Gordura",
"valueTooMany": "bastante",
"goalFiber": "Objetivo de fibras",
"plans": "Planos nutricionais",
"goalsTitle": "Objectivos",
"useGoalsHelpText": "Adicione objectivos a esse plano",
"goalEnergy": "Objetivo energético",
"goalProtein": "Objetivo proteico",
"goalCarbohydrates": "Objetivo de carboidratos",
"nutritionalDiary": "Nutrição diária",
"gramShort": "g",
"kcal": "kcal",
"planned": "Planejado",
"logged": "Logado",
"percentEnergy": "Porcentagem de energia",
"gPerBodyKg": "g por kg-corpo",
"7dayAvg": "Média em 7-dias",
"ofWhichSaturated": "dos quais saturados",
"saturatedFat": "Gordura saturada",
"pseudoMealTitle": "Outros logs",
"others": "Outros",
"fibres": "Fibras",
"planDeleteInfo": "Isto irá remover todas as entradas de dados de nutrição diárias também",
"sodium": "Sódio",
"mealDeleteInfo": "Entradas de nutrição diária para este alimento não serão removidas e irão aparecer em \"outros logs\"",
"diaryEntrySaved": "Entrada de dados diária salvo com sucesso",
"logThisMealItem": "Log esse ingrediente sem alterações para o diário nutricional",
"valueRemaining": "restantes",
"energy": "Energia",
"carbohydrates": "Carboidratos",
"logThisMeal": "Log esta alimentação sem alterações para o diário nutricional",
"copyPlan": "Faça uma cópia deste plano",
"onlyLoggingHelpText": "Apenas acompanha calorias. Selecione o box se você apenas quer logar suas calorias e não que iniciar um plano de nutrição detalhado com alimentação específica",
"useGoalsHelpTextLong": "Isto permite que você defina objetivos gerais para energia, proteína, carboidratos ou gorduras para o plano. Note que se você iniciar um plano alimentar detalhado, estes valores tem precedência",
"protein": "Proteína",
"meal": "Alimentação"
},
"downloadAsPdf": "Baixar como PDF"
}

View File

@@ -27,7 +27,10 @@
"replacementsSearch": "ыполните поиск упражнения или скопируйте и вставьте известный ID в соответствующее поле и нажмите кнопку \"загрузить\".",
"replacementsInfoText": "По желанию вы также можете выбрать упражнение, которое заменит это (например, если оно было добавлено дважды или похоже на другое). Это заменит упражнение в рутинах, а также в журналах тренировок, вместо того, чтобы просто его удаленить. Эти изменения также будут распространяться на любые экземпляры, которые синхронизируют упражнения из этого.",
"noReplacementSelected": " Не выбрано упражнение для замены",
"replacements": "Замены"
"replacements": "Замены",
"step1HeaderBasics": "Основы (на английском)",
"notEnoughRightsHeader": "Вы не можете добавлять упражнения",
"variations": "Варианты"
},
"noResults": "Нет результатов",
"noResultsDescription": "Для этого запроса не найдено результатов, рассмотрите возможность уменьшения количества фильтров.",
@@ -81,5 +84,11 @@
"copyToClipboard": "Скопируйте в буфер обмена",
"filters": "Фильтры",
"alsoSearchEnglish": "Также поищите имена на английском",
"value": "Значение"
"value": "Значение",
"unit": "Единица",
"all": "Всё",
"lastYear": "Прошлый год",
"lastHalfYear": "Последние 6 месяцев",
"lastWeek": "Предыдущая неделя",
"lastMonth": "Предыдущий месяц"
}

View File

@@ -1,12 +1,222 @@
{
"weight": "எடை",
"date": "தேதி",
"add": "சேர்",
"days": "நாட்கள்",
"currentWeight": "தற்போதைய எடை",
"workout": "பயிற்சி",
"submit": "சமர்ப்பி",
"delete": "நீக்கு",
"deleteConfirmation": "\"{{name}}\" ஐ நிச்சயமாக நீக்க விரும்புகிறீர்களா?",
"nutritionalPlan": "ஊட்டச்சத்து திட்டம்"
"weight": "எடை",
"date": "தேதி",
"add": "சேர்",
"days": "நாட்கள்",
"currentWeight": "தற்போதைய எடை",
"workout": "பயிற்சி",
"submit": "சமர்ப்பி",
"delete": "நீக்கு",
"deleteConfirmation": "\"{{name}}\" ஐ நிச்சயமாக நீக்க விரும்புகிறீர்களா?",
"nutritionalPlan": "ஊட்டச்சத்து திட்டம்",
"edit": "தொகு",
"exercises": {
"replacementsInfoText": "விருப்பமாக, இதை மாற்ற வேண்டிய ஒரு பயிற்சியையும் நீங்கள் தேர்ந்தெடுக்கலாம் (எ.கா. இது இரண்டு முறை சமர்ப்பிக்கப்பட்டது அல்லது அதற்கு ஒத்ததாக இருந்தது). இது நடைமுறைகள் மற்றும் பயிற்சி பதிவுகளை நீக்குவதற்கு பதிலாக, நடைமுறைகள் மற்றும் பயிற்சி பதிவுகளை மாற்றும். இந்த மாற்றங்கள் இதிலிருந்து பயிற்சிகளை ஒத்திசைக்கும் எந்த நிகழ்விற்கும் பிரச்சாரம் செய்யும்.",
"replacementsSearch": "ஒரு உடற்பயிற்சியைத் தேடுங்கள் அல்லது அறியப்பட்ட ஐடியை புலத்தில் நகலெடுத்து ஒட்டவும், \"சுமை\" பொத்தானைக் சொடுக்கு செய்யவும்.",
"noReplacementSelected": " மாற்றுவதற்கு எந்த உடற்பயிற்சியும் தேர்ந்தெடுக்கப்படவில்லை",
"notEnoughRightsHeader": "நீங்கள் பயிற்சிகளை பங்களிக்க முடியாது",
"notEnoughRights": "உங்கள் கணக்கு {{days}} நாட்களை விட பழையதாக இருந்தால் மட்டுமே நீங்கள் பயிற்சிகளை பங்களிக்க முடியும், மேலும் உங்கள் மின்னஞ்சலை சரிபார்த்துள்ளீர்கள்",
"muscles": "தசைகள்",
"secondaryMuscles": "இரண்டாம் நிலை தசைகள்",
"whatVariationsExist": "இந்த பயிற்சியின் எந்த மாறுபாடுகள் உள்ளன, ஏதேனும் இருந்தால்?",
"identicalExercise": "நகல் பயிற்சிகளைத் தவிர்க்கவும்",
"identicalExercisePleaseDiscard": "நீங்கள் சேர்ப்பதற்கு ஒத்த ஒரு பயிற்சியை நீங்கள் கவனித்தால், தயவுசெய்து உங்கள் வரைவை நிராகரித்து, அதற்கு பதிலாக அந்த பயிற்சியைத் திருத்தவும்.",
"equipment": "உபகரணங்கள்",
"cacheWarning": "கேச்சிங் காரணமாக விண்ணப்பம் முழுவதும் மாற்றங்கள் தெரியும் வரை சிறிது நேரம் ஆகலாம்.",
"successfullyUpdated": "உடற்பயிற்சி வெற்றிகரமாக புதுப்பிக்கப்பட்டது. கேச்சிங் காரணமாக விண்ணப்பம் முழுவதும் மாற்றங்கள் தெரியும் வரை சிறிது நேரம் ஆகலாம்.",
"deleteTranslation": "மொழிபெயர்ப்பை நீக்கு",
"deleteExerciseFull": "முழு உடற்பயிற்சியை நீக்கு",
"missingExerciseDescription": "சமூகத்தை பங்களிப்பதன் மூலம் உதவுங்கள்!",
"searchExerciseName": "உடற்பயிற்சி பெயரால் தேடுங்கள்",
"imageStylePhoto": "புகைப்படம்",
"imageStyle3D": "ZD",
"imageStyleLine": "வரி",
"imageStyleLowPoly": "குறைந்த பாலி",
"imageStyleOther": "மற்றொன்று",
"imageDetails": "பட விவரங்கள்",
"replacements": "மாற்றீடுகள்",
"description": "விவரம்",
"primaryMuscles": "முதன்மை தசைகள்",
"deleteExerciseReplace": "நீக்கவும் மாற்றவும்",
"alternativeNames": "மாற்று பெயர்கள்",
"exercises": "பயிற்சிகள்",
"changeExerciseLanguage": "இந்த உடற்பயிற்சியின் மொழியை மாற்றவும்",
"noEquipment": "உபகரணங்கள் இல்லை",
"missingExercise": "ஒரு குறிப்பிட்ட உடற்பயிற்சியைக் காணவில்லையா?",
"notesHelpText": "குறிப்புகள் \"உங்கள் உடலை நேராக வைத்திருங்கள்\" போன்ற பயிற்சியை எவ்வாறு செய்வது என்பது குறித்த குறுகிய கருத்துகள்",
"contributeExercise": "ஒரு உடற்பயிற்சியை பங்களிக்கவும்",
"step1HeaderBasics": "ஆங்கிலத்தில் அடிப்படைகள்",
"variations": "மாறுபாடுகள்",
"checkInformationBeforeSubmitting": "பயிற்சியைச் சமர்ப்பிப்பதற்கு முன் நீங்கள் உள்ளிட்ட செய்தி சரியானதா என்பதை சரிபார்க்கவும்",
"submitExercise": "உடற்பயிற்சியை சமர்ப்பிக்கவும்",
"basics": "அடிப்படைகள்",
"exerciseNotTranslatedBody": "இந்த பயிற்சி தற்போது தேர்ந்தெடுக்கப்பட்ட மொழியில் கிடைக்கவில்லை. மொழிபெயர்ப்பை பங்களிக்க விரும்புகிறீர்களா?",
"newNote": "புதிய குறிப்பு",
"filterVariations": "வடிகட்டி மாறுபாடுகளுக்கு உடற்பயிற்சி பெயரை உள்ளிடவும்",
"compatibleImagesCC": "எச்.ஏ. உரிமம் மூலம் சி.சி உடன் படங்கள் இணக்கமாக இருக்க வேண்டும். ஐயம் இருந்தால், நீங்கள் எடுத்த புகைப்படங்களை மட்டுமே பதிவேற்றவும்.",
"exerciseNotTranslated": "மொழிபெயர்ப்பு எதுவும் கிடைக்கவில்லை",
"alsoKnownAs": "மேலும் அழைக்கப்படுகிறது:",
"deleteExerciseBody": "\"{{name}}\" என்ற பயிற்சியை நீக்க விரும்புகிறீர்களா? நீங்கள் தற்போதைய {{language}} மொழிபெயர்ப்பை அல்லது அனைத்து மொழிபெயர்ப்புகள், படங்கள் போன்றவற்றைக் கொண்ட முழுமையான உடற்பயிற்சியை நீக்கலாம்.",
"translateExerciseNow": "இந்த பயிற்சியை இப்போது மொழிபெயர்க்கவும்",
"notes": "குறிப்புகள்"
},
"nutrition": {
"plans": "ஊட்டச்சத்து திட்டங்கள்",
"onlyLoggingHelpText": "கலோரிகளை மட்டுமே கண்காணிக்கவும். உங்கள் கலோரிகளை மட்டுமே உள்நுழைய விரும்பினால், குறிப்பிட்ட உணவுடன் விரிவான ஊட்டச்சத்து திட்டத்தை அமைக்க விரும்பவில்லை என்றால் பெட்டியை சரிபார்க்கவும்",
"useGoalsHelpText": "இந்த திட்டத்தில் இலக்குகளைச் சேர்க்கவும்",
"useGoalsHelpTextLong": "திட்டத்திற்கான ஆற்றல், புரதம், கார்போஐட்ரேட்டுகள் அல்லது கொழுப்புக்கான பொதுவான இலக்குகளை நிர்ணயிக்க இது உங்களை அனுமதிக்கிறது. நீங்கள் ஒரு விரிவான உணவுத் திட்டத்தை அமைத்தால், இந்த மதிப்புகள் முன்னுரிமை பெறும் என்பதை நினைவில் கொள்க.",
"goalCarbohydrates": "கார்போஐட்ரேட் இலக்கு",
"kcal": "கிலோகலோரி",
"valueEnergyKcalKj": "{{kcal}} kcal / {{kj}} kj",
"searchIngredientName": "மூலப்பொருள் பெயரால் தேடுங்கள்",
"macronutrient": "மேக்ரோனூட்ரியண்ட்",
"percentEnergy": "ஆற்றலின் விழுக்காடு",
"logged": "உள்நுழைந்த",
"loggedToday": "இன்று உள்நுழைந்துள்ளது",
"difference": "வேறுபாடு",
"7dayAvg": "7 நாள் சராசரி",
"protein": "புரதம்",
"pseudoMealTitle": "பிற பதிவுகள்",
"others": "மற்றவர்கள்",
"sodium": "உவர்மம்",
"mealDeleteInfo": "இந்த உணவுக்கான ஊட்டச்சத்து டைரி உள்ளீடுகள் நீக்கப்படாது, மேலும் \"பிற பதிவுகள்\" இன் கீழ் தோன்றும்",
"logThisMeal": "இந்த உணவை ஊட்டச்சத்து நாட்குறிப்புக்கு பதிவுசெய்க",
"logThisMealItem": "இந்த மூலப்பொருளை ஊட்டச்சத்து நாட்குறிப்புக்கு பதிவுசெய்க",
"valueRemaining": "மீதமுள்ள",
"meal": "உணவு",
"addMeal": "உணவு சேர்க்கவும்",
"copyPlan": "இந்த திட்டத்தின் நகலை உருவாக்கவும்",
"goalProtein": "புரத இலக்கு",
"addMealItem": "உணவுக்கு மூலப்பொருள் சேர்க்கவும்",
"nutritionalDiary": "ஊட்டச்சத்து நாட்குறிப்பு",
"gramShort": "g",
"valueEnergyKcal": "{{value}} கிலோகலோரி",
"planDeleteInfo": "இது அனைத்து ஊட்டச்சத்து டைரி உள்ளீடுகளையும் நீக்கிவிடும்",
"goalFiber": "ஃபைபர் இலக்கு",
"today": "இன்று",
"energy": "ஆற்றல்",
"plan": "ஊட்டச்சத்து திட்டம்",
"goalsTitle": "இலக்குகள்",
"goalEnergy": "ஆற்றல் இலக்கு",
"goalFat": "கொழுப்பு இலக்கு",
"addNutritionalDiary": "ஊட்டச்சத்து டைரி நுழைவு சேர்க்கவும்",
"gPerBodyKg": "g க்கு g-kg",
"planned": "திட்டமிடப்பட்டது",
"carbohydrates": "கார்போஐட்ரேட்டுகள்",
"sugar": "சர்க்கரை",
"ofWhichSugars": "அதில் சர்க்கரைகள்",
"fat": "கொழுப்பு",
"ofWhichSaturated": "அதில் நிறைவுற்றது",
"saturatedFat": "பூரிதக் கொழுப்பு",
"fibres": "இழைகள்",
"diaryEntrySaved": "டைரி நுழைவு வெற்றிகரமாக சேமிக்கப்பட்டது",
"valueTooMany": "பல"
},
"description": "விவரம்",
"forms": {
"maxLength": "தயவுசெய்து {{chars}} எழுத்துக்களை விட குறைவாக உள்ளிடவும்",
"minLength": "தயவுசெய்து {{chars}} எழுத்துக்களை விட அதிகமாக உள்ளிடவும்",
"supportedImageFormats": "20MB க்குக் கீழே உள்ள JPEG, PNG மற்றும் WEBP கோப்புகள் மட்டுமே ஆதரிக்கப்படுகின்றன",
"valueTooShort": "மதிப்பு மிகக் குறைவு",
"minValue": "இந்த புலத்திற்கான மதிப்பு {{value}} ஐ விட அதிகமாக இருக்க வேண்டும்",
"valueTooLong": "மதிப்பு மிக நீளமானது",
"fieldRequired": "இந்த புலம் தேவை",
"maxValue": "இந்த புலத்திற்கான மதிப்பு {{value}} than ஐ விட குறைவாக இருக்க வேண்டும்"
},
"category": "வகை",
"success": "வெற்றி!",
"noResultsDescription": "இந்த வினவலுக்கு எந்த முடிவுகளும் கிடைக்கவில்லை, வடிப்பான்களின் எண்ணிக்கையைக் குறைப்பதைக் கவனியுங்கள்.",
"routines": {
"addWeightLog": "பயிற்சி பதிவைச் சேர்க்கவும்",
"logsFilterNote": "KG அல்லது LB இன் எடை அலகு கொண்ட உள்ளீடுகள் மற்றும் மறுபடியும் மறுபடியும் பட்டியலிடப்பட்டுள்ளன என்பதை நினைவில் கொள்க, நேரம் போன்ற பிற சேர்க்கைகள் அல்லது தோல்வி வரை இங்கே புறக்கணிக்கப்படும்",
"rir": "ஆர்.ஐ.ஆர்",
"routine": "வழக்கமான",
"routines": "நடைமுறைகள்",
"addDay": "பயிற்சி நாள் சேர்க்கவும்",
"logsHeader": "வொர்க்அவுட்டுக்கான பயிற்சி பதிவு",
"addLogToDay": "இன்றுவரை பதிவைச் சேர்க்கவும்"
},
"server": {
"incline_bench": "சாய்வு பெஞ்ச்",
"kettlebell": "கெட்டில் பெல்",
"miles": "மைல்கள்",
"miles_per_hour": "ஒரு மணி நேரத்திற்கு மைல்கள்",
"minutes": "நிமிடங்கள்",
"quads": "குவாட்ச்",
"repetitions": "மறுபடியும் மறுபடியும்",
"triceps": "ட்ரைசெப்ச்",
"kg": "கிலோ",
"kilometers": "கிலோமீட்டர்",
"lats": "லாட்ச்",
"legs": "கால்கள்",
"max_reps": "அதிகபட்ச பிரதிநிதிகள்",
"none__bodyweight_exercise_": "எதுவுமில்லை (உடல் எடை உடற்பயிற்சி)",
"abs": "ஏபிஎச்",
"arms": "ஆயுதங்கள்",
"glutes": "க்ளூட்டுகள்",
"gym_mat": "சிம் பாய்",
"sz_bar": "Szar",
"seconds": "நொடிகள்",
"lb": "எல்.பி.",
"back": "பின்",
"barbell": "பார்பெல்",
"bench": "பென்ச்",
"biceps": "கயிறுகள்",
"body_weight": "உடல் எடை",
"calves": "கன்றுகள்",
"cardio": "கார்டியோ",
"chest": "மார்பு",
"dumbbell": "டம்பல்",
"hamstrings": "தொடை எலும்புகள்",
"kilometers_per_hour": "ஒரு மணி நேரத்திற்கு கிலோமீட்டர்",
"plates": "தட்டுகள்",
"pull_up_bar": "பட்டியை இழுக்கவும்",
"shoulders": "தோள்கள்",
"swiss_ball": "சுவிச் பந்து",
"until_failure": "தோல்வி வரை"
},
"licenses": {
"authors": "ஆசிரியர் (கள்)",
"authorProfile": "கிடைத்தால், ஆசிரியர் வலைத்தளம் அல்லது சுயவிவரத்திற்கான இணைப்பு",
"derivativeSourceUrl": "அசல் மூலத்துடன் இணைக்கவும், இது ஒரு வழித்தோன்றல் வேலை என்றால்",
"derivativeSourceUrlHelper": "ஒரு வழித்தோன்றல் வேலை என்பது முந்தைய படைப்பை அடிப்படையாகக் கொண்ட ஒன்றாகும், ஆனால் அதன் சொந்த பதிப்புரிமைக்கு உரிமை பெற போதுமான புதிய, ஆக்கபூர்வமான உள்ளடக்கத்தையும் கொண்டுள்ளது என்பதை நினைவில் கொள்க.",
"originalObjectUrl": "கிடைத்தால், மூல வலைத்தளத்துடன் இணைக்கவும்",
"originalTitle": "தலைப்பு"
},
"seeDetails": "விவரங்களைக் காண்க",
"actions": "செயல்கள்",
"notes": "குறிப்புகள்",
"unit": "அலகு",
"alsoSearchEnglish": "ஆங்கிலத்தில் பெயர்களையும் தேடுங்கள்",
"copyToClipboard": "கிளிப்போர்டுக்கு நகலெடுக்கவும்",
"images": "படங்கள்",
"overview": "கண்ணோட்டம்",
"goBack": "பின்",
"language": "மொழி",
"save": "சேமி",
"filters": "வடிப்பான்கள்",
"videos": "வீடியோக்கள்",
"noResults": "முடிவுகள் இல்லை",
"translation": "மொழிபெயர்ப்பு",
"English": "ஆங்கிலம்",
"timeOfDay": "நாள் நேரம்",
"close": "மூடு",
"difference": "வேறுபாடு",
"loading": "ஏற்றுகிறது ...",
"addEntry": "உள்ளீட்டைச் சேர்க்கவும்",
"value": "மதிப்பு",
"downloadAsPdf": "PDF ஆக பதிவிறக்கவும்",
"total": "மொத்தம்",
"preferences": "விருப்பத்தேர்வுகள்",
"continue": "தொடரவும்",
"name": "பெயர்",
"cannotBeUndone": "இந்த செயலை செயல்தவிர்க்க முடியாது.",
"cancel": "ரத்துசெய்",
"measurements": {
"measurements": "அளவீடுகள்",
"unitFormHelpText": "சி.எம் அல்லது % போன்ற வகை அளவிடப்படும் அலகு",
"deleteInfo": "இது வகையையும் அதன் அனைத்து உள்ளீடுகளையும் நீக்கும்"
},
"nothingHereYet": "இன்னும் இங்கே எதுவும் இல்லை ...",
"nothingHereYetAction": "தொடங்க செயல் பொத்தானை அழுத்தவும்"
}

File diff suppressed because one or more lines are too long

View File

@@ -1,23 +1,10 @@
{% extends extend_template %}
{% load i18n crispy_forms_tags %}
<!--
Title
-->
{% block title %}{{title}}{% endblock %}
<h4>{{ title }}</h4>
<p>{% translate "Are you sure you want to delete this? This action cannot be undone." %}</p>
{% if delete_message %}
<p>{{ delete_message }}</p>
{% endif %}
{% crispy form %}
<!--
Main Content
-->
{% block content %}
<div class="wger-form">
<p>{% translate "Are you sure you want to delete this? This action cannot be undone." %}</p>
{% if delete_message %}
<p>{{ delete_message }}</p>
{% endif %}
{% crispy form %}
</div>
{% endblock %}

View File

@@ -1,29 +1,4 @@
{% extends extend_template %}
{% load i18n static crispy_forms_tags %}
{% load crispy_forms_tags %}
{% block header %}
{{ form.media }}
{% if custom_js %}
<script type="text/javascript">
$(document).ready(function() {
{{custom_js|safe}}
});
</script>
{% endif %}
{% endblock %}
{% block title %}{{title}}{% endblock %}
{% block content %}
{% if form %}
{% crispy form %}
{% else %}
Looks like you clicked on an invalid link. Please try again.
{% endif %}
{% endblock %}
<!--
Side bar
-->
{% block sidebar %}{% if sidebar %}{% include sidebar %}{% endif %}{% endblock %}
<h4>{{ title }}</h4>
{% crispy form %}

View File

@@ -0,0 +1,29 @@
{% extends 'base.html' %}
{% load i18n static crispy_forms_tags %}
{% block header %}
{{ form.media }}
{% if custom_js %}
<script type="text/javascript">
$(document).ready(function () {
{{ custom_js|safe }}
});
</script>
{% endif %}
{% endblock %}
{% block title %}{{ title }}{% endblock %}
{% block content %}
{% if form %}
{% crispy form %}
{% else %}
Looks like you clicked on an invalid link. Please try again.
{% endif %}
{% endblock %}
<!--
Sidebar
-->
{% block sidebar %}{% if sidebar %}{% include sidebar %}{% endif %}{% endblock %}

View File

@@ -31,7 +31,6 @@
{# Options #}
{# #}
{% block options %}
<a href="{% url 'core:language:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url="core:language:add" text=text %}
{% endblock %}

View File

@@ -61,17 +61,15 @@
{% translate "Options" %}
</button>
<div class="dropdown-menu">
<a href="{% url 'core:language:edit' view_language.id %}"
class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'edit' %}"></span>
{% translate "Edit" %}
</a>
{% translate 'Edit' as text %}
{% url 'core:language:edit' view_language.id as url %}
{% modal_link url=url text=text css_class="dropdown-item" %}
<div role="separator" class="dropdown-divider"></div>
<a href="{% url 'core:language:delete' view_language.id %}"
class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete" %}
</a>
{% translate 'Delete' as text %}
{% url 'core:language:delete' view_language.id as url %}
{% modal_link url=url text=text css_class="dropdown-item" %}
</div>
</div>
</div>

View File

@@ -9,14 +9,18 @@
{% for license in license_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:license:edit' license.id %}"
class="dropdown-item wger-modal-dialog">{% translate 'Edit' %}</a>
<a href="{% url 'core:license:delete' license.id %}"
class="dropdown-item wger-modal-dialog">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:license:edit' license.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:license:delete' license.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{{ license }}
@@ -36,8 +40,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:license:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:license:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -1,50 +0,0 @@
% extends "base.html" %}
{% load i18n static wger_extras %}
{% block title %}{% translate "License list" %}{% endblock %}
{% block content %}
<ul class="list-group">
{% for license in license_list %}
<li class="list-group-item ">
<div class="btn-group float-end">
<button type="button" class="btn btn-light dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{% url 'core:license:edit' license.id %}"
class="wger-modal-dialog">{% translate 'Edit' %}</a>
</li>
<li>
<a href="{% url 'core:license:delete' license.id %}"
class="wger-modal-dialog">{% translate 'Delete' %}</a>
</li>
</ul>
</div>
{{ license }}
</li>
{% empty %}
<li class="list-group-item">
{% translate "Nothing found" %}
</li>
{% endfor %}
</ul>
{% endblock %}
{% block sidebar %}
{% if perms.core.add_license %}
<p>
<a href="{% url 'core:license:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
</p>
<p>{% blocktranslate %}If a license has been localized, e.g. the Creative Commons
licenses for the different countries, add them as separate entries here.{% endblocktranslate %}</p>
{% endif %}
{% endblock %}

View File

@@ -175,7 +175,8 @@
</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'nutrition:ingredient:list' %}">
<a class="dropdown-item"
href="{% url 'nutrition:ingredient:list' %}">
{% translate "Ingredient overview" %}
</a>
</li>
@@ -183,7 +184,8 @@
<li class="dropdown-divider"></li>
<li class="dropdown-header">{% translate "Administration" %}</li>
<li>
<a class="dropdown-item" href="{% url 'nutrition:weight_unit:list' %}">
<a class="dropdown-item"
href="{% url 'nutrition:weight_unit:list' %}">
{% translate "Ingredient weight units" %}
</a>
</li>
@@ -211,11 +213,8 @@
</a>
</li>
<li>
<a class="dropdown-item"
href="{% url 'weight:add' %}"
rel="nofollow">
{% translate "Add weight entry" %}
</a>
{% translate 'Add weight entry' as text %}
{% modal_link url='weight:add' text=text css_class='dropdown-item' %}
</li>
</ul>
</li>

View File

@@ -10,14 +10,18 @@
<li class="list-group-item list-group-item-action">
{% if unit.id != 1 and unit.id != 2 %}
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<a href="{% url 'core:repetition-unit:edit' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'core:repetition-unit:delete' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:repetition-unit:edit' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:repetition-unit:delete' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</ul>
</div>
{% endif %}
@@ -39,8 +43,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:repetition-unit:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:repetition-unit:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,12 @@
{% load static i18n %}
<a
href="{{ url }}"
class="{{ css_class }}"
hx-get="{{ url }}"
hx-target="#ajax-info-content"
data-bs-toggle="modal"
data-bs-target="#wger-ajax-info"
>
{{ text }}
</a>

View File

@@ -1,41 +0,0 @@
{% load i18n static wger_extras %}
<div id="svg-{{div_uuid}}"></div>
<div class="table-responsive weight-chart-table">
<table class="noborder">
<tr>
{% for date, entry_list in log.items %}
<td style="padding: 0em; border-top-width: 0px; vertical-align: top;">
<table class="table-sm">
<thead>
<tr>
<th colspan="3">{{date|date:"d.m.Y"}}</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-right">{% translate "Reps" %}</td>
<td></td>
<td style="padding:0px;">
{% trans_weight_unit 'kg' user %}
</td>
</tr>
{% for entry in entry_list %}
<tr>
<td class="text-right">{{entry.reps}}</td>
<td class="text-center" style="padding:0px;">×</td>
<td style="padding:0px;">
{{entry.weight}}
{% if entry.rir %}
{{ entry.rir }} {% translate "RiR" %}
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</td>
{% endfor %}
</tr>
</table>
</div>

View File

@@ -67,11 +67,11 @@
{% compress js %}
<script src="{% static 'yarn/jquery/dist/jquery.js' %}"></script>
<script src="{% static 'yarn/bootstrap/dist/js/bootstrap.bundle.min.js' %}"></script>
<script src="{% static 'yarn/Sortable/Sortable.min.js' %}"></script>
<script src="{% static 'yarn/d3/dist/d3.js' %}"></script>
<script src="{% static 'yarn/htmx.org/dist/htmx.min.js' %}"></script>
<script src="{% static 'yarn/popper.js/dist/umd/popper.js' %}"></script>
<script
src="{% static 'yarn/devbridge-autocomplete/dist/jquery.autocomplete.min.js' %}"></script>
<script src="{% static 'yarn/devbridge-autocomplete/dist/jquery.autocomplete.min.js' %}">
</script>
<script src="{% static 'yarn/masonry-layout/dist/masonry.pkgd.min.js' %}"></script>
<script src="{% static 'js/wger-core.js' %}"></script>
<script src="{% static 'js/nutrition.js' %}"></script>
@@ -88,12 +88,6 @@
if (typeof wgerCustomPageInit !== "undefined") {
wgerCustomPageInit();
}
// Init the modal dialog for editing forms
wgerFormModalDialog();
// Initialise the hook to reload the main-content
wgerLoadMaincontent();
});
</script>
@@ -116,7 +110,6 @@
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="ajax-info-title">Modal title</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="Close"></button>
</div>

View File

@@ -1,15 +1,7 @@
{% extends "base.html" %}
{% load i18n static wger_extras crispy_forms_tags %}
{% block title %}{% translate "Delete account" %}{% endblock %}
<h4>{% translate "Delete account" %}</h4>
{% block header %}
{% endblock %}
{% block content %}
<div class="wger-form">
<div class="card bg-light">
<div class="card-header">
<h4 class="card-title">
@@ -26,9 +18,3 @@ and can't be undone. {% endblocktranslate %}{% endwith %}</p>
</div>
<div class="mt-2"></div>
{% crispy form %}
</div>
{% endblock %}
{% block sidebar %}
{% endblock %}

View File

@@ -201,20 +201,38 @@
{% translate "Actions" %} <span class="caret"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:user:edit' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'core:user:edit' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
<div class="dropdown-divider"></div>
{% if current_user.is_active %}
<a href="{% url 'core:user:deactivate' current_user.pk %}" class="dropdown-item">{% translate "Deactivate user"%}</a>
{% else %}
<a href="{% url 'core:user:activate' current_user.pk %}" class="dropdown-item">{% translate "Activate user"%}</a>
{% endif %}
<a data-url="{% url 'gym:gym:reset-user-password' current_user.pk %}" data-bs-toggle="modal" data-bs-target="#confirmation-modal" class="dropdown-item">{% translate "Reset user password" %}</a>
{% translate 'Deactivate user' as text %}
{% url 'core:user:deactivate' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% else %}
{% translate 'Activate user' as text %}
{% url 'core:user:activate' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
<a
data-url="{% url 'gym:gym:reset-user-password' current_user.pk %}"
data-bs-toggle="modal"
data-bs-target="#confirmation-modal"
class="dropdown-item">
{% translate "Reset user password" %}
</a>
{% if perms.gym.manage_gym or perms.gym.manage_gyms %}
{% if current_user.userprofile.gym %}
<a href="{% url 'gym:gym:edit-user-permission' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Roles"%}</a>
{% translate 'Roles' as text %}
{% url 'gym:gym:edit-user-permission' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% endif %}
<a href="{% url 'core:user:delete' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Delete"%}</a>
{% translate 'Delete' as text %}
{% url 'core:user:delete' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -362,7 +380,10 @@
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'gym:admin_note:list' current_user.pk %}" class="dropdown-item">{% translate "Overview" %}</a>
<a href="{% url 'gym:admin_note:add' current_user.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Add"%}</a>
{% translate 'Add' as text %}
{% url 'gym:admin_note:add' current_user.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -442,7 +463,10 @@
{% translate "Actions" %} <span class="caret"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'gym:user_config:edit' current_user.gymuserconfig.pk %}" class="wger-modal-dialog dropdown-item">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'gym:user_config:edit' current_user.gymuserconfig.pk %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}

View File

@@ -27,7 +27,9 @@
</p>
{% endif %}
<p>
<small class="text-muted">{% translate "You need to verify your email to contribute exercises" %}</small>
<small class="text-muted">
{% translate "You need to verify your email to contribute exercises" %}
</small>
</p>
{% endblock %}
@@ -39,8 +41,11 @@
{% block options %}
<div class="btn-group">
<div class="btn-group" role="group">
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<button type="button"
class="btn btn-primary btn-sm dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false">
<span class="{% fa_class 'cog' %}"></span>
{% translate "Options" %}
</button>
@@ -54,10 +59,8 @@
{% translate "API key" %}
</a>
<div role="separator" class="dropdown-divider"></div>
<a href="{% url 'core:user:delete' %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete account" %}
</a>
{% translate 'Delete account' as text %}
{% modal_link url='core:user:delete' text=text css_class='dropdown-item' %}
</div>
</div>
</div>

View File

@@ -10,14 +10,18 @@
<li class="list-group-item list-group-item-action">
{% if unit.id != 1 and unit.id != 2 %}
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'core:weight-unit:edit' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'core:weight-unit:delete' unit.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'core:weight-unit:edit' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'core:weight-unit:delete' unit.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}
@@ -39,8 +43,7 @@
{# #}
{% block options %}
{% if perms.core.add_license %}
<a href="{% url 'core:weight-unit:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='core:weight-unit:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -26,6 +26,7 @@ from django.utils.translation import (
pgettext,
)
from wger.core.tests.base_testcase import get_reverse
# wger
from wger.utils.constants import (
PAGINATION_MAX_TOTAL_PAGES,
@@ -33,7 +34,6 @@ from wger.utils.constants import (
)
from wger.utils.language import get_language_data
register = template.Library()
@@ -79,19 +79,6 @@ def pagination(paginator, page):
return {'page': page, 'page_range': page_range}
@register.inclusion_tag('tags/render_weight_log.html')
def render_weight_log(log, div_uuid, user=None):
"""
Renders a weight log series
"""
return {
'log': log,
'div_uuid': div_uuid,
'user': user,
}
@register.inclusion_tag('tags/muscles.html')
def render_muscles(muscles=None, muscles_sec=None):
"""
@@ -177,6 +164,11 @@ def fa_class(class_name='', icon_type='fas', fixed_width=True):
return mark_safe(css)
@register.inclusion_tag('tags/modal_link.html')
def modal_link(url: str, text: str, css_class='btn btn-success btn-sm'):
return {'url': get_reverse(url), 'text': text, 'css_class': css_class}
@register.simple_tag
def trans_weight_unit(unit, user=None):
"""

View File

@@ -69,7 +69,6 @@ class CreateUserCommand(WgerTestCase):
response = self.client.post(
reverse('api_register'),
{'username': 'restapi', 'email': 'abc@cde.fg', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
count_after = User.objects.count()
self.assertEqual(response.status_code, HTTP_201_CREATED)
@@ -91,7 +90,6 @@ class CreateUserCommand(WgerTestCase):
response = self.client.post(
reverse('api_register'),
{'username': 'restapi', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
count_after = User.objects.count()
self.assertEqual(response.status_code, HTTP_201_CREATED)
@@ -102,21 +100,6 @@ class CreateUserCommand(WgerTestCase):
self.assertEqual(response.data['token'], token.key)
self.assertEqual(count_after, count_before + 1)
def test_post_not_allowed_api_user_creation(self):
"""User admin isn't allowed to register users"""
self.user_login('admin')
count_before = User.objects.count()
response = self.client.post(
reverse('api_register'),
{'username': 'restapi', 'email': 'abc@cde.fg', 'password': 'AekaiLe0ga'},
)
count_after = User.objects.count()
self.assertEqual(response.status_code, HTTP_403_FORBIDDEN)
self.assertEqual(count_after, count_before)
def test_post_unsuccessfully_registration_no_username(self):
"""Test unsuccessful registration (weak password)"""
@@ -127,7 +110,6 @@ class CreateUserCommand(WgerTestCase):
response = self.client.post(
reverse('api_register'),
{'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
@@ -142,7 +124,6 @@ class CreateUserCommand(WgerTestCase):
response = self.client.post(
reverse('api_register'),
{'username': 'restapi', 'email': 'example.com', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)
@@ -157,7 +138,6 @@ class CreateUserCommand(WgerTestCase):
response = self.client.post(
reverse('api_register'),
{'username': 'restapi', 'email': 'admin@example.com', 'password': 'AekaiLe0ga'},
Authorization=f'Token {token.key}',
)
self.assertEqual(response.status_code, HTTP_400_BAD_REQUEST)

View File

@@ -104,7 +104,6 @@ from wger.utils.generic_views import (
from wger.utils.language import load_language
from wger.weight.models import WeightEntry
logger = logging.getLogger(__name__)
@@ -297,7 +296,7 @@ def registration(request):
template_data['form'] = form
template_data['title'] = _('Register')
return render(request, 'form.html', template_data)
return render(request, 'form_content.html', template_data)
@login_required
@@ -549,8 +548,8 @@ class UserDetailView(LoginRequiredMixin, WgerMultiplePermissionRequiredMixin, De
)
context['routine_data'] = out
context['weight_entries'] = WeightEntry.objects.filter(user=self.object).order_by('-date')[
:5
]
:5
]
context['nutrition_plans'] = NutritionPlan.objects.filter(user=self.object).order_by(
'-creation_date'
)[:5]
@@ -606,7 +605,7 @@ class UserListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
class WgerPasswordChangeView(PasswordChangeView):
template_name = 'form.html'
template_name = 'form_content.html'
success_url = reverse_lazy('core:user:preferences')
title = gettext_lazy('Change password')
@@ -627,7 +626,7 @@ class WgerPasswordChangeView(PasswordChangeView):
class WgerPasswordResetView(PasswordResetView):
template_name = 'form.html'
template_name = 'form_content.html'
email_template_name = 'registration/password_reset_email.html'
success_url = reverse_lazy('core:user:password_reset_done')
from_email = settings.WGER_SETTINGS['EMAIL_FROM']
@@ -641,7 +640,7 @@ class WgerPasswordResetView(PasswordResetView):
class WgerPasswordResetConfirmView(PasswordResetConfirmView):
template_name = 'form.html'
template_name = 'form_content.html'
success_url = reverse_lazy('core:user:login')
def get_form(self, form_class=None):

View File

@@ -376,8 +376,10 @@ def search(request):
thumbnail = None
try:
thumbnail = t.get_thumbnail(aliases.get('micro_cropped')).url
except InvalidImageFormatError:
pass
except InvalidImageFormatError as e:
logger.info(f'InvalidImageFormatError while processing a thumbnail: {e}')
except OSError as e:
logger.info(f'OSError while processing a thumbnail: {e}')
result_json = {
'value': translation.name,

View File

@@ -9,14 +9,18 @@
{% for category in exercisecategory_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'exercise:category:edit' category.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'exercise:category:delete' category.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'exercise:category:edit' category.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'exercise:category:delete' category.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{{ category }}
@@ -32,11 +36,8 @@
{% block options %}
{% if perms.exercises.add_exercisecategory %}
<p>
<a href="{% url 'exercise:category:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add" %}
</a>
</p>
{% endif %}
{% if perms.exercises.add_exercisecategory %}
{% translate 'Add' as text %}
{% modal_link url='exercise:category:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -9,14 +9,18 @@
{% for equipment in equipment_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'exercise:equipment:edit' equipment.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'exercise:equipment:delete' equipment.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'exercise:equipment:edit' equipment.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'exercise:equipment:delete' equipment.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{{ equipment.name }}
@@ -30,14 +34,9 @@
{% endblock %}
{% block sidebar %}
{% block options %}
{% if perms.exercises.add_equipment %}
<p>
<a href="{% url 'exercise:equipment:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add new equipment" %}
</a>
</p>
{% translate 'Add' as text %}
{% modal_link url='exercise:equipment:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -13,10 +13,13 @@
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'exercise:muscle:edit' muscle.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'exercise:muscle:delete' muscle.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'exercise:muscle:edit' muscle.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'exercise:muscle:delete' muscle.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{{ muscle }}
@@ -34,8 +37,7 @@
{% block options %}
{% if perms.exercises.add_equipment %}
<a href="{% url 'exercise:muscle:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add muscle" %}
</a>
{% translate 'Add' as text %}
{% modal_link url='exercise:muscle:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -22,29 +22,27 @@
{% for note in adminusernote_list %}
<li class="list-group-item ">
<div class="btn-group float-end">
<button type="button" class="btn btn-light dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-light dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<div class="dropdown-menu" role="menu">
{% if perms.gym.change_adminusernote %}
<li>
<a href="{% url 'gym:admin_note:edit' note.pk %}" class="wger-modal-dialog">
{% translate 'Edit' %}
</a>
</li>
{% translate 'Edit' as text %}
{% url 'gym:admin_note:edit' note.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% if perms.gym.delete_adminusernote %}
<li>
<a href="{% url 'gym:admin_note:delete' note.pk %}" class="wger-modal-dialog">
{% translate 'Delete' %}
</a>
<li>
{% translate 'Delete' as text %}
{% url 'gym:admin_note:delete' note.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</ul>
</div>
</div>
<h4 class="list-group-item-heading">{{ note.timestamp_created }} - {{ note.user|format_username }}</h4>
<h4 class="list-group-item-heading">{{ note.timestamp_created }}
- {{ note.user|format_username }}</h4>
<p class="list-group-item-text">{{ note.note }}</p>
</li>
{% empty %}
@@ -62,9 +60,9 @@
{# Options #}
{# #}
{% block options %}
{% if perms.gym.add_adminusernote %}
<a href="{% url 'gym:admin_note:add' member.pk %}" class="wger-modal-dialog btn btn-success btn-sm">
{% translate "Add" %}
</a>
{% endif %}
{% if perms.gym.add_adminusernote %}
{% translate 'Add' as text %}
{% url 'gym:admin_note:add' member.pk as url %}
{% modal_link url=url text=text %}
{% endif %}
{% endblock %}

View File

@@ -21,24 +21,21 @@
{% for contract in contract_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<div class="dropdown-menu" role="menu">
{% if perms.gym.change_contract %}
<li>
<a href="{% url 'gym:contract:edit' contract.pk %}">
{% translate 'Edit' %}
</a>
</li>
<li>
<a href="{% url 'gym:contract:view' contract.pk %}">
{% translate 'Show' %}
</a>
</li>
{% endif %}
{% translate 'Edit' as text %}
{% url 'gym:contract:edit' contract.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</ul>
{% translate 'Show' as text %}
{% url 'gym:contract:view' contract.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</div>
</div>
@@ -69,6 +66,7 @@
{# Options #}
{# #}
{% block options %}
<a href="{% url 'gym:contract:add' member.pk %}"
class="wger-modal-dialog btn btn-sm btn-success">{% translate "Add" %}</a>
{% translate 'Add' as text %}
{% url 'gym:contract:add' member.pk as url %}
{% modal_link url=url text=text %}
{% endblock %}

View File

@@ -102,7 +102,8 @@
{% block sidebar %}
<h4>{% translate 'Options' %}</h4>
<a href="{% url 'gym:contract:edit' object.pk %}" class="wger-modal-dialog">{% translate 'Edit' %}</a>
{% block options %}
{% translate 'Edit' as text %}
{% url 'gym:contract:edit' object.pk as url %}
{% modal_link url=url text=text %}
{% endblock %}

View File

@@ -24,26 +24,20 @@
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<div class="dropdown-menu" role="menu">
{% if perms.gym.change_contracttype %}
<li>
<a href="{% url 'gym:contract-option:edit' option.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Edit' %}
</a>
</li>
{% translate 'Edit' as text %}
{% url 'gym:contract-option:edit' option.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% if perms.gym.delete_contracttype %}
<li>
<a href="{% url 'gym:contract-option:delete' option.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Delete' %}
</a>
</li>
{% translate 'Delete' as text %}
{% url 'gym:contract-option:delete' option.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</ul>
</div>
</div>
@@ -68,7 +62,7 @@
{# Options #}
{# #}
{% block options %}
<a href="{% url 'gym:contract-option:add' gym.id %}" class="wger-modal-dialog btn btn-sm btn-success">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% url 'gym:contract-option:add' gym.id as url %}
{% modal_link url=url text=text %}
{% endblock %}

View File

@@ -21,29 +21,23 @@
{% for contract_type in contracttype_list %}
<li class="list-group-item list-group-item-action">
<div class="btn-group float-end">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-dark dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<div class="dropdown-menu" role="menu">
{% if perms.gym.change_contracttype %}
<li>
<a href="{% url 'gym:contract_type:edit' contract_type.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Edit' %}
</a>
</li>
{% translate 'Edit' as text %}
{% url 'gym:contract_type:edit' contract_type.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% if perms.gym.delete_contracttype %}
<li>
<a href="{% url 'gym:contract_type:delete' contract_type.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Delete' %}
</a>
</li>
{% translate 'Delete' as text %}
{% url 'gym:contract_type:delete' contract_type.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</ul>
</div>
</div>
@@ -51,7 +45,8 @@
{{ contract_type.name }}
</h4>
<p class="list-group-item-text" style="white-space: pre-line;">{{ contract_type.description }}</p>
<p class="list-group-item-text"
style="white-space: pre-line;">{{ contract_type.description }}</p>
</li>
{% empty %}
<li class="list-group-item">
@@ -68,7 +63,7 @@
{# Options #}
{# #}
{% block options %}
<a href="{% url 'gym:contract_type:add' gym.id %}" class="wger-modal-dialog btn btn-sm btn-success">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% url 'gym:contract_type:add' gym.id as url %}
{% modal_link url=url text=text %}
{% endblock %}

View File

@@ -22,32 +22,32 @@
{% for document in userdocument_list %}
<li class="list-group-item ">
<div class="btn-group float-end">
<button type="button" class="btn btn-light dropdown-toggle btn-sm" data-bs-toggle="dropdown">
<button type="button" class="btn btn-light dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
<span class="{% fa_class 'cog' %}"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>
<a href="{{ document.document.url }}" download="{{ document.original_name }}">
<span class="glyphicon glyphicon-download"></span>
<a href="{{ document.document.url }}"
download="{{ document.original_name }}" class="dropdown-item">
{% translate 'Download' %}
</a>
</li>
{% if perms.gym.change_userdocument %}
<li>
<a href="{% url 'gym:document:edit' document.pk %}">
{% translate 'Edit' %}
</a>
{% translate 'Edit' as text %}
{% url 'gym:document:edit' document.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</li>
{% endif %}
{% if perms.gym.delete_userdocument %}
<li>
<a href="{% url 'gym:document:delete' document.pk %}" class="wger-modal-dialog">
{% translate 'Delete' %}
</a>
{% translate 'Delete' as text %}
{% url 'gym:document:delete' document.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</li>
{% endif %}
</ul>
</div>
@@ -75,8 +75,8 @@
{# #}
{% block options %}
{% if perms.gym.add_userdocument %}
<a href="{% url 'gym:document:add' member.pk %}" class="btn btn-sm btn-success">
{% translate "Add" %}
</a>
{% translate 'Add' as text %}
{% url 'gym:document:add' member.pk as url %}
{% modal_link url=url text=text %}
{% endif %}
{% endblock %}

View File

@@ -16,10 +16,13 @@
<span class="{% fa_class 'cog' %}"></span>
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'gym:gym:edit' gym.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Edit' %}</a>
<a href="{% url 'gym:gym:delete' gym.id %}"
class="wger-modal-dialog dropdown-item">{% translate 'Delete' %}</a>
{% translate 'Edit' as text %}
{% url 'gym:gym:edit' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'gym:gym:delete' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
<a href="{% url 'gym:gym:user-list' gym.pk %}">{{ gym }}</a>
@@ -42,8 +45,8 @@
{% translate "Actions" %} <span class="caret"></span>
</button>
<div class="dropdown-menu" role="menu">
<a href="{% url 'config:gym_config:edit' %}"
class="wger-modal-dialog dropdown-item">{% translate "Edit" %}</a>
{% translate 'Edit' as text %}
{% modal_link url='config:gym_config:edit' text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}
@@ -62,9 +65,8 @@
{# Options #}
{# #}
{% block options %}
{% if perms.gym.add_gym %}
<a href="{% url 'gym:gym:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add new gym" %}
</a>
{% endif %}
{% if perms.gym.add_gym %}
{% translate 'Add new gym' as text %}
{% modal_link url='gym:gym:add' text=text %}
{% endif %}
{% endblock %}

View File

@@ -58,7 +58,14 @@
{% if perms.gym.manage_gym or perms.gym.manage_gyms %}
<td style="text-align: right;">
<a href="{% url 'gym:gym:edit-user-permission' current_user.obj.pk %}" class="btn btn-light btn-sm wger-modal-dialog">
<a
href="{% url 'gym:gym:edit-user-permission' current_user.obj.pk %}"
class="btn btn-light btn-sm "
hx-get="{% url 'gym:gym:edit-user-permission' current_user.obj.pk %}"
hx-target="#ajax-info-content"
data-bs-toggle="modal"
data-bs-target="#wger-ajax-info"
>
<span class="{% fa_class 'cog' %}"></span>
</a>
</td>
@@ -84,8 +91,13 @@
{% translate "Actions" %} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'gym:gym:edit' gym.id %}" class="dropdown-item wger-modal-dialog">{% translate "Edit"%}</a>
<a href="{% url 'gym:export:users' gym.id %}" class="dropdown-item">{% translate "Export"%}</a>
{% translate 'Edit' as text %}
{% url 'gym:gym:edit' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Export' as text %}
{% url 'gym:export:users' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}
@@ -137,7 +149,9 @@
{% translate "Actions" %} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right">
<a href="{% url 'gym:config:edit' gym.config.id %}" class="wger-modal-dialog dropdown-item">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'gym:config:edit' gym.config.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}
@@ -173,7 +187,9 @@
{% translate "Actions" %} <span class="caret"></span>
</a>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'gym:admin_config:edit' user.gymadminconfig.id %}" class="dropdown-item wger-modal-dialog">{% translate "Edit"%}</a>
{% translate 'Edit' as text %}
{% url 'gym:admin_config:edit' user.gymadminconfig.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
</div>
@@ -208,7 +224,10 @@
</a>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'gym:contract_type:list' gym.id %}" class="dropdown-item">{% translate "Overview" %}</a>
<a href="{% url 'gym:contract_type:add' gym.id %}" class="dropdown-item wger-modal-dialog">{% translate "Add" %}</a>
{% translate 'Add' as text %}
{% url 'gym:contract_type:add' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -241,7 +260,10 @@
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'gym:contract-option:list' gym.id %}" class="dropdown-item">{% translate "Overview" %}</a>
<a href="{% url 'gym:contract-option:add' gym.id %}" class="dropdown-item wger-modal-dialog">{% translate "Add" %}</a>
{% translate 'Add' as text %}
{% url 'gym:contract-option:add' gym.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
@@ -311,8 +333,8 @@
{# #}
{% block options %}
{% if perms.gym.manage_gym or perms.gym.manage_gyms %}
<a href="{% url 'gym:gym:add-user' gym.pk %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add member" %}
</a>
{% translate 'Add member' as text %}
{% url 'gym:gym:add-user' gym.pk as url %}
{% modal_link url=url text=text %}
{% endif %}
{% endblock %}

View File

@@ -1,11 +1,10 @@
{% load i18n static %}
<link rel="stylesheet" type="text/css" href="{% static 'yarn/datatables/media/css/dataTables.bootstrap4.min.css' %}">
<script src="{% static 'yarn/datatables/media/js/jquery.datatables.min.js' %}" ></script>
<script src="{% static 'yarn/datatables/media/js/dataTables.bootstrap4.min.js' %}" ></script>
<link rel="stylesheet" type="text/css" href="{% static 'yarn/datatables.net-bs5/css/dataTables.bootstrap5.css' %}">
<script src="{% static 'yarn/datatables.net/js/dataTables.js' %}" ></script>
<script src="{% static 'yarn/datatables.net-bs5/js/dataTables.bootstrap5.js' %}" ></script>
<script>
$(document).ready( function () {
/* Make table sortable */
$('#main_member_list').DataTable({
paging: false,
bFilter: true,
@@ -14,6 +13,7 @@ $(document).ready( function () {
});
</script>
<h1>addasd </h1>
<table class="table table-hover table-responsive" id="main_member_list">
<thead>
<tr>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -59,19 +59,20 @@
{% if is_owner %}
<span class="dropdown">
<div class="float-end">
<button type="button" class="btn btn-link dropdown-toggle btn-sm"
<button type="button"
class="btn btn-link dropdown-toggle btn-sm"
data-bs-toggle="dropdown">
</button>
<div class="dropdown-menu">
<a href="{% url 'manager:log:edit' log.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Edit' %}
</a>
{% translate 'Edit' as text %}
{% url 'manager:log:edit' log.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
<div class="dropdown-divider"></div>
<a href="{% url 'manager:log:delete' log.pk %}"
class="wger-modal-dialog dropdown-item">
{% translate 'Delete' %}
</a>
{% translate 'Delete' as text %}
{% url 'manager:log:delete' log.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
</span>

View File

@@ -81,13 +81,13 @@ $(document).ready(function() {
<button type="button" class="btn btn-link dropdown-toggle btn-sm" data-bs-toggle="dropdown">
</button>
<div class="dropdown-menu dropdown-menu-right" role="menu">
<a href="{% url 'manager:log:edit' log.pk %}" class="wger-modal-dialog dropdown-item">
{% translate 'Edit' %}
</a>
<div class="dropdown-divider"></div>
<a href="{% url 'manager:log:delete' log.pk %}" class="wger-modal-dialog dropdown-item">
{% translate 'Delete' %}
</a>
{% translate 'Edit' as text %}
{% url 'manager:log:edit' log.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete' as text %}
{% url 'manager:log:delete' log.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
{% endif %}

View File

@@ -13,24 +13,26 @@
<span class="caret"></span>
</button>
<div class="dropdown-menu">
<a href="{% url 'manager:session:edit' value.session.pk %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'edit-o' %}"></span>
{% translate "Edit" %}
</a>
{% translate 'Edit' as text %}
{% url 'manager:session:edit' value.session.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
<div class="dropdown-divider"></div>
<a href="{% url 'manager:session:delete' value.session.pk %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete" %}
</a>
<a href="{% url 'manager:session:delete' value.session.pk 'logs' %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete with logs" %}
</a>
{% translate 'Delete' as text %}
{% url 'manager:session:delete' value.session.pk as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% translate 'Delete with logs' as text %}
{% url 'manager:session:delete' value.session.pk 'logs' as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</div>
</div>
</div>
{% else %}
<a href="{% url 'manager:session:add' value.workout.id date|date:'Y' date|date:'m' date|date:'d' %}" class="btn btn-light btn-block wger-modal-dialog">{% translate "Add workout impression" %}</a>
{% translate 'Add workout impression' as text %}
{% url 'manager:session:add' value.workout.id date|date:'Y' date|date:'m' date|date:'d' as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
{% endif %}
</div>

View File

@@ -1,4 +0,0 @@
{% load i18n %}
{% load wger_extras %}
{% render_day day %}

View File

@@ -1,6 +1,5 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load i18n static wger_extras %}
{% block title %}{% translate "Your schedules" %}{% endblock %}
@@ -11,7 +10,8 @@
{% block content %}
<div class="list-group">
{% for schedule in schedules %}
<a href="{% url 'manager:schedule:view' schedule.id %}" class="list-group-item list-group-item-action">
<a href="{% url 'manager:schedule:view' schedule.id %}"
class="list-group-item list-group-item-action">
<span class="glyphicon glyphicon-chevron-right float-end"></span>
{% if schedule.is_active %}
@@ -24,9 +24,9 @@
<p class="list-group-item-text">{{ schedule.start_date }}</p>
</a>
{% empty %}
<a href="{% url 'manager:schedule:add' %}" class="list-group-item list-group-item-action wger-modal-dialog">
{% translate "No schedules found." %}<br>{% translate "Add one now." %}
</a>
{% translate "No schedules found." %}
{% translate 'Add one now' as text %}
{% modal_link url='manager:schedule:add' text=text css_class='dropdown-item' %}
{% endfor %}
</div>
{% endblock %}
@@ -38,7 +38,8 @@
<p>{% blocktranslate %}You can indicate how long you want to do each workout
before jumping to the next. It is also possible to create a loop, so you
always do the same workouts in succession, e.g. A > B > C > A > B > C and so on.{% endblocktranslate %}</p>
always do the same workouts in succession, e.g. A > B > C > A > B > C and so
on.{% endblocktranslate %}</p>
<p>{% blocktranslate %}The currently active schedule will remain active (and be
shown e.g. in your dashboard) till it reaches the last workout or till you
@@ -47,7 +48,6 @@
{% block options %}
<a href="{% url 'manager:schedule:add' %}" class="btn btn-success btn-sm wger-modal-dialog">
{% translate "Add schedule" %}
</a>
{% translate 'Add schedule' as text %}
{% modal_link url='manager:schedule:add' text=text %}
{% endblock %}

View File

@@ -13,52 +13,6 @@
{% block title %}{{ schedule.name }}{% endblock %}
{% block header %}
<script type="text/javascript">
function init_sortable() {
$("#schedule-table").find("tbody").off();
var elements = document.getElementsByTagName('tbody');
$.each(elements, function (index, element) {
Sortable.create(element, {
handle: '.dragndrop-handle',
animation: 150,
onUpdate: function (event) {
$.each(($(event.from).children('tr')), function (index, tr_element) {
var tr_element = $(tr_element);
console.log(tr_element.data('id'));
// The last table element has no ID attribute (has only the
// 'add exercise' link
if (tr_element.data('id')) {
var step_pk = tr_element.data('id');
$.ajax({
url: '/api/v2/schedulestep/' + step_pk + '/',
type: 'PATCH',
data: {'order': index + 1}
}).done(function (data) {
// console.log(data);
});
}
});
// TODO: again, why do we need to do this twice. Otherwise, sometimes
// it doesn't get current data
var current_url = $("#current-url").data('currentUrl');
$.get(current_url);
$.get(current_url, function (data) {
$('#schedule-table').html($(data).find('#schedule-table').html());
init_sortable();
wgerFormModalDialog();
});
}
});
})
}
$(function () {
init_sortable();
});
</script>
{% endblock %}
{% block content %}
@@ -111,18 +65,29 @@
</td>
{% if is_owner %}
<td style="min-width: 6em;">
<span class="editoptions">
<span title="{% translate 'Move me' %}" class="dragndrop-handle {% fa_class 'bars' %}"></span>
<span class="editoptions">
<a
href="{% url 'manager:step:edit' step.id %}"
class="{{ css_class }}"
hx-get="{% url 'manager:step:edit' step.id %}"
hx-target="#ajax-info-content"
data-bs-toggle="modal"
data-bs-target="#wger-ajax-info"
>
<span class="{% fa_class 'edit' %}"></span>
</a>
<a href="{% url 'manager:step:edit' step.id %}"
title="{% translate 'Edit' %}"
class="wger-modal-dialog">
<span class="{% fa_class 'edit' %}"></span></a>
<a href="{% url 'manager:step:delete' step.id %}"
title="{% translate 'Delete' %}"
class="wger-modal-dialog">
<span class="{% fa_class 'trash' %}"></span></a>
</span>
<a
href="{% url 'manager:step:delete' step.id %}"
class="{{ css_class }}"
hx-get="{% url 'manager:step:delete' step.id %}"
hx-target="#ajax-info-content"
data-bs-toggle="modal"
data-bs-target="#wger-ajax-info"
>
<span class="{% fa_class 'trash' %}"></span>
</a>
</span>
</td>
{% endif %}
<td>
@@ -139,10 +104,9 @@
{% if is_owner %}
<tr>
<td colspan="4">
<a href="{% url 'manager:step:add' schedule.id %}"
class="wger-modal-dialog btn btn-block btn-light">
{% translate "No workouts found." %} {% translate "Add one now." %}
</a>
{% translate 'Add one now.' as text %}
{% url 'manager:step:add' schedule.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
</td>
</tr>
{% endif %}
@@ -151,17 +115,16 @@
<tr>
<td colspan="4">
{% if not schedule.is_active %}
<a href="{% url 'manager:schedule:start' schedule.pk %}" class="btn btn-success btn-sm">
<a href="{% url 'manager:schedule:start' schedule.pk %}"
class="btn btn-success btn-sm">
<span class="{% fa_class 'play' %}"></span>
{% translate "Start schedule" %}
</a>
{% endif %}
<a href="{% url 'manager:step:add' schedule.id %}"
class="wger-modal-dialog btn btn-light btn-sm">
<span class="{% fa_class 'plus' %}"></span>
{% translate "Add workout" %}
</a>
{% translate 'Add workout' as text %}
{% url 'manager:step:add' schedule.id as url %}
{% modal_link url=url text=text css_class='btn btn-light btn-sm' %}
</td>
<td colspan="2">
</td>
@@ -230,13 +193,16 @@
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{% translate "Export calendar file" %}</h4>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">&times;</button>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">
&times;
</button>
</div>
<div class="modal-body">
<p>{% translate "Export this schedule as a calendar file." %}</p>
<p>{% blocktranslate %}You can then import the file it into your calendar
application for example google calendar, outlook or iCal. This will create
an appointment for each training day with the appropriate exercises.{% endblocktranslate %}</p>
an appointment for each training day with the appropriate
exercises.{% endblocktranslate %}</p>
<p>
<a href="{% url 'manager:schedule:ical' schedule.id uid token %}"
class="btn btn-block btn-light">
@@ -245,7 +211,8 @@
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">{% translate "Close" %}</button>
<button type="button" class="btn btn-light"
data-bs-dismiss="modal">{% translate "Close" %}</button>
</div>
</div>
</div>
@@ -256,17 +223,21 @@
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{% translate "Download as PDF" %}</h4>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">&times;</button>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">
&times;
</button>
</div>
<div class="modal-body">
<form class="wger-form">
{% crispy download_form %}
<div id="pdf-download-info" data-schedule-id="{{ schedule.id|unlocalize }}" data-uid="{{ uid }}"
<div id="pdf-download-info" data-schedule-id="{{ schedule.id|unlocalize }}"
data-uid="{{ uid }}"
data-token="{{ token }}"></div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">{% translate "Close" %}</button>
<button type="button" class="btn btn-light"
data-bs-dismiss="modal">{% translate "Close" %}</button>
</div>
</div>
</div>
@@ -279,30 +250,33 @@
{% block options %}
<div class="btn-group">
<div class="btn-group">
<button type="button" class="btn btn-primary btn-sm dropdown-toggle" data-bs-toggle="dropdown"
<button type="button" class="btn btn-primary btn-sm dropdown-toggle"
data-bs-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
<span class="{% fa_class 'cog' %}"></span>
{% translate "Options" %}
</button>
<div class="dropdown-menu">
<a href="#" data-bs-toggle="modal" data-bs-target="#export-ical-popup" class="dropdown-item">
<a href="#" data-bs-toggle="modal" data-bs-target="#export-ical-popup"
class="dropdown-item">
<span class="{% fa_class 'calendar' %}"></span>
{% translate "Export calendar file" %}
</a>
<a data-bs-toggle="modal" data-bs-target="#download-pdf-popup" class="dropdown-item">
<a data-bs-toggle="modal" data-bs-target="#download-pdf-popup"
class="dropdown-item">
<span class="{% fa_class 'download' %}"></span>
{% translate "Download as PDF" %}
</a>
{% if is_owner %}
<a href="{% url 'manager:schedule:edit' schedule.id %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'edit' %}"></span>
{% translate "Edit schedule" %}
</a>
{% translate 'Edit' as text %}
{% url 'manager:schedule:edit' schedule.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
<div role="separator" class="dropdown-divider"></div>
<a href="{% url 'manager:schedule:delete' schedule.id %}" class="wger-modal-dialog dropdown-item">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete schedule" %}
</a>
{% translate 'Delete' as text %}
{% url 'manager:schedule:delete' schedule.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</div>
</div>

View File

@@ -1,4 +1,4 @@
{% extends extend_template %}
{% extends 'base_empty.html' %}
{% load i18n static crispy_forms_tags %}

View File

@@ -1,101 +0,0 @@
{% extends "base.html" %}
{% load i18n static wger_extras django_bootstrap_breadcrumbs %}
{# #}
{# Opengraph #}
{# #}
{% block opengraph %}
{{ block.super }}
<meta property="og:title" content="{% translate 'Weight log' %}">
<meta property="og:description" content="{% translate 'Weight log' %} / {{ workout }} / {{ owner_user.username }}">
{% endblock %}
{# #}
{# Breadcrumbs #}
{# #}
{% block breadcrumbs %}
{{ block.super }}
{% breadcrumb workout workout %}
{% breadcrumb "Weight log" "manager:log:log" workout.pk %}
{% endblock %}
{# #}
{# Title #}
{# #}
{% block title %}{% translate "Weight log for workout" %}{% endblock %}
{# #}
{# Header #}
{# #}
{% block header %}
{% endblock %}
{# #}
{# Content #}
{# #}
{% block content %}
{% for day in workout.canonical_representation.day_list %}
<h4>{{ day.obj.description }}</h4>
{% if is_owner %}
<p>
<a href="{% url 'manager:day:log' day.obj.id %}" class="btn btn-success btn-sm">
{% translate 'Add weight log to this day' %}
</a>
</p>
{% endif %}
{% for set in day.set_list %}
{% for base in set.exercise_list %}
{% with day_list=workout_log|get_item:day.obj.id %}
{% with exercise_list=day_list|get_item:base.obj.id %}
<h5 class="mt-4">{{ base.obj.get_translation.name }}</h5>
{% if exercise_list.log_by_date %}
{# TODO: perhaps move the draw_weight_chart function to render_weight_log #}
{% with list=exercise_list.log_by_date %}
{% render_weight_log list exercise_list.div_uuid owner_user %}
{% endwith %}
{% else %}
<p><em>{% translate "No weight entries here." %}</em></p>
{% endif %}
{% endwith %}
{% endwith %}
{% empty%}
<p>
<em>{% translate "No exercises for this day." %}</em>
</p>
{% endfor %}
{% endfor %}
{% endfor %}
{% endblock %}
{# #}
{# Side bar #}
{# #}
{% block sidebar %}
<h4>{% translate "Notes" %}</h4>
<p>{% blocktranslate %}This page shows the weight logs belonging to this workout
only. Click on an exercise to see all the historical data for
it.{% endblocktranslate %}</p>
<p>{% blocktranslate %}If on a single day there is more than one entry with the
same number of repetitions, but different weights, only the entry with the
higher weight is shown in the diagram.{% endblocktranslate %}</p>
<p>{% blocktranslate %}Note that only entries with a weight unit (kg or lb) and
repetitions are charted, other combinations such as time or until failure
are ignored here.{% endblocktranslate %}</p>
{% endblock %}

View File

@@ -67,21 +67,26 @@
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{% translate "Export calendar file" %}</h4>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">&times;</button>
<button type="button" class="close" data-bs-dismiss="modal" aria-hidden="true">
&times;
</button>
</div>
<div class="modal-body">
<p>{% translate "Export this workout as a calendar file." %}</p>
<p>{% blocktranslate %}You can then import the file it into your calendar
application for example google calendar, outlook or iCal. This will create
an appointment for each training day with the appropriate exercises.{% endblocktranslate %}</p>
an appointment for each training day with the appropriate
exercises.{% endblocktranslate %}</p>
<p>
<a href="{% url 'manager:workout:ical' workout.id uid token %}" class="btn btn-block btn-light">
<a href="{% url 'manager:workout:ical' workout.id uid token %}"
class="btn btn-block btn-light">
{% translate "Export calendar file" %}
</a>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">{% translate "Close" %}</button>
<button type="button" class="btn btn-light"
data-bs-dismiss="modal">{% translate "Close" %}</button>
</div>
</div>
</div>
@@ -92,35 +97,41 @@
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">{% translate "Download as PDF" %}</h4>
<button type="button" class="close float-end" data-bs-dismiss="modal" aria-hidden="true">&times;
<button type="button" class="close float-end" data-bs-dismiss="modal"
aria-hidden="true">&times;
</button>
</div>
<div class="modal-body">
<form class="wger-form">
<div id="pdf-download-info" data-workout-id="{{ workout.id|unlocalize }}" data-uid="{{ uid }}"
<div id="pdf-download-info" data-workout-id="{{ workout.id|unlocalize }}"
data-uid="{{ uid }}"
data-token="{{ token }}"></div>
<div class="form-group form-check-inline">
<input type="radio" name="pdf_type" class="form-check-input" id="id_type_log" value="log"
<input type="radio" name="pdf_type" class="form-check-input"
id="id_type_log" value="log"
checked>
<label class="form-check-label" for="id_type_log">
{% translate "Log" %}
</label>
</div>
<div class="form-group form-check-inline">
<input type="radio" name="pdf_type" id="id_type_table" class="form-check-input"
<input type="radio" name="pdf_type" id="id_type_table"
class="form-check-input"
value="table">
<label class="form-check-label" for="id_type_table">
{% translate "Table" %}
</label>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" name="images" id="id_images" checked>
<input type="checkbox" class="form-check-input" name="images"
id="id_images" checked>
<label for="id_images" class="form-check-label">
{% translate "with images" %}
</label>
</div>
<div class="form-group form-check">
<input type="checkbox" class="form-check-input" name="comments" id="id_comments" checked>
<input type="checkbox" class="form-check-input" name="comments"
id="id_comments" checked>
<label for="id_comments" class="form-check-label">
{% translate "with comments" %}
</label>
@@ -134,7 +145,8 @@
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">{% translate "Close" %}</button>
<button type="button" class="btn btn-light"
data-bs-dismiss="modal">{% translate "Close" %}</button>
</div>
</div>
</div>
@@ -143,7 +155,8 @@
{% if workout.canonical_representation.day_list %}
<h4>{% translate "Logs" %}</h4>
<p>
<a class="btn btn-primary btn-sm" href="{% url 'manager:log:log' workout.id %}">{% translate 'View logs' %}</a>
<a class="btn btn-primary btn-sm"
href="{% url 'manager:log:log' workout.id %}">{% translate 'View logs' %}</a>
</p>
{% endif %}
@@ -157,19 +170,20 @@
{% block options %}
<div class="btn-group" role="group">
<div class="btn-group" role="group">
<button id="btnGroupWorkout" type="button" class="btn btn-primary btn-sm dropdown-toggle"
<button id="btnGroupWorkout" type="button"
class="btn btn-primary btn-sm dropdown-toggle"
data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<span class="{% fa_class 'cog' %}"></span>
{% translate "Options" %}
</button>
<div class="dropdown-menu" aria-labelledby="btnGroupWorkout">
{% if is_owner %}
<a href="{% url 'manager:workout:edit' workout.id %}" class="dropdown-item wger-modal-dialog">
<span class="{% fa_class 'edit' %}"></span>
{% translate "Edit workout" %}
</a>
{% translate 'Edit' as text %}
{% url 'manager:workout:edit' workout.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
<a class="dropdown-item" data-bs-toggle="modal" data-bs-target="#download-pdf-popup">
<a class="dropdown-item" data-bs-toggle="modal"
data-bs-target="#download-pdf-popup">
<span class="{% fa_class 'download' %}"></span>
{% translate "Download as PDF" %}
</a>
@@ -177,16 +191,17 @@
<span class="{% fa_class 'calendar' %}"></span>
{% translate "Export calendar file" %}
</a>
<a href="{% url 'manager:workout:make-template' workout.id %}" class="dropdown-item wger-modal-dialog">
<span class="{% fa_class 'clone' %}"></span>
{% translate "Mark as template" %}
</a>
{% translate 'Mark as template' as text %}
{% url 'manager:workout:make-template' workout.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% if is_owner %}
<div class="dropdown-divider"></div>
<a href="{% url 'manager:workout:delete' workout.id %}" class="dropdown-item wger-modal-dialog">
<span class="{% fa_class 'trash' %}"></span>
{% translate "Delete" %}
</a>
{% translate 'Delete' as text %}
{% url 'manager:workout:delete' workout.id as url %}
{% modal_link url=url text=text css_class='dropdown-item' %}
{% endif %}
</div>
</div>

View File

@@ -228,6 +228,54 @@ patterns_session = [
),
]
# sub patterns for workout days
patterns_day = [
path(
'<int:pk>/edit',
login_required(day.DayEditView.as_view()),
name='edit',
),
path(
'<int:workout_pk>/add',
login_required(day.DayCreateView.as_view()),
name='add',
),
path(
'<int:pk>/delete',
day.delete,
name='delete',
),
path(
'<int:pk>/log/add',
log.add,
name='log',
),
]
# sub patterns for workout sets
patterns_set = [
path(
'<int:day_pk>/add',
set.create,
name='add',
),
path(
'get-formset/<int:base_pk>/<int:reps>',
set.get_formset,
name='get-formset',
), # Used by JS
path(
'<int:pk>/delete',
set.delete,
name='delete',
),
path(
'<int:pk>/edit',
set.edit,
name='edit',
),
]
# sub patterns for schedules
patterns_schedule = [
path(

View File

@@ -94,74 +94,6 @@ def add(request, pk):
return render(request, 'log/add.html', context)
class WorkoutLogDetailView(DetailView, LoginRequiredMixin):
"""
An overview of the workout's log
"""
model = Workout
template_name = 'workout/log.html'
context_object_name = 'workout'
owner_user = None
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(WorkoutLogDetailView, self).get_context_data(**kwargs)
is_owner = self.owner_user == self.request.user
# Prepare the entries for rendering and the D3 chart
workout_log = {}
for day_obj in self.object.day_set.all():
day_id = day_obj.id
workout_log[day_id] = {}
for set_obj in day_obj.set_set.all():
exercise_log = {}
for base_obj in set_obj.exercise_bases:
exercise_base_id = base_obj.id
exercise_log[exercise_base_id] = []
# Filter the logs for user and exclude all units that are not weight
logs = base_obj.workoutlog_set.filter(
user=self.owner_user,
weight_unit__in=(1, 2),
repetition_unit=1,
workout=self.object,
)
entry_log, chart_data = process_log_entries(logs)
if entry_log:
exercise_log[base_obj.id].append(entry_log)
if exercise_log:
workout_log[day_id][exercise_base_id] = {}
workout_log[day_id][exercise_base_id]['log_by_date'] = entry_log
workout_log[day_id][exercise_base_id]['div_uuid'] = 'div-' + str(
uuid.uuid4()
)
workout_log[day_id][exercise_base_id]['chart_data'] = chart_data
context['workout_log'] = workout_log
context['owner_user'] = self.owner_user
context['is_owner'] = is_owner
return context
def dispatch(self, request, *args, **kwargs):
"""
Check for ownership
"""
workout = get_object_or_404(Workout, pk=kwargs['pk'])
self.owner_user = workout.user
is_owner = request.user == self.owner_user
if not is_owner and not self.owner_user.userprofile.ro_access:
return HttpResponseForbidden()
# Dispatch normally
return super(WorkoutLogDetailView, self).dispatch(request, *args, **kwargs)
def calendar(request, username=None, year=None, month=None):
"""
Show a calendar with all the workout logs

View File

@@ -107,11 +107,12 @@ class Command(BaseCommand):
self.stdout.write(f' created plan {plan.description}')
# Add meals
for _ in range(0, meals_per_plan):
for i in range(0, meals_per_plan):
order = 1
meal = Meal(
plan=plan,
order=order,
name=f'Dummy meal {i}',
time=datetime.time(hour=randint(0, 23), minute=randint(0, 59)),
)
meal.save()

View File

@@ -1,5 +1,3 @@
# -*- coding: utf-8 -*-
# This file is part of wger Workout Manager.
#
# wger Workout Manager is free software: you can redistribute it and/or modify
@@ -14,6 +12,9 @@
#
# You should have received a copy of the GNU Affero General Public License
# Standard Library
import pathlib
# Django
from django.core.cache import cache
from django.db.models.signals import (
@@ -23,6 +24,7 @@ from django.db.models.signals import (
# wger
from wger.nutrition.models import (
Image,
Meal,
MealItem,
NutritionPlan,
@@ -43,3 +45,19 @@ post_save.connect(reset_nutritional_values_canonical_form, sender=Meal)
post_delete.connect(reset_nutritional_values_canonical_form, sender=Meal)
post_save.connect(reset_nutritional_values_canonical_form, sender=MealItem)
post_delete.connect(reset_nutritional_values_canonical_form, sender=MealItem)
def auto_delete_file_on_delete(sender, instance: Image, **kwargs):
"""
Deletes file from filesystem
when corresponding `MediaFile` object is deleted.
"""
if not instance.image:
return
path = pathlib.Path(instance.image.path)
if path.exists():
path.unlink()
post_delete.connect(auto_delete_file_on_delete, sender=Image)

View File

@@ -21,7 +21,7 @@
'use strict';
function updateIngredientValue(url) {
var formData = $('#nutritional-values-form').serializeArray();
let formData = $('#nutritional-values-form').serializeArray();
$.get(url, formData, function (data) {
// Show any validation errors
$('#calculator-errors').html('');
@@ -60,158 +60,20 @@ function wgerInitIngredientDetail(url) {
}
/*
* Draw the BMI chart
*/
function wgerRenderBodyMassIndex() {
var svg;
var area;
var nest;
var stack;
var yAxis;
var xAxis;
var z;
var y;
var x;
var margin;
var width;
var height;
var heightFactor;
var widthFactor;
// Delete the other diagrams
d3.selectAll('svg').remove();
// Calculate the size
widthFactor = 600;
heightFactor = (widthFactor / 600) * 300;
margin = {top: 20, right: 80, bottom: 30, left: 50};
width = widthFactor - margin.left - margin.right;
height = heightFactor - margin.top - margin.bottom;
x = d3.scaleLinear()
.range([0, width]);
y = d3.scaleLinear()
.range([height, 0]);
z = d3.scaleOrdinal().range(['#000080',
'#0000ff',
'#00ffff',
'#00ff00',
'#ffff00',
'#ff7f2a',
'#ff0000',
'#800000']);
xAxis = d3.axisBottom(x);
yAxis = d3.axisLeft(y);
stack = d3.stack();
area = d3.area()
.x(function (d) {
return x(d.height);
})
.y1(function (d) {
return y(d.weight);
});
svg = d3.select('#bmi-chart').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// Clip path, drawings outside are removed
svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('width', width)
.attr('height', height);
d3.json('/nutrition/calculator/bmi/chart-data').then(function (data) {
var $bmiForm;
var url;
var layers;
stack.keys(['filler',
'severe_thinness',
'moderate_thinness',
'mild_thinness',
'normal_range',
'pre_obese',
'obese_class_2',
'obese_class_3']);
layers = stack(d3.group(data, d => d.key));
// Manually set the domains
x.domain(data.map(function (d) {
return d.height;
}));
y.domain([d3.min(data, function (d) {
return d.weight;
}), d3.max(data, function (d) {
return d.weight;
})]);
svg.selectAll('.layer')
.data(layers)
.enter().append('path')
.attr('class', 'layer')
.attr('id', function (d) {
return 'key-' + d.key;
})
.attr('clip-path', 'url(#clip)')
.attr('d', function (d, i) {
return area(d[i].data.values);
})
.style('fill', function (d, i) {
return z(i);
})
.style('opacity', 1);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
$bmiForm = $('#bmi-form');
url = $bmiForm.attr('action');
$.post(url,
$bmiForm.serialize(),
function (postData) {
$('#bmi-result-container').show();
$('#bmi-result-value').html(postData.bmi);
svg.append('circle')
.attr('cx', x(postData.height))
.attr('cy', y(postData.weight))
.attr('fill', 'black')
.attr('r', 5);
});
});
}
/*
* Calories calculator
*/
function wgerInitCaloriesCalculator() {
$('#form-transfer-calories').click(function (e) {
var baseCalories;
let baseCalories;
e.preventDefault();
baseCalories = Number($('#id_base_calories').html());
$('#id_calories').val(baseCalories);
});
$('#add-calories-total').click(function (e) {
var additionalCalories;
var baseCalories;
let additionalCalories;
let baseCalories;
e.preventDefault();
baseCalories = Number($('#id_base_calories').html());
additionalCalories = Number($('#id_additional_calories').val());
@@ -224,7 +86,7 @@ function wgerInitCaloriesCalculator() {
// Get own ID and update the user profile
$.get('/api/v2/userprofile', function () {
}).done(function (userprofile) {
var totalCalories = $('#id_calories')[0].value;
let totalCalories = $('#id_calories')[0].value;
$.ajax({
url: '/api/v2/userprofile/' + userprofile.results[0].user + '/',
type: 'PATCH',
@@ -234,8 +96,8 @@ function wgerInitCaloriesCalculator() {
});
$('.calories-autoform').click(function (e) {
var $bmrForm;
var bmrUrl;
let $bmrForm;
let bmrUrl;
e.preventDefault();
// BMR
@@ -244,8 +106,8 @@ function wgerInitCaloriesCalculator() {
$.post(bmrUrl,
$bmrForm.serialize(),
function (data) {
var $activitiesForm;
var activitiesUrl;
let $activitiesForm;
let activitiesUrl;
$('#bmr-result-container').show();
$('#bmr-result-value').html(data.bmr);

View File

@@ -1,122 +0,0 @@
{% extends "base.html" %}
{% load i18n %}
{% load static %}
{% load crispy_forms_tags %}
{% block header %}
<style>
div.table {
display:table;
margin-top: 1em;
}
div.table > div {
display:table-row;
}
div.table > div > div {
display:table-cell;
padding-left: 1em;
}
div.bmi-legend {
width:1em;
height:1em;
}
</style>
<script>
$(document).ready(function () {
wgerRenderBodyMassIndex();
/*
* Process the form
*/
$("#submit-id-submit").click(function(e){
e.preventDefault();
wgerRenderBodyMassIndex();
});
});
</script>
{% endblock %}
<!--
Title
-->
{% block title %}{% translate "BMI calculator" %}{% endblock %}
<!--
Main Content
-->
{% block content %}
{% crispy form %}
<div id="bmi-result-container" style="display:none;">
<h3>{% translate 'Results' %}</h3>
<p>{% translate 'Your BMI is: ' %} <strong><span id="bmi-result-value">{{user.userprofile.calculate_bmi|floatformat:1}}</span></strong></p>
</div>
<div id="bmi-chart"></div>
<h4>{% translate "Legend" %}</h4>
<div class="table">
<div>
<div><div class="bmi-legend" style="background-color:#800000;">&nbsp;</div></div>
<div>{% translate "Adipositas III" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#ff0000;">&nbsp;</div></div>
<div>{% translate "Adipositas II" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#ff7f2a;">&nbsp;</div></div>
<div>{% translate "Adipositas I" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#ffff00;">&nbsp;</div></div>
<div>{% translate "Overweight" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#00ff00;">&nbsp;</div></div>
<div>{% translate "Normal weight" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#00ffff;">&nbsp;</div></div>
<div>{% translate "Slight underweight" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#0000ff;">&nbsp;</div></div>
<div>{% translate "Moderate underweight" %}</div>
</div>
<div>
<div><div class="bmi-legend" style="background-color:#000080;">&nbsp;</div></div>
<div>{% translate "Strong underweight" %}</div>
</div>
</div>
{% endblock %}
<!--
Side bar
-->
{% block sidebar %}
<h4>Info</h4>
<p>
{% blocktranslate %}Use the form to calculate your BMI (Body Mass Index).
If you have entered data in the weight section, the last entry will
be used automatically. Otherwise the weight you enter here will be saved
in a new entry.{% endblocktranslate %}
</p>
{% endblock %}

View File

@@ -41,8 +41,8 @@
{% endif %}
<div style="padding-top:3em;"></div>
{% pagination paginator page_obj %}
<div style="padding-top:3em;"></div>
{% pagination paginator page_obj %}
{% endblock %}
{% block sidebar %}
@@ -56,10 +56,8 @@
{# Options #}
{# #}
{% block options %}
{% if perms.nutrition.add_ingredient and user.is_authenticated and not user.userprofile.is_temporary %}
<a href="{% url 'nutrition:ingredient:add' %}"
class="btn btn-success btn-sm">
{% translate "Add ingredient" %}
</a>
{% if perms.nutrition.add_ingredient %}
{% translate 'Add' as text %}
{% modal_link url='nutrition:ingredient:add' text=text %}
{% endif %}
{% endblock %}

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