Skip to content

Commit

Permalink
ability to inherit user preferences, part I
Browse files Browse the repository at this point in the history
  • Loading branch information
jdetaeye committed Jan 25, 2024
1 parent 0ca27f8 commit 725dd19
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 40 deletions.
12 changes: 12 additions & 0 deletions doc/release-notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ Release notes

This release is scheduled for end Februari 2024. You can already check out a `preview <https://demo-preview.frepple.com>`_.

.. rubric:: User interface

- | In the `preferences screen <user-interface/getting-around/user-preferences.html>`_
you can now reset your screen personalization.
| You can restart from a clean slate. Or you can inherit them from another user.
Or you can copy them from another scenario.
.. rubric:: Odoo integration

- | 15, 16, 17: The mapping of calendars now includes the odoo identifier.
| This is needed to guarantuee uniquess of the calendars.
8.4.0 (2024-01-19)
==================

Expand Down
Binary file modified doc/user-interface/_images/user-preferences.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 6 additions & 5 deletions doc/user-interface/getting-around/changing-password.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
Changing your password
======================

From the top right of the screen "My Preferences" or from the menu bar a screen can be opened where you can change your password.
You can change your password from the login screen, or from the preferences
screen that show when you click your user name in the top right corner of
the screen.

Users often choose poor passwords.
We enforce a certain validation rules for passwords:
An administrator can use the user administration screen to change passwords as well.

Users often choose poor passwords. We enforce the following validation rules for passwords:

* Passwords need to be at least 8 characters.

Expand All @@ -15,7 +18,5 @@ We enforce a certain validation rules for passwords:

* Password cannot be part of a list of commonly used passwords.

An administrator can use the user administration screen to change passwords as well.

.. image:: ../_images/change-password.png
:alt: Change password
15 changes: 13 additions & 2 deletions doc/user-interface/getting-around/user-preferences.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,22 @@ For each user frePPle stores a number of personal settings and preferences.
* | **Avatar**:
| A small picture of yourself.
| The uploaded picture must 1) be square (ie same height and width),
| The uploaded picture must 1) be square (ie same height and width),
2) be in jpeg, png or gif format and 3) be smaller than 100kB.
* | **Password**:
| To change the password enter the current one and twice the new value.
* | **Reset personalization**:
| With the wrench icon every user can personalize the layout of each screen:
columns displayed, column ordering, column width, favorites, filters, ...
| With this option you can (re-)initialize these settings in different ways:
* Erase all personalizations and restart from the default settings.

* Inherit personalizations from another user.

* Copy personalizations between scenarios.

.. image:: ../_images/user-preferences.png
:alt: User preferences
128 changes: 95 additions & 33 deletions freppledb/common/templates/common/preferences.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
$("#scenariobtn").text($(this).text());
$("#default_scenario").val($(this).prop('name'));
});
$("#ul_personalize li a").click(function() {
$("#btn_personalize").text($(this).text());
$("#personalization").val($(this).prop('data-type') + '-' + $(this).prop('data-name'));
});
});
</script>

Expand All @@ -41,6 +45,13 @@ <h1>{{ title }}&nbsp;&nbsp;
</h1>
{% endif %}
</div>
<div class="col-auto">
<button class="btn btn-sm d-none d-md-inline-block btn-primary"
onclick="window.open('{% setting "DOCUMENTATION_URL" %}/docs/{% version_short %}/user-interface/getting-around/user-preferences.html');"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-title="{% trans 'help'|capfirst|force_escape %}">
<span class="fa fa-question"></span>
</button>
</div>
</div>
{% endblock %} {% block content %}

Expand All @@ -50,50 +61,62 @@ <h1>{{ title }}&nbsp;&nbsp;
<input type="hidden" name="language" id="language" value="{{ request.user.language }}">
<input type="hidden" name="theme" id="theme" value="{{ request.user.theme }}">
<input type="hidden" name="default_scenario" id="default_scenario" value="{{ request.user.default_scenario }}">
<input type="hidden" name="personalization" id="personalization" value="">
{% if form.non_field_errors %}
<div style="max-width: 500px" class="alert alert-danger">{{ form.non_field_errors }}</div>
{% endif %}

<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">

</div>
</div>

{% if request.user.languageList|length > 2 %}
<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_language">{% trans 'language'|capfirst%}:</label>
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_language" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="{% trans 'Language for the user interface<br>By default the language setting of your browser is used.' %}">
{% trans 'language'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<div class="dropdown">
<button id="languagebtn" class="form-control dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ LANGUAGE }}
</button>
<ul id="languageul" class="dropdown-menu w-100">
{% for i,j in request.user.languageList %}
<li><a data-lang="{{i}}" class="dropdown-item" href="#">{{ j }}</a></li>
{% endfor %}
</ul>
</button>
<ul id="languageul" class="dropdown-menu w-100">
{% for i,j in request.user.languageList %}
<li><a data-lang="{{i}}" class="dropdown-item" href="#">{{ j }}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endif %}

