0001-general-simplify-acquisition-of-parent-content-11110.patch
README | ||
---|---|---|
57 | 57 |
('order' attribute). |
58 | 58 | |
59 | 59 |
A placeholder can be marked as 'acquired' (see "footer" in the example above), |
60 |
this makes its cells automatically inherited from a parent page; the presence |
|
61 |
of a cell of type 'Unlock Marker' will stop this acquisition mechanism, this |
|
62 |
makes it possible to have a different content for those placeholders in |
|
63 |
specific pages or sections have the site. |
|
64 | ||
65 |
Note: in the case of placeholder acquisition the site index page will be |
|
66 |
inserted as the top of the page hierarchy. |
|
60 |
this way a cell of "same as parent" type will automatically be added. |
|
67 | 61 | |
68 | 62 | |
69 | 63 |
Settings |
combo/data/migrations/0018_parentcontentcell.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('auth', '0006_require_contenttypes_0002'), |
|
11 |
('data', '0017_menucell_root_page'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.CreateModel( |
|
16 |
name='ParentContentCell', |
|
17 |
fields=[ |
|
18 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
19 |
('placeholder', models.CharField(max_length=20)), |
|
20 |
('order', models.PositiveIntegerField()), |
|
21 |
('slug', models.SlugField(verbose_name='Slug', blank=True)), |
|
22 |
('public', models.BooleanField(default=True, verbose_name='Public')), |
|
23 |
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')), |
|
24 |
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)), |
|
25 |
('page', models.ForeignKey(to='data.Page')), |
|
26 |
], |
|
27 |
options={ |
|
28 |
'verbose_name': 'Parent Content', |
|
29 |
}, |
|
30 |
), |
|
31 |
] |
combo/data/migrations/0019_create_parent_cells.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.conf import settings |
|
5 |
from django.db import migrations, models |
|
6 | ||
7 |
def create_parent_content_cells(apps, schema_editor): |
|
8 |
Page = apps.get_model('data', 'Page') |
|
9 |
UnlockMarkerCell = apps.get_model('data', 'UnlockMarkerCell') |
|
10 |
ParentContentCell = apps.get_model('data', 'ParentContentCell') |
|
11 |
for page in Page.objects.all(): |
|
12 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(page.template_name) |
|
13 |
for placeholder_key, placeholder_value in combo_template['placeholders'].items(): |
|
14 |
if not placeholder_value.get('acquired'): |
|
15 |
continue |
|
16 |
is_unlocked = UnlockMarkerCell.objects.filter(page=page, |
|
17 |
placeholder=placeholder_key).count() |
|
18 |
if is_unlocked: |
|
19 |
continue |
|
20 |
# not unlocked -> add a new ParentContentCell |
|
21 |
cell, created = ParentContentCell.objects.get_or_create( |
|
22 |
page=page, placeholder=placeholder_key, order=0) |
|
23 |
if created: |
|
24 |
cell.save() |
|
25 | ||
26 | ||
27 |
class Migration(migrations.Migration): |
|
28 | ||
29 |
dependencies = [ |
|
30 |
('data', '0018_parentcontentcell'), |
|
31 |
] |
|
32 | ||
33 |
operations = [ |
|
34 |
migrations.RunPython(create_parent_content_cells), |
|
35 |
] |
combo/data/models.py | ||
---|---|---|
176 | 176 |
fill_list(object_list) |
177 | 177 |
return reordered |
178 | 178 | |
179 |
def get_unlocked_placeholders(self, cells=None): |
|
180 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(self.template_name) |
|
181 |
if self.slug == 'index': |
|
182 |
# on the site index page, there are no unlocked placeholder. |
|
183 |
return combo_template['placeholders'].keys() |
|
184 | ||
185 |
if cells is None: |
|
186 |
cells = CellBase.get_cells(page_id=self.id) |
|
187 | ||
188 |
# on the other page sites, look for unlock markers |
|
189 |
unlocked_placeholders = [] |
|
190 |
for cell in cells: |
|
191 |
if not isinstance(cell, UnlockMarkerCell): |
|
192 |
continue |
|
193 |
if cell.page_id == self.id: |
|
194 |
unlocked_placeholders.append(cell.placeholder) |
|
195 |
return unlocked_placeholders |
|
196 | ||
197 |
def get_locked_placeholders(self, cells=None): |
|
198 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(self.template_name) |
|
199 |
lockable_placeholders = [x for x in combo_template['placeholders'] if ( |
|
200 |
combo_template['placeholders'].get(x).get('acquired'))] |
|
201 |
unlocked_placeholders = self.get_unlocked_placeholders(cells) |
|
202 |
locked_placeholders = set(lockable_placeholders) - set(unlocked_placeholders) |
|
203 |
return list(locked_placeholders) |
|
204 | ||
205 | 179 |
def visibility(self): |
206 | 180 |
if self.public: |
207 | 181 |
return _('Public') |
... | ... | |
481 | 455 | |
482 | 456 |
@register_cell_class |
483 | 457 |
class UnlockMarkerCell(CellBase): |
458 |
# XXX: this is kept to smooth transitions, it should be removed once all |
|
459 |
# sites # have been migrated to ParentContentCell |
|
484 | 460 |
"""Marks an 'acquired' placeholder as unlocked.""" |
485 | 461 |
visible = False |
486 | 462 | |
... | ... | |
715 | 691 |
def get_default_form_class(self): |
716 | 692 |
from .forms import ParametersCellForm |
717 | 693 |
return ParametersCellForm |
694 | ||
695 | ||
696 |
@register_cell_class |
|
697 |
class ParentContentCell(CellBase): |
|
698 |
class Meta: |
|
699 |
verbose_name = _('Same as parent') |
|
700 | ||
701 |
def get_cells(self): |
|
702 |
if self.page.parent: |
|
703 |
parent_page = self.page.parent |
|
704 |
elif self.page.slug != 'index': |
|
705 |
parent_page = Page.objects.get(slug='index', parent=None) |
|
706 |
else: |
|
707 |
return [] |
|
708 |
cells = CellBase.get_cells(placeholder=self.placeholder, page=parent_page) |
|
709 |
for i, cell in enumerate(cells): |
|
710 |
if not isinstance(cell, ParentContentCell): |
|
711 |
continue |
|
712 |
cells[i:i+1] = cell.get_cells() |
|
713 |
break |
|
714 |
return cells |
|
715 | ||
716 |
def render(self, context): |
|
717 |
return '' |
combo/manager/templates/combo/page_view.html | ||
---|---|---|
63 | 63 | |
64 | 64 |
</div> |
65 | 65 | |
66 |
{% if extra_placeholders %} |
|
67 |
<div id="extra-placeholders"> |
|
68 |
<h2>Extra blocks</h2> |
|
69 |
<ul> |
|
70 |
{% for placeholder in extra_placeholders %} |
|
71 |
<li><a href="{% url 'combo-manager-page-add-cell' page_pk=object.id cell_type=unlock.cell_type_str variant=unlock.variant ph_key=placeholder.key %}">{{ placeholder.name }}</a></li> |
|
72 |
{% endfor %} |
|
73 |
</ul> |
|
74 |
</div> |
|
75 |
{% endif %} |
|
76 | ||
77 | 66 |
</div> <!-- #sidebar --> |
78 | 67 | |
79 | 68 |
<div id="page-content"> |
combo/manager/views.py | ||
---|---|---|
31 | 31 |
from django.views.generic import (TemplateView, RedirectView, DetailView, |
32 | 32 |
CreateView, UpdateView, ListView, DeleteView, FormView) |
33 | 33 | |
34 |
from combo.data.models import Page, CellBase, UnlockMarkerCell
|
|
34 |
from combo.data.models import Page, CellBase, ParentContentCell
|
|
35 | 35 |
from combo.data.library import get_cell_class |
36 | 36 |
from combo import plugins |
37 | 37 | |
... | ... | |
89 | 89 |
initial['title'] = _('Home') |
90 | 90 |
return initial |
91 | 91 | |
92 |
def form_valid(self, form): |
|
93 |
response = super(PageAddView, self).form_valid(form) |
|
94 |
if self.object.slug != 'index' or self.object.parent_id: |
|
95 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(self.object.template_name) |
|
96 |
for placeholder_key, placeholder in combo_template['placeholders'].items(): |
|
97 |
if placeholder.get('acquired') is True: |
|
98 |
ParentContentCell(page=self.object, placeholder=placeholder_key, order=0).save() |
|
99 | ||
100 |
return response |
|
101 | ||
92 | 102 |
def get_success_url(self): |
93 | 103 |
return reverse('combo-manager-page-view', kwargs={'pk': self.object.id}) |
94 | 104 | |
... | ... | |
161 | 171 |
cells = CellBase.get_cells(page_id=self.object.id) |
162 | 172 |
template = self.object.template_name |
163 | 173 |
placeholders = [] |
164 |
extra_placeholders = [] |
|
165 | 174 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(template) |
166 | 175 | |
167 |
unlocked_placeholders = self.object.get_unlocked_placeholders(cells) |
|
168 | ||
169 | 176 |
for placeholder_key, placeholder in combo_template['placeholders'].items(): |
170 | 177 |
placeholder_dict = { |
171 | 178 |
'key': placeholder_key, |
172 | 179 |
'name': placeholder['name'], |
173 | 180 |
} |
174 |
if placeholder.get('acquired') and not placeholder_key in unlocked_placeholders: |
|
175 |
extra_placeholders.append(placeholder_dict) |
|
176 |
else: |
|
177 |
placeholder_dict['cells'] = [x for x in cells if ( |
|
178 |
x.placeholder == placeholder_key)] |
|
179 |
placeholders.append(placeholder_dict) |
|
181 |
placeholder_dict['cells'] = [x for x in cells if ( |
|
182 |
x.placeholder == placeholder_key)] |
|
183 |
placeholders.append(placeholder_dict) |
|
180 | 184 | |
181 |
context['unlock'] = UnlockMarkerCell.get_cell_types()[0] |
|
182 | 185 |
context['placeholders'] = placeholders |
183 |
context['extra_placeholders'] = extra_placeholders |
|
184 | 186 |
return context |
185 | 187 | |
186 | 188 |
page_view = requires_csrf_token(PageView.as_view()) |
combo/public/views.py | ||
---|---|---|
35 | 35 |
except ImportError: |
36 | 36 |
get_idps = lambda: [] |
37 | 37 | |
38 |
from combo.data.models import CellBase, Page |
|
38 |
from combo.data.models import CellBase, Page, ParentContentCell
|
|
39 | 39 | |
40 | 40 | |
41 | 41 |
def login(request, *args, **kwargs): |
... | ... | |
96 | 96 |
return HttpResponse(cell.render(context), content_type='text/html') |
97 | 97 | |
98 | 98 | |
99 |
def extend_with_locked_placeholders_cells(cells, page, pages): |
|
100 |
locked_placeholders = page.get_locked_placeholders(cells) |
|
101 | ||
102 |
if locked_placeholders: |
|
103 |
# there are some acquired placeholders, look in parent pages for |
|
104 |
# appropriate content. |
|
105 |
try: |
|
106 |
# add the site index page as ultimate parent |
|
107 |
if pages[0].slug != 'index': |
|
108 |
pages.insert(0, Page.objects.get(slug='index')) |
|
109 |
except Page.DoesNotExist: |
|
110 |
pass |
|
111 |
unlocker_cells = CellBase.get_cells(page_id__in=[x.id for x in pages]) |
|
112 |
found_placeholders = {} |
|
113 |
for parent_page in reversed(pages[:-1]): |
|
114 |
for placeholder in parent_page.get_unlocked_placeholders(unlocker_cells): |
|
115 |
if not placeholder in locked_placeholders: |
|
116 |
continue |
|
117 |
if not placeholder in found_placeholders: |
|
118 |
found_placeholders[placeholder] = parent_page.id |
|
119 |
if len(found_placeholders) == len(locked_placeholders): |
|
120 |
break |
|
121 | ||
122 |
# add found cells to the page cells |
|
123 |
for placeholder_key, page_id in found_placeholders.items(): |
|
124 |
cells.extend([x for x in unlocker_cells if x.page_id == page_id and |
|
125 |
x.placeholder == placeholder_key]) |
|
99 |
def extend_with_parent_cells(cells): |
|
100 |
for cell in cells: |
|
101 |
if not isinstance(cell, ParentContentCell): |
|
102 |
continue |
|
103 |
idx = cells.index(cell) |
|
104 |
cells[idx:idx+1] = cell.get_cells() |
|
126 | 105 | |
127 | 106 | |
128 | 107 |
def skeleton(request): |
... | ... | |
187 | 166 | |
188 | 167 |
pages = selected_page.get_parents_and_self() |
189 | 168 |
cells = CellBase.get_cells(page_id=selected_page.id) |
190 |
extend_with_locked_placeholders_cells(cells, selected_page, pages)
|
|
169 |
extend_with_parent_cells(cells)
|
|
191 | 170 | |
192 | 171 |
combo_template = settings.COMBO_PUBLIC_TEMPLATES[selected_page.template_name] |
193 | 172 | |
... | ... | |
259 | 238 |
return HttpResponseRedirect(page.redirect_url) |
260 | 239 | |
261 | 240 |
cells = CellBase.get_cells(page_id=page.id) |
262 |
extend_with_locked_placeholders_cells(cells, page, pages)
|
|
241 |
extend_with_parent_cells(cells)
|
|
263 | 242 |
cells = [x for x in cells if x.is_visible(user=request.user)] |
264 | 243 | |
265 | 244 |
ctx = { |
tests/test_public.py | ||
---|---|---|
4 | 4 |
import urllib |
5 | 5 | |
6 | 6 |
from combo.wsgi import application |
7 |
from combo.data.models import Page, CellBase, TextCell |
|
7 |
from combo.data.models import Page, CellBase, TextCell, ParentContentCell
|
|
8 | 8 | |
9 | 9 |
pytestmark = pytest.mark.django_db |
10 | 10 | |
... | ... | |
60 | 60 | |
61 | 61 |
page = Page(title='Second', slug='second', template_name='standard') |
62 | 62 |
page.save() |
63 |
ParentContentCell(page=page, placeholder='footer', order=0).save() |
|
63 | 64 |
resp = app.get('/second', status=301) |
64 | 65 |
assert resp.location == 'http://localhost:80/second/' |
65 | 66 |
resp = app.get('/second/', status=200) |
66 |
- |