family: clean app, remove unused models (#56015)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details

This commit is contained in:
Lauréline Guérin 2021-09-27 14:21:34 +02:00
parent b5e7234acd
commit 71a362bb14
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
16 changed files with 20 additions and 555 deletions

View File

@ -1,8 +0,0 @@
Combo family cell
=================
To be visible, this cell needs a 'passerelle' entry in settings.KNOWN_SERVICES
(see ../wcs/README) and a FAMILY_SERVICE attribute in settings:
FAMILY_SERVICE = {'root': '/connector/instance/'}

View File

@ -1,31 +0,0 @@
# combo - content management system
# Copyright (C) 2015 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/>.
import django.apps
from django.utils.translation import ugettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.family'
verbose_name = _('Family')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
default_app_config = 'combo.apps.family.AppConfig'

View File

@ -1,27 +0,0 @@
# combo - content management system
# Copyright (C) 2015 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 FamilyLinkForm(forms.Form):
family_id = forms.CharField(
label=_('Family identifier'), widget=forms.TextInput(attrs={'required': 'required'})
)
family_code = forms.CharField(
label=_('Secret code'), widget=forms.PasswordInput(attrs={'required': 'required'})
)

View File

@ -0,0 +1,14 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('family', '0007_weekly_agenda_user_template'),
]
operations = [
migrations.DeleteModel(
name='FamilyInfosCell',
),
]

View File

@ -19,39 +19,7 @@ from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from combo.data.library import register_cell_class from combo.data.library import register_cell_class
from combo.data.models import CellBase, JsonCellBase from combo.data.models import JsonCellBase
from .utils import get_family, is_family_enabled
@register_cell_class
class FamilyInfosCell(CellBase):
default_template_name = 'family/infos.html'
user_dependant = True
class Meta:
verbose_name = _('Family Information Cell')
class Media:
js = (
'xstatic/jquery-ui.min.js',
'js/gadjo.js',
)
@classmethod
def is_enabled(cls):
return is_family_enabled()
def get_cell_extra_context(self, context):
if context.get('placeholder_search_mode'):
return {}
user = self.get_concerned_user(context)
if not user or user.is_anonymous:
return {}
response = get_family(user=user, raise_if_not_cached=not (context.get('synchronous')))
if response.status_code == 200:
return {'family': response.json()}
return {'error': _('An error occured while retrieving family details.')}
@register_cell_class @register_cell_class

View File

