0001-cells-display-invalidity-date-or-delay-43605.patch
combo/data/models.py | ||
---|---|---|
547 | 547 |
class Meta: |
548 | 548 |
unique_together = [('content_type', 'object_id')] |
549 | 549 | |
550 |
@property |
|
551 |
def invalid_datetime(self): |
|
552 |
if not self.invalid_since: |
|
553 |
return |
|
554 |
return self.invalid_since + datetime.timedelta(days=2) |
|
555 | ||
550 | 556 | |
551 | 557 |
class CellMeta(MediaDefiningClass, ModelBase): |
552 | 558 |
pass |
... | ... | |
917 | 923 |
def is_visible(self, user=None, check_validity_info=True): |
918 | 924 |
if check_validity_info: |
919 | 925 |
validity_info = self.get_validity_info() |
920 |
if validity_info is not None and validity_info.invalid_since and validity_info.invalid_since < now() - datetime.timedelta(days=2):
|
|
926 |
if validity_info is not None and validity_info.invalid_datetime and validity_info.invalid_datetime <= now():
|
|
921 | 927 |
return False |
922 | 928 |
return element_is_visible(self, user=user) |
923 | 929 |
combo/manager/templates/combo/page_view.html | ||
---|---|---|
150 | 150 |
<span class="extra-css-class">[{{ cell.extra_css_class }}]</span> |
151 | 151 |
{% endif %} |
152 | 152 |
<span class="additional-label"><i>{{cell.get_additional_label|default_if_none:""}}</i></span> |
153 |
{% with cell.get_invalid_reason as invalid_reason %} |
|
154 |
{% if invalid_reason %} |
|
155 |
<span class="invalid">{{ invalid_reason }}</span> |
|
153 |
{% if cell.get_invalid_reason %} |
|
154 |
<span class="invalid">{{ cell.get_invalid_reason }} - |
|
155 |
{% if cell.get_validity_info.invalid_datetime|date_in_past %} |
|
156 |
{% blocktrans with cell.get_validity_info.invalid_datetime|date:"DATETIME_FORMAT" as invalidity_date %}This cell is no longer displayed since {{ invalidity_date }}.{% endblocktrans %} |
|
157 |
{% else %} |
|
158 |
{% blocktrans with cell.get_validity_info.invalid_datetime|timeuntil as invalidity_delay %}This cell will no longer be displayed in {{ invalidity_delay }}.{% endblocktrans %} |
|
159 |
{% endif %} |
|
160 |
</span> |
|
156 | 161 |
{% endif %} |
157 |
{% endwith %} |
|
158 | 162 |
</span> |
159 | 163 |
{% if not cell.public %} |
160 | 164 |
<span class="visibility-summary |
combo/public/templatetags/combo.py | ||
---|---|---|
25 | 25 |
import time |
26 | 26 | |
27 | 27 |
from django import template |
28 |
from django.conf import settings |
|
29 | 28 |
from django.core import signing |
30 | 29 |
from django.core.exceptions import PermissionDenied |
31 | 30 |
from django.core.serializers.json import DjangoJSONEncoder |
... | ... | |
43 | 42 |
from django.utils.encoding import force_text |
44 | 43 |
from django.utils.html import format_html |
45 | 44 |
from django.utils.safestring import mark_safe |
45 |
from django.utils.timezone import now |
|
46 | 46 | |
47 | 47 |
from combo.data.models import Page, Placeholder |
48 | 48 |
from combo.public.menu import get_menu_context |
... | ... | |
450 | 450 |
return '' |
451 | 451 |
return years * 12 + months |
452 | 452 | |
453 | ||
454 |
@register.filter() |
|
455 |
def date_in_past(value): |
|
456 |
try: |
|
457 |
return value <= now() |
|
458 |
except TypeError: |
|
459 |
return False |
|
460 | ||
461 | ||
453 | 462 |
def parse_decimal(value, default=Decimal(0)): |
454 | 463 |
if isinstance(value, six.string_types): |
455 | 464 |
# replace , by . for French users comfort |
tests/test_cells.py | ||
---|---|---|
13 | 13 |
from django.conf import settings |
14 | 14 |
from django.db import connection |
15 | 15 |
from django.forms.widgets import Media |
16 |
from django.template import Context |
|
17 | 16 |
from django.test import override_settings |
18 | 17 |
from django.test.client import RequestFactory |
19 | 18 |
from django.test.utils import CaptureQueriesContext |
... | ... | |
1127 | 1126 |
assert cell.is_visible() is False |
1128 | 1127 | |
1129 | 1128 | |
1129 |
def test_cell_invalidity_marker(): |
|
1130 |
page = Page.objects.create() |
|
1131 |
cell = TextCell.objects.create(page=page, order=0) |
|
1132 |
cell.mark_as_invalid('foo_bar_reason') |
|
1133 |
validity_info = ValidityInfo.objects.latest('pk') |
|
1134 |
old_reason = validity_info.invalid_reason_code |
|
1135 |
old_date = validity_info.invalid_since |
|
1136 | ||
1137 |
cell.mark_as_invalid('another_foo_bar_reason', force=False) |
|
1138 |
validity_info.refresh_from_db() |
|
1139 |
assert old_reason == validity_info.invalid_reason_code |
|
1140 |
assert old_date == validity_info.invalid_since |
|
1141 | ||
1142 |
cell.mark_as_invalid('another_foo_bar_reason') |
|
1143 |
validity_info.refresh_from_db() |
|
1144 |
assert validity_info.invalid_reason_code == 'another_foo_bar_reason' |
|
1145 |
assert old_date < validity_info.invalid_since |
|
1146 | ||
1147 |
cell.mark_as_valid() |
|
1148 |
assert ValidityInfo.objects.exists() is False |
|
1149 | ||
1150 | ||
1130 | 1151 |
def test_hourly(): |
1131 | 1152 |
appconfig = apps.get_app_config('data') |
1132 | 1153 |
page = Page.objects.create(title='xxx', slug='test_current_forms_cell_render', template_name='standard') |
tests/test_manager.py | ||
---|---|---|
1 | 1 |
import base64 |
2 |
import json |
|
2 |
import datetime |
|
3 |
import mock |
|
3 | 4 |
import os |
4 | 5 |
import re |
5 | 6 |
import shutil |
6 | 7 | |
7 |
import mock |
|
8 | ||
9 | 8 |
from django.core.files.storage import default_storage |
10 | 9 |
from django.urls import reverse |
11 | 10 |
from django.conf import settings |
12 |
from django.contrib.auth.models import User, Group
|
|
11 |
from django.contrib.auth.models import Group |
|
13 | 12 |
from django.db import connection |
14 | 13 |
from django.template import TemplateSyntaxError |
15 | 14 |
from django.test import override_settings |
16 | 15 |
from django.test.client import RequestFactory |
17 | 16 |
from django.test.utils import CaptureQueriesContext |
18 | 17 |
from django.utils.http import urlencode |
18 |
from django.utils.timezone import now |
|
19 | 19 |
from django.utils.six import BytesIO |
20 | 20 |
from django.utils.six.moves.urllib import parse as urlparse |
21 | 21 | |
22 | 22 |
import pytest |
23 |
from webtest import TestApp |
|
24 | 23 |
from webtest import Upload |
25 | 24 | |
26 |
from combo.wsgi import application |
|
27 | 25 |
from combo.data.forms import LinkCellForm |
28 | 26 |
from combo.data.models import ( |
29 | 27 |
Page, CellBase, TextCell, LinkCell, ConfigJsonCell, JsonCell, PageSnapshot, |
... | ... | |
169 | 167 |
assert Page.objects.all()[0].exclude_from_navigation is False |
170 | 168 | |
171 | 169 | |
172 |
def test_edit_page_cell_invalid_placeholder(app, admin_user): |
|
170 |
def test_edit_page_cell_invalid_placeholder(freezer, app, admin_user): |
|
171 |
freezer.move_to('2020-06-05 12:00:01') |
|
173 | 172 |
page = Page.objects.create(title='One', slug='one', template_name='standard') |
174 | 173 |
cell = TextCell.objects.create(page=page, placeholder='content', text='Foobar', order=1) |
175 | 174 |
cell.mark_as_invalid('foo_bar_reason') |
176 |
validity_info = ValidityInfo.objects.latest('pk') |
|
177 |
old_reason = validity_info.invalid_reason_code |
|
178 |
old_date = validity_info.invalid_since |
|
179 | 175 | |
180 | 176 |
app = login(app) |
177 |
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=1)) |
|
181 | 178 |
resp = app.get('/manage/pages/%s/' % page.pk) |
182 |
assert '<span class="invalid">foo_bar_reason</span>' in resp.text |
|
179 |
assert '<span class="invalid">foo_bar_reason' in resp.text |
|
180 |
assert 'This cell will no longer be displayed in 1 day, 23 hours' in resp.text |
|
181 | ||
182 |
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=47*60-1)) |
|
183 |
resp = app.get('/manage/pages/%s/' % page.pk) |
|
184 |
assert '<span class="invalid">foo_bar_reason' in resp.text |
|
185 |
assert 'This cell will no longer be displayed in 1 hour, 1 minute.' in resp.text |
|
183 | 186 | |
184 |
cell.mark_as_invalid('another_foo_bar_reason', force=False)
|
|
185 |
validity_info.refresh_from_db()
|
|
186 |
assert old_reason == validity_info.invalid_reason_code
|
|
187 |
assert old_date == validity_info.invalid_since
|
|
187 |
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(minutes=47*60+29))
|
|
188 |
resp = app.get('/manage/pages/%s/' % page.pk)
|
|
189 |
assert '<span class="invalid">foo_bar_reason' in resp.text
|
|
190 |
assert 'This cell will no longer be displayed in 31 minutes.' in resp.text
|
|
188 | 191 | |
189 |
cell.mark_as_invalid('another_foo_bar_reason') |
|
190 |
validity_info.refresh_from_db() |
|
191 |
assert validity_info.invalid_reason_code == 'another_foo_bar_reason' |
|
192 |
assert old_date < validity_info.invalid_since |
|
192 |
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(days=2)) |
|
193 |
resp = app.get('/manage/pages/%s/' % page.pk) |
|
194 |
assert '<span class="invalid">foo_bar_reason' in resp.text |
|
195 |
assert 'This cell is no longer displayed since June 5, 2020, noon.' in resp.text |
|
196 | ||
197 |
ValidityInfo.objects.update(invalid_since=now() - datetime.timedelta(days=2, minutes=10*60+2)) |
|
198 |
resp = app.get('/manage/pages/%s/' % page.pk) |
|
199 |
assert '<span class="invalid">foo_bar_reason' in resp.text |
|
200 |
assert 'This cell is no longer displayed since June 5, 2020, 1:58 a.m.' in resp.text |
|
193 | 201 | |
194 | 202 |
cell.mark_as_valid() |
195 | 203 |
assert ValidityInfo.objects.exists() is False |
196 | 204 |
resp = app.get('/manage/pages/%s/' % page.pk) |
197 |
assert '<span class="invalid">foo_bar_reason</span>' not in resp.text
|
|
205 |
assert '<span class="invalid">foo_bar_reason' not in resp.text |
|
198 | 206 | |
199 | 207 |
cell2 = LinkListCell.objects.create(order=0, placeholder='content', page=page) |
200 | 208 |
item = LinkCell.objects.create(page=page, placeholder=cell2.link_placeholder, order=0) |
201 | 209 |
item.mark_as_invalid('foo_bar_reason') |
202 | 210 |
cell2.check_validity() |
203 | 211 |
resp = app.get('/manage/pages/%s/' % page.pk) |
204 |
assert '<span class="invalid">Invalid link</span>' in resp.text |
|
212 |
assert '<span class="invalid">Invalid link' in resp.text |
|
213 |
assert 'This cell will no longer be displayed in 2 days.' in resp.text |
|
205 | 214 |
assert '<span class="invalid">foo_bar_reason</span>' in resp.text |
206 | 215 | |
207 | 216 | |
208 |
- |