diff --git a/common/static/common/css/common.css b/common/static/common/css/common.css index f54a0205b9e1235f23956ffb8a0c6238bc4c57aa..fa4ecbd776c4d79caabb1fc60459500ccfc24d63 100644 --- a/common/static/common/css/common.css +++ b/common/static/common/css/common.css @@ -29,6 +29,11 @@ main { max-width: 500px; } +.btn-xs { + padding: .2rem .4rem; + line-height: 0.8rem; +} + /* TODO: doesn’t work under Firefox 89, possibly older too */ ::-webkit-scrollbar { width: 4px; diff --git a/common/static/common/js/utils.js b/common/static/common/js/utils.js index 2c5106e0e01159435640351f641ad386fb3ab7b8..8e8763c8ea943aa416c74b3fb6d32c7ab5f47464 100644 --- a/common/static/common/js/utils.js +++ b/common/static/common/js/utils.js @@ -15,7 +15,7 @@ function tooltipped_span(text, tooltip_text, cls) { } function tooltipped_info(text) { - return tooltipped_span('<img src="/static/common/img/info.svg" alt="info" width="14" height="14"/>', text); + return tooltipped_span('<img src="' + window.STATIC_URL + 'common/img/info.svg" alt="info" width="14" height="14"/>', text); } function activate_tooltips(selector) { diff --git a/common/templates/base.html b/common/templates/base.html index a24c6112dc3a039d5fc7cc8c322cb8d670730a43..b041c547547c78fcabd6f7a31f3eb00164a63f62 100644 --- a/common/templates/base.html +++ b/common/templates/base.html @@ -10,7 +10,7 @@ <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>{% block title %}{% endblock %} – Walenty [beta]</title> - <link rel="icon" href="/static/common/favicon.ico"> + <link rel="icon" href="{% static 'common/favicon.ico' %}"> <link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/lux/bootstrap.min.css"> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@300;400;700&display=swap"> {% block styles %}{% endblock %} @@ -22,6 +22,9 @@ <!--Bootstrap’s tooltips require Popper--> <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script> <script type="text/javascript" src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> + <script type="text/javascript"> + window.STATIC_URL = '{% static '' %}'; + </script> <script type="text/javascript" src="{% static 'common/js/utils.js' %}"></script> <script type="text/javascript" src="{% static 'common/js/init.js' %}"></script> <!-- translations: https://docs.djangoproject.com/en/2.2/topics/i18n/translation/#using-the-javascript-translation-catalog --> @@ -60,9 +63,31 @@ {% trans "Statystyki" %} </a> </li> + {% if perms.users.view_user %} + <li class="nav-item" id="nav-users"> + <a class="nav-link text-light" href="{% url 'users:user_list' %}"> + {% trans "Użytkownicy" %} + </a> + </li> + {% endif %} </ul> </div> <span id="import-status" class="navbar-text text-warning mr-3"></span> + {% if request.user.is_authenticated %} + <div class="dropdown mr-3"> + <a href="#" class="btn btn-sm btn-outline-light dropdown-toggle" data-toggle="dropdown">{{ request.user.get_full_name|default:request.user.username }}</a> + <div class="dropdown-menu dropdown-menu-right"> + <a href="{% url 'users:user_profile' %}" class="dropdown-item font-weight-bold text-dark text-uppercase"> + {% trans "Twój profil" %} + </a> + <a href="{% url 'users:logout' %}" class="dropdown-item font-weight-bold text-dark text-uppercase"> + {% trans "Wyloguj siÄ™" %} + </a> + </div> + </div> + {% else %} + <a id="login-btn" class="btn btn-sm btn-outline-light mr-3" href="{% url 'users:login' %}">{% trans "Zaloguj siÄ™" %}</a> + {% endif %} <a id="lang-btn" class="btn btn-sm btn-outline-light" diff --git a/dictionary_statistics/templates/dictionary_statistics.html b/dictionary_statistics/templates/dictionary_statistics.html index 0b9d3662929e74a6ec12378cd5a5b7c6f1852272..9736e81c4a8d57b2e61825c9ba85eff93873437b 100644 --- a/dictionary_statistics/templates/dictionary_statistics.html +++ b/dictionary_statistics/templates/dictionary_statistics.html @@ -47,7 +47,7 @@ <tr> {% for opinion, opinion_key, n in schema_stats %} <th style="width: 10em;" scope="col"> - {% if opinion_key != "all" %}<img src="/static/entries/img/{{ opinion_key }}.svg" width="12" height="12" alt="{{ opinion }}"> {% endif %}{{ opinion }} + {% if opinion_key != "all" %}<img src="{% static 'entries/img' %}/{{ opinion_key }}.svg" width="12" height="12" alt="{{ opinion }}"> {% endif %}{{ opinion }} </th> {% endfor %} </tr> @@ -71,7 +71,7 @@ <tr> {% for opinion, opinion_key, n in frame_stats %} <th scope="col"> - {% if opinion_key != "all" %}<img src="/static/entries/img/{{ opinion_key }}.svg" width="12" height="12" alt="{{ opinion }}"> {% endif %}{{ opinion }} + {% if opinion_key != "all" %}<img src="{% static 'entries/img' %}/{{ opinion_key }}.svg" width="12" height="12" alt="{{ opinion }}"> {% endif %}{{ opinion }} </th> {% endfor %} </tr> diff --git a/entries/forms.py b/entries/forms.py index c104bad09d1121af2e79bfddfcd1fc92f2fa0ba2..c30edf0c5d087d417b904ac626bff84412c2e21d 100644 --- a/entries/forms.py +++ b/entries/forms.py @@ -1,7 +1,7 @@ from random import randint from django import forms - +from django.conf import settings from django.db.models import OneToOneField, ForeignKey, CharField, ManyToManyField, Q from django.utils.text import format_lazy @@ -118,7 +118,7 @@ def and_or_form_creator(button_label, button_id, field=None, data_add=None, add_ if help: help_tooltip = '' if tooltip: - help_tooltip = ' <span data-toggle="tooltip" data-placement="bottom" title="{}"><img src="/static/common/img/info.svg" alt="info" width="12" height="12"/></span>'.format(tooltip) + help_tooltip = f' <span data-toggle="tooltip" data-placement="bottom" title="{tooltip}"><img src="{settings.STATIC_URL}common/img/info.svg" alt="info" width="12" height="12"/></span>' ret.insert(-1, layout.HTML('<small class="form-text text-muted">{}{}</small>'.format(help, help_tooltip))) return ret diff --git a/entries/static/entries/css/entries.css b/entries/static/entries/css/entries.css index 17b7bea386094bab5a5189e82f9a66cf7f0e3de4..4a6d18cce28d3651a29d417a6e4fbc87c7dffb2b 100644 --- a/entries/static/entries/css/entries.css +++ b/entries/static/entries/css/entries.css @@ -156,7 +156,7 @@ legend { } .negated { - background-image: url("/static/entries/img/negated.png"); + background-image: url("../img/negated.png"); background-repeat: repeat; } @@ -170,7 +170,7 @@ legend { } .frame.highlight .lemma, .example-role .lemma, .frame.active .lemma, .phrase.lemma { - background-image: url("/static/entries/img/lemma.png"); + background-image: url("../img/lemma.png"); background-repeat: repeat; } @@ -196,12 +196,12 @@ legend { } .gutter.gutter-horizontal { - background-image: url("/static/entries/img/gutter-h.png"); + background-image: url("../img/gutter-h.png"); cursor: col-resize; } .gutter.gutter-vertical { - background-image: url("/static/entries/img/gutter-v.png"); + background-image: url("../img/gutter-v.png"); cursor: row-resize; } diff --git a/entries/static/entries/js/entries.js b/entries/static/entries/js/entries.js index 7230452406b7371151bed20be6671257184649ea..ce013f844713dd648d7e59953e72f9588b11815b 100644 --- a/entries/static/entries/js/entries.js +++ b/entries/static/entries/js/entries.js @@ -13,7 +13,7 @@ function make_opinion_row(item, span, width) { var opinion_row = document.createElement('tr'); opinion_row.className = 'opinion-row'; opinion_row.innerHTML = '<th scope="row" class="py-2 px-1 text-secondary" style="width: ' + width + 'em;">' + gettext('Opinia') + '</td>'; - opinion_row.innerHTML += '<td class="opinion-cell py-2 px-1" colspan="' + span + '"><img src="/static/entries/img/' + item.opinion_key + '.svg" width="12" height="12" alt="' + item.opinion + '"> ' + item.opinion + '</td>'; + opinion_row.innerHTML += '<td class="opinion-cell py-2 px-1" colspan="' + span + '"><img src="' + window.STATIC_URL + 'entries/img/' + item.opinion_key + '.svg" width="12" height="12" alt="' + item.opinion + '"> ' + item.opinion + '</td>'; return opinion_row; } @@ -126,9 +126,9 @@ function frame2dom(frame) { lu_html += ' ' + tooltipped_info('<i>' + tooltip.join('; ') + '</i>'); } if (lu.url) { - //lu_html += ' ' + tooltipped_span('<a href="' + lu.url + '" target="_blank"><img src="/static/common/img/plwn.svg" alt="external link" height="14"/></a>', gettext('Przejdź do strony tej jednostki w <i>SÅ‚owosieci</i>'), 'plwn-url'); - //lu_html += ' ' + tooltipped_span('<a href="' + lu.url + '" target="_blank"><img src="/static/common/img/ext-link.svg" alt="external link" height="14"/></a>', gettext('Przejdź do strony tej jednostki w <i>SÅ‚owosieci</i>'), 'plwn-url'); - lu_html += ' <a class="lu-plwn" href="' + lu.url + '" target="_blank"><img src="/static/common/img/ext-link.svg" alt="external link" height="14"/></a>'; +// lu_html += ' ' + tooltipped_span('<a href="' + lu.url + '" target="_blank"><img src="' + window.STATIC_URL + 'common/img/plwn.svg" alt="external link" height="14"/></a>', gettext('Przejdź do strony tej jednostki w <i>SÅ‚owosieci</i>'), 'plwn-url'); +// lu_html += ' ' + tooltipped_span('<a href="' + lu.url + '" target="_blank"><img src="' + window.STATIC_URL + 'common/img/ext-link.svg" alt="external link" height="14"/></a>', gettext('Przejdź do strony tej jednostki w <i>SÅ‚owosieci</i>'), 'plwn-url'); + lu_html += ' <a class="lu-plwn" href="' + lu.url + '" target="_blank"><img src="' + window.STATIC_URL + '/common/img/ext-link.svg" alt="external link" height="14"/></a>'; } lexical_units.push(lu_html); } @@ -163,7 +163,7 @@ function frame2dom(frame) { } preferences_html += '<div class="preference py-2 px-1' + cls + '">'; if (preference.url) { - //preferences_html += ' <a class="synset-plwn" href="' + preference.url + '" target="_blank"><img src="/static/common/img/ext-link.svg" alt="external link" height="14"/></a>'; +// preferences_html += ' <a class="synset-plwn" href="' + preference.url + '" target="_blank"><img src="' + window.STATIC_URL + 'common/img/ext-link.svg" alt="external link" height="14"/></a>'; preferences_html += ' <a class="synset-plwn" href="' + preference.url + '" target="_blank">' + preference.str + '</a>'; } else { preferences_html += preference.str; diff --git a/entries/templates/checkboxselectmultiple_with_tooltips.html b/entries/templates/checkboxselectmultiple_with_tooltips.html index 0ee0c68da9d953d46b00614ea29ca1c6cbd48a8d..b0cbaef385728f68dc73fa99153a4c99b9c62c7b 100644 --- a/entries/templates/checkboxselectmultiple_with_tooltips.html +++ b/entries/templates/checkboxselectmultiple_with_tooltips.html @@ -1,5 +1,6 @@ {% load crispy_forms_filters %} {% load l10n %} +{% load static %} <div class="{% if field_class %} {{ field_class }}{% endif %}"{% if flat_attrs %} {{ flat_attrs|safe }}{% endif %}> @@ -8,7 +9,7 @@ <input type="checkbox" class="{%if use_custom_control%}custom-control-input{% else %}form-check-input{% endif %}{%if is_bound %} is-{% if field.errors %}in{%endif%}valid{% endif %}"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}> <label class="{%if use_custom_control%}custom-control-label{% else %}form-check-label{% endif %}" for="id_{{ field.html_name }}_{{ forloop.counter }}"> {% if choice.1.1 %} - {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="/static/common/img/info.svg" alt="info" width="14" height="14"/></span> + {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="14" height="14"/></span> {% else %} {{ choice.1.0|unlocalize }} {% endif %} diff --git a/entries/templates/entries.html b/entries/templates/entries.html index 0814f1f43ba66887f40b6ae0d9737bae6f5e7064..674d39f56e74127a3feea3b78d9cd0c209f63077 100644 --- a/entries/templates/entries.html +++ b/entries/templates/entries.html @@ -63,13 +63,13 @@ <div class="form-check custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="show-realisation-descriptions"{% if request.session.show_reals_desc %} checked{% endif %}> <label class="custom-control-label" for="show-realisation-descriptions"> - {% trans "WyÅ›wietlaj opisy realizacji" %} <span data-toggle="tooltip" data-placement="bottom" title="{% trans "Po wybraniu ramy wyÅ›wietlaj opisy jej realizacji skÅ‚adniowych. Opisy (dla caÅ‚ej realizacji i dla poszczególnych fraz) sÄ… wyÅ›wietlane wewnÄ…trz schematów." %}"><img src="/static/common/img/info.svg" alt="info" width="10" height="10"/></span> + {% trans "WyÅ›wietlaj opisy realizacji" %} <span data-toggle="tooltip" data-placement="bottom" title="{% trans "Po wybraniu ramy wyÅ›wietlaj opisy jej realizacji skÅ‚adniowych. Opisy (dla caÅ‚ej realizacji i dla poszczególnych fraz) sÄ… wyÅ›wietlane wewnÄ…trz schematów." %}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="10" height="10"/></span> </label> </div> <div class="form-check custom-control custom-checkbox"> <input type="checkbox" class="custom-control-input" id="show-linked-entries"{% if request.session.show_linked_entries %} checked{% endif %}> <label class="custom-control-label" for="show-linked-entries"> - {% trans "WyÅ›wietlaj powiÄ…zane hasÅ‚a" %} <span data-toggle="tooltip" data-placement="bottom" data-html="true" title="{% trans "Przy filtrowaniu haseÅ‚ wyÅ›wietlaj, oprócz haseÅ‚ speÅ‚niajÄ…cych kryteria filtrowania, hasÅ‚a powiÄ…zane z nimi znaczeniowo (np. <i>podarować</i> – <i>podarunek</i> – <i>podarek</i>). HasÅ‚a powiÄ…zane niespeÅ‚niajÄ…ce kryteriów filtrowania sÄ… wyróżnione jaÅ›niejszym kolorem na liÅ›cie oraz nie podlegajÄ… filtrowaniu schematów i ram (sÄ… zawsze wyÅ›wietlane w caÅ‚oÅ›ci niezależnie od użytych filtrów dla schematów/ram)." %}"><img src="/static/common/img/info.svg" alt="info" width="10" height="10"/></span> + {% trans "WyÅ›wietlaj powiÄ…zane hasÅ‚a" %} <span data-toggle="tooltip" data-placement="bottom" data-html="true" title="{% trans "Przy filtrowaniu haseÅ‚ wyÅ›wietlaj, oprócz haseÅ‚ speÅ‚niajÄ…cych kryteria filtrowania, hasÅ‚a powiÄ…zane z nimi znaczeniowo (np. <i>podarować</i> – <i>podarunek</i> – <i>podarek</i>). HasÅ‚a powiÄ…zane niespeÅ‚niajÄ…ce kryteriów filtrowania sÄ… wyróżnione jaÅ›niejszym kolorem na liÅ›cie oraz nie podlegajÄ… filtrowaniu schematów i ram (sÄ… zawsze wyÅ›wietlane w caÅ‚oÅ›ci niezależnie od użytych filtrów dla schematów/ram)." %}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="10" height="10"/></span> </label> </div> </div> diff --git a/entries/templates/role_checkboxselectmultiple_inner.html b/entries/templates/role_checkboxselectmultiple_inner.html index 23a00fe0d4f1e935c052042df6363d0e58829afc..6310fe05320954dfef444387b26231e5f5ba0308 100644 --- a/entries/templates/role_checkboxselectmultiple_inner.html +++ b/entries/templates/role_checkboxselectmultiple_inner.html @@ -26,7 +26,7 @@ <input type="checkbox" class="{%if use_custom_control%}custom-control-input{% else %}form-check-input{% endif %}{%if is_bound %} is-{% if field.errors %}in{%endif%}valid{% endif %}"{% if choice.0 in field.value or choice.0|stringformat:"s" in field.value or choice.0|stringformat:"s" == field.value|default_if_none:""|stringformat:"s" %} checked="checked"{% endif %} name="{{ field.html_name }}" id="id_{{ field.html_name }}_{{ forloop.parentloop.parentloop.counter }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}" value="{{ choice.0|unlocalize }}" {{ field.field.widget.attrs|flatatt }}> <label class="{%if use_custom_control%}custom-control-label{% else %}form-check-label{% endif %} text-dark" for="id_{{ field.html_name }}_{{ forloop.parentloop.parentloop.counter }}_{{ forloop.parentloop.counter }}_{{ forloop.counter }}"> {% if choice.1.1 %} - {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="/static/common/img/info.svg" alt="info" width="14" height="14"/></span> + {{ choice.1.0|unlocalize }} <span data-toggle="tooltip" data-placement="bottom" title="{{ choice.1.1 }}"><img src="{% static 'common/img/info.svg' %}" alt="info" width="14" height="14"/></span> {% else %} {{ choice.1.0|unlocalize }} {% endif %} diff --git a/entries/templates/test.html b/entries/templates/test.html index 7f838aae952aea64d4f99b6b0c28892b7786b668..c3c96aaf6ff5973fc2d3e7260cd688dc917ea496 100644 --- a/entries/templates/test.html +++ b/entries/templates/test.html @@ -8,7 +8,7 @@ <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> - <link rel="icon" href="/static/common/favicon.ico"> + <link rel="icon" href="{% static 'common/favicon.ico' %}"> <link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/smoothness/jquery-ui.css"> <link rel="stylesheet" type="text/css" href="https://bootswatch.com/4/lux/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> diff --git a/filters/static/filters/js/filters_test.js b/filters/static/filters/js/filters_test.js index 43f45779829d7db5bcab954b1e190b14a6bd3f21..c0fa13bf9eca4a52df765554c5524ebde9d6a7d4 100644 --- a/filters/static/filters/js/filters_test.js +++ b/filters/static/filters/js/filters_test.js @@ -34,14 +34,14 @@ function show_spinner() { clear_info(); // silly cat gifs for development /*var rnd = Math.floor(Math.random() * 3) + 1; - show_status('<div class="text-center mb-1"><p>Siem filtruje!</p><img src="/static/common/img/loading' + rnd + '.gif" height=180px></div>');*/ + show_status('<div class="text-center mb-1"><p>Siem filtruje!</p><img src="' + window.STATIC_URL + 'common/img/loading' + rnd + '.gif" height=180px></div>');*/ show_status('<div class="text-center"><div class="spinner-grow text-light" role="status"> <span class="sr-only">ProszÄ™ czekać, trwa filtrowanie...</span></div></div>'); } function show_error() { clear_info(); // silly cat gif for development - /*show_status('<div class="text-center mb-1"><p>Ajajaj, straszny bÅ‚Ä…d!</p><img src="/static/common/img/error.gif" height="320px"></div>');*/ + /*show_status('<div class="text-center mb-1"><p>Ajajaj, straszny bÅ‚Ä…d!</p><img src="' + window.STATIC_URL + 'common/img/error.gif" height="320px"></div>');*/ show_warning_text('CoÅ› poszÅ‚o nie tak... :('); } diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo index 63b3175bf7c12ea66fc505d606cae8959df2877b..f7595614722432da63fc8ccf1e3efcd5f783fe8f 100644 Binary files a/locale/en/LC_MESSAGES/django.mo and b/locale/en/LC_MESSAGES/django.mo differ diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po index 020256e4bf0b2c973e67271a7cb0cdaa17e066ed..56413e06d44ffb05795ec4e8352700e97c68a701 100644 --- a/locale/en/LC_MESSAGES/django.po +++ b/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-14 16:24+0200\n" +"POT-Creation-Date: 2022-04-07 23:17+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -18,49 +18,69 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: common/templates/base.html:49 entries/templates/entries.html:7 +#: common/templates/base.html:52 entries/templates/entries.html:7 #: entries/templates/entries.html:38 msgid "HasÅ‚a" msgstr "Entries" -#: common/templates/base.html:55 +#: common/templates/base.html:58 msgid "Typy fraz" msgstr "Phrase types" -#: common/templates/base.html:60 +#: common/templates/base.html:63 #: dictionary_statistics/templates/dictionary_statistics.html:9 msgid "Statystyki" msgstr "Statistics" -#: common/templates/base.html:71 +#: common/templates/base.html:69 users/templates/user_list.html:5 +#: users/templates/user_list.html:9 +msgid "Użytkownicy" +msgstr "Users" + +#: common/templates/base.html:81 users/templates/user_profile.html:7 +#: users/templates/user_profile.html:10 +msgid "Twój profil" +msgstr "Your profile" + +#: common/templates/base.html:84 +msgid "Wyloguj siÄ™" +msgstr "Sign out" + +#: common/templates/base.html:89 users/forms.py:71 +#: users/templates/registration/login.html:6 +#: users/templates/registration/login.html:12 +msgid "Zaloguj siÄ™" +msgstr "Sign in" + +#: common/templates/base.html:96 msgid "EN" msgstr "PL" -#: common/templates/base.html:81 +#: common/templates/base.html:106 msgid "Instytut Podstaw Informatyki PAN" msgstr "Institute of Computer Science PAS" -#: common/templates/base.html:82 +#: common/templates/base.html:107 msgid "Praca współfinansowana przez" msgstr "Work co-founded by" -#: common/templates/base.html:83 +#: common/templates/base.html:108 msgid "Strona wykorzystuje" msgstr "Webpage powered by" -#: common/templates/base.html:84 +#: common/templates/base.html:109 msgid "oraz" msgstr "and " -#: common/templates/base.html:85 +#: common/templates/base.html:110 msgid "z motywem opartym na" msgstr "with" -#: common/templates/base.html:86 +#: common/templates/base.html:111 msgid " i krojem pisma" msgstr "-based motive and" -#: common/templates/base.html:87 +#: common/templates/base.html:112 msgid "." msgstr " font." @@ -122,7 +142,7 @@ msgstr "all" msgid "Pobieranie" msgstr "Download" -#: entries/autocompletes.py:27 entries/views.py:464 +#: entries/autocompletes.py:27 entries/views.py:463 msgid "definicja:" msgstr "definition:" @@ -250,7 +270,7 @@ msgstr "Schema" msgid "Schemat(y) wystÄ™pujÄ…ce w haÅ›le" msgstr "Phrase type(s) occurring in the entry." -#: entries/forms.py:147 entries/forms.py:422 entries/forms.py:643 +#: entries/forms.py:147 entries/forms.py:422 msgid "Pozycja" msgstr "Position" @@ -268,9 +288,9 @@ msgstr "" "positions co-occuring in one schema, use the POSITION filter inside SCHEMA " "filter above." -#: entries/forms.py:151 entries/forms.py:483 entries/forms.py:648 -#: entries/forms.py:769 entries/forms.py:771 entries/forms.py:773 -#: entries/forms.py:775 +#: entries/forms.py:151 entries/forms.py:483 entries/forms.py:649 +#: entries/forms.py:770 entries/forms.py:772 entries/forms.py:774 +#: entries/forms.py:776 msgid "Fraza" msgstr "Phrase" @@ -296,7 +316,7 @@ msgstr "Frame" msgid "Rama/y wystÄ™pujÄ…ce w haÅ›le" msgstr "Frame(s) occurring in the entry." -#: entries/forms.py:165 entries/forms.py:600 +#: entries/forms.py:165 entries/forms.py:593 msgid "Argument" msgstr "Argument" @@ -349,7 +369,7 @@ msgstr "Collapse" msgid "UsuÅ„" msgstr "Remove" -#: entries/forms.py:311 entries/forms.py:807 +#: entries/forms.py:311 entries/forms.py:808 msgid "Zaneguj" msgstr "Negate" @@ -433,7 +453,7 @@ msgstr "Lemma choice" msgid "ÅÄ…czenie lematów" msgstr "Lemma joining" -#: entries/forms.py:548 entries/forms.py:828 +#: entries/forms.py:548 entries/forms.py:829 msgid "Typ skÅ‚adniowy frazy zleksykalizowanej." msgstr "Syntactic type of lexicalised phrase." @@ -451,22 +471,22 @@ msgstr "Opinion" msgid "Liczba argumentów" msgstr "Number of arguments" -#: entries/forms.py:594 -msgid "Liczba preferencyj selekcyjnych argumentu" -msgstr "Number of argument’s selectional preferences" - -#: entries/forms.py:616 +#: entries/forms.py:609 msgid "Argument semantyczny" msgstr "Semantic argument" -#: entries/forms.py:622 +#: entries/forms.py:615 msgid "Rola" msgstr "Role" -#: entries/forms.py:629 +#: entries/forms.py:622 msgid "Atrybut roli" msgstr "Role attribute" +#: entries/forms.py:629 +msgid "Liczba preferencyj selekcyjnych argumentu" +msgstr "Number of argument’s selectional preferences" + #: entries/forms.py:636 entries/forms.py:639 msgid "Preferencja selekcyjna" msgstr "Selectional preference" @@ -483,59 +503,59 @@ msgstr "Expressed by relation" msgid "Wyrażona przez jednostkÄ™ leksykalnÄ… SÅ‚owosieci" msgstr "Expressed by plWordnet lexical unit" -#: entries/forms.py:647 +#: entries/forms.py:648 msgid "Typ frazy, przez którÄ… może być realizowany argument." msgstr "" -#: entries/forms.py:670 +#: entries/forms.py:671 msgid "Preferencja predefiniowana" msgstr "Predefined preference" -#: entries/forms.py:676 +#: entries/forms.py:677 msgid "Predefiniowane" msgstr "Predefined" -#: entries/forms.py:690 +#: entries/forms.py:691 msgid "Preferencja – relacja" msgstr "Relational preference" -#: entries/forms.py:696 +#: entries/forms.py:697 msgid "Relacja" msgstr "Relation" -#: entries/forms.py:707 +#: entries/forms.py:708 msgid "Do: rola" msgstr "To: role" -#: entries/forms.py:714 +#: entries/forms.py:715 msgid "Do: atrybut" msgstr "To: attribute" -#: entries/forms.py:726 +#: entries/forms.py:727 msgid "Preferencja – SÅ‚owosieć" msgstr "plWordnet preference" -#: entries/forms.py:732 +#: entries/forms.py:733 msgid "Jednostka leksykalna" msgstr "Lexical unit" -#: entries/forms.py:780 +#: entries/forms.py:781 msgid "Fraza {}" msgstr "{} phrase" -#: entries/forms.py:782 entries/phrase_descriptions/descriptions.py:124 +#: entries/forms.py:783 entries/phrase_descriptions/descriptions.py:124 msgid "zleksykalizowana" msgstr "lexicalised" -#: entries/forms.py:818 +#: entries/forms.py:819 msgid "Realizacja skÅ‚adniowa frazy." msgstr "Syntactic realisation of the phrase." -#: entries/forms.py:822 +#: entries/forms.py:823 msgid "Fraza skÅ‚adowa zleksykalizowanej konstrukcji porównawczej." msgstr "Component phrase of lexicalised comparative construction." -#: entries/forms.py:824 +#: entries/forms.py:825 msgid "Fraza zleksykalizowana." msgstr "Lexicalised phrase." @@ -2157,7 +2177,13 @@ msgid "" "filtrowania sÄ… wyróżnione jaÅ›niejszym kolorem na liÅ›cie oraz nie podlegajÄ… " "filtrowaniu schematów i ram (sÄ… zawsze wyÅ›wietlane w caÅ‚oÅ›ci niezależnie od " "użytych filtrów dla schematów/ram)." -msgstr "When filtering entries, show (aside from entries satisfying filtering criteria) entries with related meanings, (eg. <i>podarować</i> – <i>podarunek</i> – <i>podarek</i>). Related entries that don’t satisfy filtering criteria are displayed in a lighter color on the entries list and are not subject to schema/frame filtering (ie. are always shown in their entirety regardless of schema/frame filters applied)." +msgstr "" +"When filtering entries, show (aside from entries satisfying filtering " +"criteria) entries with related meanings, (eg. <i>podarować</i> – " +"<i>podarunek</i> – <i>podarek</i>). Related entries that don’t satisfy " +"filtering criteria are displayed in a lighter color on the entries list and " +"are not subject to schema/frame filtering (ie. are always shown in their " +"entirety regardless of schema/frame filters applied)." #: entries/templates/entries.html:103 msgid "Filtrowanie haseÅ‚" @@ -2205,22 +2231,22 @@ msgstr "Source" msgid "Brak przykÅ‚adów" msgstr "No examples" -#: entries/views.py:452 +#: entries/views.py:451 msgid "" "Realizacja tego argumentu w zdaniu powinna być powiÄ…zana jakÄ…kolwiek relacjÄ…" msgstr "Realisation of this argument in the sentence should be in any relation" -#: entries/views.py:454 +#: entries/views.py:453 msgid "" "Realizacja tego argumentu w zdaniu powinna być powiÄ…zana relacjÄ… <i>{}</i>" msgstr "" "Realisation of this argument in the sentence should be in <i>{}</i> relation" -#: entries/views.py:455 +#: entries/views.py:454 msgid "z realizacjÄ… argumentu <i>{}</i>." msgstr "with realisation of the <i>{}</i> argument." -#: entries/views.py:468 +#: entries/views.py:467 msgid "hiperonimy:" msgstr "hypernyms" @@ -2264,6 +2290,55 @@ msgstr "distributive phrase" msgid "fraza posesywna" msgstr "possesive phrase" +#: users/forms.py:10 users/templates/user_list.html:21 +msgid "Grupa" +msgstr "Group" + +#: users/forms.py:29 users/forms.py:59 +msgid "Zapisz" +msgstr "Save" + +#: users/forms.py:30 users/forms.py:60 +msgid "Wróć" +msgstr "Back" + +#: users/forms.py:84 +msgid "Zresetuj hasÅ‚o" +msgstr "Reset password" + +#: users/templates/registration/password_reset.html:6 +#: users/templates/registration/password_reset.html:12 +msgid "Zresetuj swoje hasÅ‚o" +msgstr "Reset your password" + +#: users/templates/user_list.html:12 users/views.py:35 +msgid "Dodaj użytkownika" +msgstr "Add a user" + +#: users/templates/user_list.html:19 +msgid "ImiÄ™ i nazwisko" +msgstr "Full name" + +#: users/templates/user_list.html:20 +msgid "Nazwa użytkownika" +msgstr "Username" + +#: users/templates/user_list.html:22 +msgid "Aktywny" +msgstr "Active" + +#: users/templates/user_list.html:23 +msgid "Akcje" +msgstr "Actions" + +#: users/templates/user_list.html:33 +msgid "Edytuj" +msgstr "Edit" + +#: users/views.py:48 +msgid "Edytuj użytkownika" +msgstr "Edit user" + #~ msgid "niepewna" #~ msgstr "uncertain" diff --git a/locale/en/LC_MESSAGES/djangojs.po b/locale/en/LC_MESSAGES/djangojs.po index 43fea1062dec5b7425e2404f4a6c74e3ece07c9c..fb7fa6fc0cacfb9be3b7ebf56bdaeacf3a7298e4 100644 --- a/locale/en/LC_MESSAGES/djangojs.po +++ b/locale/en/LC_MESSAGES/djangojs.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-07-14 16:24+0200\n" +"POT-Creation-Date: 2022-04-07 23:17+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -22,157 +22,157 @@ msgstr "" msgid "Trwa import danych!" msgstr "Data import in progress!" -#: entries/static/entries/js/entries.js:14 +#: entries/static/entries/js/entries.js:15 msgid "Opinia" msgstr "Opinion" -#: entries/static/entries/js/entries.js:33 +#: entries/static/entries/js/entries.js:34 msgid "Funkcja" msgstr "Function" -#: entries/static/entries/js/entries.js:36 +#: entries/static/entries/js/entries.js:37 msgid "Typy fraz" msgstr "Phrase types" -#: entries/static/entries/js/entries.js:97 +#: entries/static/entries/js/entries.js:98 msgid "brak schematów" msgstr "no schemata" -#: entries/static/entries/js/entries.js:122 +#: entries/static/entries/js/entries.js:123 msgid "nowa jednostka spoza <i>SÅ‚owosieci</i>" msgstr "new lexical unit not in <i>plWordnet</i>" -#: entries/static/entries/js/entries.js:146 +#: entries/static/entries/js/entries.js:147 msgid "Rola" msgstr "Role" -#: entries/static/entries/js/entries.js:148 +#: entries/static/entries/js/entries.js:149 msgid "Preferencje selekcyjne" msgstr "Selectional preferences" -#: entries/static/entries/js/entries.js:195 +#: entries/static/entries/js/entries.js:196 msgid "brak ram" msgstr "no frames" -#: entries/static/entries/js/entries.js:213 +#: entries/static/entries/js/entries.js:214 msgid "Kliknij, aby wyÅ›wietlić przykÅ‚ady dla tego schematu." msgstr "Click to show examples for this schema." -#: entries/static/entries/js/entries.js:250 +#: entries/static/entries/js/entries.js:251 msgid "Kliknij, aby cofnąć wyÅ›wietlanie przykÅ‚adów dla tego schematu." msgstr "Click to undo showing examples for this schema." -#: entries/static/entries/js/entries.js:262 -#: entries/static/entries/js/entries.js:453 -#: entries/static/entries/js/entries.js:485 -#: entries/static/entries/js/entries.js:531 +#: entries/static/entries/js/entries.js:263 +#: entries/static/entries/js/entries.js:454 +#: entries/static/entries/js/entries.js:486 +#: entries/static/entries/js/entries.js:532 msgid "" "Kliknij, aby cofnąć ograniczenie wyÅ›wietlanych przykÅ‚adów do powiÄ…zanych z" msgstr "Click to undo restriction to examples linked to" -#: entries/static/entries/js/entries.js:262 -#: entries/static/entries/js/entries.js:264 +#: entries/static/entries/js/entries.js:263 +#: entries/static/entries/js/entries.js:265 msgid "tÄ… pozycjÄ…" msgstr "this position" -#: entries/static/entries/js/entries.js:262 -#: entries/static/entries/js/entries.js:264 +#: entries/static/entries/js/entries.js:263 +#: entries/static/entries/js/entries.js:265 msgid "tÄ… frazÄ…" msgstr "this phrase" -#: entries/static/entries/js/entries.js:264 -#: entries/static/entries/js/entries.js:455 -#: entries/static/entries/js/entries.js:487 -#: entries/static/entries/js/entries.js:533 +#: entries/static/entries/js/entries.js:265 +#: entries/static/entries/js/entries.js:456 +#: entries/static/entries/js/entries.js:488 +#: entries/static/entries/js/entries.js:534 msgid "Kliknij, aby wyÅ›wietlić wyÅ‚Ä…cznie przykÅ‚ady powiÄ…zane z" msgstr "Click to show only examples linked to" -#: entries/static/entries/js/entries.js:350 +#: entries/static/entries/js/entries.js:351 msgid "" "Kliknij, aby wyÅ›wietlić przykÅ‚ady dla tej ramy oraz jej realizacje " "skÅ‚adniowe." msgstr "" "Click to show examples linked to this frame and its syntactic realisations." -#: entries/static/entries/js/entries.js:453 -#: entries/static/entries/js/entries.js:455 +#: entries/static/entries/js/entries.js:454 +#: entries/static/entries/js/entries.js:456 msgid "tym znaczeniem" msgstr "this meaning" -#: entries/static/entries/js/entries.js:485 -#: entries/static/entries/js/entries.js:487 +#: entries/static/entries/js/entries.js:486 +#: entries/static/entries/js/entries.js:488 msgid "tÄ… rolÄ…" msgstr "this role" -#: entries/static/entries/js/entries.js:531 -#: entries/static/entries/js/entries.js:533 +#: entries/static/entries/js/entries.js:532 +#: entries/static/entries/js/entries.js:534 msgid "tym schematem" msgstr "this schema" -#: entries/static/entries/js/entries.js:561 +#: entries/static/entries/js/entries.js:562 msgid "Kliknij, aby cofnąć wybór tej ramy." msgstr "Click to undo choice if this frame." -#: entries/static/entries/js/entries.js:664 -#: entries/static/entries/js/entries.js:770 +#: entries/static/entries/js/entries.js:665 +#: entries/static/entries/js/entries.js:771 msgid "Komentarz" msgstr "Comment" -#: entries/static/entries/js/entries.js:681 +#: entries/static/entries/js/entries.js:682 msgid "" "Kliknij, aby cofnąć wyÅ›wietlanie typów fraz powiÄ…zanych z tym przykÅ‚adem." msgstr "Click to undo showing phrase types linked to this example." -#: entries/static/entries/js/entries.js:683 +#: entries/static/entries/js/entries.js:684 msgid "Kliknij, aby wyÅ›wietlić typy fraz powiÄ…zane z tym przykÅ‚adem." msgstr "Click to show phrase types linked to this example." -#: entries/static/entries/js/entries.js:722 +#: entries/static/entries/js/entries.js:723 msgid "" "Kliknij, aby cofnąć wyÅ›wietlanie argumentów i typów fraz powiÄ…zanych z tym " "przykÅ‚adem." msgstr "" "Click to undo showing arguments and phrase types linked to this example." -#: entries/static/entries/js/entries.js:724 +#: entries/static/entries/js/entries.js:725 msgid "" "Kliknij, aby wyÅ›wietlić argumenty i typy fraz powiÄ…zane z tym przykÅ‚adem." msgstr "Click to show arguments and phrase types linked to this example." -#: entries/static/entries/js/entries.js:981 +#: entries/static/entries/js/entries.js:984 msgid "Przetwarzanie..." msgstr "Processing" -#: entries/static/entries/js/entries.js:982 -#: entries/static/entries/js/entries.js:1039 +#: entries/static/entries/js/entries.js:985 +#: entries/static/entries/js/entries.js:1049 msgid "Szukaj:" msgstr "Search:" -#: entries/static/entries/js/entries.js:983 +#: entries/static/entries/js/entries.js:986 msgid "Liczba haseÅ‚: _TOTAL_" msgstr "_TOTAL_ entries" -#: entries/static/entries/js/entries.js:984 -#: entries/static/entries/js/entries.js:1040 +#: entries/static/entries/js/entries.js:987 +#: entries/static/entries/js/entries.js:1050 msgid "Liczba haseÅ‚: 0" msgstr "0 entries" -#: entries/static/entries/js/entries.js:985 +#: entries/static/entries/js/entries.js:988 msgid "(spoÅ›ród _MAX_)" msgstr "(out of _MAX_)" -#: entries/static/entries/js/entries.js:986 -#: entries/static/entries/js/entries.js:1041 +#: entries/static/entries/js/entries.js:989 +#: entries/static/entries/js/entries.js:1051 msgid "Brak haseÅ‚ do wyÅ›wietlenia." msgstr "No entries to display." -#: entries/static/entries/js/entries.js:988 -#: entries/static/entries/js/entries.js:1043 +#: entries/static/entries/js/entries.js:991 +#: entries/static/entries/js/entries.js:1053 msgid ": sortuj kolumnÄ™ rosnÄ…co" msgstr ": sort column in ascending order" -#: entries/static/entries/js/entries.js:989 -#: entries/static/entries/js/entries.js:1044 +#: entries/static/entries/js/entries.js:992 +#: entries/static/entries/js/entries.js:1054 msgid ": sortuj kolumnÄ™ malejÄ…co" msgstr ": sort column in descending order" diff --git a/phrase_expansions/templates/phrase_expansions.html b/phrase_expansions/templates/phrase_expansions.html index c5242aebc46dda1c93cc2bf351777b623aba5d69..a975489d63511fd6fa2dcc7ab99bbae2b4b0ee3f 100644 --- a/phrase_expansions/templates/phrase_expansions.html +++ b/phrase_expansions/templates/phrase_expansions.html @@ -55,7 +55,7 @@ {{ subtype_expansions.phrase_subtype }} </th> {% endif %} - <td class="py-2 px-1" style="width: 7em;"><img src="/static/entries/img/{{ expansion.opinion_sym }}.svg" alt="{{ expansion.opinion_str }}" width="12" height="12"> + <td class="py-2 px-1" style="width: 7em;"><img src="{% static 'entries/img' %}/{{ expansion.opinion_sym }}.svg" alt="{{ expansion.opinion_str }}" width="12" height="12"> {{ expansion.opinion_str }} </td> {% for position in expansion.positions %} diff --git a/reset_db.sh b/reset_db.sh index d4446d256020e1a1c28075709208ce33ba2d55c4..a90511373c2f276b92e9e472ab9d60cd9c0e75cd 100755 --- a/reset_db.sh +++ b/reset_db.sh @@ -13,6 +13,7 @@ python manage.py migrate rm import.log || true +time python manage.py create_groups_and_permissions time python manage.py start_import time python manage.py import_expansions time python manage.py import_plWordnet diff --git a/shellvalier/settings.py b/shellvalier/settings.py index 797463aca63bea7d6a5c7e0579df919c03a1f0da..89ee949596a18b838e7573bf307d4a64a48b9cfc 100644 --- a/shellvalier/settings.py +++ b/shellvalier/settings.py @@ -11,6 +11,9 @@ https://docs.djangoproject.com/en/2.1/ref/settings/ """ import os +import uuid + +from django.urls import reverse_lazy from .environment import get_environment, boolean_mapper, list_mapper_factory @@ -58,6 +61,7 @@ INSTALLED_APPS = [ 'phrase_expansions.apps.PhraseExpansionsConfig', 'dictionary_statistics.apps.DictionaryStatisticsConfig', 'download.apps.DownloadConfig', + 'users.apps.UsersConfig', 'crispy_forms', 'django_extensions', ] @@ -151,9 +155,13 @@ LOCALE_PATHS = [ # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.1/howto/static-files/ -STATIC_URL = '/static/' +VERSION = get_environment('VERSION', default=str(uuid.uuid4())) +STATIC_URL = f'/static/{VERSION}/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'common/static/'), ] +LOGIN_URL = reverse_lazy("users:login") +LOGIN_REDIRECT_URL = reverse_lazy('dash') +LOGOUT_REDIRECT_URL = reverse_lazy('dash') diff --git a/shellvalier/urls.py b/shellvalier/urls.py index d1178a8cbcafe0f184d611d7f7c739fbafea9440..1fa7a875c60dc8c90fddf7b461547362e17ce90b 100644 --- a/shellvalier/urls.py +++ b/shellvalier/urls.py @@ -13,6 +13,7 @@ urlpatterns = i18n_patterns( path('phrase_expansions/', include('phrase_expansions.urls')), path('dictionary_statistics/', include('dictionary_statistics.urls')), path('download/', include('download.urls')), + path('users/', include('users.urls')), path('jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'), path('admin/', admin.site.urls, name='admin'), path('', dash, name='dash'), diff --git a/users/__init__.py b/users/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/users/apps.py b/users/apps.py new file mode 100644 index 0000000000000000000000000000000000000000..4ce1fabc032a21e92bb0e0299a650bcdc4fffefe --- /dev/null +++ b/users/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + name = 'users' diff --git a/users/forms.py b/users/forms.py new file mode 100644 index 0000000000000000000000000000000000000000..e0d98f1683665d27456c77d4808a485879d91055 --- /dev/null +++ b/users/forms.py @@ -0,0 +1,87 @@ +from django import forms +from django.contrib.auth.models import Group, User +from django.utils.translation import gettext as _ + +from crispy_forms.helper import FormHelper +from crispy_forms.layout import HTML, Layout, Fieldset, ButtonHolder, Submit + + +class UserForm(forms.ModelForm): + group = forms.ModelChoiceField(queryset=Group.objects.all(), label=_("Grupa")) + + class Meta: + model = User + fields = [ + 'first_name', + 'last_name', + 'username', + 'email', + 'group', + 'is_active', + ] + + def __init__(self, *args, instance, **kwargs): + super().__init__(*args, instance=instance, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Fieldset('', 'first_name', 'last_name', 'username', 'email', 'group', 'is_active'), + ButtonHolder( + Submit('submit', _('Zapisz'), css_class='btn btn-sm btn-success'), + HTML('''<a class="btn btn-sm btn-light" href="{% url 'users:user_list' %}">''' + _('Wróć') + '</a>'), + ), + ) + for field in ['first_name', 'last_name', 'email']: + self.fields[field].required = True + if instance.pk: + self.initial['group'] = instance.groups.first() + + def save(self, commit=True): + instance = super().save(commit=commit) + instance.groups.set([self.cleaned_data['group']]) + return instance + + +class UserProfileForm(forms.ModelForm): + class Meta: + model = User + fields = [ + 'first_name', + 'last_name', + 'email', + ] + + def __init__(self, *args, instance, **kwargs): + super().__init__(*args, instance=instance, **kwargs) + self.helper = FormHelper() + self.helper.layout = Layout( + Fieldset('', 'first_name', 'last_name', 'email'), + ButtonHolder( + Submit('submit', _('Zapisz'), css_class='btn btn-sm btn-success'), + HTML('''<a class="btn btn-sm btn-light" href="{% url 'dash' %}">''' + _('Wróć') + '</a>'), + ), + ) + for field in ['first_name', 'last_name', 'email']: + self.fields[field].required = True + + +login_form_helper = FormHelper() +login_form_helper.layout = Layout( + Fieldset('', 'username', 'password'), + ButtonHolder( + Submit('submit', _('Zaloguj siÄ™'), css_class='btn btn-sm btn-success'), + HTML( + '''<a class="btn btn-sm btn-light" href="{% url 'users:password_reset' %}">''' + f"{_('Nie pamiÄ™tam hasÅ‚a')}" + '</a>' + ), + ), +) + +password_reset_form_helper = FormHelper() +password_reset_form_helper.layout = Layout( + Fieldset('', 'email'), + ButtonHolder( + Submit('submit', _('Zresetuj hasÅ‚o'), css_class='btn btn-sm btn-success'), + HTML('''<a class="btn btn-sm btn-light" href="{% url 'users:login' %}">''' f"{_('Wróć')}" '</a>'), + ) +) diff --git a/users/management/__init__.py b/users/management/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/users/management/commands/__init__.py b/users/management/commands/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/users/management/commands/create_groups_and_permissions.py b/users/management/commands/create_groups_and_permissions.py new file mode 100644 index 0000000000000000000000000000000000000000..195d2d1942458bd7a63f4d41ff8c4a97c051dc5f --- /dev/null +++ b/users/management/commands/create_groups_and_permissions.py @@ -0,0 +1,22 @@ +from django.contrib.auth.models import Group, Permission, User +from django.contrib.contenttypes.models import ContentType +from django.core.management.base import BaseCommand + + +class Command(BaseCommand): + def handle(self, **options): + admins, __ = Group.objects.get_or_create(name="Admini") + admins.permissions.add( + Permission.objects.get(codename="view_user", content_type=ContentType.objects.get_for_model(User)), + Permission.objects.get(codename="add_user", content_type=ContentType.objects.get_for_model(User)), + Permission.objects.get(codename="change_user", content_type=ContentType.objects.get_for_model(User)), + Permission.objects.get(codename="delete_user", content_type=ContentType.objects.get_for_model(User)), + ) + lexicographs, __ = Group.objects.get_or_create(name="Leksykografowie") + lexicographs.permissions.add( + # TODO + ) + super_lexicographs, __ = Group.objects.get_or_create(name="Super Leksykografowie") + super_lexicographs.permissions.add( + # TODO + ) diff --git a/users/templates/registration/login.html b/users/templates/registration/login.html new file mode 100644 index 0000000000000000000000000000000000000000..10834c7ffb611fb88384be4752384c3879a255c2 --- /dev/null +++ b/users/templates/registration/login.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% load i18n %} +{% load crispy_forms_filters %} + +{% block title %}{% trans "Zaloguj siÄ™" %}{% endblock %} + +{% block content %} + <div class="row m-0"> + <div class="col-lg-4 col-md-6 m-auto"> + <div class="bg-white mt-4 p-3 clearfix"> + <h5 class="mt-2 mb-4">{% trans "Zaloguj siÄ™" %}</h5> + <form action="." method="post"> + {% crispy form helper %} + </form> + </div> + </div> + </div> +{% endblock %} diff --git a/users/templates/registration/password_reset.html b/users/templates/registration/password_reset.html new file mode 100644 index 0000000000000000000000000000000000000000..7d37fa921a0e368d281916c3a87cd588213ea538 --- /dev/null +++ b/users/templates/registration/password_reset.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% load i18n %} +{% load crispy_forms_filters %} + +{% block title %}{% trans "Zresetuj swoje hasÅ‚o" %}{% endblock %} + +{% block content %} + <div class="row m-0"> + <div class="col-lg-4 col-md-6 m-auto"> + <div class="bg-white mt-4 p-3 clearfix"> + <h5 class="mt-2 mb-4">{% trans "Zresetuj swoje hasÅ‚o" %}</h5> + <form action="." method="post"> + {% crispy form helper %} + </form> + </div> + </div> + </div> +{% endblock %} diff --git a/users/templates/user_form.html b/users/templates/user_form.html new file mode 100644 index 0000000000000000000000000000000000000000..82fbbf3540492162a340f08a1165c63a2e1fa62b --- /dev/null +++ b/users/templates/user_form.html @@ -0,0 +1,13 @@ +{% extends "base-margins.html" %} + +{% load i18n %} + +{% load crispy_forms_filters %} + +{% block title %}{{ title }}{% endblock %} + +{% block content2 %} + <h5 class="mt-4 mb-4">{{ title }}</h5> + + {% crispy form %} +{% endblock %} diff --git a/users/templates/user_list.html b/users/templates/user_list.html new file mode 100644 index 0000000000000000000000000000000000000000..c3b00516c9eac5e6ebe75f76222384c388421f16 --- /dev/null +++ b/users/templates/user_list.html @@ -0,0 +1,38 @@ +{% extends "base-margins.html" %} + +{% load i18n %} + +{% block title %}{% trans 'Użytkownicy' %}{% endblock %} + +{% block content2 %} +<div class="mt-3"> + <h5 class="float-left mt-2">{% trans 'Użytkownicy' %}</h5> + <div class="mb-4 float-right"> + {% if perms.users.add_user %} + <a href="{% url 'users:user_add' %}" class="btn btn-sm btn-outline-dark">+ {% trans 'Dodaj użytkownika' %}</a> + {% endif %} + </div> +</div> +<table class="table"> + <thead> + <tr> + <th>{% trans "ImiÄ™ i nazwisko" %}</th> + <th>{% trans "Nazwa użytkownika" %}</th> + <th>{% trans "Grupa" %}</th> + <th>{% trans "Aktywny" %}</th> + <th>{% trans "Akcje" %}</th> + </tr> + </thead> + <tbody> + {% for user in users %} + <tr> + <td>{{ user.get_full_name }}</td> + <td>{{ user.username }}</td> + <td>{{ user.groups.all|join:", " }}</td> + <td>{{ user.is_active|yesno }}</td> + <td><a href="{% url 'users:user_edit' pk=user.pk %}" class="btn btn-xs btn-outline-dark">{% trans 'Edytuj' %}</a></td> + </tr> + {% endfor %} + </tbody> +</table> +{% endblock %} diff --git a/users/templates/user_profile.html b/users/templates/user_profile.html new file mode 100644 index 0000000000000000000000000000000000000000..35db9c221e1fd076b4dce4c02ff56ab1cd350170 --- /dev/null +++ b/users/templates/user_profile.html @@ -0,0 +1,13 @@ +{% extends "base-margins.html" %} + +{% load i18n %} + +{% load crispy_forms_filters %} + +{% block title %}{% trans 'Twój profil' %}{% endblock %} + +{% block content2 %} + <h5 class="mt-4 mb-4">{% trans 'Twój profil' %}</h5> + + {% crispy form %} +{% endblock %} diff --git a/users/urls.py b/users/urls.py new file mode 100644 index 0000000000000000000000000000000000000000..76ae1faa248c31572c181cbb1e88b1829f8eef33 --- /dev/null +++ b/users/urls.py @@ -0,0 +1,29 @@ +from django.urls import include, path, reverse_lazy + +from django.contrib.auth import views as auth_views + +from . import views +from .forms import login_form_helper, password_reset_form_helper + +app_name = 'users' + +urlpatterns = [ + path('', views.user_list, name="user_list"), + path('add/', views.user_add, name="user_add"), + path('<int:pk>/edit/', views.user_edit, name="user_edit"), + path('profile/', views.user_profile, name="user_profile"), + path( + 'login/', + auth_views.LoginView.as_view(extra_context={"helper": login_form_helper}, success_url=reverse_lazy('dash')), + name="login", + ), + path('logout/', auth_views.LogoutView.as_view(), name="logout"), + path( + 'password-reset/', + auth_views.PasswordResetView.as_view( + template_name="registration/password_reset.html", + extra_context={"helper": password_reset_form_helper}, + ), + name="password_reset", + ), +] diff --git a/users/views.py b/users/views.py new file mode 100644 index 0000000000000000000000000000000000000000..97b3b5aac9bed68aad34067282e246964e79c094 --- /dev/null +++ b/users/views.py @@ -0,0 +1,48 @@ +from django.contrib.auth.decorators import login_required, permission_required +from django.contrib.auth.models import User +from django.shortcuts import get_object_or_404, render, redirect +from django.utils.translation import gettext_lazy as _ + +from users.forms import UserForm, UserProfileForm + + +@permission_required('users.view_user') +def user_list(request): + return render(request, 'user_list.html', {'users': User.objects.order_by('username')}) + + +@login_required +def user_profile(request): + if request.method == 'POST': + form = UserProfileForm(instance=request.user, data=request.POST) + if form.is_valid(): + form.save() + return redirect('dash') + else: + form = UserProfileForm(instance=request.user) + return render(request, 'user_profile.html', {'form': form}) + + +@permission_required('users.add_user') +def user_add(request): + if request.method == 'POST': + form = UserForm(instance=User(), data=request.POST) + if form.is_valid(): + form.save() + return redirect('users:user_list') + else: + form = UserForm(instance=User()) + return render(request, 'user_form.html', {'form': form, 'title': _('Dodaj użytkownika')}) + + +@permission_required('users.change_user') +def user_edit(request, pk): + user = get_object_or_404(User, pk=pk) + if request.method == 'POST': + form = UserForm(instance=user, data=request.POST) + if form.is_valid(): + form.save() + return redirect('users:user_list') + else: + form = UserForm(instance=user) + return render(request, 'user_form.html', {'form': form, 'title': _('Edytuj użytkownika')})