@ -1,66 +0,0 @@
{% load i18n %}
{% block cell-content %}
<h2>{% trans "Informations related to your family" %}</h2>
{% trans "Top content for unlinked users" as top_content %}
{% placeholder "family_unlinked_user" name=top_content render=False %}
<div>
{% if not user.is_authenticated %}
{% placeholder "family_unlinked_user" %}
{% elif error %}
<p>{{error}}</p>
{% elif not family.data %}
{% url 'family-link' as link_url %}
{% placeholder "family_unlinked_user" %}
<div class="family-link">
<a href="{{ link_url }}" rel="popup">{% trans "Link to your family" %}</a>
</div>
{% else %}
<div class="family_unlink">
<a href="{% url 'family-unlink' %}" rel="popup">{% trans "Unlink" %}</a>
</div>
{% with data=family.data %}
{% if data.address or data.quotient %}
<div class="family-data">
{% if data.address %}
<div class="address">
<h4>{% trans "Address" %}</h4>
<p>{{ data.address }}</p>
</div>
{% endif %}
{% if data.quotient %}
<p class="family-quotient">
<span class="label">{% trans "Family quotient:" %}</span>
<span class="value">{{ data.quotient }}</span>
</p>
{% endif %}
</div>
{% endif %}
<div class="family_members">
{% if data.adults %}
<div class="family_adults">
<h3>{% trans "Adults" %}</h3>
{% for adult in data.adults %}
<div>
{% include 'family/person.html' with person=adult %}
</div>
{% endfor %}
</div>
{% endif %}
{% if data.children %}
<div class="family_children">
<h3>{% trans "Children" %}</h3>
{% for child in data.children %}
<div>
{% include 'family/person.html' with person=child %}
</div>
{% endfor %}
</div>
{% endif %}
</div>
{% endwith %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,27 +0,0 @@
{% load i18n %}
<div id="content">
<div id="appbar">
<h2>{% trans "Link to a family" %}</h2>
</div>
<form method="post" class="quixote" action='{% url "family-link" %}'>
{% csrf_token %}
{% if family_link_intro_text %}
{{family_link_intro_text|linebreaks}}
{% else %}
<p>
{% blocktrans %}
Enter your family credentials below.
{% endblocktrans %}
</p>
{% endif %}
{% for field in form %}
<div class="widget grid-1-2">
<div class="title"><label>{{field.label_tag}}</label></div>
<div class="content">{{field}}</div>
</div>
{% endfor %}
<div class="buttons">
<button class="submit-button">{% trans "Submit" %}</button>
</div>
</form>
</div>

View File

@ -1,36 +0,0 @@
{% load i18n %}
<p class="name"><span>{{ person.first_name }} {{ person.last_name }}</span></p>
{% if person.birthdate %}
<p class="birthdate">
<span class="label">{% trans "Born on" %}</span>
<span class="value">{{ person.birthdate }}</span>
</p>
{% endif %}
<div class="address">
<h4>{% trans "Address" %}</h4>
<p>{{ person.address }}</p>
</div>
{% if person.phone or person.cellphone or person.email %}
<div class="contact">
{% if person.phone %}
<p class="phone">
<span class="label">{% trans "Phone:" %}</span>
<span class="value">{{ person.phone }}</span>
</p>
{% endif %}
{% if person.cellphone %}
<p class="cellphone">
<span class="label">{% trans "Cellphone:" %}</span>
<span class="value">{{ person.cellphone }}</span>
</p>
{% endif %}
{% if person.email %}
<p class="email">
<span class="label">{% trans "Email:" %}</span>
<span class="value"{{ person.email }}</span>
</p>
{% endif %}
</div>
{% endif %}

View File

@ -1,11 +0,0 @@
{% load i18n %}
<div id="content">
<form method="post">
{% csrf_token %}
{% trans "Unlink your personal account from this family account?" %}
<div class="buttons">
<button class="delete-button">{% trans 'Unlink' %}</button>
</div>
</form>
</div>

View File

@ -1,24 +0,0 @@
# combo - content management system
# Copyright (C) 2015 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.conf.urls import url
from .views import FamilyLinkView, FamilyUnlinkView
urlpatterns = [
url(r'^family/link/?$', FamilyLinkView.as_view(), name='family-link'),
url(r'^family/unlink/?$', FamilyUnlinkView.as_view(), name='family-unlink'),
]

View File

@ -1,61 +0,0 @@
# combo - content management system
# Copyright (C) 2015-2016 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.conf import settings
from combo.utils import requests
def get_passerelle_service():
try:
return [x for x in settings.KNOWN_SERVICES['passerelle'].values() if not x.get('secondary')][0]
except (AttributeError, IndexError, KeyError):
return None
def is_family_enabled():
return get_passerelle_service() and hasattr(settings, 'FAMILY_SERVICE')
def remote_service(endpoint, **kwargs):
path = settings.FAMILY_SERVICE.get('root') + endpoint
return requests.get(
path, remote_service=get_passerelle_service(), headers={'accept': 'application/json'}, **kwargs
)
def get_family(**kwargs):
endpoint = 'family/'
return remote_service(endpoint, **kwargs)
def link_family(user, family_id, family_code):
endpoint = 'family/link/'
kwargs = {
'user': user,
'invalidate_cache': True,
'params': {
'login': family_id,
'password': family_code,
},
}
return remote_service(endpoint, **kwargs)
def unlink_family(user):
endpoint = 'family/unlink/'
kwargs = {'user': user, 'invalidate_cache': True}
return remote_service(endpoint, **kwargs)

View File

@ -1,72 +0,0 @@
# combo - content management system
# Copyright (C) 2015 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.contrib import messages
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from django.views.generic import FormView, TemplateView
from .forms import FamilyLinkForm
from .utils import link_family, unlink_family
ERROR_MESSAGES = {
100: _('Wrong credentials: make sure you typed them correctly.'),
101: _('This family account is blocked.'),
}
class FamilyLinkView(FormView):
form_class = FamilyLinkForm
template_name = 'family/link_form.html'
def get_success_url(self):
if self.request.META.get('HTTP_REFERER'):
return self.request.META['HTTP_REFERER']
# return to current location if no referer
return '.'
def form_valid(self, form):
response = link_family(
self.request.user, form.cleaned_data['family_id'], form.cleaned_data['family_code']
)
if not response.ok or response.json().get('err'):
error_code = response.json().get('err')
error_message = ERROR_MESSAGES.get(
error_code, _('Failed to link to family. Please check your credentials and retry later.')
)
messages.error(self.request, error_message)
else:
messages.info(self.request, _('Your account was successfully linked.'))
return super().form_valid(form)
class FamilyUnlinkView(TemplateView):
template_name = 'family/unlink_confirm.html'
def post(self, request, *args, **kwargs):
response = unlink_family(request.user)
if not response.ok or response.json().get('err'):
messages.error(request, _('An error occured when unlinking.'))
else:
messages.info(request, _('Your account was successfully unlinked.'))
if self.request.META.get('HTTP_REFERER'):
return HttpResponseRedirect(self.request.META['HTTP_REFERER'])
# fallback to homepage if no referer
return HttpResponseRedirect('/')

View File

@ -79,8 +79,6 @@ if 'DISABLE_MIGRATIONS' in os.environ:
MIGRATION_MODULES = DisableMigrations() MIGRATION_MODULES = DisableMigrations()
FAMILY_SERVICE = {'root': '/'}
BOOKING_CALENDAR_CELL_ENABLED = True BOOKING_CALENDAR_CELL_ENABLED = True
LEGACY_CHART_CELL_ENABLED = True LEGACY_CHART_CELL_ENABLED = True

View File

@ -27,7 +27,6 @@ from pyquery import PyQuery
from webtest import Upload from webtest import Upload
from combo.apps.assets.models import Asset from combo.apps.assets.models import Asset
from combo.apps.family.models import FamilyInfosCell
from combo.apps.maps.models import MapLayer from combo.apps.maps.models import MapLayer
from combo.apps.search.models import SearchCell from combo.apps.search.models import SearchCell
from combo.data.forms import LinkCellForm from combo.data.forms import LinkCellForm
@ -895,7 +894,7 @@ def test_site_export_import_json(app, admin_user):
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json') resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
with CaptureQueriesContext(connection) as ctx: with CaptureQueriesContext(connection) as ctx:
resp = resp.form.submit() resp = resp.form.submit()
assert len(ctx.captured_queries) in [307, 308] assert len(ctx.captured_queries) in [302, 303]
assert Page.objects.count() == 4 assert Page.objects.count() == 4
assert PageSnapshot.objects.all().count() == 4 assert PageSnapshot.objects.all().count() == 4
@ -906,7 +905,7 @@ def test_site_export_import_json(app, admin_user):
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json') resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json')
with CaptureQueriesContext(connection) as ctx: with CaptureQueriesContext(connection) as ctx:
resp = resp.form.submit() resp = resp.form.submit()
assert len(ctx.captured_queries) == 276 assert len(ctx.captured_queries) == 272
assert set(Page.objects.get(slug='one').related_cells['cell_types']) == {'data_textcell', 'data_linkcell'} assert set(Page.objects.get(slug='one').related_cells['cell_types']) == {'data_textcell', 'data_linkcell'}
assert Page.objects.count() == 4 assert Page.objects.count() == 4
assert LinkCell.objects.count() == 2 assert LinkCell.objects.count() == 2
@ -2156,22 +2155,6 @@ def test_page_cell_placeholder(app, admin_user):
assert re.findall('<h2>(.*)</h2>', resp.text) == ['Page - One', 'Content', 'JSON Prototype / Foobar'] assert re.findall('<h2>(.*)</h2>', resp.text) == ['Page - One', 'Content', 'JSON Prototype / Foobar']
def test_page_familycell_placeholder(app, admin_user):
page = Page(title='My family', slug='my-family', template_name='standard')
page.save()
cell = FamilyInfosCell(page=page, placeholder='content', order=0)
cell.save()
app = login(app)
resp = app.get('/manage/pages/%s/' % page.id)
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', "family_unlinked_user"]
assert re.findall('<h2>(.*)</h2>', resp.text) == [
'Page - My family',
'Content',
'Family Information Cell / Top content for unlinked users',
]
def test_page_discover_placeholder_with_error_cells(app, admin_user): def test_page_discover_placeholder_with_error_cells(app, admin_user):
page = Page(title='One', slug='one', template_name='standard') page = Page(title='One', slug='one', template_name='standard')
page.save() page.save()
@ -2237,7 +2220,7 @@ def test_page_versionning(app, admin_user):
with CaptureQueriesContext(connection) as ctx: with CaptureQueriesContext(connection) as ctx:
resp2 = resp.click('view', index=1) resp2 = resp.click('view', index=1)
assert len(ctx.captured_queries) == 71 assert len(ctx.captured_queries) == 70
assert Page.snapshots.latest('pk').related_cells == {'cell_types': ['data_textcell']} assert Page.snapshots.latest('pk').related_cells == {'cell_types': ['data_textcell']}
assert resp2.text.index('Hello world') < resp2.text.index('Foobar3') assert resp2.text.index('Hello world') < resp2.text.index('Foobar3')
@ -2298,7 +2281,7 @@ def test_page_versionning(app, admin_user):
resp = resp.click('restore', index=6) resp = resp.click('restore', index=6)
with CaptureQueriesContext(connection) as ctx: with CaptureQueriesContext(connection) as ctx:
resp = resp.form.submit().follow() resp = resp.form.submit().follow()
assert len(ctx.captured_queries) == 146 assert len(ctx.captured_queries) == 144
resp2 = resp.click('See online') resp2 = resp.click('See online')
assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3') assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3')

