0001-assets-add-generic-assets-for-cells-40223.patch
combo/apps/assets/templatetags/assets.py | ||
---|---|---|
62 | 62 | |
63 | 63 |
return get_thumbnail(asset, geometry_string, **kwargs).url |
64 | 64 | |
65 | ||
65 | 66 |
@register.simple_tag |
66 | 67 |
def asset_css_url(*args, **kwargs): |
67 | 68 |
url = asset_url(*args, **kwargs) |
... | ... | |
72 | 73 | |
73 | 74 | |
74 | 75 |
@register.simple_tag |
75 |
def get_asset(key): |
|
76 |
def get_asset(*args, **kwargs): |
|
77 |
key = None |
|
78 |
if 'cell' in kwargs and 'type' in kwargs: |
|
79 |
try: |
|
80 |
if not kwargs['cell'].can_have_assets(): |
|
81 |
return None |
|
82 |
key = kwargs['cell'].get_asset_slot_key(kwargs['type']) |
|
83 |
except AttributeError: |
|
84 |
return None |
|
85 |
elif len(args) == 1: |
|
86 |
key = args[0] |
|
87 | ||
88 |
if not key: |
|
89 |
return None |
|
90 | ||
76 | 91 |
try: |
77 | 92 |
return Asset.objects.get(key=key) |
78 | 93 |
except Asset.DoesNotExist: |
combo/apps/assets/views.py | ||
---|---|---|
103 | 103 |
assets = dict([(x.key, x) for x in Asset.objects.all()]) |
104 | 104 |
uniq_slots = {} |
105 | 105 |
uniq_slots.update(settings.COMBO_ASSET_SLOTS) |
106 |
for cell in CellBase.get_cells( |
|
107 |
cell_filter=lambda x: bool(x.get_asset_slots)): |
|
106 |
cells = CellBase.get_cells(select_related={ |
|
107 |
'__all__': ['page'], |
|
108 |
'data_linkcell': ['link_page']}) |
|
109 |
for cell in cells: |
|
108 | 110 |
uniq_slots.update(cell.get_asset_slots()) |
109 | 111 |
for key, value in uniq_slots.items(): |
110 | 112 |
yield cls(key, |
combo/data/models.py | ||
---|---|---|
577 | 577 |
# get_badge(self, context); set to None so cell types can be skipped easily |
578 | 578 |
get_badge = None |
579 | 579 | |
580 |
# get_asset_slots(self); set to None so cell types can be skipped easily |
|
581 |
get_asset_slots = None |
|
582 | ||
583 | 580 |
# message displayed when the cell is loaded asynchronously |
584 | 581 |
loading_message = _('Loading...') |
585 | 582 | |
... | ... | |
623 | 620 |
def css_class_names(self): |
624 | 621 |
return ' '.join([self.class_name, self.legacy_class_name, self.extra_css_class]) |
625 | 622 | |
623 |
def can_have_assets(self): |
|
624 |
return self.get_slug_for_asset() and self.get_asset_slot_templates() |
|
625 | ||
626 |
def get_slug_for_asset(self): |
|
627 |
return self.slug |
|
628 | ||
629 |
def get_label_for_asset(self): |
|
630 |
return _(u'%(cell_label)s on page %(page_name)s (%(page_slug)s)') % { |
|
631 |
'cell_label': str(self), |
|
632 |
'page_name': str(self.page), |
|
633 |
'page_slug': self.page.slug, |
|
634 |
} |
|
635 | ||
636 |
def get_asset_slot_key(self, key): |
|
637 |
return 'cell:%s:%s:%s' % ( |
|
638 |
self.get_cell_type_str(), |
|
639 |
key, |
|
640 |
self.get_slug_for_asset()) |
|
641 | ||
642 |
def get_asset_slot_templates(self): |
|
643 |
return settings.COMBO_CELL_ASSET_SLOTS.get(self.get_cell_type_str()) or {} |
|
644 | ||
645 |
def get_asset_slots(self): |
|
646 |
if not self.can_have_assets(): |
|
647 |
return {} |
|
648 | ||
649 |
slot_templates = self.get_asset_slot_templates() |
|
650 |
slots = {} |
|
651 |
for slot_template_key, slot_template_data in slot_templates.items(): |
|
652 |
suffix = '' |
|
653 |
if slot_template_data.get('suffix'): |
|
654 |
suffix = ' (%s)' % slot_template_data['suffix'] |
|
655 |
slot_key = self.get_asset_slot_key(slot_template_key) |
|
656 |
label = u'%(prefix)s — %(label)s%(suffix)s' % { |
|
657 |
'prefix': slot_template_data['prefix'], |
|
658 |
'label': self.get_label_for_asset(), |
|
659 |
'suffix': suffix |
|
660 |
} |
|
661 |
slots[slot_key] = { |
|
662 |
'label': label |
|
663 |
} |
|
664 |
slots[slot_key].update(slot_template_data) |
|
665 |
return slots |
|
666 | ||
626 | 667 |
@classmethod |
627 | 668 |
def get_cell_classes(cls, class_filter=lambda x: True): |
628 | 669 |
for klass in get_cell_classes(): |
... | ... | |
673 | 714 |
continue |
674 | 715 |
cells_queryset = klass.objects.filter(**kwargs) |
675 | 716 |
if select_related: |
676 |
cells_queryset = cells_queryset.select_related(*select_related) |
|
717 |
cells_queryset = cells_queryset.select_related( |
|
718 |
*select_related.get('__all__', []), |
|
719 |
*select_related.get(klass.get_cell_type_str(), [])) |
|
677 | 720 |
cells.extend(cells_queryset) |
678 | 721 |
if prefetch_validity_info: |
679 | 722 |
validity_info_list = list(ValidityInfo.objects.select_related('content_type')) |
... | ... | |
1059 | 1102 |
return None |
1060 | 1103 |
return utils.ellipsize(title) |
1061 | 1104 | |
1105 |
def get_slug_for_asset(self): |
|
1106 |
if self.placeholder and self.placeholder.startswith('_'): |
|
1107 |
return |
|
1108 |
if self.link_page: |
|
1109 |
return self.link_page.slug |
|
1110 |
return self.slug |
|
1111 | ||
1112 |
def get_label_for_asset(self): |
|
1113 |
if self.link_page: |
|
1114 |
return str(self) |
|
1115 |
return super().get_label_for_asset() |
|
1116 | ||
1062 | 1117 |
def get_url(self, context=None): |
1063 | 1118 |
context = context or {} |
1064 | 1119 |
if self.link_page: |
combo/manager/views.py | ||
---|---|---|
95 | 95 | |
96 | 96 |
def invalid_cell_report(request): |
97 | 97 |
invalid_cells = CellBase.get_cells( |
98 |
select_related=['page'],
|
|
98 |
select_related={'__all__': ['page']},
|
|
99 | 99 |
page__snapshot__isnull=True, |
100 | 100 |
validity_info__invalid_since__isnull=False) |
101 | 101 |
invalid_cells = [c for c in invalid_cells if c.placeholder and not c.placeholder.startswith('_')] |
combo/public/templates/combo/asset_picture_fragment.html | ||
---|---|---|
1 |
{% load assets %} |
|
2 |
{% get_asset cell=cell type='picture' as asset %} |
|
3 |
{% if asset %} |
|
4 |
<picture> |
|
5 |
<img src="{% asset_url asset size="660x360" crop="center" upscale=False %}" alt=""> |
|
6 |
</picture> |
|
7 |
{% endif %} |
combo/public/templates/combo/feed-cell.html | ||
---|---|---|
3 | 3 |
{% if cell.title %} |
4 | 4 |
<h2>{{ cell.title }}</h2> |
5 | 5 |
{% endif %} |
6 |
{% include "combo/asset_picture_fragment.html" %} |
|
6 | 7 |
<div class="feed-content"> |
7 | 8 |
{% for entry in feed.entries %} |
8 | 9 |
{% if entry.link %} |
combo/public/templates/combo/json-cell.html | ||
---|---|---|
1 | 1 |
{% block cell-content %} |
2 | 2 |
{% if title %}<h2>{{title}}</h2>{% endif %} |
3 |
{% include "combo/asset_picture_fragment.html" %} |
|
3 | 4 |
<!-- |
4 | 5 |
{{json}} |
5 | 6 |
--> |
combo/public/templates/combo/json-list-cell.html | ||
---|---|---|
1 | 1 |
{% block cell-content %} |
2 | 2 |
<div class="links-list"> |
3 | 3 |
{% if title %}<h2>{{title}}</h2>{% endif %} |
4 |
{% include "combo/asset_picture_fragment.html" %} |
|
4 | 5 |
{% for row in json.data %} |
5 | 6 |
<ul> |
6 | 7 |
<li><a href="{{row.url}}">{{row.text}}</a></li> |
combo/public/templates/combo/link-cell.html | ||
---|---|---|
1 | 1 |
{% block cell-content %} |
2 |
{% include "combo/asset_picture_fragment.html" %} |
|
2 | 3 |
<a href="{{url}}">{{title}}</a> |
3 | 4 |
{% endblock %} |
combo/public/templates/combo/link-list-cell.html | ||
---|---|---|
2 | 2 |
{% spaceless %} |
3 | 3 |
<div class="links-list"> |
4 | 4 |
{% if title %}<h2>{{title}}</h2>{% endif %} |
5 |
{% include "combo/asset_picture_fragment.html" %} |
|
5 | 6 |
<ul> |
6 | 7 |
{% for link in links %} |
7 | 8 |
<li><a href="{{ link.url }}">{{ link.title }}</a></li> |
combo/public/templates/combo/text-cell.html | ||
---|---|---|
1 | 1 |
{% block cell-content %} |
2 |
{% include "combo/asset_picture_fragment.html" %} |
|
2 | 3 |
{{text}} |
3 | 4 |
{% endblock %} |
combo/public/views.py | ||
---|---|---|
499 | 499 | |
500 | 500 |
return publish_page(request, page) |
501 | 501 | |
502 | ||
502 | 503 |
def publish_page(request, page, status=200, template_name=None): |
503 | 504 |
pages = page.get_parents_and_self() |
504 | 505 | |
... | ... | |
518 | 519 |
if redirect_url: |
519 | 520 |
return HttpResponseRedirect(redirect_url) |
520 | 521 | |
521 |
cells = CellBase.get_cells(page=page, prefetch_validity_info=True) |
|
522 |
cells = CellBase.get_cells( |
|
523 |
page=page, |
|
524 |
select_related={'data_linkcell': ['link_page']}, |
|
525 |
prefetch_validity_info=True) |
|
522 | 526 |
extend_with_parent_cells(cells, hierarchy=pages) |
523 | 527 |
cells = [x for x in cells if x.is_visible(user=request.user)] |
524 | 528 |
mark_duplicated_slugs(cells) |
combo/settings.py | ||
---|---|---|
324 | 324 | |
325 | 325 |
WCS_FORM_ASSET_SLOTS = {} |
326 | 326 | |
327 |
COMBO_CELL_ASSET_SLOTS = { |
|
328 |
'data_feedcell': { |
|
329 |
'picture': { |
|
330 |
'prefix': _('Picture'), |
|
331 |
}, |
|
332 |
}, |
|
333 |
'data_jsoncell': { |
|
334 |
'picture': { |
|
335 |
'prefix': _('Picture'), |
|
336 |
}, |
|
337 |
}, |
|
338 |
'data_linkcell': { |
|
339 |
'picture': { |
|
340 |
'prefix': _('Picture'), |
|
341 |
}, |
|
342 |
}, |
|
343 |
'data_linklistcell': { |
|
344 |
'picture': { |
|
345 |
'prefix': _('Picture'), |
|
346 |
}, |
|
347 |
}, |
|
348 |
'data_textcell': { |
|
349 |
'picture': { |
|
350 |
'prefix': _('Picture'), |
|
351 |
}, |
|
352 |
}, |
|
353 |
} |
|
354 | ||
327 | 355 |
# known services |
328 | 356 |
KNOWN_SERVICES = {} |
329 | 357 |
tests/test_cells.py | ||
---|---|---|
1051 | 1051 |
assert check_validity.call_args_list == [mock.call()] |
1052 | 1052 |
else: |
1053 | 1053 |
assert hasattr(klass, 'check_validity') is False |
1054 | ||
1055 | ||
1056 |
def test_cell_assets(settings, app, admin_user): |
|
1057 |
page = Page.objects.create(title='xxx', slug='test_cell_assets', template_name='standard') |
|
1058 |
text_cell = TextCell.objects.create(page=page, order=0, slug='foo') |
|
1059 |
list_cell = LinkListCell.objects.create(page=page, order=2, slug='bar') |
|
1060 |
item = LinkCell.objects.create( |
|
1061 |
page=page, |
|
1062 |
placeholder=list_cell.link_placeholder, |
|
1063 |
title='Example Site', |
|
1064 |
link_page=page, |
|
1065 |
order=1, |
|
1066 |
) |
|
1067 | ||
1068 |
app = login(app) |
|
1069 |
settings.COMBO_CELL_ASSET_SLOTS = {} |
|
1070 |
resp = app.get('/manage/assets/') |
|
1071 |
assert 'have any asset yet.' in resp.text |
|
1072 | ||
1073 |
# only text cells are defined for assets |
|
1074 |
settings.COMBO_CELL_ASSET_SLOTS = {'data_textcell': {'picture': {'prefix': 'Picture'}}} |
|
1075 |
resp = app.get('/manage/assets/') |
|
1076 |
assert u'Picture — %s' % text_cell.get_label_for_asset() in resp.text |
|
1077 |
assert u'Picture — %s' % list_cell.get_label_for_asset() not in resp.text |
|
1078 |
assert u'Picture — %s' % item.get_label_for_asset() not in resp.text |
|
1079 | ||
1080 |
# text and list link cells are defined for assets |
|
1081 |
settings.COMBO_CELL_ASSET_SLOTS = { |
|
1082 |
'data_textcell': {'picture': {'prefix': 'Picture'}}, |
|
1083 |
'data_linklistcell': {'picture': {'prefix': 'Picture'}}, |
|
1084 |
'data_linkcell': {'picture': {'prefix': 'Picture', 'suffix': 'test'}}, |
|
1085 |
} |
|
1086 |
resp = app.get('/manage/assets/') |
|
1087 |
assert u'Picture — %s' % text_cell.get_label_for_asset() in resp.text |
|
1088 |
assert u'Picture — %s' % list_cell.get_label_for_asset() in resp.text |
|
1089 |
# but items are excluded |
|
1090 |
assert item.get_slug_for_asset() is None # slug for asset is always None for items |
|
1091 |
assert u'Picture — %s' % item.get_label_for_asset() not in resp.text |
|
1092 | ||
1093 |
# test slugs |
|
1094 |
link_cell = LinkCell.objects.create( |
|
1095 |
page=page, |
|
1096 |
url='http://example.net/', |
|
1097 |
order=1, |
|
1098 |
) |
|
1099 |
resp = app.get('/manage/assets/') |
|
1100 |
assert link_cell.get_slug_for_asset() == '' |
|
1101 |
assert u'Picture — %s' % link_cell.get_label_for_asset() not in resp.text |
|
1102 | ||
1103 |
link_cell.slug = 'foo' |
|
1104 |
link_cell.save() |
|
1105 |
resp = app.get('/manage/assets/') |
|
1106 |
assert link_cell.get_slug_for_asset() == 'foo' |
|
1107 |
assert u'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text |
|
1108 | ||
1109 |
link_cell.slug = '' |
|
1110 |
link_cell.url = '' |
|
1111 |
link_cell.link_page = page |
|
1112 |
link_cell.save() |
|
1113 |
resp = app.get('/manage/assets/') |
|
1114 |
assert link_cell.get_slug_for_asset() == 'test_cell_assets' |
|
1115 |
assert u'Picture — %s (test)' % link_cell.get_label_for_asset() in resp.text |
tests/test_manager.py | ||
---|---|---|
352 | 352 |
assert '<li class="nav-right"' not in resp.text |
353 | 353 | |
354 | 354 | |
355 |
def test_edit_page_num_queries(app, admin_user): |
|
355 |
def test_edit_page_num_queries(settings, app, admin_user): |
|
356 |
settings.COMBO_CELL_ASSET_SLOTS = {} |
|
356 | 357 |
page = Page.objects.create(title='One', slug='one', parent=None, template_name='standard') |
357 | 358 |
page2 = Page.objects.create(title='Two', slug='two', parent=page, template_name='standard') |
358 | 359 |
MenuCell.objects.create(page=page, order=0) |
tests/test_public_templatetags.py | ||
---|---|---|
13 | 13 |
from django.contrib.auth.models import User, Group, AnonymousUser |
14 | 14 |
from django.utils.six import StringIO |
15 | 15 | |
16 |
from combo.data.models import Page |
|
16 |
from combo.data.models import Page, TextCell
|
|
17 | 17 |
from combo.apps.assets.models import Asset |
18 | 18 | |
19 | 19 |
pytestmark = pytest.mark.django_db |
... | ... | |
141 | 141 |
'{% for c in country_list|get_group:"USA" %}{{c.name}},{% endfor %}') |
142 | 142 |
assert t.render(context) == 'New York,Chicago,' |
143 | 143 | |
144 | ||
144 | 145 |
def test_asset_template_tags(): |
145 | 146 |
for path in ('uploads', 'assets'): |
146 | 147 |
if os.path.exists(default_storage.path(path)): |
... | ... | |
194 | 195 |
t = Template('''{% load assets %}{% asset_url page.picture "collectivity:banner" size="200x200" %}''') |
195 | 196 |
assert t.render(Context({'page': page})) == '/media/page-pictures/test2.svg' |
196 | 197 | |
198 |
cell = TextCell() |
|
199 |
with override_settings(COMBO_CELL_ASSET_SLOTS={'data_textcell': {'picture': {'prefix': 'Picture'}}}): |
|
200 |
# no slug |
|
201 |
t = Template('''{% load assets %}{% get_asset cell=cell type='picture' as banner %}{% if banner %}BANNER{% endif %}''') |
|
202 |
assert t.render(Context({'cell': cell})) == '' |
|
203 | ||
204 |
# no asset |
|
205 |
cell.slug = 'foobar' |
|
206 |
t = Template('''{% load assets %}{% get_asset cell=cell type='picture' as banner %}{% if banner %}BANNER{% endif %}''') |
|
207 |
assert t.render(Context({'cell': cell})) == '' |
|
208 | ||
209 |
# ok |
|
210 |
Asset.objects.create(key=cell.get_asset_slot_key('picture'), asset=File(StringIO('test'), 'test.png')) |
|
211 |
t = Template('''{% load assets %}{% get_asset cell=cell type='picture' as banner %}{% if banner %}BANNER{% endif %}''') |
|
212 |
assert t.render(Context({'cell': cell})) == 'BANNER' |
|
213 | ||
214 |
# no context: AttributeError |
|
215 |
t = Template('''{% load assets %}{% get_asset cell=cell type='picture' as banner %}{% if banner %}BANNER{% endif %}''') |
|
216 |
assert t.render(Context()) == '' |
|
217 | ||
218 |
with override_settings(COMBO_CELL_ASSET_SLOTS={}): |
|
219 |
# cell type not defined in COMBO_CELL_ASSET_SLOTS |
|
220 |
t = Template('''{% load assets %}{% get_asset cell=cell type='picture' as banner %}{% if banner %}BANNER{% endif %}''') |
|
221 |
assert t.render(Context({'cell': cell})) == '' |
|
222 | ||
223 | ||
197 | 224 |
def test_startswith(): |
198 | 225 |
t = Template('{% if foo|startswith:"bar" %}ok{% endif %}') |
199 | 226 |
context = Context({'foo': None}) |
200 |
- |