Projet

Général

Profil

0001-wcs-allow-setting-up-a-manual-order-of-forms-of-a-ca.patch

Frédéric Péters, 22 mars 2016 09:53

Télécharger (12,9 ko)

Voir les différences:

Subject: [PATCH] wcs: allow setting up a manual order of forms of a category
 (#8914)

 combo/apps/wcs/forms.py                            | 47 ++++++++++++++++++-
 .../0012_wcsformsofcategorycell_manual_order.py    | 27 +++++++++++
 combo/apps/wcs/models.py                           | 32 +++++++++----
 combo/apps/wcs/utils.py                            |  7 ++-
 combo/manager/static/css/combo.manager.css         | 18 ++++++++
 combo/manager/static/js/combo.manager.js           | 52 ++++++++++++++++++++++
 tests/test_wcs.py                                  | 21 +++++++++
 7 files changed, 192 insertions(+), 12 deletions(-)
 create mode 100644 combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py
combo/apps/wcs/forms.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
from django import forms
18
from django.utils.datastructures import MultiValueDict, MergeDict
19
from django.utils.safestring import mark_safe
18 20

  
19 21
from .models import WcsFormCell, WcsCategoryCell, WcsFormsOfCategoryCell
20 22
from .utils import get_wcs_options
......
41 43
        self.fields['category_reference'].widget = forms.Select(choices=references)
42 44

  
43 45

  
46

  
47
class MultiSortWidget(forms.SelectMultiple):
48
    def render(self, name, value, attrs=None, choices=()):
49
        # reorder choices to get them in the current value order
50
        self_choices = self.choices[:]
51
        choices_dict = dict(self_choices)
52
        if value:
53
            for option in reversed(value.get('data')):
54
                if not option in choices_dict:
55
                    continue
56
                option_tuple = (option, choices_dict[option])
57
                self.choices.remove(option_tuple)
58
                self.choices.insert(0, option_tuple)
59

  
60
        # render the <select multiple>
61
        rendered = super(MultiSortWidget, self).render(name, value,
62
                attrs=attrs, choices=choices)
63

  
64
        # include it in a <div> that will be turned into an appropriate widget
65
        # in javascript
66
        id_ = 'wid-%s' % name
67
        return mark_safe('''<div class="multisort" id="%s">%s</div>
68
        <script type="text/javascript">multisort($("#%s"));</script>
69
        ''' % (id_, rendered, id_))
70

  
71
    def render_options(self, choices, value):
72
        value = value.get('data') or []
73
        return super(MultiSortWidget, self).render_options(choices, value)
74

  
75
    def value_from_datadict(self, data, files, name):
76
        if isinstance(data, MultiValueDict):
77
            return {'data': data.getlist(name)}
78
        return data.get(name, None)
79

  
80

  
44 81
class WcsFormsOfCategoryCellForm(forms.ModelForm):
45 82
    class Meta:
46 83
        model = WcsFormsOfCategoryCell
47
        fields = ('category_reference', 'ordering', 'limit')
84
        fields = ('category_reference', 'ordering', 'manual_order', 'limit')
48 85

  
49 86
    def __init__(self, *args, **kwargs):
50 87
        super(WcsFormsOfCategoryCellForm, self).__init__(*args, **kwargs)
51 88
        references = get_wcs_options('categories')
52
        self.fields['category_reference'].widget = forms.Select(choices=references)
89
        formdef_references = get_wcs_options('json', include_category_slug=True)
90
        self.fields['ordering'].widget = forms.Select(
91
                choices=self.fields['ordering'].choices,
92
                attrs={'class': 'ordering-select'})
93
        self.fields['category_reference'].widget = forms.Select(choices=references,
94
                attrs={'class': 'category-select'})
95
        self.fields['manual_order'].widget = MultiSortWidget(choices=formdef_references)
combo/apps/wcs/migrations/0012_wcsformsofcategorycell_manual_order.py
1
# -*- coding: utf-8 -*-
2
from __future__ import unicode_literals
3

  
4
from django.db import models, migrations
5
import jsonfield.fields
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('wcs', '0011_auto_20151215_1121'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='wcsformsofcategorycell',
17
            name='manual_order',
18
            field=jsonfield.fields.JSONField(default=dict, help_text='Use drag and drop to reorder forms', verbose_name='Manual Order', blank=True),
19
            preserve_default=True,
20
        ),
21
        migrations.AlterField(
22
            model_name='wcsformsofcategorycell',
23
            name='ordering',
24
            field=models.CharField(default=b'alpha', max_length=20, verbose_name='Order', choices=[(b'alpha', 'Default (alphabetical)'), (b'popularity', 'Popularity'), (b'manual', 'Manual')]),
25
            preserve_default=True,
26
        ),
27
    ]