<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_pagesize">{% trans 'page size'|capfirst%}:</label>
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_pagesize" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="{% trans 'Number of records per page' %}">
{% trans 'page size'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<input class="form-control" id="id_pagesize" min="-7" max="10009999" name="pagesize" type="number" value="{{ request.user.pagesize }}">
<span class="text-muted">{% trans "Number of records per page" %}</span>
</div>
</div>

{% if THEMES|length > 1 %}
<div class="row pb-3">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_theme">{% trans 'theme'|capfirst%}:</label>
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_theme" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="Choose a styling theme.">
{% trans 'theme'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<div class="dropdown">
Expand All @@ -111,43 +134,56 @@ <h1>{{ title }}&nbsp;&nbsp;
{% endif %}

{% if SCENARIOS|length > 1 %}
<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_default_scenario">{% trans 'default scenario'|capfirst%}:</label>
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_default_scenario" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="{% trans 'After login you will be directed to the selected scenario' %}">
{% trans 'default scenario'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<div class="dropdown">
<button id="scenariobtn" class="form-control dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
{{ request.user.default_scenario }}
</button>
<ul id="scenarioul" class="dropdown-menu w-100">
{% for j in SCENARIOS %}
<li><a name="{{ j.name }}" class="dropdown-item" href="#">{%if j.description %}{{j.description}}{% else %}{{j.name}}{% endif %}</a></li>
{% endfor %}
</ul>
</button>
<ul id="scenarioul" class="dropdown-menu w-100">
{% for j in SCENARIOS %}
<li><a name="{{ j.name }}" class="dropdown-item" href="#">{%if j.description %}{{j.description}}{% else %}{{j.name}}{% endif %}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endif %}

<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_avatar">{% trans 'avatar'|capfirst%}:</label>
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_avatar" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="{% trans 'Give your user name a face' %}">
{% trans 'avatar'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<div class="text-center">
{% if request.user.avatar %}
<img style="margin-bottom: 1rem; border-radius: 50%; width:100px; height:100px" src="{{request.user.avatar.url}}">
{% else %}<i style="font-size: 8em" class="fa fa-user-circle-o"></i>
{% else %}<i style="margin-bottom: 1rem; font-size: 8em" class="fa fa-user-circle-o"></i>
{% endif %}
</div>
<input data-bs-toggle="tooltip" data-bs-html="true" class="form-control" type="file" name="avatar" accept="image/*" id="id_avatar"
data-bs-title="{% trans "Upload a square picture of yourself.<br>Accepted formats are jpeg, png and gif.<br>Max file size is 100kB." %}">
</div>
</div>

<div class="row" style="padding-bottom: 20px;">
<div class="col-3" style="max-width:150px; text-align: right">
<label class="col-form-label" for="id_cur_password">{% trans 'Change password'|capfirst%}:</label>
<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_cur_password" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="Change your password.<br>You can also do this on the login page.">
{% trans 'Change password'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<input class="form-control" id="id_cur_password" name="cur_password" type="password" autocomplete="off" placeholder=" {% trans "Old password"|escape %} " style="margin-bottom: 10px">
Expand All @@ -162,6 +198,32 @@ <h1>{{ title }}&nbsp;&nbsp;
{% endif %}
</div>
</div>

<div class="row pb-3">
<div class="col-3" style="max-width:200px; text-align: right">
<label class="col-form-label" for="id_preferences" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="right"
data-bs-title="{% trans 'The layout of each screen can be personalized using the <span class=\'fa fa-wrench\'></span> icon.<br>Here you can (re-)initiliaze your personalizations.' %}">
{% trans 'reset personalizations'|capfirst%}
<span class="fa fa-question-circle"></span>
</label>
</div>
<div class="col-9" style="max-width:260px">
<div class="dropdown">
<button id="btn_personalize" class="form-control dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">-</button>
<ul id="ul_personalize" class="dropdown-menu w-100">
<li><a data-type="nochange" data-name="" class="dropdown-item" href="#">{% trans "no change"|capfirst %}</a></li>
<li><a data-type="resetall" data-name="" class="dropdown-item" href="#">{% trans "reset all"|capfirst %}</a></li>
{% for j in SCENARIOS %}
<li><a data-type="scenario" data-name="{{ j.name }}" class="dropdown-item" href="#">{% trans "copy from scenario"|capfirst %} {%if j.description %}{{j.description}}{% else %}{{j.name}}{% endif %}</a></li>
{% endfor %}
{% for j in USERS %}
<li><a data-type="user" data-name="{{ j.username }}" class="dropdown-item" href="#">{% trans "copy from user"|capfirst %} {{j.username}}</a></li>
{% endfor %}
</ul>
</div>
</div>
</div>

</div>
</form>

Expand Down
4 changes: 4 additions & 0 deletions freppledb/common/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,10 @@ def preferences(request):
"SCENARIOS": Scenario.objects.using(DEFAULT_DB_ALIAS)
.filter(status="In use")
.order_by("name"),
"USERS": User.objects.using(DEFAULT_DB_ALIAS)
.order_by("username")
.exclude(username=request.user.username)
.only("username"),
},
)

Expand Down

0 comments on commit 725dd19

Please sign in to comment.