manager: rough (re-)ordering of pages

This is not perfect as it uses Sortable from jquery-ui which is adapted for
flat lists, not nested lists, and ideally we would want to create multiple
placeholders per element, to make it clear the lement is moved as a child
or as a sibling.
This commit is contained in:
Frédéric Péters 2014-12-16 15:17:15 +01:00
parent f11420e24f
commit 6cedb0e9d2
7 changed files with 116 additions and 18 deletions

View File

@ -19,6 +19,7 @@ import inspect
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Max
from django.forms import models as model_forms
from django.template import Context
from django.utils.html import strip_tags
@ -35,10 +36,23 @@ class Page(models.Model):
slug = models.SlugField()
template_name = models.CharField(max_length=50)
parent = models.ForeignKey('self', null=True, blank=True)
order = models.PositiveIntegerField()
_level = None
_children = None
class Meta:
ordering = ['order']
def __unicode__(self):
return self.title
def save(self, *args, **kwargs):
if not self.order:
max_order = Page.objects.all().aggregate(Max('order')).get('order__max') or 0
self.order = max_order + 1
return super(Page, self).save(*args, **kwargs)
def get_online_url(self):
parts = [self.slug]
page = self
@ -50,6 +64,19 @@ class Page(models.Model):
def has_children(self):
return Page.objects.filter(parent_id=self.id).exists()
@classmethod
def get_as_reordered_flat_hierarchy(cls, object_list):
reordered = []
def fill_list(object_sublist, level=0, parent=None):
for page in object_sublist:
page._children = [x for x in object_list if x.parent_id == page.id]
if page.parent == parent:
page.level = level
reordered.append(page)
fill_list(object_sublist, level=level+1, parent=page)
fill_list(object_list)
return reordered
class CellBase(models.Model):
page = models.ForeignKey(Page)

View File

@ -22,6 +22,7 @@ from combo.data.models import Page
class PageForm(forms.ModelForm):
class Meta:
model = Page
exclude = ('order', )
def __init__(self, *args, **kwargs):
super(PageForm, self).__init__(*args, **kwargs)

View File

@ -135,3 +135,18 @@ li.cell-type input:checked ~ ul {
}
.icon-eye-open:before { content: "\f06e "; }
div#pages-list > div {
border: 1px solid #eee;
margin: 0.5ex 0;
box-shadow: rgba(0, 0, 0, 0.04) 0px 1px 1px 0px;
padding: 1ex;
}
div#pages-list > div.level-1 {
margin-left: 25px;
}
div#pages-list > div.level-2 {
margin-left: 50px;
}

View File

@ -1,3 +1,52 @@
function init_pages_list()
{
if ($('#pages-list').length == 0)
return;
var list_offset = $('#pages-list').offset().left;
$('#pages-list').sortable({
stop: function(event, ui) {
var moved_page_id = ui.item.data('page-id');
/* 25 is the per-level margin-left applied to pages, it needs to be kept
* in sync with the css file */
var item_offset = ui.offset.left - list_offset + parseInt($(ui.item).data('level'))*25;
var new_level = Math.abs(Math.round(item_offset / 25));
console.log(new_level);
if (new_level <= 0) {
new_level = 0;
} else {
var previous_page = $('#pages-list div[data-page-id=' + moved_page_id + ']').prev();
var previous_page_level = parseInt($(previous_page).data('level'));
if (new_level > previous_page_level+1) {
new_level = previous_page_level+1;
}
}
var new_parent = null;
if (new_level != 0) {
new_parent = $($(ui.item).prevAll('[data-level='+(new_level-1)+']')[0]).data('page-id');
}
/* remove classes and add new values */
$(ui.item).removeClass('level-0').removeClass('level-1').removeClass('level-2');
$(ui.item).addClass('level-' + new_level);
$(ui.item).data('level', new_level).attr('data-level', new_level);
var new_order = $('#pages-list div').map(function() { return $(this).data('page-id'); }).get().join();
$.ajax({
url: $('#pages-list').data('page-order-url'),
data: {'new-order': new_order,
'moved-page-id': moved_page_id,
'moved-page-new-parent': new_parent
},
success: function(data, status) {
$('#pages-list').replaceWith($(data).find('#pages-list'));
init_pages_list();
}
});
}
});
}
$(function() {
$('div.cell-list h3').on('click', function() {
$(this).parent().toggleClass('toggled').toggleClass('untoggled');
@ -6,6 +55,7 @@ $(function() {
$(this).parents('.toggled').toggleClass('toggled').toggleClass('untoggled');
return false;
});
init_pages_list();
$('.cell-list').sortable({
connectWith: '.cell-list',
handle: 'h3',

View File

@ -2,16 +2,13 @@
{% block content %}
<ul>
{% if parent %}
<li><a href="{% if parent.parent_id %}?parent={{ parent.parent_id }}{% else %}.{% endif %}">..</a></li>
{% endif %}
<div id="pages-list" data-page-order-url="{% url 'combo-manager-page-order' %}">
{% for page in object_list %}
<li><a href="{% url 'combo-manager-page-view' pk=page.id %}">{{ page.title }}</a>
{% if page.has_children %}<a href="?parent={{ page.id }}">+</a>{% endif %}
</li>
<div class="level-{{page.level}}" data-page-id="{{page.id}}" data-level="{{page.level}}">
<a href="{% url 'combo-manager-page-view' pk=page.id %}">{{ page.title }}</a>
</div>
{% endfor %}
</ul>
</div>
<a href="{% url 'combo-manager-page-add' %}">+</a>

View File

@ -36,5 +36,7 @@ urlpatterns = patterns('combo.views',
name='combo-manager-page-delete-cell'),
url(r'^pages/(?P<page_pk>\w+)/order$', views.cell_order,
name='combo-manager-cell-order'),
url(r'^pages/order$', views.page_order,
name='combo-manager-page-order'),
(r'^ckeditor/', include('ckeditor.urls')),
)

View File

@ -19,6 +19,7 @@ from django.contrib.contenttypes.models import ContentType
from django.core.urlresolvers import reverse
from django.forms import models as model_forms
from django.http import HttpResponse, Http404
from django.shortcuts import redirect
from django.views.decorators.csrf import requires_csrf_token
from django.views.generic import (TemplateView, RedirectView, DetailView,
CreateView, UpdateView, ListView, DeleteView)
@ -38,18 +39,9 @@ class PagesListView(ListView):
model = Page
template_name = 'combo/pages_list.html'
def get_queryset(self):
queryset = super(PagesListView, self).get_queryset()
try:
parent_id = self.request.GET['parent']
except KeyError:
return queryset.filter(parent_id__isnull=True)
return queryset.filter(parent_id=parent_id)
def get_context_data(self, **kwargs):
self.object_list = Page.get_as_reordered_flat_hierarchy(self.object_list)
context = super(PagesListView, self).get_context_data(**kwargs)
if self.request.GET.get('parent'):
context['parent'] = Page.objects.get(id=self.request.GET['parent'])
return context
@ -169,3 +161,17 @@ def cell_order(request, page_pk):
cell.placeholder = new_placeholder
cell.save()
return HttpResponse(status=206)
def page_order(request):
new_order = [int(x) for x in request.GET['new-order'].split(',')]
moved_page = Page.objects.get(id=request.GET['moved-page-id']);
if request.GET['moved-page-new-parent']:
moved_page.parent_id = request.GET['moved-page-new-parent'];
else:
moved_page.parent_id = None
moved_page.save()
for page in Page.objects.filter(parent_id=moved_page.parent_id):
page.order = new_order.index(page.id)+1
page.save()
return redirect(reverse('combo-manager-pages-list'))