combo/apps/wcs/models.py
296 296
@register_cell_class
297 297
class WcsFormsOfCategoryCell(WcsCommonCategoryCell, WcsBlurpMixin):
298 298
    ordering = models.CharField(_('Order'), max_length=20,
299
            default='', blank=True,
300
            choices=[('', _('Default')),
301
                     ('alpha', _('Alphabetical')),
302
                     ('popularity', _('Popularity'))])
299
            default='alpha', blank=False,
300
            choices=[('alpha', _('Default (alphabetical)')),
301
                     ('popularity', _('Popularity')),
302
                     ('manual', _('Manual'))])
303
    manual_order = JSONField(blank=True,
304
            verbose_name=_('Manual Order'),
305
            help_text=_('Use drag and drop to reorder forms'))
303 306
    limit = models.PositiveSmallIntegerField(_('Limit'),
304 307
            null=True, blank=True)
305 308

  
......
337 340
        except (KeyError, TypeError) as e:
338 341
            # an error occured in the blurp
339 342
            context['forms'] = []
340
        if self.ordering == 'alpha':
341
            context['forms'] = sorted(context['forms'], lambda x, y: cmp(x.get('title'), y.get('title')))
342
        elif self.ordering == 'popularity':
343
            context['forms'] = sorted(context['forms'], lambda x, y: -cmp(x.get('count'), y.get('count')))
343

  
344
        # default sort is alphabetical, it's always done as this will serve as
345
        # secondary sort key (thanks to Python stable sort)
346
        context['forms'] = sorted(context['forms'], key=lambda x: x.get('title'))
347

  
348
        if self.ordering == 'popularity':
349
            context['forms'] = sorted(context['forms'], key=lambda x: x.get('count'), reverse=True)
350
        elif self.ordering == 'manual':
351
            if self.manual_order:
352
                manual_order = self.manual_order.get('data')
353
                for form in context['forms']:
354
                    form_reference = '%s:%s' % (self.category_reference, form['slug'])
355
                    try:
356
                        form['order'] = manual_order.index(form_reference)
357
                    except ValueError:
358
                        form['order'] = 9999
359
            context['forms'] = sorted(context['forms'], key=lambda x: x.get('order'))
344 360

  
345 361
        if self.limit:
346 362
            if len(context['forms']) > self.limit:
combo/apps/wcs/utils.py
45 45
        cache.set(url, response_json)
46 46
    return response_json
47 47

  
48
def get_wcs_options(url):
48
def get_wcs_options(url, include_category_slug=False):
49 49
    references = []
50 50
    for wcs_key, wcs_site in get_wcs_services().iteritems():
51 51
        site_title = wcs_site.get('title')
......
59 59
                label = title
60 60
            else:
61 61
                label = '%s : %s' % (site_title, title)
62
            reference = '%s:%s' % (wcs_key, slug)
62
            if include_category_slug:
63
                reference = '%s:%s:%s' % (wcs_key, element.get('category_slug') or '', slug)
64
            else:
65
                reference = '%s:%s' % (wcs_key, slug)
63 66
            references.append((reference, label))
64 67
    references.sort(lambda x, y: cmp(x[1], y[1]))
65 68
    return references
combo/manager/static/css/combo.manager.css
270 270
div#asset-cmds {
271 271
	text-align: right;
