Compare commits

..

13 Commits

Author SHA1 Message Date
Lauréline Guérin a28af6567f
applications: add visible flag (#75115)
gitea/hobo/pipeline/head This commit looks good Details
2023-03-28 13:19:54 +02:00
Lauréline Guérin 2a2641f792
applications: unlink app in services on deletion (#74659)
gitea/hobo/pipeline/head This commit looks good Details
2023-03-28 13:19:26 +02:00
Lauréline Guérin e3b9b65544
applications: declare an app to services on version generation (#74659) 2023-03-28 13:19:25 +02:00
Benjamin Dauvergne 6835120e65 environment: prevent a race condition in get_or_create_local_hobo (#73207)
gitea/hobo/pipeline/head This commit looks good Details
Uniqueness is guaranteed by the new uniqueness constraint on local=True.
2023-03-16 16:30:50 +01:00
Benjamin Dauvergne 00172e0673 cook: improve create_site ordering of operations (#73207)
The logic is changed to match the one in ModelForm:
* first we try to get an object from the slug, or create a new one
* fields are filled
* we do a full_clean()
* then if needed the object is saved.

Keeping the full clean after the first .save() would raise an
IntegrityError because of the new unique constraints on the slug and
title fields.
2023-03-16 16:30:50 +01:00
Benjamin Dauvergne 645d6566f1 environment: initialize title from slug on save (#73207)
To simplify tests, slug is always globally unique for the model, using
it for title will always work.
2023-03-16 16:30:50 +01:00
Benjamin Dauvergne ea8a633d29 environment: ensure slug and title are unique for their scope (#73207)
* slug must be globally unique
* title must be locally unique, i.e. unique for primary services
* also there must be only one Hobo service with local=True
2023-03-16 16:30:50 +01:00
Valentin Deniaud c04fb9ab39 ci: remove Django 2.2 target (#75507)
gitea/hobo/pipeline/head This commit looks good Details
2023-03-16 12:05:26 +01:00
Valentin Deniaud c766be2305 multitenant: pass --skip-checks to migrate command (#74968)
gitea/hobo/pipeline/head This commit looks good Details
2023-03-01 12:05:32 +01:00
Frédéric Péters 6ed101d94e translation update
gitea/hobo/pipeline/head This commit looks good Details
2023-03-01 09:16:15 +01:00
Paul Marillonnet 111591a0b0 user_name: fallback on default full name when var defined yet empty (#74507)
gitea/hobo/pipeline/head This commit looks good Details
2023-02-28 14:52:30 +01:00
Paul Marillonnet 5956ce4036 profile: add user full name template definition popup (#74507) 2023-02-28 14:52:28 +01:00
Emmanuel Cazenave 92f827d9f6 sms: authorize underscore in sms_sender (#74219)
gitea/hobo/pipeline/head This commit looks good Details
2023-02-24 06:57:50 +01:00
18 changed files with 355 additions and 35 deletions

View File

@ -172,23 +172,34 @@ class Command(BaseCommand):
def create_site(self, klass, base_url, title, slug, template_name, variables):
if slug is None:
slug = klass.Extra.service_default_slug
obj, must_save = klass.objects.get_or_create(
slug=slug, defaults={'title': title, 'base_url': base_url, 'template_name': template_name}
)
try:
obj = klass.objects.get(slug=slug)
must_save = False
except klass.DoesNotExist:
obj = klass(slug=slug)
must_save = True
for attr in ('title', 'base_url', 'template_name'):
if getattr(obj, attr) != locals().get(attr):
setattr(obj, attr, locals().get(attr))
must_save = True
if must_save:
try:
obj.full_clean(
exclude=['last_operational_success_timestamp', 'last_operational_check_timestamp']
)
except ValidationError as e:
raise CommandError(str(e))
try:
obj.full_clean(
exclude=[
'secret_key',
'last_operational_success_timestamp',
'last_operational_check_timestamp',
]
)
except ValidationError as e:
raise CommandError(str(e))
if must_save:
obj.save()
self.must_notify = True
variables = variables or {}
obj_type = ContentType.objects.get_for_model(klass)
for variable_name in variables.keys():

View File

@ -0,0 +1,145 @@
# Generated by Django 3.2.18 on 2023-03-14 17:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('environment', '0028_clean_internal_ips'),
]
operations = [
migrations.AlterField(
model_name='authentic',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='bijoe',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='chrono',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='combo',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='fargo',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='hobo',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='lingo',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='passerelle',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='wcs',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AlterField(
model_name='welco',
name='slug',
field=models.SlugField(max_length=200, unique=True, verbose_name='Slug'),
),
migrations.AddConstraint(
model_name='authentic',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_authentic_title_idx',
),
),
migrations.AddConstraint(
model_name='bijoe',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_bijoe_title_idx',
),
),
migrations.AddConstraint(
model_name='chrono',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_chrono_title_idx',
),
),
migrations.AddConstraint(
model_name='combo',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_combo_title_idx',
),
),
migrations.AddConstraint(
model_name='fargo',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_fargo_title_idx',
),
),
migrations.AddConstraint(
model_name='hobo',
constraint=models.UniqueConstraint(
condition=models.Q(('local', True)), fields=('local',), name='environment_hobo_local_true_idx'
),
),
migrations.AddConstraint(
model_name='hobo',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)), fields=('title',), name='environment_hobo_title_idx'
),
),
migrations.AddConstraint(
model_name='lingo',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_lingo_title_idx',
),
),
migrations.AddConstraint(
model_name='passerelle',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_passerelle_title_idx',
),
),
migrations.AddConstraint(
model_name='wcs',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)), fields=('title',), name='environment_wcs_title_idx'
),
),
migrations.AddConstraint(
model_name='welco',
constraint=models.UniqueConstraint(
condition=models.Q(('secondary', False)),
fields=('title',),
name='environment_welco_title_idx',
),
),
]

