From d6b6f047a68b8a4ee6eccd0f54062265c6005ccf Mon Sep 17 00:00:00 2001
From: Todd Dembrey <todd.dembrey@torchbox.com>
Date: Fri, 5 Jan 2018 11:21:51 +0000
Subject: [PATCH] Pull in latest changes to cookiecutter build

---
 .travis.yml                                   | 13 ++++++++++++
 README.md                                     | 16 ++++++++++++++
 .../navigation/migrations/0001_initial.py     |  2 +-
 opentech/navigation/models.py                 |  2 +-
 opentech/news/migrations/0001_initial.py      |  2 +-
 opentech/people/migrations/0001_initial.py    |  2 +-
 .../people/admin/js/update_person_title.js    | 21 +++++++++++++++++++
 opentech/people/wagtail_hooks.py              |  2 +-
 .../standardpages/migrations/0001_initial.py  |  2 +-
 opentech/static_src/package.json              |  2 +-
 opentech/templates/blocks/quote_block.html    |  5 +++--
 opentech/utils/blocks.py                      |  2 +-
 vagrant/provision.sh                          | 10 +++++++--
 13 files changed, 69 insertions(+), 12 deletions(-)
 create mode 100644 opentech/people/static/people/admin/js/update_person_title.js

diff --git a/.travis.yml b/.travis.yml
index 26694aaaf..74c62d229 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -35,6 +35,19 @@ install:
   # Install project dependencies
   - pip install -r requirements.txt
 
+  # Install test dependencies
+  - pip install flake8
+
+  # Install node
+  - nvm install 8
+
+  # Install node dependencies
+  - cd ./opentech/static_src && npm install --quiet
+
+  # Build the static files
+  - cd ./opentech/static_src && npm run build:prod
+
+
 # Run the tests
 script:
   # Run python code style checks
