0001-family-clean-app-remove-unused-models-56015.patch
combo/apps/family/README | ||
---|---|---|
1 |
Combo family cell |
|
2 |
================= |
|
3 | ||
4 |
To be visible, this cell needs a 'passerelle' entry in settings.KNOWN_SERVICES |
|
5 |
(see ../wcs/README) and a FAMILY_SERVICE attribute in settings: |
|
6 | ||
7 |
FAMILY_SERVICE = {'root': '/connector/instance/'} |
|
8 |
combo/apps/family/__init__.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import django.apps |
|
18 |
from django.utils.translation import ugettext_lazy as _ |
|
19 | ||
20 | ||
21 |
class AppConfig(django.apps.AppConfig): |
|
22 |
name = 'combo.apps.family' |
|
23 |
verbose_name = _('Family') |
|
24 | ||
25 |
def get_before_urls(self): |
|
26 |
from . import urls |
|
27 | ||
28 |
return urls.urlpatterns |
|
29 | ||
30 | ||
31 |
default_app_config = 'combo.apps.family.AppConfig' |
combo/apps/family/forms.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django import forms |
|
18 |
from django.utils.translation import ugettext_lazy as _ |
|
19 | ||
20 | ||
21 |
class FamilyLinkForm(forms.Form): |
|
22 |
family_id = forms.CharField( |
|
23 |
label=_('Family identifier'), widget=forms.TextInput(attrs={'required': 'required'}) |
|
24 |
) |
|
25 |
family_code = forms.CharField( |
|
26 |
label=_('Secret code'), widget=forms.PasswordInput(attrs={'required': 'required'}) |
|
27 |
) |
combo/apps/family/migrations/0008_clean.py | ||
---|---|---|
1 |
from django.db import migrations |
|
2 | ||
3 | ||
4 |
class Migration(migrations.Migration): |
|
5 | ||
6 |
dependencies = [ |
|
7 |
('family', '0007_weekly_agenda_user_template'), |
|
8 |
] |
|
9 | ||
10 |
operations = [ |
|
11 |
migrations.DeleteModel( |
|
12 |
name='FamilyInfosCell', |
|
13 |
), |
|
14 |
] |
combo/apps/family/models.py | ||
---|---|---|
19 | 19 |
from django.utils.translation import ugettext_lazy as _ |
20 | 20 | |
21 | 21 |
from combo.data.library import register_cell_class |
22 |
from combo.data.models import CellBase, JsonCellBase |
|
23 | ||
24 |
from .utils import get_family, is_family_enabled |
|
25 | ||
26 | ||
27 |
@register_cell_class |
|
28 |
class FamilyInfosCell(CellBase): |
|
29 |
default_template_name = 'family/infos.html' |
|
30 |
user_dependant = True |
|
31 | ||
32 |
class Meta: |
|
33 |
verbose_name = _('Family Information Cell') |
|
34 | ||
35 |
class Media: |
|
36 |
js = ( |
|
37 |
'xstatic/jquery-ui.min.js', |
|
38 |
'js/gadjo.js', |
|
39 |
) |
|
40 | ||
41 |
@classmethod |
|
42 |
def is_enabled(cls): |
|
43 |
return is_family_enabled() |
|
44 | ||
45 |
def get_cell_extra_context(self, context): |
|
46 |
if context.get('placeholder_search_mode'): |
|
47 |
return {} |
|
48 |
user = self.get_concerned_user(context) |
|
49 |
if not user or user.is_anonymous: |
|
50 |
return {} |
|
51 |
response = get_family(user=user, raise_if_not_cached=not (context.get('synchronous'))) |
|
52 |
if response.status_code == 200: |
|
53 |
return {'family': response.json()} |
|
54 |
return {'error': _('An error occured while retrieving family details.')} |
|
22 |
from combo.data.models import JsonCellBase |
|
55 | 23 | |
56 | 24 | |
57 | 25 |
@register_cell_class |
combo/apps/family/templates/family/infos.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
{% block cell-content %} |
|
3 |
<h2>{% trans "Informations related to your family" %}</h2> |
|
4 | ||
5 |
{% trans "Top content for unlinked users" as top_content %} |
|
6 |
{% placeholder "family_unlinked_user" name=top_content render=False %} |
|
7 | ||
8 |
<div> |
|
9 |
{% if not user.is_authenticated %} |
|
10 |
{% placeholder "family_unlinked_user" %} |
|
11 |
{% elif error %} |
|
12 |
<p>{{error}}</p> |
|
13 |
{% elif not family.data %} |
|
14 |
{% url 'family-link' as link_url %} |
|
15 |
{% placeholder "family_unlinked_user" %} |
|
16 |
<div class="family-link"> |
|
17 |
<a href="{{ link_url }}" rel="popup">{% trans "Link to your family" %}</a> |
|
18 |
</div> |
|
19 |
{% else %} |
|
20 |
<div class="family_unlink"> |
|
21 |
<a href="{% url 'family-unlink' %}" rel="popup">{% trans "Unlink" %}</a> |
|
22 |
</div> |
|
23 |
{% with data=family.data %} |
|
24 |
{% if data.address or data.quotient %} |
|
25 |
<div class="family-data"> |
|
26 |
{% if data.address %} |
|
27 |
<div class="address"> |
|
28 |
<h4>{% trans "Address" %}</h4> |
|
29 |
<p>{{ data.address }}</p> |
|
30 |
</div> |
|
31 |
{% endif %} |
|
32 |
{% if data.quotient %} |
|
33 |
<p class="family-quotient"> |
|
34 |
<span class="label">{% trans "Family quotient:" %}</span> |
|
35 |
<span class="value">{{ data.quotient }}</span> |
|
36 |
</p> |
|
37 |
{% endif %} |
|
38 |
</div> |
|
39 |
{% endif %} |
|
40 |
<div class="family_members"> |
|
41 |
{% if data.adults %} |
|
42 |
<div class="family_adults"> |
|
43 |
<h3>{% trans "Adults" %}</h3> |
|
44 |
{% for adult in data.adults %} |
|
45 |
<div> |
|
46 |
{% include 'family/person.html' with person=adult %} |
|
47 |
</div> |
|
48 |
{% endfor %} |
|
49 |
</div> |
|
50 |
{% endif %} |
|
51 | ||
52 |
{% if data.children %} |
|
53 |
<div class="family_children"> |
|
54 |
<h3>{% trans "Children" %}</h3> |
|
55 |
{% for child in data.children %} |
|
56 |
<div> |
|
57 |
{% include 'family/person.html' with person=child %} |
|
58 |
</div> |
|
59 |
{% endfor %} |
|
60 |
</div> |
|
61 |
{% endif %} |
|
62 |
</div> |
|
63 |
{% endwith %} |
|
64 |
{% endif %} |
|
65 |
</div> |
|
66 |
{% endblock %} |
combo/apps/family/templates/family/link_form.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
<div id="content"> |
|
3 |
<div id="appbar"> |
|
4 |
<h2>{% trans "Link to a family" %}</h2> |
|
5 |
</div> |
|
6 |
<form method="post" class="quixote" action='{% url "family-link" %}'> |
|
7 |
{% csrf_token %} |
|
8 |
{% if family_link_intro_text %} |
|
9 |
{{family_link_intro_text|linebreaks}} |
|
10 |
{% else %} |
|
11 |
<p> |
|
12 |
{% blocktrans %} |
|
13 |
Enter your family credentials below. |
|
14 |
{% endblocktrans %} |
|
15 |
</p> |
|
16 |
{% endif %} |
|
17 |
{% for field in form %} |
|
18 |
<div class="widget grid-1-2"> |
|
19 |
<div class="title"><label>{{field.label_tag}}</label></div> |
|
20 |
<div class="content">{{field}}</div> |
|
21 |
</div> |
|
22 |
{% endfor %} |
|
23 |
<div class="buttons"> |
|
24 |
<button class="submit-button">{% trans "Submit" %}</button> |
|
25 |
</div> |
|
26 |
</form> |
|
27 |
</div> |
combo/apps/family/templates/family/person.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
<p class="name"><span>{{ person.first_name }} {{ person.last_name }}</span></p> |
|
3 |
{% if person.birthdate %} |
|
4 |
<p class="birthdate"> |
|
5 |
<span class="label">{% trans "Born on" %}</span> |
|
6 |
<span class="value">{{ person.birthdate }}</span> |
|
7 |
</p> |
|
8 |
{% endif %} |
|
9 | ||
10 |
<div class="address"> |
|
11 |
<h4>{% trans "Address" %}</h4> |
|
12 |
<p>{{ person.address }}</p> |
|
13 |
</div> |
|
14 | ||
15 |
{% if person.phone or person.cellphone or person.email %} |
|
16 |
<div class="contact"> |
|
17 |
{% if person.phone %} |
|
18 |
<p class="phone"> |
|
19 |
<span class="label">{% trans "Phone:" %}</span> |
|
20 |
<span class="value">{{ person.phone }}</span> |
|
21 |
</p> |
|
22 |
{% endif %} |
|
23 |
{% if person.cellphone %} |
|
24 |
<p class="cellphone"> |
|
25 |
<span class="label">{% trans "Cellphone:" %}</span> |
|
26 |
<span class="value">{{ person.cellphone }}</span> |
|
27 |
</p> |
|
28 |
{% endif %} |
|
29 |
{% if person.email %} |
|
30 |
<p class="email"> |
|
31 |
<span class="label">{% trans "Email:" %}</span> |
|
32 |
<span class="value"{{ person.email }}</span> |
|
33 |
</p> |
|
34 |
{% endif %} |
|
35 |
</div> |
|
36 |
{% endif %} |
combo/apps/family/templates/family/unlink_confirm.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 | ||
3 |
<div id="content"> |
|
4 |
<form method="post"> |
|
5 |
{% csrf_token %} |
|
6 |
{% trans "Unlink your personal account from this family account?" %} |
|
7 |
<div class="buttons"> |
|
8 |
<button class="delete-button">{% trans 'Unlink' %}</button> |
|
9 |
</div> |
|
10 |
</form> |
|
11 |
</div> |
combo/apps/family/urls.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import url |
|
18 | ||
19 |
from .views import FamilyLinkView, FamilyUnlinkView |
|
20 | ||
21 |
urlpatterns = [ |
|
22 |
url(r'^family/link/?$', FamilyLinkView.as_view(), name='family-link'), |
|
23 |
url(r'^family/unlink/?$', FamilyUnlinkView.as_view(), name='family-unlink'), |
|
24 |
] |
combo/apps/family/utils.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015-2016 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf import settings |
|
18 | ||
19 |
from combo.utils import requests |
|
20 | ||
21 | ||
22 |
def get_passerelle_service(): |
|
23 |
try: |
|
24 |
return [x for x in settings.KNOWN_SERVICES['passerelle'].values() if not x.get('secondary')][0] |
|
25 |
except (AttributeError, IndexError, KeyError): |
|
26 |
return None |
|
27 | ||
28 | ||
29 |
def is_family_enabled(): |
|
30 |
return get_passerelle_service() and hasattr(settings, 'FAMILY_SERVICE') |
|
31 | ||
32 | ||
33 |
def remote_service(endpoint, **kwargs): |
|
34 |
path = settings.FAMILY_SERVICE.get('root') + endpoint |
|
35 |
return requests.get( |
|
36 |
path, remote_service=get_passerelle_service(), headers={'accept': 'application/json'}, **kwargs |
|
37 |
) |
|
38 | ||
39 | ||
40 |
def get_family(**kwargs): |
|
41 |
endpoint = 'family/' |
|
42 |
return remote_service(endpoint, **kwargs) |
|
43 | ||
44 | ||
45 |
def link_family(user, family_id, family_code): |
|
46 |
endpoint = 'family/link/' |
|
47 |
kwargs = { |
|
48 |
'user': user, |
|
49 |
'invalidate_cache': True, |
|
50 |
'params': { |
|
51 |
'login': family_id, |
|
52 |
'password': family_code, |
|
53 |
}, |
|
54 |
} |
|
55 |
return remote_service(endpoint, **kwargs) |
|
56 | ||
57 | ||
58 |
def unlink_family(user): |
|
59 |
endpoint = 'family/unlink/' |
|
60 |
kwargs = {'user': user, 'invalidate_cache': True} |
|
61 |
return remote_service(endpoint, **kwargs) |
combo/apps/family/views.py | ||
---|---|---|
1 |
# combo - content management system |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 | ||
18 |
from django.contrib import messages |
|
19 |
from django.http import HttpResponseRedirect |
|
20 |
from django.utils.translation import ugettext_lazy as _ |
|
21 |
from django.views.generic import FormView, TemplateView |
|
22 | ||
23 |
from .forms import FamilyLinkForm |
|
24 |
from .utils import link_family, unlink_family |
|
25 | ||
26 |
ERROR_MESSAGES = { |
|
27 |
100: _('Wrong credentials: make sure you typed them correctly.'), |
|
28 |
101: _('This family account is blocked.'), |
|
29 |
} |
|
30 | ||
31 | ||
32 |
class FamilyLinkView(FormView): |
|
33 |
form_class = FamilyLinkForm |
|
34 |
template_name = 'family/link_form.html' |
|
35 | ||
36 |
def get_success_url(self): |
|
37 |
if self.request.META.get('HTTP_REFERER'): |
|
38 |
return self.request.META['HTTP_REFERER'] |
|
39 |
# return to current location if no referer |
|
40 |
return '.' |
|
41 | ||
42 |
def form_valid(self, form): |
|
43 |
response = link_family( |
|
44 |
self.request.user, form.cleaned_data['family_id'], form.cleaned_data['family_code'] |
|
45 |
) |
|
46 | ||
47 |
if not response.ok or response.json().get('err'): |
|
48 |
error_code = response.json().get('err') |
|
49 |
error_message = ERROR_MESSAGES.get( |
|
50 |
error_code, _('Failed to link to family. Please check your credentials and retry later.') |
|
51 |
) |
|
52 |
messages.error(self.request, error_message) |
|
53 |
else: |
|
54 |
messages.info(self.request, _('Your account was successfully linked.')) |
|
55 |
return super().form_valid(form) |
|
56 | ||
57 | ||
58 |
class FamilyUnlinkView(TemplateView): |
|
59 |
template_name = 'family/unlink_confirm.html' |
|
60 | ||
61 |
def post(self, request, *args, **kwargs): |
|
62 |
response = unlink_family(request.user) |
|
63 | ||
64 |
if not response.ok or response.json().get('err'): |
|
65 |
messages.error(request, _('An error occured when unlinking.')) |
|
66 |
else: |
|
67 |
messages.info(request, _('Your account was successfully unlinked.')) |
|
68 | ||
69 |
if self.request.META.get('HTTP_REFERER'): |
|
70 |
return HttpResponseRedirect(self.request.META['HTTP_REFERER']) |
|
71 |
# fallback to homepage if no referer |
|
72 |
return HttpResponseRedirect('/') |
tests/settings.py | ||
---|---|---|
79 | 79 |
MIGRATION_MODULES = DisableMigrations() |
80 | 80 | |
81 | 81 | |
82 |
FAMILY_SERVICE = {'root': '/'} |
|
83 | ||
84 | 82 |
BOOKING_CALENDAR_CELL_ENABLED = True |
85 | 83 |
LEGACY_CHART_CELL_ENABLED = True |
86 | 84 |
tests/test_manager.py | ||
---|---|---|
27 | 27 |
from webtest import Upload |
28 | 28 | |
29 | 29 |
from combo.apps.assets.models import Asset |
30 |
from combo.apps.family.models import FamilyInfosCell |
|
31 | 30 |
from combo.apps.maps.models import MapLayer |
32 | 31 |
from combo.apps.search.models import SearchCell |
33 | 32 |
from combo.data.forms import LinkCellForm |
... | ... | |
878 | 877 |
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json') |
879 | 878 |
with CaptureQueriesContext(connection) as ctx: |
880 | 879 |
resp = resp.form.submit() |
881 |
assert len(ctx.captured_queries) in [307, 308]
|
|
880 |
assert len(ctx.captured_queries) in [302, 303]
|
|
882 | 881 |
assert Page.objects.count() == 4 |
883 | 882 |
assert PageSnapshot.objects.all().count() == 4 |
884 | 883 | |
... | ... | |
889 | 888 |
resp.form['site_file'] = Upload('site-export.json', site_export, 'application/json') |
890 | 889 |
with CaptureQueriesContext(connection) as ctx: |
891 | 890 |
resp = resp.form.submit() |
892 |
assert len(ctx.captured_queries) == 276
|
|
891 |
assert len(ctx.captured_queries) == 272
|
|
893 | 892 |
assert set(Page.objects.get(slug='one').related_cells['cell_types']) == {'data_textcell', 'data_linkcell'} |
894 | 893 |
assert Page.objects.count() == 4 |
895 | 894 |
assert LinkCell.objects.count() == 2 |
... | ... | |
2139 | 2138 |
assert re.findall('<h2>(.*)</h2>', resp.text) == ['Page - One', 'Content', 'JSON Prototype / Foobar'] |
2140 | 2139 | |
2141 | 2140 | |
2142 |
def test_page_familycell_placeholder(app, admin_user): |
|
2143 |
page = Page(title='My family', slug='my-family', template_name='standard') |
|
2144 |
page.save() |
|
2145 |
cell = FamilyInfosCell(page=page, placeholder='content', order=0) |
|
2146 |
cell.save() |
|
2147 | ||
2148 |
app = login(app) |
|
2149 |
resp = app.get('/manage/pages/%s/' % page.id) |
|
2150 |
assert re.findall('data-placeholder-key="(.*)">', resp.text) == ['content', "family_unlinked_user"] |
|
2151 |
assert re.findall('<h2>(.*)</h2>', resp.text) == [ |
|
2152 |
'Page - My family', |
|
2153 |
'Content', |
|
2154 |
'Family Information Cell / Top content for unlinked users', |
|
2155 |
] |
|
2156 | ||
2157 | ||
2158 | 2141 |
def test_page_discover_placeholder_with_error_cells(app, admin_user): |
2159 | 2142 |
page = Page(title='One', slug='one', template_name='standard') |
2160 | 2143 |
page.save() |
... | ... | |
2220 | 2203 | |
2221 | 2204 |
with CaptureQueriesContext(connection) as ctx: |
2222 | 2205 |
resp2 = resp.click('view', index=1) |
2223 |
assert len(ctx.captured_queries) == 71
|
|
2206 |
assert len(ctx.captured_queries) == 70
|
|
2224 | 2207 |
assert Page.snapshots.latest('pk').related_cells == {'cell_types': ['data_textcell']} |
2225 | 2208 |
assert resp2.text.index('Hello world') < resp2.text.index('Foobar3') |
2226 | 2209 | |
... | ... | |
2281 | 2264 |
resp = resp.click('restore', index=6) |
2282 | 2265 |
with CaptureQueriesContext(connection) as ctx: |
2283 | 2266 |
resp = resp.form.submit().follow() |
2284 |
assert len(ctx.captured_queries) == 146
|
|
2267 |
assert len(ctx.captured_queries) == 144
|
|
2285 | 2268 | |
2286 | 2269 |
resp2 = resp.click('See online') |
2287 | 2270 |
assert resp2.text.index('Foobar1') < resp2.text.index('Foobar2') < resp2.text.index('Foobar3') |
tests/test_public.py | ||
---|---|---|
23 | 23 |
import requests |
24 | 24 | |
25 | 25 |
from combo.apps.assets.models import Asset |
26 |
from combo.apps.family.models import FamilyInfosCell |
|
27 | 26 |
from combo.data.models import ( |
28 | 27 |
ConfigJsonCell, |
29 | 28 |
FeedCell, |
... | ... | |
833 | 832 |
assert resp2.content == b'a,b\n1,2' |
834 | 833 | |
835 | 834 | |
836 |
def test_familyinfos_cell_with_placeholders(app, admin_user): |
|
837 |
Page.objects.all().delete() |
|
838 |
page = Page(title='Family', slug='index', template_name='standard') |
|
839 |
page.save() |
|
840 |
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0) |
|
841 |
family_cell.save() |
|
842 |
TextCell( |
|
843 |
page=page, |
|
844 |
placeholder='family_unlinked_user', |
|
845 |
text='<p>Hello anonymous user</p>', |
|
846 |
order=0, |
|
847 |
restricted_to_unlogged=True, |
|
848 |
public=True, |
|
849 |
).save() |
|
850 |
TextCell( |
|
851 |
page=page, |
|
852 |
placeholder='family_unlinked_user', |
|
853 |
text='<p>You are not linked</p>', |
|
854 |
order=1, |
|
855 |
public=False, |
|
856 |
restricted_to_unlogged=False, |
|
857 |
).save() |
|
858 | ||
859 |
with override_settings(FAMILY_SERVICE={'root': '/family/'}): |
|
860 |
with mock.patch('combo.utils.requests.send') as requests_send: |
|
861 |
resp = app.get('/') |
|
862 |
assert "<p>Hello anonymous user</p>" in resp.text |
|
863 |
assert "<p>You are not linked</p>" not in resp.text |
|
864 | ||
865 |
app = login(app) |
|
866 |
data = {'err': 0, 'data': None} |
|
867 |
requests_send.return_value = mock.Mock( |
|
868 |
json=lambda: data, content=json.dumps(data), status_code=200 |
|
869 |
) |
|
870 |
# make sure no data are loaded |
|
871 |
resp = app.get('/') |
|
872 |
assert resp.html.body.find('div', {'class': 'familyinfoscell'}).text.strip() == 'Loading...' |
|
873 |
# put data in cache |
|
874 |
resp = app.get( |
|
875 |
reverse( |
|
876 |
'combo-public-ajax-page-cell', |
|
877 |
kwargs={'page_pk': page.pk, 'cell_reference': family_cell.get_reference()}, |
|
878 |
) |
|
879 |
) |
|
880 |
resp = app.get('/') |
|
881 |
assert "<p>Hello anonymous user</p>" not in resp.text |
|
882 |
assert "<p>You are not linked</p>" in resp.text |
|
883 | ||
884 | ||
885 |
@mock.patch('combo.utils.requests.get') |
|
886 |
def test_familycell_link(requests_get, app, settings, admin_user): |
|
887 |
settings.FAMILY_SERVICE = {'root': '/family/'} |
|
888 |
Page.objects.all().delete() |
|
889 |
page = Page(title='Family', slug='index', template_name='standard') |
|
890 |
page.save() |
|
891 |
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0) |
|
892 |
family_cell.save() |
|
893 |
TextCell( |
|
894 |
page=page, |
|
895 |
placeholder='family_unlinked_user', |
|
896 |
text='<p>Hello anonymous user</p>', |
|
897 |
order=0, |
|
898 |
restricted_to_unlogged=True, |
|
899 |
public=True, |
|
900 |
).save() |
|
901 |
TextCell( |
|
902 |
page=page, |
|
903 |
placeholder='family_unlinked_user', |
|
904 |
text='<p>You are not linked</p>', |
|
905 |
order=1, |
|
906 |
public=False, |
|
907 |
restricted_to_unlogged=False, |
|
908 |
).save() |
|
909 |
app = login(app) |
|
910 |
# when linking fails |
|
911 |
requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102}) |
|
912 |
resp = app.get('/family/link/') |
|
913 |
resp.form['family_id'] = '123' |
|
914 |
resp.form['family_code'] = 'l33t' |
|
915 |
resp = resp.form.submit().follow() |
|
916 |
messages = resp.context['messages'] |
|
917 |
assert len(messages._loaded_messages) == 1 |
|
918 |
assert ( |
|
919 |
messages._loaded_messages[0].message |
|
920 |
== 'Failed to link to family. Please check your credentials and retry later.' |
|
921 |
) |
|
922 |
requests_get.return_value = mock.Mock(ok=True, json=lambda: {}) |
|
923 |
resp.form['family_id'] = '123' |
|
924 |
resp.form['family_code'] = 'l33t' |
|
925 |
resp = resp.form.submit().follow() |
|
926 |
messages = resp.context['messages'] |
|
927 |
assert len(messages._loaded_messages) == 2 |
|
928 |
assert messages._loaded_messages[1].message == 'Your account was successfully linked.' |
|
929 | ||
930 | ||
931 |
@mock.patch('combo.utils.requests.get') |
|
932 |
def test_familycell_unlink(requests_get, app, settings, admin_user): |
|
933 |
settings.FAMILY_SERVICE = {'root': '/family/'} |
|
934 |
Page.objects.all().delete() |
|
935 |
page = Page(title='Family', slug='index', template_name='standard') |
|
936 |
page.save() |
|
937 |
family_cell = FamilyInfosCell(page=page, placeholder='content', order=0) |
|
938 |
family_cell.save() |
|
939 |
TextCell( |
|
940 |
page=page, |
|
941 |
placeholder='family_unlinked_user', |
|
942 |
text='<p>Hello anonymous user</p>', |
|
943 |
order=0, |
|
944 |
restricted_to_unlogged=True, |
|
945 |
public=True, |
|
946 |
).save() |
|
947 |
TextCell( |
|
948 |
page=page, |
|
949 |
placeholder='family_unlinked_user', |
|
950 |
text='<p>You are not linked</p>', |
|
951 |
order=1, |
|
952 |
public=False, |
|
953 |
restricted_to_unlogged=False, |
|
954 |
).save() |
|
955 |
app = login(app) |
|
956 |
requests_get.return_value = mock.Mock(ok=False, json=lambda: {'err': 102}) |
|
957 |
resp = app.get('/family/unlink/') |
|
958 |
resp = resp.form.submit().follow() |
|
959 |
messages = resp.context['messages'] |
|
960 |
assert len(messages._loaded_messages) == 1 |
|
961 |
assert messages._loaded_messages[0].message == 'An error occured when unlinking.' |
|
962 |
requests_get.return_value = mock.Mock(ok=True, json=lambda: {}) |
|
963 |
resp = app.get('/family/unlink/') |
|
964 |
resp = resp.form.submit().follow() |
|
965 |
messages = resp.context['messages'] |
|
966 |
assert len(messages._loaded_messages) == 2 |
|
967 |
assert messages._loaded_messages[1].message == 'Your account was successfully unlinked.' |
|
968 | ||
969 | ||
970 | 835 |
def test_synchronous_placeholder(app): |
971 | 836 |
page = Page(title='foo', slug='foo', template_name='standard', order=0) |
972 | 837 |
page.save() |
tests/test_search.py | ||
---|---|---|
1371 | 1371 |
assert IndexedCell.objects.count() == 50 |
1372 | 1372 |
with CaptureQueriesContext(connection) as ctx: |
1373 | 1373 |
index_site() |
1374 |
assert len(ctx.captured_queries) == 223
|
|
1374 |
assert len(ctx.captured_queries) == 222
|
|
1375 | 1375 | |
1376 | 1376 |
SearchCell.objects.create( |
1377 | 1377 |
page=page, placeholder='content', order=0, _search_services={'data': ['search1']} |
1378 |
- |