View File

@ -123,9 +123,16 @@ def has_valid_certificate(url):
class ServiceBase(models.Model):
class Meta:
abstract = True
constraints = [
models.UniqueConstraint(
fields=['title'],
condition=models.Q(secondary=False),
name='%(app_label)s_%(class)s_title_idx',
),
]
title = models.CharField(_('Title'), max_length=50)
slug = models.SlugField(_('Slug'), max_length=200)
slug = models.SlugField(_('Slug'), max_length=200, unique=True)
base_url = models.CharField(_('Base URL'), max_length=200, validators=[URLValidator()])
legacy_urls = JSONField(null=True, default=list, blank=True)
secret_key = models.CharField(_('Secret Key'), max_length=60)
@ -218,6 +225,9 @@ class ServiceBase(models.Model):
else:
self.secret_key = get_random_string(50, SECRET_CHARS)
if not self.title and self.slug:
self.title = self.slug
is_new = self.id is None
super().save(*args, **kwargs)
@ -332,11 +342,13 @@ class Authentic(ServiceBase):
verbose_name = _('Authentic Identity Provider')
verbose_name_plural = _('Authentic Identity Providers')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'authentic'
service_label = _('Authentic')
service_default_slug = 'idp'
constraints = ServiceBase.Meta.constraints
def get_admin_zones(self):
return [
@ -359,6 +371,7 @@ class Wcs(ServiceBase):
verbose_name = _('w.c.s. Web Forms')
verbose_name_plural = _('w.c.s. Web Forms')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'wcs'
@ -382,6 +395,7 @@ class Passerelle(ServiceBase):
verbose_name = _('Passerelle')
verbose_name_plural = _('Passerelle')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'passerelle'
@ -403,6 +417,7 @@ class Combo(ServiceBase):
verbose_name = _('Combo Portal')
verbose_name_plural = _('Combo Portals')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'combo'
@ -424,6 +439,7 @@ class Fargo(ServiceBase):
verbose_name = _('Fargo document box')
verbose_name_plural = _('Fargo document box')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'fargo'
@ -441,6 +457,7 @@ class Welco(ServiceBase):
class Meta:
verbose_name = _('Welco Mail Channel')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'welco'
@ -462,6 +479,7 @@ class Chrono(ServiceBase):
verbose_name = _('Chrono Agendas')
verbose_name_plural = _('Chrono Agendas')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'chrono'
@ -483,6 +501,13 @@ class Hobo(ServiceBase):
verbose_name = _('Hobo Deployment Server')
verbose_name_plural = _('Hobo Deployment Servers')
ordering = ['title']
constraints = [
models.UniqueConstraint(
fields=['local'],
condition=models.Q(local=True),
name='%(app_label)s_%(class)s_local_true_idx',
),
] + ServiceBase.Meta.constraints
class Extra:
service_id = 'hobo'
@ -521,6 +546,7 @@ class BiJoe(ServiceBase):
verbose_name = _('Statistics')
verbose_name_plural = _('Statistics')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'bijoe'
@ -542,6 +568,7 @@ class Lingo(ServiceBase):
verbose_name = _('Lingo Billing and Payment')
verbose_name_plural = _('Lingo Billing and Payment')
ordering = ['title']
constraints = ServiceBase.Meta.constraints
class Extra:
service_id = 'lingo'

View File

@ -70,13 +70,15 @@ def get_or_create_local_hobo():
if not build_absolute_uri:
return None
hobo = Hobo.objects.create(
secret_key=get_local_key(build_absolute_uri('/')),
title='Hobo',
slug='hobo',
base_url=build_absolute_uri(reverse('home')),
secondary=False,
hobo, _ = Hobo.objects.get_or_create(
local=True,
defaults={
'secret_key': get_local_key(build_absolute_uri('/')),
'title': 'Hobo',
'slug': 'hobo',
'base_url': build_absolute_uri(reverse('home')),
'secondary': False,
},
)
return hobo

View File

@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: hobo 0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-02-10 11:06+0100\n"
"PO-Revision-Date: 2023-02-10 11:07+0100\n"
"POT-Creation-Date: 2023-03-01 08:14+0000\n"
"PO-Revision-Date: 2023-03-01 09:16+0100\n"
"Last-Translator: Frederic Peters <fpeters@entrouvert.com>\n"
"Language: French\n"
"MIME-Version: 1.0\n"
@ -144,6 +144,7 @@ msgstr "Ajouter"
#: hobo/matomo/templates/hobo/matomo_enable_auto.html
#: hobo/matomo/templates/hobo/matomo_enable_manual.html
#: hobo/profile/templates/profile/attributedefinition_form.html
#: hobo/profile/templates/profile/edit_full_name_template.html
#: hobo/seo/templates/hobo/robots_txt.html
#: hobo/theme/templates/hobo/theme_options.html
msgid "Cancel"
@ -340,6 +341,7 @@ msgstr "Débogage"
#: hobo/debug/templates/hobo/debug_home.html
#: hobo/matomo/templates/hobo/matomo_home.html
#: hobo/profile/templates/profile/attributedefinition_form.html
#: hobo/profile/templates/profile/edit_full_name_template.html
#: hobo/seo/templates/hobo/robots_txt.html
#: hobo/seo/templates/hobo/seo_home.html
msgid "Save"
@ -593,6 +595,10 @@ msgstr "Non respect des droits des usagers."
msgid "URL must end with a slash."
msgstr "LURL doit comporter un slash final."
#: hobo/profile/forms.py
msgid "User full name template (Django)"
msgstr "Gabarit (Django) pour le nom de lutilisateur"
#: hobo/profile/models.py
msgid ""
"Enter valid variable name starting with a letter and consisting of letters, "
@ -702,6 +708,10 @@ msgstr "Adresse électronique"
msgid "User Profile"
msgstr "Profil usager"
#: hobo/profile/templates/profile/attributedefinition_list.html
msgid "User full name template"
msgstr "Gabarit pour le nom de lutilisateur"
#: hobo/profile/templates/profile/attributedefinition_list.html
msgid "New attribute"
msgstr "Nouvel attribut"
@ -715,6 +725,10 @@ msgstr ""
msgid "options"
msgstr "options"
#: hobo/profile/views.py
msgid "User full name template has been updated."
msgstr "Le gabarit pour le nom de lutilisateur a été mis à jour."
#: hobo/seo/forms.py
msgid "Content of robots.txt file"
msgstr "Contenu du fichier robots.txt"

View File

@ -40,6 +40,7 @@ class MigrateSchemasCommand(SyncCommon):
parser.set_defaults(verbosity=0)
def handle(self, *args, **options):
options['skip_checks'] = True
super().handle(*args, **options)
if self.domain:
try:

26
hobo/profile/forms.py Normal file
View File

@ -0,0 +1,26 @@
# hobo - portal to configure and deploy applications
# Copyright (C) 2015-2023 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import ugettext_lazy as _
class EditFullNameTemplateForm(forms.Form):
user_full_name_template = forms.CharField(
label=_('User full name template (Django)'),
widget=forms.Textarea,
required=False,
)

View File

@ -9,6 +9,7 @@
{% block appbar %}
<h2>{% trans 'User Profile' %}</h2>
<span class="actions">
<a rel="popup" href="{% url 'profile-edit-user-full-name-template' %}">{% trans 'User full name template' %}</a>
<a rel="popup" href="{% url 'profile-add-attribute' %}">{% trans 'New attribute' %}</a>
</span>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "hobo/base.html" %}
{% load i18n %}
{% block content %}
<form method="post" enctype="multipart/form-data">
<div id="profile-full-name-template-form">
{% csrf_token %}
{{ form.as_p }}
</div>
{% block buttons %}
<div class="buttons">
<button class="submit-button">{% trans 'Save' %}</button>
<a class="cancel" href="{% url 'profile-home' %}">{% trans 'Cancel' %}</a>
</div>
{% endblock %}
</form>
{% endblock %}

View File

@ -23,4 +23,9 @@ urlpatterns = [
re_path(r'(?P<name>[\w-]+)/options', views.options, name='profile-attribute-options'),
path('reorder', views.reorder, name='profile-reorder'),
path('add-attribute', views.add_attribute, name='profile-add-attribute'),
path(
'edit-user-full-name-template',
views.edit_user_full_name_template,
name='profile-edit-user-full-name-template',
),
]

View File

@ -16,10 +16,13 @@
from django.shortcuts import redirect
from django.urls import reverse, reverse_lazy
from django.views.generic import CreateView, ListView, RedirectView, UpdateView
from django.utils.translation import ugettext as _
from django.views.generic import CreateView, ListView, RedirectView, TemplateView, UpdateView
from hobo.deploy.signals import notify_agents
from hobo.environment.forms import VariablesFormMixin
from .forms import EditFullNameTemplateForm
from .models import AttributeDefinition
@ -72,6 +75,16 @@ class OptionsView(UpdateView):
options = OptionsView.as_view()
class EditFullNameTemplateView(VariablesFormMixin, TemplateView):
template_name = 'profile/edit_full_name_template.html'
form_class = EditFullNameTemplateForm
variables = ['user_full_name_template']
success_message = _('User full name template has been updated.')
edit_user_full_name_template = EditFullNameTemplateView.as_view()
def reorder(request):
new_order_list = [int(x) for x in request.GET['new-order'].split(',')]
for attribute in AttributeDefinition.objects.all():

View File

@ -27,7 +27,7 @@ class SMSForm(forms.Form):
label=_('Sender'),
max_length=11,
validators=[
RegexValidator('^[A-Za-z0-9 ]{0,11}$', _('Only alphanumeric characters and spaces are allowed.'))
RegexValidator('^[A-Za-z0-9_ ]{0,11}$', _('Only alphanumeric characters and spaces are allowed.'))
],
help_text=_(
'Sender name or phone number. It must neither exceed 11 characters nor contain special characters.'

View File

@ -17,7 +17,7 @@ def get_full_name(user):
context = {}
context['user'] = user
template_vars = getattr(settings, 'TEMPLATE_VARS', {})
if 'user_full_name_template' in template_vars:
if template_vars.get('user_full_name_template'):
try:
template = engines['django'].from_string(template_vars['user_full_name_template'])
return template.render(context)

View File

@ -17,7 +17,8 @@
import pytest
from hobo.environment.models import Authentic
from hobo.environment.models import Authentic, Variable
from hobo.environment.utils import get_variable
from hobo.profile import models
from hobo.profile.models import AttributeDefinition
@ -78,13 +79,39 @@ def test_add_attribute(logged_app, admin_user, kind):
assert models.AttributeDefinition.objects.filter(kind=kind).filter(name='test').count() == 1
def test_edit_user_full_name_template(logged_app, admin_user, settings):
app = logged_app
value = '{{ user.first_name }}'
assert not Variable.objects.filter(name='user_full_name_template')
page = app.get('/profile/edit-user-full-name-template', status=200)
page.form['user_full_name_template'] = value
page.form.submit()
assert Variable.objects.get(name='user_full_name_template').value == value
value = '{{ user.last_name }} etc.'
page = app.get('/profile/edit-user-full-name-template', status=200)
page.form['user_full_name_template'] = value
page.form.submit()
assert Variable.objects.get(name='user_full_name_template').value == value
page = app.get('/profile/edit-user-full-name-template', status=200)
page.form['user_full_name_template'] = 'whatever'
page.click('Cancel')
assert Variable.objects.get(name='user_full_name_template').value == value
page = app.get('/profile/edit-user-full-name-template', status=200)
page.form['user_full_name_template'] = ''
page.form.submit()
assert Variable.objects.get(name='user_full_name_template').value == ''
def test_attribute_kind_not_restricted_at_model_level(db):
assert models.AttributeDefinition.objects.create(label='test', kind='somestring')
def test_profile_home_view(logged_app):
resp = logged_app.get('/profile/', status=200)
assert [x['href'] for x in resp.html.findAll('a', {'rel': 'popup'})][1:4] == [
assert [x['href'] for x in resp.html.findAll('a', {'rel': 'popup'})][2:5] == [
'/profile/title/options',
'/profile/first_name/options',
'/profile/last_name/options',
@ -98,7 +125,7 @@ def test_reorder_view(logged_app):
assert resp.location == '/profile/'
assert AttributeDefinition.objects.filter(name='last_name')[0].order == 1
resp = resp.follow()
assert [x['href'] for x in resp.html.findAll('a', {'rel': 'popup'})][1:4] == [
assert [x['href'] for x in resp.html.findAll('a', {'rel': 'popup'})][2:5] == [
'/profile/last_name/options',
'/profile/first_name/options',
'/profile/title/options',
@ -156,3 +183,18 @@ def test_debug_home(logged_app):
page = page.form.submit()
assert 'Enter a valid IPv4 or IPv6 address' in page.text
assert page.form['debug_ips']._value == 'not_an_IP' # get 'n o t _ a n _ I P'
def test_sms(logged_app):
resp = logged_app.get('/sms/')
resp.form['sms_sender'] = 'foo'
resp.form['sms_url'] = 'https://foo.invalid'
resp = resp.form.submit().follow()
assert get_variable('sms_sender').value == 'foo'
assert get_variable('sms_url').value == 'https://foo.invalid'
resp.form['sms_sender'] = 'foo_bar'
resp.form['sms_url'] = 'https://foo.invalid'
resp = resp.form.submit()
assert get_variable('sms_sender').value == 'foo_bar'
assert get_variable('sms_url').value == 'https://foo.invalid'

View File

@ -231,12 +231,18 @@ def test_get_variable_value():
def test_get_tenant_name_and_public_urls():
Combo.objects.create(base_url='https://combo.dev.publik.love', template_name='...portal-user...')
Combo.objects.create(base_url='https://agent-combo.dev.publik.love', template_name='...portal-agent...')
Combo.objects.create(base_url='https://no-template-combo.dev.publik.love')
Wcs.objects.create(base_url='https://wcs.dev.publik.love')
Fargo.objects.create(base_url='https://fargo.dev.publik.love')
Hobo.objects.create(base_url='https://hobo.dev.publik.love')
Combo.objects.create(
slug='portal', base_url='https://combo.dev.publik.love', template_name='...portal-user...'
)
Combo.objects.create(
slug='portal-agent',
base_url='https://agent-combo.dev.publik.love',
template_name='...portal-agent...',
)
Combo.objects.create(slug='portal-no-template', base_url='https://no-template-combo.dev.publik.love')
Wcs.objects.create(slug='wcs', base_url='https://wcs.dev.publik.love')
Fargo.objects.create(slug='fargo', base_url='https://fargo.dev.publik.love')
Hobo.objects.create(slug='hobo', base_url='https://hobo.dev.publik.love')
tenant_name, site_urls = get_tenant_name_and_public_urls()
assert tenant_name == 'combo.dev.publik.love'
assert site_urls == [

View File

@ -36,9 +36,15 @@ def test_user_get_full_name_from_template(user):
):
assert get_full_name(user) == 'Jane bar'
with override_settings(TEMPLATE_VARS={'user_full_name_template': ''}):
assert get_full_name(user) == 'Jane Doe'
def test_user_get_full_name(user):
with override_settings(
TEMPLATE_VARS={'user_full_name_template': '{{ user.first_name }} {{ user.attributes.foo }}'}
):
assert user.get_full_name() == 'Jane bar'
with override_settings(TEMPLATE_VARS={'user_full_name_template': ''}):
assert user.get_full_name() == 'Jane Doe'

View File

@ -49,6 +49,9 @@ def test_get_full_name_from_template_utils_from_multiple_attrs(db, tenant, setti
):
assert get_full_name(user) == 'Jane Milly Minnie'
with override_settings(TEMPLATE_VARS={'user_full_name_template': ''}):
assert get_full_name(user) == 'Jane Doe'
def test_get_full_name_from_template_accessor_from_multiple_attrs(db, tenant, settings):
with tenant_context(tenant):
@ -90,3 +93,6 @@ def test_get_full_name_from_template_accessor_from_multiple_attrs(db, tenant, se
}
):
assert user.get_full_name() == 'Jane Milly Minnie'
with override_settings(TEMPLATE_VARS={'user_full_name_template': ''}):
assert user.get_full_name() == 'Jane Doe'

View File

@ -6,7 +6,6 @@
min_version = 4
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/hobo/{env:BRANCH_NAME:}
envlist =
py3-django22-{hobo,authentic,multipublik,multitenant,schemas,passerelle}
py3-django32-{hobo,authentic,multipublik,multitenant,schemas,passerelle}
code-style
@ -45,9 +44,6 @@ setenv =
authentic: TEST_DIRECTORY=tests_authentic/
passerelle: TEST_DIRECTORY=tests_passerelle/
deps:
django22: django>=2.2,<2.3
django22: psycopg2-binary<2.9
django22: psycopg2<2.9
django32: django>=3.2,<3.3
django32: psycopg2-binary
django32: psycopg