diff --git a/opentech/projects/migrations/0001_initial.py b/opentech/projects/migrations/0001_initial.py new file mode 100644 index 0000000000000000000000000000000000000000..91221490357a3d5d0f5e7cfa753d447cfe6284ad --- /dev/null +++ b/opentech/projects/migrations/0001_initial.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.8 on 2018-01-05 16:18 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion +import modelcluster.fields +import wagtail.wagtailcore.blocks +import wagtail.wagtailcore.fields +import wagtail.wagtaildocs.blocks +import wagtail.wagtailembeds.blocks +import wagtail.wagtailimages.blocks +import wagtail.wagtailsnippets.blocks + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('wagtailcore', '0040_page_draft_title'), + ('images', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='ProjectIndexPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ('social_text', models.CharField(blank=True, max_length=255)), + ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)), + ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)), + ('introduction', models.TextField(blank=True)), + ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', models.Model), + ), + migrations.CreateModel( + name='ProjectPage', + fields=[ + ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.Page')), + ('social_text', models.CharField(blank=True, max_length=255)), + ('listing_title', models.CharField(blank=True, help_text='Override the page title used when this page appears in listings', max_length=255)), + ('listing_summary', models.CharField(blank=True, help_text="The text summary used when this page appears in listings. It's also used as the description for search engines if the 'Search description' field above is not defined.", max_length=255)), + ('introduction', models.TextField(blank=True)), + ('body', wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title', icon='title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('image', wagtail.wagtailcore.blocks.StructBlock((('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('caption', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('quote', wagtail.wagtailcore.blocks.StructBlock((('quote', wagtail.wagtailcore.blocks.CharBlock(classname='title')), ('attribution', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('embed', wagtail.wagtailembeds.blocks.EmbedBlock()), ('call_to_action', wagtail.wagtailsnippets.blocks.SnippetChooserBlock('utils.CallToActionSnippet', template='blocks/call_to_action_block.html')), ('document', wagtail.wagtailcore.blocks.StructBlock((('document', wagtail.wagtaildocs.blocks.DocumentChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(required=False)))))))), + ('listing_image', models.ForeignKey(blank=True, help_text='Choose the image you wish to be displayed when this page appears in listings', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ('social_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ], + options={ + 'abstract': False, + }, + bases=('wagtailcore.page', models.Model), + ), + migrations.CreateModel( + name='ProjectPageRelatedPage', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sort_order', models.IntegerField(blank=True, editable=False, null=True)), + ('page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.Page')), + ('source_page', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='related_pages', to='projects.ProjectPage')), + ], + options={ + 'ordering': ['sort_order'], + 'abstract': False, + }, + ), + ] diff --git a/opentech/projects/models.py b/opentech/projects/models.py index 71a836239075aa6e6e4ecb700e9c42c95c022d91..037e691682145d92c08e2abd6357aa37e3747b20 100644 --- a/opentech/projects/models.py +++ b/opentech/projects/models.py @@ -1,3 +1,85 @@ from django.db import models +from django.conf import settings +from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator -# Create your models here. +from modelcluster.fields import ParentalKey +from wagtail.wagtailadmin.edit_handlers import ( + FieldPanel, + InlinePanel, + PageChooserPanel, + StreamFieldPanel, +) + +from wagtail.wagtailcore.fields import StreamField +from wagtail.wagtailsearch import index + +from opentech.utils.blocks import StoryBlock +from opentech.utils.models import ( + BasePage, + RelatedPage, +) + + +class ProjectPageRelatedPage(RelatedPage): + source_page = ParentalKey('ProjectPage', related_name='related_pages') + + panels = [ + PageChooserPanel('page', 'projects.ProjectPage'), + ] + + +class ProjectPage(BasePage): + subpage_types = [] + parent_page_types = ['ProjectIndexPage'] + + introduction = models.TextField(blank=True) + body = StreamField(StoryBlock()) + + # Fields to add: + # otf_status + # status + # social_accounts + # website + # funding + + search_fields = BasePage.search_fields + [ + index.SearchField('introduction'), + index.SearchField('body'), + ] + + content_panels = BasePage.content_panels + [ + FieldPanel('introduction'), + StreamFieldPanel('body'), + InlinePanel('related_pages', label="Related pages"), + ] + + +class ProjectIndexPage(BasePage): + + introduction = models.TextField(blank=True) + + content_panels = BasePage.content_panels + [ + FieldPanel('introduction'), + ] + + search_fields = BasePage.search_fields + [ + index.SearchField('introduction'), + ] + + def get_context(self, request, *args, **kwargs): + context = super().get_context(request, *args, **kwargs) + subpages = self.get_children().live() + per_page = settings.DEFAULT_PER_PAGE + page_number = request.GET.get('page') + paginator = Paginator(subpages, per_page) + + try: + subpages = paginator.page(page_number) + except PageNotAnInteger: + subpages = paginator.page(1) + except EmptyPage: + subpages = paginator.page(paginator.num_pages) + + context['subpages'] = subpages + + return context diff --git a/opentech/settings/base.py b/opentech/settings/base.py index ced6888bc9e76d811695568ff455bf531c856afa..20e1f931d20ed890fd44616706fbac86ed0016ce 100644 --- a/opentech/settings/base.py +++ b/opentech/settings/base.py @@ -27,6 +27,7 @@ INSTALLED_APPS = [ 'opentech.public.navigation', 'opentech.public.news', 'opentech.public.people', + 'opentech.projects', 'opentech.public.search', 'opentech.public.standardpages', 'opentech.public.utils',