0001-misc-allow-page-with-same-slug-but-different-parents.patch
combo/data/models.py | ||
---|---|---|
172 | 172 |
i = 1 |
173 | 173 |
while True: |
174 | 174 |
try: |
175 |
Page.objects.get(slug=slug) |
|
175 |
Page.objects.get(slug=slug, parent_id=self.parent_id)
|
|
176 | 176 |
except ObjectDoesNotExist: |
177 | 177 |
break |
178 | 178 |
i += 1 |
combo/manager/forms.py | ||
---|---|---|
38 | 38 |
value = self.cleaned_data.get('slug') |
39 | 39 |
if self.instance.slug == value: |
40 | 40 |
return value |
41 |
if Page.objects.filter(slug=value).count() > 0: |
|
41 |
if Page.objects.filter(slug=value, parent_id=self.instance.parent_id).count() > 0:
|
|
42 | 42 |
raise ValidationError(_('Slug must be unique'), code='unique') |
43 | 43 |
return value |
44 | 44 |
combo/manager/views.py | ||
---|---|---|
14 | 14 |
# You should have received a copy of the GNU Affero General Public License |
15 | 15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 |
import hashlib |
|
17 | 18 |
import json |
18 | 19 |
import os |
19 | 20 | |
... | ... | |
452 | 453 |
def page_order(request): |
453 | 454 |
new_order = [int(x) for x in request.GET['new-order'].split(',')] |
454 | 455 |
moved_page = Page.objects.get(id=request.GET['moved-page-id']) |
456 |
current_parent_id = moved_page.parent_id |
|
455 | 457 |
if request.GET['moved-page-new-parent']: |
456 | 458 |
moved_page.parent_id = request.GET['moved-page-new-parent'] |
457 | 459 |
else: |
458 | 460 |
moved_page.parent_id = None |
459 | 461 |
moved_page.save() |
462 |
slug_conflict = False |
|
460 | 463 |
for page in Page.objects.filter(parent_id=moved_page.parent_id): |
461 | 464 |
page.order = new_order.index(page.id)+1 |
462 | 465 |
page.save() |
466 |
if moved_page.id != page.id and moved_page.slug == page.slug: |
|
467 |
slug_conflict = True |
|
468 |
if slug_conflict: |
|
469 |
# slug conflict after a page got moved, reload and rename |
|
470 |
moved_page = Page.objects.get(id=request.GET['moved-page-id']) |
|
471 |
moved_page.slug = moved_page.slug + '-' + hashlib.md5(str(moved_page.id)).hexdigest()[:4] |
|
472 |
moved_page.save() |
|
463 | 473 |
return redirect(reverse('combo-manager-homepage')) |
464 | 474 | |
465 | 475 |
combo/public/views.py | ||
---|---|---|
350 | 350 |
request.session['visited'] = True |
351 | 351 |
return HttpResponseRedirect(settings.COMBO_WELCOME_PAGE_PATH) |
352 | 352 | |
353 |
slugs = {'parent__'*len(parts) + 'isnull': True} |
|
354 |
for i, part in enumerate(reversed(parts)): |
|
355 |
slugs['parent__'*i + 'slug'] = part |
|
353 | 356 |
try: |
354 |
page = Page.objects.get(slug=parts[-1])
|
|
357 |
page = Page.objects.get(**slugs)
|
|
355 | 358 |
except Page.DoesNotExist: |
356 | 359 |
if Page.objects.count() == 0 and parts == ['index']: |
357 | 360 |
return empty_site(request) |
358 |
page = None |
|
361 |
# maybe the page is a children of /index/, as /index/ is silent the |
|
362 |
# page would appear directly under /; this is not a suggested practice. |
|
363 |
parts = ['index'] + parts |
|
364 |
slugs = {'parent__'*len(parts) + 'isnull': True} |
|
365 |
for i, part in enumerate(reversed(parts)): |
|
366 |
slugs['parent__'*i + 'slug'] = part |
|
367 |
try: |
|
368 |
page = Page.objects.get(**slugs) |
|
369 |
except Page.DoesNotExist: |
|
370 |
page = None |
|
359 | 371 | |
360 | 372 |
if page is None or page.get_online_url() != url: |
361 | 373 |
if not url.endswith('/') and settings.APPEND_SLASH: |
tests/test_manager.py | ||
---|---|---|
222 | 222 |
assert resp.location.endswith('/manage/') |
223 | 223 |
assert Page.objects.count() == 0 |
224 | 224 | |
225 |
def test_page_reorder(app, admin_user): |
|
226 |
Page.objects.all().delete() |
|
227 |
page1 = Page(title='One', slug='one', parent=None, order=0, template_name='standard') |
|
228 |
page1.save() |
|
229 |
page2 = Page(title='Two', slug='two', parent=None, order=1, template_name='standard') |
|
230 |
page2.save() |
|
231 |
page3 = Page(title='Three', slug='three', parent=page2, order=2, template_name='standard') |
|
232 |
page3.save() |
|
233 |
page4 = Page(title='Four', slug='four', parent=page2, order=3, template_name='standard') |
|
234 |
page4.save() |
|
235 | ||
236 |
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())] |
|
237 |
ordered_ids = [page1.id, page2.id, page3.id, page4.id] |
|
238 | ||
239 |
# move page4 before page3 |
|
240 |
app = login(app) |
|
241 |
resp = app.get('/manage/pages/order', params={ |
|
242 |
'moved-page-id': page4.id, |
|
243 |
'moved-page-new-parent': page2.id, |
|
244 |
'new-order': ','.join([str(x) for x in [page1.id, page2.id, page4.id, page3.id]])}) |
|
245 | ||
246 |
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())] |
|
247 |
ordered_ids = [page1.id, page2.id, page4.id, page3.id] |
|
248 | ||
249 |
# move page4 to level0 |
|
250 |
resp = app.get('/manage/pages/order', params={ |
|
251 |
'moved-page-id': page4.id, |
|
252 |
'moved-page-new-parent': '', |
|
253 |
'new-order': ','.join([str(x) for x in [page1.id, page4.id, page2.id, page3.id]])}) |
|
254 |
ordered_ids = [x.id for x in Page.get_as_reordered_flat_hierarchy(Page.objects.all())] |
|
255 |
ordered_ids = [page1.id, page4.id, page2.id, page3.id] |
|
256 | ||
257 |
# change slug to check for autochange on duplicate |
|
258 |
page4.slug = 'three' |
|
259 |
page4.save() |
|
260 |
# move it as a sibling of page3 |
|
261 |
resp = app.get('/manage/pages/order', params={ |
|
262 |
'moved-page-id': page4.id, |
|
263 |
'moved-page-new-parent': page2.id, |
|
264 |
'new-order': ','.join([str(x) for x in [page1.id, page2.id, page4.id, page3.id]])}) |
|
265 |
assert Page.objects.get(id=page4.id).slug.startswith('three-') |
|
266 | ||
225 | 267 |
def test_export_page(app, admin_user): |
226 | 268 |
Page.objects.all().delete() |
227 | 269 |
page = Page(title='One', slug='one', template_name='standard') |
228 |
- |