wcs: allow setting up a manual order of forms of a category (#8914)
This commit is contained in:
parent
12f9987f5e
commit
a2299d423a
|
@ -15,6 +15,8 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from django import forms
|
||||
from django.utils.datastructures import MultiValueDict, MergeDict
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from .models import WcsFormCell, WcsCategoryCell, WcsFormsOfCategoryCell
|
||||
from .utils import get_wcs_options
|
||||
|
@ -41,12 +43,53 @@ class WcsCategoryCellForm(forms.ModelForm):
|
|||
self.fields['category_reference'].widget = forms.Select(choices=references)
|
||||
|
||||
|
||||
|
||||
class MultiSortWidget(forms.SelectMultiple):
|
||||
def render(self, name, value, attrs=None, choices=()):
|
||||
# reorder choices to get them in the current value order
|
||||
self_choices = self.choices[:]
|
||||
choices_dict = dict(self_choices)
|
||||
if value:
|
||||
for option in reversed(value.get('data')):
|
||||
if not option in choices_dict:
|
||||
continue
|
||||
option_tuple = (option, choices_dict[option])
|
||||
self.choices.remove(option_tuple)
|
||||
self.choices.insert(0, option_tuple)
|
||||
|
||||
# render the <select multiple>
|
||||
rendered = super(MultiSortWidget, self).render(name, value,
|
||||
attrs=attrs, choices=choices)
|
||||
|
||||
# include it in a <div> that will be turned into an appropriate widget
|
||||
# in javascript
|
||||
id_ = 'wid-%s' % name
|
||||
return mark_safe('''<div class="multisort" id="%s">%s</div>
|
||||
<script type="text/javascript">multisort($("#%s"));</script>
|
||||
''' % (id_, rendered, id_))
|
||||
|
||||
def render_options(self, choices, value):
|
||||
value = value.get('data') or []
|
||||
return super(MultiSortWidget, self).render_options(choices, value)
|
||||
|
||||
def value_from_datadict(self, data, files, name):
|
||||
if isinstance(data, MultiValueDict):
|
||||
return {'data': data.getlist(name)}
|
||||
return data.get(name, None)
|
||||
|
||||
|
||||
class WcsFormsOfCategoryCellForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = WcsFormsOfCategoryCell
|
||||
fields = ('category_reference', 'ordering', 'limit')
|
||||
fields = ('category_reference', 'ordering', 'manual_order', 'limit')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(WcsFormsOfCategoryCellForm, self).__init__(*args, **kwargs)
|
||||
references = get_wcs_options('categories')
|
||||
self.fields['category_reference'].widget = forms.Select(choices=references)
|
||||
formdef_references = get_wcs_options('json', include_category_slug=True)
|
||||
self.fields['ordering'].widget = forms.Select(
|
||||
choices=self.fields['ordering'].choices,
|
||||
attrs={'class': 'ordering-select'})
|
||||
self.fields['category_reference'].widget = forms.Select(choices=references,
|
||||
attrs={'class': 'category-select'})
|
||||
self.fields['manual_order'].widget = MultiSortWidget(choices=formdef_references)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import jsonfield.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('wcs', '0011_auto_20151215_1121'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='wcsformsofcategorycell',
|
||||
name='manual_order',
|
||||
field=jsonfield.fields.JSONField(default=dict, help_text='Use drag and drop to reorder forms', verbose_name='Manual Order', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='wcsformsofcategorycell',
|
||||
name='ordering',
|
||||
field=models.CharField(default=b'alpha', max_length=20, verbose_name='Order', choices=[(b'alpha', 'Default (alphabetical)'), (b'popularity', 'Popularity'), (b'manual', 'Manual')]),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
|
@ -296,10 +296,13 @@ class WcsCurrentDraftsCell(WcsUserDataBaseCell):
|
|||
@register_cell_class
|
||||
class WcsFormsOfCategoryCell(WcsCommonCategoryCell, WcsBlurpMixin):
|
||||
ordering = models.CharField(_('Order'), max_length=20,
|
||||
default='', blank=True,
|
||||
choices=[('', _('Default')),
|
||||
('alpha', _('Alphabetical')),
|
||||
('popularity', _('Popularity'))])
|
||||
default='alpha', blank=False,
|
||||
choices=[('alpha', _('Default (alphabetical)')),
|
||||
('popularity', _('Popularity')),
|
||||
('manual', _('Manual'))])
|
||||
manual_order = JSONField(blank=True,
|
||||
verbose_name=_('Manual Order'),
|
||||
help_text=_('Use drag and drop to reorder forms'))
|
||||
limit = models.PositiveSmallIntegerField(_('Limit'),
|
||||
null=True, blank=True)
|
||||
|
||||
|
@ -337,10 +340,23 @@ class WcsFormsOfCategoryCell(WcsCommonCategoryCell, WcsBlurpMixin):
|
|||
except (KeyError, TypeError) as e:
|
||||
# an error occured in the blurp
|
||||
context['forms'] = []
|
||||
if self.ordering == 'alpha':
|
||||
context['forms'] = sorted(context['forms'], lambda x, y: cmp(x.get('title'), y.get('title')))
|
||||
elif self.ordering == 'popularity':
|
||||
context['forms'] = sorted(context['forms'], lambda x, y: -cmp(x.get('count'), y.get('count')))
|
||||
|
||||
# default sort is alphabetical, it's always done as this will serve as
|
||||
# secondary sort key (thanks to Python stable sort)
|
||||
context['forms'] = sorted(context['forms'], key=lambda x: x.get('title'))
|
||||
|
||||
if self.ordering == 'popularity':
|
||||
context['forms'] = sorted(context['forms'], key=lambda x: x.get('count'), reverse=True)
|
||||
elif self.ordering == 'manual':
|
||||
if self.manual_order:
|
||||
manual_order = self.manual_order.get('data')
|
||||
for form in context['forms']:
|
||||
form_reference = '%s:%s' % (self.category_reference, form['slug'])
|
||||
try:
|
||||
form['order'] = manual_order.index(form_reference)
|
||||
except ValueError:
|
||||
form['order'] = 9999
|
||||
context['forms'] = sorted(context['forms'], key=lambda x: x.get('order'))
|
||||
|
||||
if self.limit:
|
||||
if len(context['forms']) > self.limit:
|
||||
|
|
|
@ -45,7 +45,7 @@ def get_wcs_json(wcs_site, path):
|
|||
cache.set(url, response_json)
|
||||
return response_json
|
||||
|
||||
def get_wcs_options(url):
|
||||
def get_wcs_options(url, include_category_slug=False):
|
||||
references = []
|
||||
for wcs_key, wcs_site in get_wcs_services().iteritems():
|
||||
site_title = wcs_site.get('title')
|
||||
|
@ -59,7 +59,10 @@ def get_wcs_options(url):
|
|||
label = title
|
||||
else:
|
||||
label = '%s : %s' % (site_title, title)
|
||||
reference = '%s:%s' % (wcs_key, slug)
|
||||
if include_category_slug:
|
||||
reference = '%s:%s:%s' % (wcs_key, element.get('category_slug') or '', slug)
|
||||
else:
|
||||
reference = '%s:%s' % (wcs_key, slug)
|
||||
references.append((reference, label))
|
||||
references.sort(lambda x, y: cmp(x[1], y[1]))
|
||||
return references
|
||||
|
|
|
@ -270,3 +270,21 @@ div#asset-img img {
|
|||
div#asset-cmds {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
ul.multisort {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-bottom: 2ex;
|
||||
max-height: 250px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
ul.multisort li {
|
||||
border: 1px solid #eee;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
margin-top: -1px;
|
||||
padding: 1ex;
|
||||
background: white;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,53 @@
|
|||
function multisort(element)
|
||||
{
|
||||
var $category_selector = $(element).parents('.cell').find('select.category-select');
|
||||
var category_value = null;
|
||||
if ($category_selector.length) {
|
||||
category_value = $category_selector.val();
|
||||
$category_selector.off('change').on('change', function() {
|
||||
multisort(element);
|
||||
});
|
||||
}
|
||||
var $ordering_selector = $(element).parents('.cell').find('select.ordering-select');
|
||||
if ($ordering_selector.length) {
|
||||
$ordering_selector.off('change').on('change', function() {
|
||||
var val = $(this).val();
|
||||
if (val == 'manual') {
|
||||
$(element).prev().show();
|
||||
$(element).show();
|
||||
} else {
|
||||
$(element).prev().hide();
|
||||
$(element).hide();
|
||||
}
|
||||
}).trigger('change');
|
||||
}
|
||||
|
||||
$(element).find('ul').remove();
|
||||
$(element).find('select').hide();
|
||||
|
||||
var $ul = $('<ul class="multisort"></ul>');
|
||||
|
||||
$(element).find('option').each(function(i, x) {
|
||||
if (category_value && $(x).val().indexOf(category_value + ':') != 0) {
|
||||
return;
|
||||
}
|
||||
$('<li data-value="' + $(x).val() + '">' + $(x).text() + '</li>').appendTo($ul);
|
||||
});
|
||||
$ul.appendTo(element);
|
||||
$ul.sortable({
|
||||
update: function(event, ui) {
|
||||
var options = Array();
|
||||
var select = $(element).find('select');
|
||||
$ul.find('li').each(function(i, x) {
|
||||
options.push($(element).find("option[value='" + $(x).data('value') + "']").attr('selected', 'selected').detach());
|
||||
});
|
||||
while (options.length) {
|
||||
select.prepend(options.pop());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function init_pages_list()
|
||||
{
|
||||
if ($('#pages-list').length == 0)
|
||||
|
@ -132,11 +182,13 @@ $(function() {
|
|||
$('div.cell').each(function(i, x) {
|
||||
$(this).attr('id', 'div-cell-'+i);
|
||||
var h = $(this).find('h3 + div').height() + 10;
|
||||
h += $(this).find('.multisort').length * 250;
|
||||
h += $(this).find('.django-ckeditor-widget').length * 200;
|
||||
var style = '<style>div#div-cell-'+i+'.toggled h3 + div { max-height: '+h+'px; }</style>';
|
||||
$(style).appendTo('head');
|
||||
$(this).addClass('untoggled');
|
||||
});
|
||||
|
||||
$('a.menu-opener').on('click', function() {
|
||||
$('#appbar .menu').toggle();
|
||||
});
|
||||
|
|
|
@ -330,6 +330,27 @@ def test_forms_of_category_cell_render():
|
|||
assert 'form title' in result and 'a second form title' in result
|
||||
assert result.index('form title') < result.index('a second form title')
|
||||
|
||||
cell.ordering = 'manual'
|
||||
cell.save()
|
||||
result = cell.render(Context({'synchronous': True}))
|
||||
# by default all forms should be present, in alphabetical order
|
||||
assert result.index('form title') > result.index('a second form title')
|
||||
|
||||
# set a manual order
|
||||
cell.manual_order = {'data': ['default:test-9:a-second-form-title',
|
||||
'default:test-9:form-title']}
|
||||
cell.save()
|
||||
result = cell.render(Context({'synchronous': True}))
|
||||
assert result.index('form title') > result.index('a second form title')
|
||||
|
||||
# make sure all forms are displayed even if the manual order only specify
|
||||
# some.
|
||||
cell.manual_order = {'data': ['default:test-9:a-second-form-title']}
|
||||
cell.save()
|
||||
result = cell.render(Context({'synchronous': True}))
|
||||
assert result.index('form title') > result.index('a second form title')
|
||||
assert 'form title' in result and 'a second form title' in result
|
||||
|
||||
@wcsctl_present
|
||||
def test_current_drafts_cell_render():
|
||||
page = Page(title='xxx', slug='test_current_drafts_cell_render', template_name='standard')
|
||||
|
|
Loading…
Reference in New Issue