From 5c8340e220c24caae41055624511dd99ae840829 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Thu, 18 Jan 2018 07:30:55 +0000 Subject: [PATCH] Allow storing of category options on the project --- .../migrations/0004_projectpage_categories.py | 21 +++++++++ opentech/public/projects/models.py | 12 ++++++ .../templates/projects/project_page.html | 13 ++++++ .../projects/widgets/categories_widget.html | 1 + .../projects/widgets/options_option.html | 1 + .../projects/widgets/options_widget.html | 15 +++++++ opentech/public/projects/widgets.py | 43 +++++++++++++++++++ 7 files changed, 106 insertions(+) create mode 100644 opentech/public/projects/migrations/0004_projectpage_categories.py create mode 100644 opentech/public/projects/templates/projects/widgets/categories_widget.html create mode 100644 opentech/public/projects/templates/projects/widgets/options_option.html create mode 100644 opentech/public/projects/templates/projects/widgets/options_widget.html create mode 100644 opentech/public/projects/widgets.py diff --git a/opentech/public/projects/migrations/0004_projectpage_categories.py b/opentech/public/projects/migrations/0004_projectpage_categories.py new file mode 100644 index 000000000..39b935e45 --- /dev/null +++ b/opentech/public/projects/migrations/0004_projectpage_categories.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-17 22:22 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0003_projectpage_status'), + ] + + operations = [ + migrations.AddField( + model_name='projectpage', + name='categories', + field=models.TextField(default='{}'), + preserve_default=False, + ), + ] diff --git a/opentech/public/projects/models.py b/opentech/public/projects/models.py index 91f65f364..5c16fe01f 100644 --- a/opentech/public/projects/models.py +++ b/opentech/public/projects/models.py @@ -1,3 +1,5 @@ +import json + from django.db import models from django.conf import settings from django.core.exceptions import ValidationError @@ -16,6 +18,7 @@ from wagtail.wagtailcore.fields import StreamField from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index +from opentech.apply.categories.models import Option from opentech.public.utils.blocks import StoryBlock from opentech.public.utils.models import ( BaseFunding, @@ -24,6 +27,8 @@ from opentech.public.utils.models import ( RelatedPage, ) +from .widgets import CategoriesWidget + class ProjectContactDetails(models.Model): project_page = ParentalKey( @@ -104,6 +109,8 @@ class ProjectPage(FundingMixin, BasePage): status = models.CharField(choices=STATUSES, max_length=25, default=STATUSES[0][0]) body = StreamField(StoryBlock()) + categories = models.TextField() + search_fields = BasePage.search_fields + [ index.SearchField('introduction'), index.SearchField('body'), @@ -114,10 +121,15 @@ class ProjectPage(FundingMixin, BasePage): FieldPanel('introduction'), FieldPanel('status'), StreamFieldPanel('body'), + FieldPanel('categories', widget=CategoriesWidget), InlinePanel('contact_details', label="Contact Details"), InlinePanel('related_pages', label="Related Projects"), ] + FundingMixin.content_panels + def category_options(self): + categories = json.loads(self.categories) + options = [int(id) for options in categories.values() for id in options] + return Option.objects.select_related().filter(id__in=options).order_by('category') class ProjectIndexPage(BasePage): subpage_types = ['ProjectPage'] diff --git a/opentech/public/projects/templates/projects/project_page.html b/opentech/public/projects/templates/projects/project_page.html index bb064432c..823a17436 100644 --- a/opentech/public/projects/templates/projects/project_page.html +++ b/opentech/public/projects/templates/projects/project_page.html @@ -31,4 +31,17 @@ </div> {% include "utils/includes/funding.html" %} + {% regroup page.category_options by category as categories %} + {% for category, options in categories %} + <ul> + <li><h3>{{ category.name }}</h3> + <ul> + {% for option in options %} + <li>{{ option.value }}</li> + {% endfor %} + </ul> + </li> + </ul> + {% endfor %} + {% endblock content %} diff --git a/opentech/public/projects/templates/projects/widgets/categories_widget.html b/opentech/public/projects/templates/projects/widgets/categories_widget.html new file mode 100644 index 000000000..95344239b --- /dev/null +++ b/opentech/public/projects/templates/projects/widgets/categories_widget.html @@ -0,0 +1 @@ +{% spaceless %}<ul class="multiple">{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}</ul>{% endspaceless %} diff --git a/opentech/public/projects/templates/projects/widgets/options_option.html b/opentech/public/projects/templates/projects/widgets/options_option.html new file mode 100644 index 000000000..a1e97f516 --- /dev/null +++ b/opentech/public/projects/templates/projects/widgets/options_option.html @@ -0,0 +1 @@ +<label{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %} style="width:50%;">{{ widget.label }}</label><div class="field-content" style="width:50%;"><div class="input">{% include "django/forms/widgets/input.html" %}</div></div> diff --git a/opentech/public/projects/templates/projects/widgets/options_widget.html b/opentech/public/projects/templates/projects/widgets/options_widget.html new file mode 100644 index 000000000..db195aa39 --- /dev/null +++ b/opentech/public/projects/templates/projects/widgets/options_widget.html @@ -0,0 +1,15 @@ +<li> +<h2>{{ widget.attrs.label_tag }}</h2> +<fieldset> +{% with id=widget.attrs.id %} +<ul{% if id %} id="{{ id }}"{% endif %}class="fields {% if widget.attrs.class %}{{ widget.attrs.class }}{% endif %}">{% for group, options, index in widget.optgroups %} + {% for option in options %} + <li {% if id %} id="{{ id }}_{{ index }}"{% endif %}> + <div class="field checkbox_input boolean_field"> + {% include option.template_name with widget=option %}{% endfor %} + </div> + </li> + {% endfor %} +</ul>{% endwith %} +</fieldset> +</li> diff --git a/opentech/public/projects/widgets.py b/opentech/public/projects/widgets.py new file mode 100644 index 000000000..a17bdac7f --- /dev/null +++ b/opentech/public/projects/widgets.py @@ -0,0 +1,43 @@ +import json + +from django import forms + +from opentech.apply.categories.models import Category + + +class OptionsWidget(forms.CheckboxSelectMultiple): + template_name = 'projects/widgets/options_widget.html' + option_template_name = 'projects/widgets/options_option.html' + +class CategoriesWidget(forms.MultiWidget): + template_name = 'projects/widgets/categories_widget.html' + + def __init__(self, *args, **kwargs): + widgets = [ + OptionsWidget( + attrs={'id': cat.id, 'label_tag': cat.name}, + choices=cat.options.all().values_list('id', 'value'), + ) + for cat in Category.objects.all() + ] + kwargs['widgets'] = widgets + super().__init__(*args, **kwargs) + + def decompress(self, value): + data = json.loads(value) + return [ + data.get(str(widget.attrs['id']), list()) for widget in self.widgets + ] + + def value_from_datadict(self, data, files, name): + data = { + widget.attrs['id']: widget.value_from_datadict(data, files, name + '_%s' % i) + for i, widget in enumerate(self.widgets) + } + return json.dumps(data) + + def get_context(self, *args, **kwargs): + context = super().get_context(*args, **kwargs) + # Mutliwidget kills the wrap_label option when it is building the context + context['wrap_label'] = True + return context -- GitLab