mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Merge branch 'inventree:master' into matmair/issue2279
This commit is contained in:
@ -16,6 +16,7 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_SSO" icon="fa-info-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_PWD_FORGOT" icon="fa-info-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_MAIL_REQUIRED" icon="fa-info-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENFORCE_MFA" %}
|
||||
<tr>
|
||||
<th><h5>{% trans 'Signup' %}</h5></th>
|
||||
<td colspan='4'></td>
|
||||
|
@ -42,193 +42,317 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-span'>
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-span'>
|
||||
<h4>{% trans "Email" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
<div class='col-sm-6'>
|
||||
{% if user.emailaddress_set.all %}
|
||||
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
|
||||
<div class="col-sm-6">
|
||||
{% if user.emailaddress_set.all %}
|
||||
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
|
||||
|
||||
<form action="{% url 'account_email' %}" class="email_list" method="post">
|
||||
<form action="{% url 'account_email' %}" class="email_list" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="blockLabels">
|
||||
<fieldset class="blockLabels">
|
||||
|
||||
{% for emailaddress in user.emailaddress_set.all %}
|
||||
<div>
|
||||
<div class="ctrlHolder">
|
||||
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
|
||||
|
||||
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
|
||||
|
||||
{% if emailaddress.primary %}
|
||||
<b>{{ emailaddress.email }}</b>
|
||||
{% else %}
|
||||
{{ emailaddress.email }}
|
||||
{% endif %}
|
||||
</label>
|
||||
{% if emailaddress.verified %}
|
||||
<span class='badge badge-right rounded-pill bg-success'>{% trans "Verified" %}</span>
|
||||
<div class="ctrlHolder">
|
||||
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
|
||||
|
||||
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked" {%endif %} value="{{emailaddress.email}}" />
|
||||
|
||||
{% if emailaddress.primary %}
|
||||
<b>{{ emailaddress.email }}</b>
|
||||
{% else %}
|
||||
<span class='badge badge-right rounded-pill bg-warning'>{% trans "Unverified" %}</span>
|
||||
{{ emailaddress.email }}
|
||||
{% endif %}
|
||||
{% if emailaddress.primary %}<span class='badge badge-right rounded-pill bg-primary'>{% trans "Primary" %}</span>{% endif %}
|
||||
</div>
|
||||
</label>
|
||||
|
||||
{% if emailaddress.verified %}
|
||||
<span class='badge badge-right rounded-pill bg-success'>{% trans "Verified" %}</span>
|
||||
{% else %}
|
||||
<span class='badge badge-right rounded-pill bg-warning'>{% trans "Unverified" %}</span>
|
||||
{% endif %}
|
||||
{% if emailaddress.primary %}<span class='badge badge-right rounded-pill bg-primary'>{% trans "Primary" %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<div class="buttonHolder">
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_primary">{% trans 'Make Primary' %}</button>
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_send" {% if not mail_conf %}disabled{% endif %}>{% trans 'Re-send Verification' %}</button>
|
||||
<button class="btn btn-primary primaryAction" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
|
||||
<button class="btn btn-primary primaryAction" type="submit" name="action_remove">{% trans 'Remove' %}</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<strong>{% trans 'Warning:'%}</strong>
|
||||
{% trans "You currently do not have any email address set up. You should really add an email address so you can receive notifications, reset your password, etc." %}
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if can_add_email %}
|
||||
<div class="col-sm-6">
|
||||
<h5>{% trans "Add Email Address" %}</h5>
|
||||
|
||||
<form method="post" action="{% url 'account_email' %}" class="add_email">
|
||||
{% csrf_token %}
|
||||
{{ add_email_form|crispy }}
|
||||
<button class="btn btn-primary" name="action_add" type="submit">{% trans "Add Email" %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Social Accounts" %}</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
{% if social_form.accounts %}
|
||||
<p>{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}</p>
|
||||
|
||||
|
||||
<form method="post" action="{% url 'socialaccount_connections' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<fieldset>
|
||||
{% if social_form.non_field_errors %}
|
||||
<div id="errorMsg">{{ social_form.non_field_errors }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% for base_account in social_form.accounts %}
|
||||
{% with base_account.get_provider_account as account %}
|
||||
<div>
|
||||
<label for="id_account_{{ base_account.id }}">
|
||||
<input id="id_account_{{ base_account.id }}" type="radio" name="account"
|
||||
value="{{ base_account.id }}" />
|
||||
<span class="socialaccount_provider {{ base_account.provider }} {{ account.get_brand.id }}">
|
||||
<span class='brand-icon'
|
||||
brand_name='{{account.get_brand.id}}'></span>{{account.get_brand.name}}</span>
|
||||
{{ account }}
|
||||
</label>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">{% trans 'Remove' %}</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<p><strong>{% trans 'Warning:'%}</strong>
|
||||
{% trans "You currently do not have any email address set up. You should really add an email address so you can receive notifications, reset your password, etc." %}
|
||||
</p>
|
||||
|
||||
<p>{% trans 'You currently have no social network accounts connected to this account.' %}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>{% trans 'Add a 3rd Party Account' %}</h5>
|
||||
<div>
|
||||
{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
|
||||
</div>
|
||||
{% include "socialaccount/snippets/login_extra.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Multifactor" %}</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
{% if user.staticdevice_set.all or user.totpdevice_set.all %}
|
||||
<p>{% trans 'You have these factors available:' %}</p>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<th>Type</th>
|
||||
<th>Name</th>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for token in user.totpdevice_set.all %}
|
||||
<tr>
|
||||
<td>{% trans 'TOTP' %}</td>
|
||||
<td>{{ token.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for token in user.staticdevice_set.all %}
|
||||
<tr>
|
||||
<td>{% trans 'Static' %}</td>
|
||||
<td>{{ token.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% else %}
|
||||
<p><strong>{% trans 'Warning:'%}</strong>
|
||||
{% trans "You currently do not have any factors set up." %}
|
||||
</p>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5>{% trans "Change factors" %}</h5>
|
||||
<a href="{% url 'two-factor-setup' %}" class="btn btn-primary {% if user.staticdevice_set.all and user.totpdevice_set.all %}disabled{% endif %}" role="button">{% trans "Setup multifactor" %}</a>
|
||||
{% if user.staticdevice_set.all or user.totpdevice_set.all %}
|
||||
<a href="{% url 'two-factor-remove' %}" class="btn btn-primary" role="button">{% trans "Remove multifactor" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Theme Settings" %}</h4>
|
||||
</div>
|
||||
|
||||
<div class='col-sm-6'>
|
||||
{% if can_add_email %}
|
||||
<h5>{% trans "Add Email Address" %}</h5>
|
||||
|
||||
<form method="post" action="{% url 'account_email' %}" class="add_email">
|
||||
<form action='{% url "settings-appearance" %}' method='post'>
|
||||
{% csrf_token %}
|
||||
|
||||
<label for="id_email" class=" requiredField">
|
||||
E-mail<span class="asteriskField">*</span>
|
||||
<input name='next' type='hidden' value='{% url "settings" %}'>
|
||||
<label for='theme' class=' requiredField'>
|
||||
{% trans "Select theme" %}
|
||||
</label>
|
||||
<div id="div_id_email" class="form-group input-group mb-3">
|
||||
<div class='input-group-prepend'><span class='input-group-text'>@</span></div>
|
||||
<input type="email" name="email" placeholder='{% trans "Enter e-mail address" %}' class="textinput textInput form-control" required="" id="id_email">
|
||||
<div class='form-group input-group mb-3'>
|
||||
<select id='theme' name='theme' class='select form-control'>
|
||||
{% get_available_themes as themes %}
|
||||
{% for theme in themes %}
|
||||
<option value='{{ theme.key }}'>{{ theme.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<button class="btn btn-primary" name="action_add" type="submit">{% trans "Add Email" %}</button>
|
||||
<input type="submit" value="{% trans 'Set Theme' %}" class="btn btn-primary">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Social Accounts" %}</h4>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if social_form.accounts %}
|
||||
<p>{% blocktrans %}You can sign in to your account using any of the following third party accounts:{% endblocktrans %}</p>
|
||||
|
||||
|
||||
<form method="post" action="{% url 'socialaccount_connections' %}">
|
||||
{% csrf_token %}
|
||||
|
||||
<fieldset>
|
||||
{% if social_form.non_field_errors %}
|
||||
<div id="errorMsg">{{ social_form.non_field_errors }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% for base_account in social_form.accounts %}
|
||||
{% with base_account.get_provider_account as account %}
|
||||
<div>
|
||||
<label for="id_account_{{ base_account.id }}">
|
||||
<input id="id_account_{{ base_account.id }}" type="radio" name="account" value="{{ base_account.id }}"/>
|
||||
<span class="socialaccount_provider {{ base_account.provider }} {{ account.get_brand.id }}">
|
||||
<span class='brand-icon' brand_name='{{account.get_brand.id}}'></span>{{account.get_brand.name}}</span>
|
||||
{{ account }}
|
||||
</label>
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Language Settings" %}</h4>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
||||
<div>
|
||||
<button class="btn btn-primary" type="submit">{% trans 'Remove' %}</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
|
||||
<div class="col">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{% url 'settings' %}">
|
||||
<label for='language' class=' requiredField'>
|
||||
{% trans "Select language" %}
|
||||
</label>
|
||||
<div class='form-group input-group mb-3'>
|
||||
<select name="language" class="select form-control w-25">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
|
||||
{% for language in languages %}
|
||||
{% define language.code as lang_code %}
|
||||
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
||||
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
||||
{% if ALL_LANG or use_lang %}
|
||||
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
||||
{{ language.name_local }} ({{ lang_code }})
|
||||
{% if lang_translated %}
|
||||
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
||||
{% else %}
|
||||
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
||||
{% endif %}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||
</div>
|
||||
<p>{% trans "Some languages are not complete" %}
|
||||
{% if ALL_LANG %}
|
||||
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
||||
{% else %}
|
||||
and hidden. <a href="?alllang">{% trans "Show them too" %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "There are no social network accounts connected to your InvenTree account" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
<h4>{% trans 'Add a 3rd Party Account' %}</h4>
|
||||
<div>
|
||||
{% include "socialaccount/snippets/provider_list.html" with process="connect" %}
|
||||
<div class="col-sm-6">
|
||||
<h4>{% trans "Help the translation efforts!" %}</h4>
|
||||
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the
|
||||
InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are
|
||||
welcomed and encouraged.{% endblocktrans %}</p>
|
||||
</div>
|
||||
{% include "socialaccount/snippets/login_extra.html" %}
|
||||
<br>
|
||||
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% trans "Active Sessions" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if session_list.count > 1 %}
|
||||
<form method="post" action="{% url 'session_delete_other' %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-sm btn-default btn-danger" title='{% trans "Log out active sessions (except this one)" %}'>
|
||||
{% trans "Log Out Active Sessions" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% trans "Active Sessions" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if session_list.count > 1 %}
|
||||
<form method="post" action="{% url 'session_delete_other' %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-sm btn-default btn-danger" title='{% trans "Log out active sessions (except this one)" %}'>
|
||||
{% trans "Log Out Active Sessions" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
|
||||
{% trans "<em>unknown</em>" as unknown %}
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "IP Address" %}</th>
|
||||
<th>{% trans "Device" %}</th>
|
||||
<th>{% trans "Last Activity" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for object in session_list %}
|
||||
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
|
||||
<td>{{ object.ip }}</td>
|
||||
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
|
||||
<td>
|
||||
{% if object.session_key == session_key %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
<div>
|
||||
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
|
||||
{% trans "<em>unknown</em>" as unknown %}
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "IP Address" %}</th>
|
||||
<th>{% trans "Device" %}</th>
|
||||
<th>{% trans "Last Activity" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for object in session_list %}
|
||||
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
|
||||
<td>{{ object.ip }}</td>
|
||||
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
|
||||
<td>
|
||||
{% if object.session_key == session_key %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
(function() {
|
||||
var message = "{% trans 'Do you really want to remove the selected email address?' %}";
|
||||
var actions = document.getElementsByName('action_remove');
|
||||
if (actions.length) {
|
||||
actions[0].addEventListener("click", function(e) {
|
||||
if (! confirm(message)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
var message = "{% trans 'Do you really want to remove the selected email address?' %}";
|
||||
var actions = document.getElementsByName('action_remove');
|
||||
if (actions.length) {
|
||||
actions[0].addEventListener("click", function(e) {
|
||||
if (! confirm(message)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
{% endblock %}
|
15
InvenTree/templates/allauth_2fa/authenticate.html
Normal file
15
InvenTree/templates/allauth_2fa/authenticate.html
Normal file
@ -0,0 +1,15 @@
|
||||
{% extends "account/base.html" %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Two-Factor Authentication" %}</h1>
|
||||
|
||||
<form method="post" class="login">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<button type="submit" class="btn btn-primary">
|
||||
{% trans 'Authenticate' %}
|
||||
</button>
|
||||
</form>
|
||||
{% endblock %}
|
33
InvenTree/templates/allauth_2fa/backup_tokens.html
Normal file
33
InvenTree/templates/allauth_2fa/backup_tokens.html
Normal file
@ -0,0 +1,33 @@
|
||||
{% extends "account/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h3>
|
||||
{% trans "Two-Factor Authentication Backup Tokens" %}
|
||||
</h3>
|
||||
|
||||
{% if backup_tokens %}
|
||||
{% if reveal_tokens %}
|
||||
<ul>
|
||||
{% for token in backup_tokens %}
|
||||
<li>{{ token.token }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
{% trans 'Backup tokens have been generated, but are not revealed here for security reasons. Press the button below to generate new ones.' %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% trans 'No tokens. Press the button below to generate some.' %}
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
{% trans 'Generate backup tokens' %}
|
||||
</button>
|
||||
</form>
|
||||
<br>
|
||||
<a href="{% url 'settings' %}" class="btn btn-secondary w-100 btn-sm">{% trans "back to settings" %}</a>
|
||||
|
||||
{% endblock %}
|
18
InvenTree/templates/allauth_2fa/remove.html
Normal file
18
InvenTree/templates/allauth_2fa/remove.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "account/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h3>
|
||||
{% trans "Disable Two-Factor Authentication" %}
|
||||
</h3>
|
||||
|
||||
<p>{% trans "Are you sure?" %}</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-danger w-100">
|
||||
{% trans 'Disable Two-Factor' %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
42
InvenTree/templates/allauth_2fa/setup.html
Normal file
42
InvenTree/templates/allauth_2fa/setup.html
Normal file
@ -0,0 +1,42 @@
|
||||
{% extends "account/base.html" %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<h3>
|
||||
{% trans "Setup Two-Factor Authentication" %}
|
||||
</h3>
|
||||
|
||||
<h4>
|
||||
{% trans 'Step 1' %}:
|
||||
</h4>
|
||||
|
||||
<p>
|
||||
{% trans 'Scan the QR code below with a token generator of your choice (for instance Google Authenticator).' %}
|
||||
</p>
|
||||
|
||||
<div class="bg-light rounded">
|
||||
<img src="{{ qr_code_url }}" class="mx-auto d-block"/>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<h4>
|
||||
{% trans 'Step 2' %}:
|
||||
</h4>
|
||||
|
||||
<p>
|
||||
{% trans 'Input a token generated by the app:' %}
|
||||
</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block w-100">
|
||||
{% trans 'Verify' %}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<a href="{% url 'settings' %}" class="btn btn-secondary w-100 btn-sm mt-3">{% trans "back to settings" %}</a>
|
||||
</div>
|
||||
{% endblock %}
|
@ -175,7 +175,6 @@ function enableBreadcrumbTree(options) {
|
||||
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
node = data[i];
|
||||
node.nodes = [];
|
||||
nodes[node.pk] = node;
|
||||
node.selectable = false;
|
||||
|
||||
@ -193,10 +192,17 @@ function enableBreadcrumbTree(options) {
|
||||
node = data[i];
|
||||
|
||||
if (node.parent != null) {
|
||||
nodes[node.parent].nodes.push(node);
|
||||
if (nodes[node.parent].nodes) {
|
||||
nodes[node.parent].nodes.push(node);
|
||||
} else {
|
||||
nodes[node.parent].nodes = [node];
|
||||
}
|
||||
|
||||
if (node.state.expanded) {
|
||||
nodes[node.parent].state.expanded = true;
|
||||
while (node.parent != null) {
|
||||
nodes[node.parent].state.expanded = true;
|
||||
node = nodes[node.parent];
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@ -212,7 +218,6 @@ function enableBreadcrumbTree(options) {
|
||||
collapseIcon: 'fa fa-chevron-down',
|
||||
});
|
||||
|
||||
setBreadcrumbTreeState(label, state);
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -220,26 +225,11 @@ function enableBreadcrumbTree(options) {
|
||||
$('#breadcrumb-tree-toggle').click(function() {
|
||||
// Add callback to "collapse" and "expand" the sidebar
|
||||
|
||||
// By default, the menu is "expanded"
|
||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
||||
// Toggle treeview visibilty
|
||||
$('#breadcrumb-tree-collapse').toggle();
|
||||
|
||||
// We wish to "toggle" the state!
|
||||
setBreadcrumbTreeState(label, state == 'expanded' ? 'collapsed' : 'expanded');
|
||||
});
|
||||
|
||||
// Set the initial state (default = expanded)
|
||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
||||
|
||||
function setBreadcrumbTreeState(label, state) {
|
||||
|
||||
if (state == 'collapsed') {
|
||||
$('#breadcrumb-tree-collapse').hide(100);
|
||||
} else {
|
||||
$('#breadcrumb-tree-collapse').show(100);
|
||||
}
|
||||
|
||||
localStorage.setItem(`inventree-tree-state-${label}`, state);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
/* globals
|
||||
constructForm,
|
||||
exportFormatOptions,
|
||||
imageHoverIcon,
|
||||
inventreeGet,
|
||||
inventreePut,
|
||||
@ -14,6 +15,8 @@
|
||||
*/
|
||||
|
||||
/* exported
|
||||
downloadBomTemplate,
|
||||
exportBom,
|
||||
newPartFromBomWizard,
|
||||
loadBomTable,
|
||||
loadUsedInTable,
|
||||
@ -21,12 +24,121 @@
|
||||
removeColFromBomWizard,
|
||||
*/
|
||||
|
||||
/* BOM management functions.
|
||||
* Requires follwing files to be loaded first:
|
||||
* - api.js
|
||||
* - part.js
|
||||
* - modals.js
|
||||
function downloadBomTemplate(options={}) {
|
||||
|
||||
var format = options.format;
|
||||
|
||||
if (!format) {
|
||||
format = inventreeLoad('bom-export-format', 'csv');
|
||||
}
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Download BOM Template" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select file format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: format,
|
||||
choices: exportFormatOptions(),
|
||||
}
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
var format = getFormFieldValue('format', fields['format'], opts);
|
||||
|
||||
// Save the format for next time
|
||||
inventreeSave('bom-export-format', format);
|
||||
|
||||
// Hide the modal
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
// Download the file
|
||||
location.href = `{% url "bom-upload-template" %}?format=${format}`;
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Export BOM (Bill of Materials) for the specified Part instance
|
||||
*/
|
||||
function exportBom(part_id, options={}) {
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Export BOM" %}',
|
||||
fields: {
|
||||
format: {
|
||||
label: '{% trans "Format" %}',
|
||||
help_text: '{% trans "Select file format" %}',
|
||||
required: true,
|
||||
type: 'choice',
|
||||
value: inventreeLoad('bom-export-format', 'csv'),
|
||||
choices: exportFormatOptions(),
|
||||
},
|
||||
cascading: {
|
||||
label: '{% trans "Cascading" %}',
|
||||
help_text: '{% trans "Download cascading / multi-level BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-cascading', true),
|
||||
},
|
||||
levels: {
|
||||
label: '{% trans "Levels" %}',
|
||||
help_text: '{% trans "Select maximum number of BOM levels to export (0 = all levels)" %}',
|
||||
type: 'integer',
|
||||
value: 0,
|
||||
min_value: 0,
|
||||
},
|
||||
parameter_data: {
|
||||
label: '{% trans "Include Parameter Data" %}',
|
||||
help_text: '{% trans "Include part parameter data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-parameter_data', false),
|
||||
},
|
||||
stock_data: {
|
||||
label: '{% trans "Include Stock Data" %}',
|
||||
help_text: '{% trans "Include part stock data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-stock_data', false),
|
||||
},
|
||||
manufacturer_data: {
|
||||
label: '{% trans "Include Manufacturer Data" %}',
|
||||
help_text: '{% trans "Include part manufacturer data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-manufacturer_data', false),
|
||||
},
|
||||
supplier_data: {
|
||||
label: '{% trans "Include Supplier Data" %}',
|
||||
help_text: '{% trans "Include part supplier data in exported BOM" %}',
|
||||
type: 'boolean',
|
||||
value: inventreeLoad('bom-export-supplier_data', false),
|
||||
}
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
// Extract values from the form
|
||||
var field_names = ['format', 'cascading', 'levels', 'parameter_data', 'stock_data', 'manufacturer_data', 'supplier_data'];
|
||||
|
||||
var url = `/part/${part_id}/bom-download/?`;
|
||||
|
||||
field_names.forEach(function(fn) {
|
||||
var val = getFormFieldValue(fn, fields[fn], opts);
|
||||
|
||||
// Update user preferences
|
||||
inventreeSave(`bom-export-${fn}`, val);
|
||||
|
||||
url += `${fn}=${val}&`;
|
||||
});
|
||||
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
// Redirect to the BOM file download
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
function bomItemFields() {
|
||||
|
@ -811,7 +811,9 @@ function updateFieldValue(name, value, field, options) {
|
||||
|
||||
switch (field.type) {
|
||||
case 'boolean':
|
||||
el.prop('checked', value);
|
||||
if (value == true || value.toString().toLowerCase() == 'true') {
|
||||
el.prop('checked');
|
||||
}
|
||||
break;
|
||||
case 'related field':
|
||||
// Clear?
|
||||
@ -2034,8 +2036,15 @@ function constructInputOptions(name, classes, type, parameters) {
|
||||
}
|
||||
|
||||
if (parameters.value != null) {
|
||||
// Existing value?
|
||||
opts.push(`value='${parameters.value}'`);
|
||||
if (parameters.type == 'boolean') {
|
||||
// Special consideration of a boolean (checkbox) value
|
||||
if (parameters.value == true || parameters.value.toString().toLowerCase() == 'true') {
|
||||
opts.push('checked');
|
||||
}
|
||||
} else {
|
||||
// Existing value?
|
||||
opts.push(`value='${parameters.value}'`);
|
||||
}
|
||||
} else if (parameters.default != null) {
|
||||
// Otherwise, a defualt value?
|
||||
opts.push(`value='${parameters.default}'`);
|
||||
|
Reference in New Issue
Block a user