272 272
}
273

  
274
ul.multisort {
275
	list-style: none;
276
	padding: 0;
277
	margin: 0;
278
	margin-bottom: 2ex;
279
	max-height: 250px;
280
	overflow-y: auto;
281
}
282

  
283
ul.multisort li {
284
	border: 1px solid #eee;
285
	position: relative;
286
	margin: 0;
287
	margin-top: -1px;
288
	padding: 1ex;
289
	background: white;
290
}
combo/manager/static/js/combo.manager.js
1
function multisort(element)
2
{
3
  var $category_selector = $(element).parents('.cell').find('select.category-select');
4
  var category_value = null;
5
  if ($category_selector.length) {
6
    category_value = $category_selector.val();
7
    $category_selector.off('change').on('change', function() {
8
      multisort(element);
9
    });
10
  }
11
  var $ordering_selector = $(element).parents('.cell').find('select.ordering-select');
12
  if ($ordering_selector.length) {
13
    $ordering_selector.off('change').on('change', function() {
14
      var val = $(this).val();
15
      if (val == 'manual') {
16
        $(element).prev().show();
17
        $(element).show();
18
      } else {
19
        $(element).prev().hide();
20
        $(element).hide();
21
      }
22
    }).trigger('change');
23
  }
24

  
25
  $(element).find('ul').remove();
26
  $(element).find('select').hide();
27

  
28
  var $ul = $('<ul class="multisort"></ul>');
29

  
30
  $(element).find('option').each(function(i, x) {
31
    if (category_value && $(x).val().indexOf(category_value + ':') != 0) {
32
      return;
33
    }
34
    $('<li data-value="' + $(x).val() + '">' + $(x).text() + '</li>').appendTo($ul);
35
  });
36
  $ul.appendTo(element);
37
  $ul.sortable({
38
    update: function(event, ui) {
39
      var options = Array();
40
      var select = $(element).find('select');
41
      $ul.find('li').each(function(i, x) {
42
        options.push($(element).find("option[value='" + $(x).data('value') + "']").attr('selected', 'selected').detach());
43
      });
44
      while (options.length) {
45
        select.prepend(options.pop());
46
      }
47
    }
48
  });
49
}
50

  
1 51
function init_pages_list()
2 52
{
3 53
  if ($('#pages-list').length == 0)
......
132 182
  $('div.cell').each(function(i, x) {
133 183
    $(this).attr('id', 'div-cell-'+i);
134 184
    var h = $(this).find('h3 + div').height() + 10;
185
    h += $(this).find('.multisort').length * 250;
135 186
    h += $(this).find('.django-ckeditor-widget').length * 200;
136 187
    var style = '<style>div#div-cell-'+i+'.toggled h3 + div { max-height: '+h+'px; }</style>';
137 188
    $(style).appendTo('head');
138 189
    $(this).addClass('untoggled');
139 190
  });
191

  
140 192
  $('a.menu-opener').on('click', function() {
141 193
    $('#appbar .menu').toggle();
142 194
  });
tests/test_wcs.py
330 330
    assert 'form title' in result and 'a second form title' in result
331 331
    assert result.index('form title') < result.index('a second form title')
332 332

  
333
    cell.ordering = 'manual'
334
    cell.save()
335
    result = cell.render(Context({'synchronous': True}))
336
    # by default all forms should be present, in alphabetical order
337
    assert result.index('form title') > result.index('a second form title')
338

  
339
    # set a manual order
340
    cell.manual_order = {'data': ['default:test-9:a-second-form-title',
341
                                  'default:test-9:form-title']}
342
    cell.save()
343
    result = cell.render(Context({'synchronous': True}))
344
    assert result.index('form title') > result.index('a second form title')
345

  
346
    # make sure all forms are displayed even if the manual order only specify
347
    # some.
348
    cell.manual_order = {'data': ['default:test-9:a-second-form-title']}
349
    cell.save()
350
    result = cell.render(Context({'synchronous': True}))
351
    assert result.index('form title') > result.index('a second form title')
352
    assert 'form title' in result and 'a second form title' in result
353

  
333 354
@wcsctl_present
334 355
def test_current_drafts_cell_render():
335 356
    page = Page(title='xxx', slug='test_current_drafts_cell_render', template_name='standard')
336
-