View File

@ -23,7 +23,6 @@ except ImportError:
import requests import requests
from combo.apps.assets.models import Asset from combo.apps.assets.models import Asset
from combo.apps.family.models import FamilyInfosCell
from combo.data.models import ( from combo.data.models import (
ConfigJsonCell, ConfigJsonCell,
FeedCell, FeedCell,
@ -840,140 +839,6 @@ def test_post_cell(app):
assert resp2.content == b'a,b\n1,2' assert resp2.content == b'a,b\n1,2'
def test_familyinfos_cell_with_placeholders(app, admin_user):
Page.objects.all().delete()
page = Page(title='Family', slug='index', template_name='standard')
page.save()
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
family_cell.save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>Hello anonymous user</p>',
order=0,
restricted_to_unlogged=True,
public=True,
).save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>You are not linked</p>',
order=1,
public=False,
restricted_to_unlogged=False,
).save()
with override_settings(FAMILY_SERVICE={'root': '/family/'}):
with mock.patch('combo.utils.requests.send') as requests_send:
resp = app.get('/')
assert "<p>Hello anonymous user</p>" in resp.text
assert "<p>You are not linked</p>" not in resp.text
app = login(app)
data = {'err': 0, 'data': None}
requests_send.return_value = mock.Mock(
json=lambda: data, content=json.dumps(data), status_code=200
)
# make sure no data are loaded
resp = app.get('/')
assert resp.html.body.find('div', {'class': 'familyinfoscell'}).text.strip() == 'Loading...'
# put data in cache
resp = app.get(
reverse(
'combo-public-ajax-page-cell',
kwargs={'page_pk': page.pk, 'cell_reference': family_cell.get_reference()},
)
)
resp = app.get('/')
assert "<p>Hello anonymous user</p>" not in resp.text
assert "<p>You are not linked</p>" in resp.text
@mock.patch('combo.utils.requests.get')
def test_familycell_link(requests_get, app, settings, admin_user):
settings.FAMILY_SERVICE = {'root': '/family/'}
Page.objects.all().delete()
page = Page(title='Family', slug='index', template_name='standard')
page.save()
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
family_cell.save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>Hello anonymous user</p>',
order=0,
restricted_to_unlogged=True,
public=True,
).save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>You are not linked</p>',
order=1,
public=False,
restricted_to_unlogged=False,
).save()
app = login(app)
# when linking fails
requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102})
resp = app.get('/family/link/')
resp.form['family_id'] = '123'
resp.form['family_code'] = 'l33t'
resp = resp.form.submit().follow()
messages = resp.context['messages']
assert len(messages._loaded_messages) == 1
assert (
messages._loaded_messages[0].message
== 'Failed to link to family. Please check your credentials and retry later.'
)
requests_get.return_value = mock.Mock(ok=True, json=lambda: {})
resp.form['family_id'] = '123'
resp.form['family_code'] = 'l33t'
resp = resp.form.submit().follow()
messages = resp.context['messages']
assert len(messages._loaded_messages) == 2
assert messages._loaded_messages[1].message == 'Your account was successfully linked.'
@mock.patch('combo.utils.requests.get')
def test_familycell_unlink(requests_get, app, settings, admin_user):
settings.FAMILY_SERVICE = {'root': '/family/'}
Page.objects.all().delete()
page = Page(title='Family', slug='index', template_name='standard')
page.save()
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0)
family_cell.save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>Hello anonymous user</p>',
order=0,
restricted_to_unlogged=True,
public=True,
).save()
TextCell(
page=page,
placeholder='family_unlinked_user',
text='<p>You are not linked</p>',
order=1,
public=False,
restricted_to_unlogged=False,
).save()
app = login(app)
requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102})
resp = app.get('/family/unlink/')
resp = resp.form.submit().follow()
messages = resp.context['messages']
assert len(messages._loaded_messages) == 1
assert messages._loaded_messages[0].message == 'An error occured when unlinking.'
requests_get.return_value = mock.Mock(ok=True, json=lambda: {})
resp = app.get('/family/unlink/')
resp = resp.form.submit().follow()
messages = resp.context['messages']
assert len(messages._loaded_messages) == 2
assert messages._loaded_messages[1].message == 'Your account was successfully unlinked.'
def test_synchronous_placeholder(app): def test_synchronous_placeholder(app):
page = Page(title='foo', slug='foo', template_name='standard', order=0) page = Page(title='foo', slug='foo', template_name='standard', order=0)
page.save() page.save()

View File

@ -1370,7 +1370,7 @@ def test_index_site_num_queries(settings, app):
assert IndexedCell.objects.count() == 50 assert IndexedCell.objects.count() == 50
with CaptureQueriesContext(connection) as ctx: with CaptureQueriesContext(connection) as ctx:
index_site() index_site()
assert len(ctx.captured_queries) == 223 assert len(ctx.captured_queries) == 222
SearchCell.objects.create( SearchCell.objects.create(
page=page, placeholder='content', order=0, _search_services={'data': ['search1']} page=page, placeholder='content', order=0, _search_services={'data': ['search1']}