diff --git a/fec/fec/constants.py b/fec/fec/constants.py
index c6534a76b8..22c0e131d3 100644
--- a/fec/fec/constants.py
+++ b/fec/fec/constants.py
@@ -123,3 +123,9 @@
"Rulemakings",
"Other agency actions",
])
+
+update_types = OrderedDict([
+ ("press-release", "Press release"),
+ ("fec-record", "FEC Record"),
+ ("weekly-digest", "Weekly Digest")
+])
diff --git a/fec/fec/static/js/fec.js b/fec/fec/static/js/fec.js
index 2f60c6cd6d..37f922cbfa 100644
--- a/fec/fec/static/js/fec.js
+++ b/fec/fec/static/js/fec.js
@@ -21,6 +21,7 @@ window.$ = window.jQuery = $;
var Sticky = require('component-sticky');
var calendar = require('./calendar');
var calendarHelpers = require('./calendar-helpers');
+var FormNav = require('./form-nav').FormNav;
var legal = require('./legal');
@@ -91,6 +92,11 @@ $(document).ready(function() {
// Initialize filters
var filterPanel = new FilterPanel();
+ if (document.querySelector('.js-form-nav')) {
+ var formNav = document.querySelector('.js-form-nav');
+ new FormNav(formNav);
+ }
+
// Initialize calendar
new calendar.Calendar({
selector: '#calendar',
diff --git a/fec/fec/static/js/form-nav.js b/fec/fec/static/js/form-nav.js
new file mode 100644
index 0000000000..4cd06962e9
--- /dev/null
+++ b/fec/fec/static/js/form-nav.js
@@ -0,0 +1,25 @@
+'use strict';
+
+/* FormNav
+ * Submits the form on select change and clears out unnecessary params
+ */
+
+function FormNav(form) {
+ this.form = form;
+ this.form.addEventListener('change', this.handleChange.bind(this));
+}
+
+FormNav.prototype.handleChange = function() {
+ var allSelects = this.form.querySelectorAll('select,input');
+ // Remove names from all selects with no values
+ for(var i = 0; i < allSelects.length; i++) {
+ var select = allSelects[i];
+ if(select.getAttribute('name') && !select.value) {
+ select.setAttribute('name', '');
+ }
+ }
+
+ this.form.submit();
+};
+
+module.exports = {FormNav: FormNav};
diff --git a/fec/fec/templates/partials/breadcrumbs.html b/fec/fec/templates/partials/breadcrumbs.html
new file mode 100644
index 0000000000..fb17786ae1
--- /dev/null
+++ b/fec/fec/templates/partials/breadcrumbs.html
@@ -0,0 +1,15 @@
+
diff --git a/fec/fec/templates/partials/disclaimer.html b/fec/fec/templates/partials/disclaimer.html
new file mode 100644
index 0000000000..664220024f
--- /dev/null
+++ b/fec/fec/templates/partials/disclaimer.html
@@ -0,0 +1,6 @@
+
diff --git a/fec/fec/templates/partials/update.html b/fec/fec/templates/partials/update.html
new file mode 100644
index 0000000000..15a7c293b5
--- /dev/null
+++ b/fec/fec/templates/partials/update.html
@@ -0,0 +1,18 @@
+{% load wagtailimages_tags %}
+
+
+
+ {{ update.date|date:'F j, Y' }}
+ From: {% if update.get_update_type == 'FEC Record' %}Information Division{% else %}Press Office{% endif %}
+
+
+ {% if update.get_update_type != 'Weekly Digest' %}
+ {{ update.body | truncatewords_html:30 }}
+ {% endif %}
+
+
diff --git a/fec/fec/urls.py b/fec/fec/urls.py
index a253cb5ecd..cface97db0 100644
--- a/fec/fec/urls.py
+++ b/fec/fec/urls.py
@@ -7,6 +7,7 @@
from wagtail.wagtaildocs import urls as wagtaildocs_urls
from wagtail.wagtailcore import urls as wagtail_urls
+from home import views as home_views
from legal import views as legal_views
from search import views as search_views
@@ -17,6 +18,8 @@
url(r'^admin/', include(wagtailadmin_urls)),
url(r'^documents/', include(wagtaildocs_urls)),
+ url(r'^updates/$', home_views.updates),
+
url(r'^search/$', search_views.search, name='search'),
url(r'^legal-resources/$', legal_views.home, name='legal'),
diff --git a/fec/home/migrations/0019_auto_20160907_2152.py b/fec/home/migrations/0019_auto_20160907_2152.py
new file mode 100644
index 0000000000..06b774c7ff
--- /dev/null
+++ b/fec/home/migrations/0019_auto_20160907_2152.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.9 on 2016-09-07 21:52
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import wagtail.contrib.table_block.blocks
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+import wagtail.wagtailimages.blocks
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('wagtailcore', '0028_merge'),
+ ('home', '0018_record_digest_press_release'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PressLandingPage',
+ 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')),
+ ('hero', wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True)),
+ ('option_blocks', wagtail.wagtailcore.fields.StreamField((('option_blocks', wagtail.wagtailcore.blocks.StructBlock((('title', wagtail.wagtailcore.blocks.CharBlock(required=True)), ('intro', wagtail.wagtailcore.blocks.RichTextBlock(blank=False, null=False, required=False)), ('button_text', wagtail.wagtailcore.blocks.CharBlock(blank=False, null=False, required=True)), ('related_page', wagtail.wagtailcore.blocks.PageChooserBlock())))),))),
+ ('feed_intro', wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True)),
+ ('contact_intro', wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ bases=('wagtailcore.page',),
+ ),
+ migrations.AlterField(
+ model_name='digestpage',
+ name='body',
+ field=wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='digestpageauthors',
+ name='role',
+ field=models.CharField(choices=[('author', 'Author'), ('writer', 'Written by'), ('graphics', 'Graphics by'), ('contact', 'Contact')], default='author', max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='pressreleasepage',
+ name='body',
+ field=wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='pressreleasepage',
+ name='category',
+ field=models.CharField(choices=[('audit reports', 'Audit reports'), ('campaign finance data summaries', 'Campaign finance data summaries'), ('commission appointments', 'Commission appointments'), ('disclosure initiatives', 'Disclosure initiatives'), ('enforcement matters', 'Enforcement matters'), ('hearings', 'Hearings'), ('litigation', 'Litigation'), ('non-filer publications', 'Non-filer publications'), ('open meetings and related matters', 'Open meetings and related matters'), ('presidential public funds', 'Presidential public funds'), ('rulemakings', 'Rulemakings'), ('other agency actions', 'Other agency actions')], max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='pressreleasepageauthors',
+ name='role',
+ field=models.CharField(choices=[('author', 'Author'), ('writer', 'Written by'), ('graphics', 'Graphics by'), ('contact', 'Contact')], default='author', max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='recordpage',
+ name='body',
+ field=wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock()), ('html', wagtail.wagtailcore.blocks.RawHTMLBlock()), ('image', wagtail.wagtailimages.blocks.ImageChooserBlock()), ('table', wagtail.contrib.table_block.blocks.TableBlock())), blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='recordpage',
+ name='category',
+ field=models.CharField(choices=[('advisory opinions', 'Advisory opinions'), ('commission', 'Commission'), ('compliance', 'Compliance'), ('litigation', 'Litigation'), ('outreach', 'Outreach'), ('public funding', 'Public funding'), ('regulations', 'Regulations'), ('reporting', 'Reporting'), ('statistics', 'Statistics')], max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='recordpage',
+ name='related_section_title',
+ field=models.CharField(blank=True, default='Explore campaign finance data', max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='recordpage',
+ name='related_section_url',
+ field=models.CharField(blank=True, default='/data/', max_length=255),
+ ),
+ migrations.AlterField(
+ model_name='recordpageauthors',
+ name='role',
+ field=models.CharField(choices=[('author', 'Author'), ('writer', 'Written by'), ('graphics', 'Graphics by'), ('contact', 'Contact')], default='author', max_length=255),
+ ),
+ ]
diff --git a/fec/home/migrations/0021_merge.py b/fec/home/migrations/0021_merge.py
new file mode 100644
index 0000000000..9d299b4ef7
--- /dev/null
+++ b/fec/home/migrations/0021_merge.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.9 on 2016-09-09 23:23
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('home', '0019_auto_20160907_2152'),
+ ('home', '0020_auto_20160909_0139'),
+ ]
+
+ operations = [
+ ]
diff --git a/fec/home/models.py b/fec/home/models.py
index aa594e706c..a504e65e62 100644
--- a/fec/home/models.py
+++ b/fec/home/models.py
@@ -17,7 +17,6 @@
from fec import constants
-
stream_factory = functools.partial(
StreamField,
[
@@ -142,7 +141,6 @@ class Meta:
class RecordPageAuthors(Orderable, PageAuthors):
page = ParentalKey('RecordPage', related_name='authors')
-
def get_previous_record_page():
return RecordPage.objects.order_by('-date', '-pk').first()
@@ -172,6 +170,10 @@ class RecordPage(ContentPage):
def content_section(self):
return ''
+ @property
+ def get_update_type(self):
+ return constants.update_types['fec-record']
+
class DigestPageAuthors(Orderable, PageAuthors):
page = ParentalKey('DigestPage', related_name='authors')
@@ -197,6 +199,10 @@ class DigestPage(ContentPage):
def content_section(self):
return ''
+ @property
+ def get_update_type(self):
+ return constants.update_types['weekly-digest']
+
class PressReleasePageAuthors(Orderable, PageAuthors):
page = ParentalKey('PressReleasePage', related_name='authors')
@@ -225,6 +231,10 @@ class PressReleasePage(ContentPage):
def content_section(self):
return ''
+ @property
+ def get_update_type(self):
+ return constants.update_types['press-release']
+
class CustomPage(Page):
"""Flexible customizable page."""
@@ -240,6 +250,28 @@ class CustomPage(Page):
StreamFieldPanel('sidebar'),
]
+class OptionBlock(blocks.StructBlock):
+ title = blocks.CharBlock(required=True)
+ intro = blocks.RichTextBlock(blank=False, null=False, required=False)
+ button_text = blocks.CharBlock(required=True, null=False, blank=False)
+ related_page = blocks.PageChooserBlock()
+
+class PressLandingPage(Page):
+ hero = stream_factory(null=True, blank=True)
+ option_blocks = StreamField([
+ ('option_blocks', OptionBlock())
+ ])
+
+ feed_intro = stream_factory(null=True, blank=True)
+ contact_intro = stream_factory(null=True, blank=True)
+
+ content_panels = Page.content_panels + [
+ StreamFieldPanel('hero'),
+ StreamFieldPanel('feed_intro'),
+ StreamFieldPanel('contact_intro'),
+ StreamFieldPanel('option_blocks'),
+ ]
+
class CollectionList(blocks.StructBlock):
CHECKLIST = 'check'
BULLET = 'bullet'
diff --git a/fec/home/templates/home/checklist_page.html b/fec/home/templates/home/checklist_page.html
index e95a56a8c8..0b0c7d8a3a 100644
--- a/fec/home/templates/home/checklist_page.html
+++ b/fec/home/templates/home/checklist_page.html
@@ -181,11 +181,6 @@ Need help?
-
+{% include 'partials/disclaimer.html' %}
{% endblock %}
diff --git a/fec/home/templates/home/custom_page.html b/fec/home/templates/home/custom_page.html
index 1701cee4a9..74a815b37c 100644
--- a/fec/home/templates/home/custom_page.html
+++ b/fec/home/templates/home/custom_page.html
@@ -47,10 +47,6 @@
-
+{% include 'partials/disclaimer.html' %}
+
{% endblock %}
diff --git a/fec/home/templates/home/digest_page.html b/fec/home/templates/home/digest_page.html
index aedcd27221..db906d9367 100644
--- a/fec/home/templates/home/digest_page.html
+++ b/fec/home/templates/home/digest_page.html
@@ -10,7 +10,7 @@
Home
›
- Weekly Digests
+ Weekly Digest
›
@@ -22,7 +22,6 @@
diff --git a/fec/home/templates/home/landing_page.html b/fec/home/templates/home/landing_page.html
index 47f1c804fe..d88c13b55d 100644
--- a/fec/home/templates/home/landing_page.html
+++ b/fec/home/templates/home/landing_page.html
@@ -163,12 +163,8 @@ Need help?
-
+ {% include 'partials/disclaimer.html' %}
+
{% endblock %}
diff --git a/fec/home/templates/home/latest_updates.html b/fec/home/templates/home/latest_updates.html
new file mode 100644
index 0000000000..adc5b5e38b
--- /dev/null
+++ b/fec/home/templates/home/latest_updates.html
@@ -0,0 +1,87 @@
+{% extends "base.html" %}
+{% load wagtailcore_tags %}
+{% load staticfiles %}
+{% load filters %}
+
+{% block title %}Latest updates | FEC{% endblock %}
+
+{% block content %}
+
+{% include 'partials/breadcrumbs.html' with page=page_context style='secondary' %}
+
+
+
+
+
+ {% for update in updates %}
+ {% include 'partials/update.html' with update=update %}
+ {% endfor %}
+
+
Page {{ updates.number }} of {{ updates.paginator.num_pages }}
+ {% if updates.has_previous %}
+
Previous
+ {% endif %}
+ {% if updates.has_next %}
+
Next
+ {% endif %}
+
+
+
+
+{% endblock %}
diff --git a/fec/home/templates/home/press_landing_page.html b/fec/home/templates/home/press_landing_page.html
new file mode 100644
index 0000000000..48effafac3
--- /dev/null
+++ b/fec/home/templates/home/press_landing_page.html
@@ -0,0 +1,88 @@
+{% extends "base.html" %}
+{% load wagtailcore_tags %}
+{% load staticfiles %}
+{% load updates %}
+{% block body_class %}template-{{ self.get_verbose_name | slugify }}{% endblock %}
+
+{% block content %}
+
+
+
+
{{ self.title }}
+
{{ self.hero }}
+
+
+
+
+
+
+
+ {% for option in self.option_blocks %}
+
+ {% endfor %}
+
+
+
+ {{ self.feed_intro }}
+
+
+
+ {% press_updates %}
+
+
+
+
+
+ {% include 'partials/disclaimer.html' %}
+
+
+{% endblock %}
diff --git a/fec/home/templates/home/press_release_page.html b/fec/home/templates/home/press_release_page.html
index 8b8b6736bf..13b860a13b 100644
--- a/fec/home/templates/home/press_release_page.html
+++ b/fec/home/templates/home/press_release_page.html
@@ -10,7 +10,7 @@
Home
›
- Press releases
+ Press releases: {{ self.get_category_display }}
›
@@ -68,11 +68,6 @@
-
+{% include 'partials/disclaimer.html' %}
{% endblock %}
diff --git a/fec/home/templates/home/record_page.html b/fec/home/templates/home/record_page.html
index 1701087c65..a189ae7bf9 100644
--- a/fec/home/templates/home/record_page.html
+++ b/fec/home/templates/home/record_page.html
@@ -10,7 +10,7 @@
Home
›
- FEC Record: {{ self.get_category_display }}
+ FEC Record: {{ self.get_category_display }}
›
diff --git a/fec/home/templates/partials/press-feed.html b/fec/home/templates/partials/press-feed.html
new file mode 100644
index 0000000000..0c9db6dbe3
--- /dev/null
+++ b/fec/home/templates/partials/press-feed.html
@@ -0,0 +1,9 @@
+{% comment %}
+ This partial is used by the press_updates inclusion tag in updates.py
+{% endcomment %}
+
+
+ {% for update in updates %}
+ {% include 'partials/update.html' with update=update %}
+ {% endfor %}
+
diff --git a/fec/home/templatetags/filters.py b/fec/home/templatetags/filters.py
index 302666ff53..0c0fe268a6 100644
--- a/fec/home/templatetags/filters.py
+++ b/fec/home/templatetags/filters.py
@@ -7,3 +7,7 @@
@register.filter
def clean_whitespace(value):
return re.sub(r'\s+', '-', value)
+
+@register.filter
+def lookup(dict, arg):
+ return dict.get(arg, '')
diff --git a/fec/home/templatetags/updates.py b/fec/home/templatetags/updates.py
new file mode 100644
index 0000000000..f033061783
--- /dev/null
+++ b/fec/home/templatetags/updates.py
@@ -0,0 +1,21 @@
+import re
+
+from django import template
+from operator import attrgetter
+from itertools import chain
+from home.models import DigestPage
+from home.models import RecordPage
+from home.models import PressReleasePage
+
+register = template.Library()
+
+@register.inclusion_tag('partials/press-feed.html')
+def press_updates():
+ press_releases = PressReleasePage.objects.all()
+ digests = DigestPage.objects.all()
+ updates = sorted(
+ chain(press_releases, digests),
+ key=attrgetter('date'),
+ reverse=True
+ )
+ return {'updates': updates}
diff --git a/fec/home/views.py b/fec/home/views.py
new file mode 100644
index 0000000000..3b6085aeb2
--- /dev/null
+++ b/fec/home/views.py
@@ -0,0 +1,107 @@
+from django.shortcuts import render
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from itertools import chain
+from operator import attrgetter
+from home.models import DigestPage
+from home.models import RecordPage
+from home.models import PressReleasePage
+
+def replace_dash(string):
+ return string.replace('-', ' ')
+
+def replace_space(string):
+ return string.replace(' ', '-')
+
+def get_records(category_list=False, year=False):
+ records = RecordPage.objects.all()
+ if category_list != '':
+ for category in category_list:
+ records = records.filter(category=category)
+ if year != '':
+ records = records.filter(date__year=year)
+ return records
+
+def get_digests(year=False):
+ digests = DigestPage.objects.all()
+ if year != '':
+ digests = digests.filter(date__year=year)
+ return digests
+
+def get_press_releases(category_list=False, year=False):
+ press_releases = PressReleasePage.objects.all()
+ if category_list:
+ for category in category_list:
+ press_releases = press_releases.filter(category=category)
+ if year:
+ press_releases = press_releases.filter(date__year=year)
+ return press_releases
+
+def updates(request):
+ digests = ''
+ records = ''
+ press_releases = ''
+ categories = ''
+
+ # Get values from query
+ update_types = request.GET.getlist('update_type', None)
+ category_list = request.GET.getlist('category', '')
+ year = request.GET.get('year', '')
+
+ category_list = list(map(replace_dash, category_list))
+
+ # If there's a query, only get the types in the query
+ if update_types:
+ if 'for-media' in update_types:
+ press_releases = get_press_releases(category_list=category_list, year=year)
+ records = get_records(category_list=category_list, year=year)
+ if 'for-committees' in update_types:
+ records = get_records(category_list=category_list, year=year)
+ if 'fec-record' in update_types:
+ records = get_records(category_list=category_list, year=year)
+ if 'press-release' in update_types:
+ press_releases = get_press_releases(category_list=category_list, year=year)
+ if 'weekly-digest' in update_types:
+ digests = get_digests(year=year)
+
+ else:
+ # Get everything and filter by year if necessary
+ records = RecordPage.objects.live()
+ digests = DigestPage.objects.live()
+ press_releases = PressReleasePage.objects.live()
+
+ if year:
+ records = records.filter(date__year=year)
+ press_releases = press_releases.filter(date__year=year)
+ digests = digests.filter(date__year=year)
+
+ # Chain all the QuerySets together
+ # via http://stackoverflow.com/a/434755/1864981
+ updates = sorted(
+ chain(press_releases, digests, records),
+ key=attrgetter('date'),
+ reverse=True
+ )
+
+ # Handle pagination
+ page = request.GET.get('page', 1)
+ paginator = Paginator(updates, 20)
+ try:
+ updates = paginator.page(page)
+ except PageNotAnInteger:
+ updates = paginator.page(1)
+ except EmptyPage:
+ updates = paginator.page(paginator.num_pages)
+
+ page_context = {
+ 'title': 'Latest updates',
+ }
+
+ category_list = list(map(replace_space, category_list))
+
+ return render(request, 'home/latest_updates.html', {
+ 'page_context': page_context,
+ 'category_list': category_list,
+ 'update_types': update_types,
+ 'updates': updates,
+ 'year': year
+ })
diff --git a/package.json b/package.json
index 2b5d5bdc3b..4201a57e65 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,7 @@
"ace-builds": "1.2.2",
"aria-accordion": "0.1.1",
"component-sticky": "1.0.0",
- "fec-style": "4.0.2",
+ "fec-style": "5.0.0",
"fullcalendar": "2.5.0",
"glossary-panel": "0.1.2",
"jquery": "2.1.4",