diff --git a/README.md b/README.md
index c7d2f35c6..df325faa3 100644
--- a/README.md
+++ b/README.md
@@ -34,3 +34,19 @@ djrun
 ```
 
 This will make the site available on the host machine at: http://127.0.0.1:8000/
+
+# Updating front-end files
+
+Any changes made to sass or js files will need to be recompiled using:
+
+``` bash
+yarn build
+```
+
+Alternatively you can run the watcher that will rebuild on change to files:
+
+``` bash
+yarn start
+```
+
+Both commands should be run from within the `opentech/static_src` folder in the vagrant box.
diff --git a/opentech/navigation/migrations/0001_initial.py b/opentech/navigation/migrations/0001_initial.py
index 260fefa7b..f213cf804 100644
--- a/opentech/navigation/migrations/0001_initial.py
+++ b/opentech/navigation/migrations/0001_initial.py
@@ -23,7 +23,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('primary_navigation', wagtail.wagtailcore.fields.StreamField((('link', wagtail.wagtailcore.blocks.StructBlock((('page', wagtail.wagtailcore.blocks.PageChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(help_text="Leave blank to use the page's own title", required=False))))),), blank=True, help_text='Main site navigation')),
                 ('secondary_navigation', wagtail.wagtailcore.fields.StreamField((('link', wagtail.wagtailcore.blocks.StructBlock((('page', wagtail.wagtailcore.blocks.PageChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(help_text="Leave blank to use the page's own title", required=False))))),), blank=True, help_text='Alternative navigation')),
-                ('footer_navigation', wagtail.wagtailcore.fields.StreamField((('column', wagtail.wagtailcore.blocks.StructBlock((('heading', wagtail.wagtailcore.blocks.CharBlock(blank=True, help_text='Leave blank if no header required.')), ('links', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailcore.blocks.StructBlock((('page', wagtail.wagtailcore.blocks.PageChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(help_text="Leave blank to use the page's own title", required=False))))))))),), blank=True, help_text='Multiple columns of footer links with optional header.')),
+                ('footer_navigation', wagtail.wagtailcore.fields.StreamField((('column', wagtail.wagtailcore.blocks.StructBlock((('heading', wagtail.wagtailcore.blocks.CharBlock(required=False, help_text='Leave blank if no header required.')), ('links', wagtail.wagtailcore.blocks.ListBlock(wagtail.wagtailcore.blocks.StructBlock((('page', wagtail.wagtailcore.blocks.PageChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(help_text="Leave blank to use the page's own title", required=False))))))))),), blank=True, help_text='Multiple columns of footer links with optional header.')),
                 ('footer_links', wagtail.wagtailcore.fields.StreamField((('link', wagtail.wagtailcore.blocks.StructBlock((('page', wagtail.wagtailcore.blocks.PageChooserBlock()), ('title', wagtail.wagtailcore.blocks.CharBlock(help_text="Leave blank to use the page's own title", required=False))))),), blank=True, help_text='Single list of elements at the base of the page.')),
                 ('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.Site')),
             ],
diff --git a/opentech/navigation/models.py b/opentech/navigation/models.py
index 53d73d84a..c270b1478 100644
--- a/opentech/navigation/models.py
+++ b/opentech/navigation/models.py
@@ -16,7 +16,7 @@ class LinkBlock(blocks.StructBlock):
 
 
 class LinkColumnWithHeader(blocks.StructBlock):
-    heading = blocks.CharBlock(blank=True, help_text="Leave blank if no header required.")
+    heading = blocks.CharBlock(required=False, help_text="Leave blank if no header required.")
     links = blocks.ListBlock(LinkBlock())
 
     class Meta:
diff --git a/opentech/news/migrations/0001_initial.py b/opentech/news/migrations/0001_initial.py
index e8a803d18..e9c99a7d7 100644
--- a/opentech/news/migrations/0001_initial.py
+++ b/opentech/news/migrations/0001_initial.py
@@ -55,7 +55,7 @@ class Migration(migrations.Migration):
                 ('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)),
                 ('publication_date', models.DateTimeField(blank=True, help_text='Use this field to override the date that the news item appears to have been published.', null=True)),
                 ('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')), ('citation_link', wagtail.wagtailcore.blocks.URLBlock(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))))), ('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')),
             ],
diff --git a/opentech/people/migrations/0001_initial.py b/opentech/people/migrations/0001_initial.py
index cc6045ca5..61ae8c60b 100644
--- a/opentech/people/migrations/0001_initial.py
+++ b/opentech/people/migrations/0001_initial.py
@@ -55,7 +55,7 @@ class Migration(migrations.Migration):
                 ('job_title', models.CharField(max_length=255)),
                 ('introduction', models.TextField(blank=True)),
                 ('website', models.URLField(blank=True, max_length=255)),
-                ('biography', 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')), ('citation_link', wagtail.wagtailcore.blocks.URLBlock(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)))))), blank=True)),
+                ('biography', 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)))))), blank=True)),
                 ('email', models.EmailField(blank=True, max_length=254)),
                 ('mobile_phone', models.CharField(blank=True, max_length=255)),
                 ('landline_phone', models.CharField(blank=True, max_length=255)),
diff --git a/opentech/people/static/people/admin/js/update_person_title.js b/opentech/people/static/people/admin/js/update_person_title.js
new file mode 100644
index 000000000..589f89eec
--- /dev/null
+++ b/opentech/people/static/people/admin/js/update_person_title.js
@@ -0,0 +1,21 @@
+$(document).ready(function () {
+    var $lastNameInput = $('#id_last_name');
+    var $firstNameInput = $('#id_first_name');
+    var $titleInput = $('#id_title');
+    var $slugInput = $('#id_slug');
+
+    $firstNameInput.on('input', function () {joinFirstNameLastName();});
+
+    $lastNameInput.on('input', function () {joinFirstNameLastName();});
+
+    function joinFirstNameLastName() {
+        var firstName = $firstNameInput.val();
+        var lastName = $lastNameInput.val();
+        var title = firstName + ' ' + lastName;
+
+        $slugInput.data('previous-val', $slugInput.val());
+        $titleInput.data('previous-val', $titleInput.val());
+        $titleInput.val(title);
+        $titleInput.blur();  // Trigger slug update
+    }
+});
diff --git a/opentech/people/wagtail_hooks.py b/opentech/people/wagtail_hooks.py
index 9100b81a8..ceaff4b99 100644
--- a/opentech/people/wagtail_hooks.py
+++ b/opentech/people/wagtail_hooks.py
@@ -7,5 +7,5 @@ from wagtail.wagtailcore import hooks
 @hooks.register('insert_editor_js')
 def editor_js():
     return mark_safe(
-        '<script src="%s"></script>' % static('js/update_person_title.js')
+        '<script src="%s"></script>' % static('people/admin/js/update_person_title.js')
     )
diff --git a/opentech/standardpages/migrations/0001_initial.py b/opentech/standardpages/migrations/0001_initial.py
index 51278eef3..3e244f23e 100644
--- a/opentech/standardpages/migrations/0001_initial.py
+++ b/opentech/standardpages/migrations/0001_initial.py
@@ -48,7 +48,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)),
-                ('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')), ('citation_link', wagtail.wagtailcore.blocks.URLBlock(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))))), ('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')),
             ],
diff --git a/opentech/static_src/package.json b/opentech/static_src/package.json
index 44baaff12..0e844e9bf 100755
--- a/opentech/static_src/package.json
+++ b/opentech/static_src/package.json
@@ -69,7 +69,7 @@
     "watch": "npm-run-all -p watch:*",
 
     "//[ Syncs ]//": "",
-    "sync:fonts": "rsync -rtvu --delete $npm_package_config_src_font/ $npm_package_config_dest_font/",
+    "sync:font": "rsync -rtvu --delete $npm_package_config_src_font/ $npm_package_config_dest_font/",
     "sync:img": "rsync -rtvu --delete $npm_package_config_src_img/ $npm_package_config_dest_img/",
     "sync": "npm-run-all -p sync:*",
 
diff --git a/opentech/templates/blocks/quote_block.html b/opentech/templates/blocks/quote_block.html
index 5f95799aa..33bea5ac6 100644
--- a/opentech/templates/blocks/quote_block.html
+++ b/opentech/templates/blocks/quote_block.html
@@ -1,3 +1,4 @@
-<blockquote{% if value.citation_link %} cite="{{ value.citation_link }}"{% endif %}>
-    {{ value.quote }}
+<blockquote>
+    <p>{{ value.quote }}</p>
+    {% if value.attribution %}{{ value.attribution }}{% endif %}
 </blockquote>
diff --git a/opentech/utils/blocks.py b/opentech/utils/blocks.py
index 7c97eec03..973b0372b 100644
--- a/opentech/utils/blocks.py
+++ b/opentech/utils/blocks.py
@@ -25,7 +25,7 @@ class DocumentBlock(blocks.StructBlock):
 
 class QuoteBlock(blocks.StructBlock):
     quote = blocks.CharBlock(classname="title")
-    citation_link = blocks.URLBlock(required=False)
+    attribution = blocks.CharBlock(required=False)
 
     class Meta:
         icon = "openquote"
diff --git a/vagrant/provision.sh b/vagrant/provision.sh
index 77866f4b2..4ccbdd8e3 100755
--- a/vagrant/provision.sh
+++ b/vagrant/provision.sh
@@ -50,7 +50,13 @@ alias djrunp="dj runserver_plus 0.0.0.0:8000"
 source $VIRTUALENV_DIR/bin/activate
 export PS1="[$PROJECT_NAME \W]\\$ "
 cd $PROJECT_DIR
+EOF
 
-alias djtestapply="dj test opentech.apply --keepdb; mypy ."
+# Install node.js and npm
+su - vagrant -c "curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -"
+su - vagrant -c "sudo apt-get install -y nodejs"
 
-EOF
+# Build the static files
+su - vagrant -c "sudo npm install -g yarn"
+su - vagrant -c "cd $PROJECT_DIR/$DEST_DIR/$PROJECT_NAME/$PROJECT_NAME/static_src; yarn install"
+su - vagrant -c "cd $PROJECT_DIR/$DEST_DIR/$PROJECT_NAME/$PROJECT_NAME/static_src; yarn build"
-- 
GitLab