From 4192c886ede08ffa58fafb0f717b9a4ebea8325b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Sat, 11 Aug 2018 10:35:49 +0200 Subject: [PATCH 03/11] search: add custom widget to sort and enable engines (#23534) --- combo/apps/search/engines.py | 8 +++ combo/apps/search/forms.py | 34 ++++++++++++ combo/apps/search/models.py | 4 ++ combo/apps/wcs/forms.py | 42 ++------------- combo/manager/static/js/combo.manager.js | 48 +++++++++++++---- combo/utils/forms.py | 69 ++++++++++++++++++++++++ tests/test_search.py | 43 +++++++++++++++ 7 files changed, 200 insertions(+), 48 deletions(-) create mode 100644 combo/apps/search/forms.py create mode 100644 combo/utils/forms.py diff --git a/combo/apps/search/engines.py b/combo/apps/search/engines.py index 282b5fb..7b07290 100644 --- a/combo/apps/search/engines.py +++ b/combo/apps/search/engines.py @@ -32,4 +32,12 @@ class Engines(object): return settings.COMBO_SEARCH_SERVICES[key] return self.engines.get(key) + def get_engines(self): + data = {} + for key in settings.COMBO_SEARCH_SERVICES: + data[key] = settings.COMBO_SEARCH_SERVICES[key] + for key in self.engines: + data[key] = self.engines[key] + return data + engines = Engines() # singleton object diff --git a/combo/apps/search/forms.py b/combo/apps/search/forms.py new file mode 100644 index 0000000..5bc5f4b --- /dev/null +++ b/combo/apps/search/forms.py @@ -0,0 +1,34 @@ +# combo - content management system +# Copyright (C) 2014-2018 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from django import forms + +from combo.utils.forms import MultiSortWidget + +from . import engines +from .models import SearchCell + + +class SearchCellForm(forms.ModelForm): + class Meta: + model = SearchCell + fields = ('_search_services',) + + def __init__(self, *args, **kwargs): + super(SearchCellForm, self).__init__(*args, **kwargs) + options = [(x, engines.get_engines()[x]['label']) for x in engines.get_engines().keys()] + self.fields['_search_services'].widget = MultiSortWidget(choices=options, + with_checkboxes=True) diff --git a/combo/apps/search/models.py b/combo/apps/search/models.py index 088422b..97a241c 100644 --- a/combo/apps/search/models.py +++ b/combo/apps/search/models.py @@ -46,6 +46,10 @@ class SearchCell(CellBase): return False return super(SearchCell, self).is_visible(user=user) + def get_default_form_class(self): + from .forms import SearchCellForm + return SearchCellForm + @property def varname(self): if self.slug: diff --git a/combo/apps/wcs/forms.py b/combo/apps/wcs/forms.py index c8ea33b..1aee714 100644 --- a/combo/apps/wcs/forms.py +++ b/combo/apps/wcs/forms.py @@ -20,10 +20,13 @@ from django.utils.datastructures import MultiValueDict from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ +from combo.utils.forms import MultiSortWidget + from .models import (WcsFormCell, WcsCategoryCell, WcsFormsOfCategoryCell, WcsCurrentFormsCell) from .utils import get_wcs_options, get_wcs_services + class WcsFormCellForm(forms.ModelForm): class Meta: model = WcsFormCell @@ -46,45 +49,6 @@ 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 ' + } else { + checkbox = '' + } + } + $('
  • '+ checkbox + $(x).text() + '
  • ').appendTo($ul); }); $ul.appendTo(element); + + function multisort_sync() { + var $select = $(element).find('select'); + var options = Array(); + $ul.find('li').each(function(i, x) { + var selected = true; + if (checkboxes && $(x).find('input[type=checkbox]:checked').length == 0) { + selected = false; + } + var value = $(x).data('value'); + var $option = $select.find('[value="' + value + '"]'); + if (selected) { + $option.prop('selected', 'selected'); + } else { + $option.prop('selected', null); + } + $option.detach(); + options.push($option); + }); + while (options.length) { + $select.prepend(options.pop()); + } + } + + $ul.find('input[type=checkbox]').on('change', function() { + multisort_sync(); + }); $ul.sortable({ handle: '.handle', 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()); - } + multisort_sync(); } }); } diff --git a/combo/utils/forms.py b/combo/utils/forms.py new file mode 100644 index 0000000..681da8e --- /dev/null +++ b/combo/utils/forms.py @@ -0,0 +1,69 @@ +# combo - content management system +# Copyright (C) 2014-2018 Entr'ouvert +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import django +from django import forms +from django.utils.datastructures import MultiValueDict +from django.utils.safestring import mark_safe + + +class MultiSortWidget(forms.SelectMultiple): + def __init__(self, *args, **kwargs): + if 'with_checkboxes' in kwargs: + self.with_checkboxes = kwargs.pop('with_checkboxes') + else: + self.with_checkboxes = False + super(MultiSortWidget, self).__init__(*args, **kwargs) + + 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 option not in choices_dict: + continue + option_tuple = (option, choices_dict[option]) + self.choices.remove(option_tuple) + self.choices.insert(0, option_tuple) + + # render the