From 826feb21442b56e0fff3c5b46bd4d3d99b8bce36 Mon Sep 17 00:00:00 2001 From: Todd Dembrey <todd.dembrey@torchbox.com> Date: Mon, 15 Jan 2018 17:14:10 +0000 Subject: [PATCH] Add more of the project fields --- .../projects/migrations/0001_initial.py | 22 ++++++- opentech/public/projects/models.py | 59 ++++++++++++++++++- .../templates/projects/project_page.html | 33 +++++++++++ 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 opentech/public/projects/templates/projects/project_page.html diff --git a/opentech/public/projects/migrations/0001_initial.py b/opentech/public/projects/migrations/0001_initial.py index 912214903..eaa4ee3ef 100644 --- a/opentech/public/projects/migrations/0001_initial.py +++ b/opentech/public/projects/migrations/0001_initial.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Generated by Django 1.11.8 on 2018-01-05 16:18 +# Generated by Django 1.11.8 on 2018-01-15 16:50 from __future__ import unicode_literals from django.db import migrations, models @@ -18,11 +18,19 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('wagtailcore', '0040_page_draft_title'), ('images', '0001_initial'), + ('wagtailcore', '0040_page_draft_title'), ] operations = [ + migrations.CreateModel( + name='ProjectContactDetails', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('service', models.CharField(choices=[('website', 'Main Website URL'), ('twitter', 'Twitter Handle'), ('github', 'Github Organisation or Project')], max_length=200)), + ('value', models.CharField(max_length=255)), + ], + ), migrations.CreateModel( name='ProjectIndexPage', fields=[ @@ -31,6 +39,7 @@ class Migration(migrations.Migration): ('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)), + ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), ('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')), ], @@ -47,7 +56,9 @@ class Migration(migrations.Migration): ('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)))))))), + ('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)), ('job_title', 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)))))))), + ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), + ('icon', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='images.CustomImage')), ('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')), ], @@ -69,4 +80,9 @@ class Migration(migrations.Migration): 'abstract': False, }, ), + migrations.AddField( + model_name='projectcontactdetails', + name='project_page', + field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='contact_details', to='projects.ProjectPage'), + ), ] diff --git a/opentech/public/projects/models.py b/opentech/public/projects/models.py index 004942abf..3535dc2cb 100644 --- a/opentech/public/projects/models.py +++ b/opentech/public/projects/models.py @@ -1,6 +1,8 @@ from django.db import models from django.conf import settings +from django.core.exceptions import ValidationError from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator +from django.core.validators import URLValidator from modelcluster.fields import ParentalKey from wagtail.wagtailadmin.edit_handlers import ( @@ -11,6 +13,7 @@ from wagtail.wagtailadmin.edit_handlers import ( ) from wagtail.wagtailcore.fields import StreamField +from wagtail.wagtailimages.edit_handlers import ImageChooserPanel from wagtail.wagtailsearch import index from opentech.public.utils.blocks import StoryBlock @@ -20,6 +23,51 @@ from opentech.public.utils.models import ( ) +class ProjectContactDetails(models.Model): + project_page = ParentalKey( + 'ProjectPage', + related_name='contact_details' + ) + site_titles = ( + ('website', "Main Website URL"), + ('twitter', "Twitter Handle"), + ('github', "Github Organisation or Project"), + ) + site_urls = ( + ('website', ''), + ('twitter', 'https://twitter.com/'), + ('github', 'https://github.com/'), + ) + service = models.CharField( + max_length=200, + choices=site_titles, + ) + value = models.CharField(max_length=255) + + @property + def url(self): + return dict(self.site_urls)[self.service] + self.value + + def service_name(self): + site_display = { + 'twitter': '@' + self.value, + 'github': 'Github', + 'website': 'Main Website', + } + return site_display[self.service] + + def clean(self): + if self.service == 'twitter' and self.value.startswith('@'): + self.username = self.username[1:] + + if self.service == 'website': + validate = URLValidator() + try: + validate(self.value) + except ValidationError as e: + raise ValidationError({'value': e}) + + class ProjectPageRelatedPage(RelatedPage): source_page = ParentalKey('ProjectPage', related_name='related_pages') @@ -33,13 +81,18 @@ class ProjectPage(BasePage): parent_page_types = ['ProjectIndexPage'] introduction = models.TextField(blank=True) + icon = models.ForeignKey( + 'images.CustomImage', + null=True, + blank=True, + related_name='+', + on_delete=models.SET_NULL + ) body = StreamField(StoryBlock()) # Fields to add: # otf_status # status - # social_accounts - # website # funding search_fields = BasePage.search_fields + [ @@ -48,8 +101,10 @@ class ProjectPage(BasePage): ] content_panels = BasePage.content_panels + [ + ImageChooserPanel('icon'), FieldPanel('introduction'), StreamFieldPanel('body'), + InlinePanel('contact_details', label="Contact Details"), InlinePanel('related_pages', label="Related pages"), ] diff --git a/opentech/public/projects/templates/projects/project_page.html b/opentech/public/projects/templates/projects/project_page.html new file mode 100644 index 000000000..a6740d230 --- /dev/null +++ b/opentech/public/projects/templates/projects/project_page.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% load wagtailcore_tags wagtailimages_tags %} + + +{% block content %} + <div class="intro"> + <div class="container"> + {% if page.icon %} + {% image page.icon original %} + {% endif %} + <h1>{{ page.title }}</h1> + {% if page.introduction %} + <p>{{ page.introduction }}</p> + {% endif %} + + {{ page.body }} + + {% with contact_details=page.contact_details.all %} + {% if contact_details %} + {% for contact in contact_details %} + <p><a href="{{ contact.url }}"> + <span class="icon {{ contact.service }}">{{ contact.service_name }} + </a></p> + {% endfor %} + + {% endif %} + {% endwith %} + + </div> + </div> + +{% endblock content %} -- GitLab