Selaa lähdekoodia

Initial styling without merge errors

Edd Baldry 8 vuotta sitten
vanhempi
commit
e0b93c6aec
47 muutettua tiedostoa jossa 1466 lisäystä ja 368 poistoa
  1. 0 1
      README
  2. 7 14
      bakerydemo/base/blocks.py
  3. 18 0
      bakerydemo/base/fixtures/bakerydemo.json
  4. 307 0
      bakerydemo/base/fixtures/breads.json
  5. 10 0
      bakerydemo/base/fixtures/collections.json
  6. 217 0
      bakerydemo/base/fixtures/images.json
  7. 0 0
      bakerydemo/base/management/__init__.py
  8. 0 0
      bakerydemo/base/management/commands/__init__.py
  9. 13 0
      bakerydemo/base/management/commands/load_initial_data.py
  10. 124 0
      bakerydemo/base/migrations/0001_initial.py
  11. 30 0
      bakerydemo/base/migrations/0002_auto_20170210_1217.py
  12. 23 0
      bakerydemo/base/migrations/0003_footertext.py
  13. 19 0
      bakerydemo/base/migrations/0004_auto_20170210_1420.py
  14. 67 37
      bakerydemo/base/models.py
  15. 13 2
      bakerydemo/base/templatetags/navigation_tags.py
  16. 82 0
      bakerydemo/blog/migrations/0001_initial.py
  17. 77 32
      bakerydemo/blog/models.py
  18. 77 0
      bakerydemo/breads/migrations/0001_initial.py
  19. 16 16
      bakerydemo/breads/models.py
  20. 62 0
      bakerydemo/locations/migrations/0001_initial.py
  21. 16 14
      bakerydemo/locations/models.py
  22. 0 0
      bakerydemo/search/__init__.py
  23. 57 0
      bakerydemo/search/views.py
  24. 5 2
      bakerydemo/settings/base.py
  25. 2 10
      bakerydemo/settings/dev.py
  26. 0 175
      bakerydemo/settings/production.py
  27. 4 4
      bakerydemo/templates/about_page.html
  28. 12 2
      bakerydemo/templates/base.html
  29. 1 1
      bakerydemo/templates/base/about_page.html
  30. 11 14
      bakerydemo/templates/base/form_page.html
  31. 7 9
      bakerydemo/templates/base/form_page_landing.html
  32. 4 0
      bakerydemo/templates/base/include/footer.html
  33. 8 8
      bakerydemo/templates/blocks/heading_block.html
  34. 2 4
      bakerydemo/templates/blocks/image_block.html
  35. 1 1
      bakerydemo/templates/blocks/paragraph_block.html
  36. 9 3
      bakerydemo/templates/blog/blog_index_page.html
  37. 4 4
      bakerydemo/templates/blog/blog_page.html
  38. 1 2
      bakerydemo/templates/breads/breads_index_page.html
  39. 38 2
      bakerydemo/templates/locations/location_page.html
  40. 14 0
      bakerydemo/templates/search/search_box.html
  41. 33 0
      bakerydemo/templates/search/search_results.html
  42. 0 4
      bakerydemo/templates/tags/sidebar_menu.html
  43. 1 2
      bakerydemo/templates/tags/top_menu.html
  44. 1 0
      bakerydemo/templates/tags/top_menu_children.html
  45. 4 2
      bakerydemo/urls.py
  46. 67 2
      readme.md
  47. 2 1
      vagrant/provision.sh

+ 0 - 1
README

@@ -1 +0,0 @@
-Hello

+ 7 - 14
bakerydemo/base/blocks.py

@@ -1,14 +1,7 @@
 from wagtail.wagtailimages.blocks import ImageChooserBlock
 from wagtail.wagtailembeds.blocks import EmbedBlock
 from wagtail.wagtailcore.blocks import (
-    StructBlock,
-    TextBlock,
-    StreamBlock,
-    RichTextBlock,
-    CharBlock,
-    ListBlock,
-    ChoiceBlock,
-    PageChooserBlock
+    CharBlock, ChoiceBlock, RichTextBlock, StreamBlock, StructBlock, TextBlock,
 )
 
 
@@ -25,11 +18,11 @@ class ImageBlock(StructBlock):
 class HeadingBlock(StructBlock):
     heading_text = CharBlock(classname="title", required=True)
     size = ChoiceBlock(choices=[
-            ('', 'Select a header size'),
-            ('h2', 'H2'),
-            ('h3', 'H3'),
-            ('h4', 'H4')
-        ], blank=True, required=False)
+        ('', 'Select a header size'),
+        ('h2', 'H2'),
+        ('h3', 'H3'),
+        ('h4', 'H4')
+    ], blank=True, required=False)
 
     class Meta:
         icon = "title"
@@ -39,7 +32,7 @@ class HeadingBlock(StructBlock):
 class BlockQuote(StructBlock):
     text = TextBlock(),
     attribute_name = CharBlock(
-            blank=True, required=False, label='e.g. Guy Picciotto')
+        blank=True, required=False, label='e.g. Guy Picciotto')
 
     class Meta:
         icon = "fa-quote-left"

+ 18 - 0
bakerydemo/base/fixtures/bakerydemo.json

@@ -0,0 +1,18 @@
+[{
+    "model": "auth.user",
+    "pk": 1,
+    "fields": {
+        "password": "pbkdf2_sha256$30000$E9Uhcl4mkQtv$WxS3m0ZzdJXWmI7nAae9wmybd2L2HK5zgjHHXc65+oo=",
+        "last_login": null,
+        "is_superuser": true,
+        "username": "admin",
+        "first_name": "",
+        "last_name": "",
+        "email": "",
+        "is_staff": true,
+        "is_active": true,
+        "date_joined": "2017-02-10T12:48:21.920Z",
+        "groups": [],
+        "user_permissions": []
+    }
+}]

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 307 - 0
bakerydemo/base/fixtures/breads.json


+ 10 - 0
bakerydemo/base/fixtures/collections.json

@@ -0,0 +1,10 @@
+[{
+  "model": "wagtailcore.collection",
+  "pk": 2,
+  "fields": {
+    "path": "00010001",
+    "depth": 2,
+    "numchild": 0,
+    "name": "Breads"
+  }
+}]

+ 217 - 0
bakerydemo/base/fixtures/images.json

@@ -0,0 +1,217 @@
+[
+{
+  "model": "wagtailimages.image",
+  "pk": 2,
+  "fields": {
+    "collection": 2,
+    "title": "Anadama",
+    "file": "original_images/Anadama_bread_1.jpg",
+    "width": 4752,
+    "height": 3168,
+    "created_at": "2017-02-10T13:03:58.541Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 3,
+  "fields": {
+    "collection": 2,
+    "title": "Anpan",
+    "file": "original_images/Bean-jam-bunanpankatori-cityjapan.JPG",
+    "width": 800,
+    "height": 640,
+    "created_at": "2017-02-10T13:05:45.828Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 4,
+  "fields": {
+    "collection": 2,
+    "title": "Bazin",
+    "file": "original_images/Bazin.jpg",
+    "width": 1600,
+    "height": 1200,
+    "created_at": "2017-02-10T13:08:04.012Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 5,
+  "fields": {
+    "collection": 2,
+    "title": "Belgian Waffle",
+    "file": "original_images/Belgische_waffeln.jpg",
+    "width": 2025,
+    "height": 1536,
+    "created_at": "2017-02-10T13:10:05.988Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 6,
+  "fields": {
+    "collection": 2,
+    "title": "Bhakri",
+    "file": "original_images/Another_Vegetarian_Meal.jpg",
+    "width": 1600,
+    "height": 1200,
+    "created_at": "2017-02-10T13:15:25.730Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 7,
+  "fields": {
+    "collection": 2,
+    "title": "Black Bread",
+    "file": "original_images/Mischbrot-1.jpg",
+    "width": 2000,
+    "height": 1500,
+    "created_at": "2017-02-10T13:17:14.968Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 8,
+  "fields": {
+    "collection": 2,
+    "title": "Appam",
+    "file": "original_images/Appam_served_with_Coconut_Milk_in_Tamil_Nadu.JPG",
+    "width": 2362,
+    "height": 1064,
+    "created_at": "2017-02-10T13:19:24.588Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 9,
+  "fields": {
+    "collection": 1,
+    "title": "Arepa",
+    "file": "original_images/Arepa_asada.JPG",
+    "width": 1600,
+    "height": 1200,
+    "created_at": "2017-02-10T13:22:40.113Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 10,
+  "fields": {
+    "collection": 2,
+    "title": "Bolani",
+    "file": "original_images/Bolani_Afghan_bread_01.jpg",
+    "width": 1280,
+    "height": 960,
+    "created_at": "2017-02-10T13:24:28.559Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 11,
+  "fields": {
+    "collection": 2,
+    "title": "Baguette",
+    "file": "original_images/Baguette_de_pain_WikiCheese_Lausanne.jpg",
+    "width": 4032,
+    "height": 1958,
+    "created_at": "2017-02-10T13:24:47.838Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 12,
+  "fields": {
+    "collection": 2,
+    "title": "Bammie",
+    "file": "original_images/Bammies.jpg",
+    "width": 1700,
+    "height": 1527,
+    "created_at": "2017-02-10T13:27:19.252Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+},
+{
+  "model": "wagtailimages.image",
+  "pk": 13,
+  "fields": {
+    "collection": 1,
+    "title": "Bagel",
+    "file": "original_images/Plain-Bagel.jpg",
+    "width": 1920,
+    "height": 1660,
+    "created_at": "2017-02-10T13:28:30.500Z",
+    "uploaded_by_user": 1,
+    "focal_point_x": null,
+    "focal_point_y": null,
+    "focal_point_width": null,
+    "focal_point_height": null,
+    "file_size": null
+  }
+}]

+ 0 - 0
bakerydemo/base/management/__init__.py


+ 0 - 0
bakerydemo/base/management/commands/__init__.py


+ 13 - 0
bakerydemo/base/management/commands/load_initial_data.py

@@ -0,0 +1,13 @@
+import os
+
+from django.conf import settings
+from django.core.management.base import BaseCommand
+from django.core.management import call_command
+
+
+class Command(BaseCommand):
+    def handle(self, **options):
+        fixtures_dir = os.path.join(settings.BASE_DIR, 'base', 'fixtures')
+        fixture_file = os.path.join(fixtures_dir, 'bakerydemo.json')
+
+        call_command('loaddata', fixture_file, verbosity=0)

+ 124 - 0
bakerydemo/base/migrations/0001_initial.py

@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 12:17
+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.wagtailembeds.blocks
+import wagtail.wagtailimages.blocks
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('wagtailcore', '0032_add_bulk_delete_page_permission'),
+        ('wagtailimages', '0017_reduce_focal_point_key_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='AboutLocationRelationship',
+            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)),
+            ],
+            options={
+                'abstract': False,
+                'ordering': ['sort_order'],
+            },
+        ),
+        migrations.CreateModel(
+            name='AboutPage',
+            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')),
+                ('body', wagtail.wagtailcore.fields.StreamField((('heading_block', wagtail.wagtailcore.blocks.StructBlock((('heading_text', wagtail.wagtailcore.blocks.CharBlock(classname='title', required=True)), ('size', wagtail.wagtailcore.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a header size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))))), ('paragraph_block', wagtail.wagtailcore.blocks.RichTextBlock(icon='fa-paragraph', template='blocks/paragraph_block.html')), ('image_block', wagtail.wagtailcore.blocks.StructBlock((('image', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('attribution', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('block_quote', wagtail.wagtailcore.blocks.StructBlock((('attribute_name', wagtail.wagtailcore.blocks.CharBlock(blank=True, label='e.g. Guy Picciotto', required=False)),))), ('embed_block', wagtail.wagtailembeds.blocks.EmbedBlock(help_text='Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks', icon='fa-s15', template='blocks/embed_block.html'))), blank=True, verbose_name='About page detail')),
+                ('image', models.ForeignKey(blank=True, help_text='Location image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='FormField',
+            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)),
+                ('label', models.CharField(help_text='The label of the form field', max_length=255, verbose_name='label')),
+                ('field_type', models.CharField(choices=[('singleline', 'Single line text'), ('multiline', 'Multi-line text'), ('email', 'Email'), ('number', 'Number'), ('url', 'URL'), ('checkbox', 'Checkbox'), ('checkboxes', 'Checkboxes'), ('dropdown', 'Drop down'), ('radio', 'Radio buttons'), ('date', 'Date'), ('datetime', 'Date/time')], max_length=16, verbose_name='field type')),
+                ('required', models.BooleanField(default=True, verbose_name='required')),
+                ('choices', models.TextField(blank=True, help_text='Comma separated list of choices. Only applicable in checkboxes, radio and dropdown.', verbose_name='choices')),
+                ('default_value', models.CharField(blank=True, help_text='Default value. Comma separated values supported for checkboxes.', max_length=255, verbose_name='default value')),
+                ('help_text', models.CharField(blank=True, max_length=255, verbose_name='help text')),
+            ],
+            options={
+                'abstract': False,
+                'ordering': ['sort_order'],
+            },
+        ),
+        migrations.CreateModel(
+            name='FormPage',
+            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')),
+                ('to_address', models.CharField(blank=True, help_text='Optional - form submissions will be emailed to these addresses. Separate multiple addresses by comma.', max_length=255, verbose_name='to address')),
+                ('from_address', models.CharField(blank=True, max_length=255, verbose_name='from address')),
+                ('subject', models.CharField(blank=True, max_length=255, verbose_name='subject')),
+                ('body', wagtail.wagtailcore.fields.StreamField((('heading_block', wagtail.wagtailcore.blocks.StructBlock((('heading_text', wagtail.wagtailcore.blocks.CharBlock(classname='title', required=True)), ('size', wagtail.wagtailcore.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a header size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))))), ('paragraph_block', wagtail.wagtailcore.blocks.RichTextBlock(icon='fa-paragraph', template='blocks/paragraph_block.html')), ('image_block', wagtail.wagtailcore.blocks.StructBlock((('image', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('attribution', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('block_quote', wagtail.wagtailcore.blocks.StructBlock((('attribute_name', wagtail.wagtailcore.blocks.CharBlock(blank=True, label='e.g. Guy Picciotto', required=False)),))), ('embed_block', wagtail.wagtailembeds.blocks.EmbedBlock(help_text='Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks', icon='fa-s15', template='blocks/embed_block.html'))))),
+                ('thank_you_text', wagtail.wagtailcore.fields.RichTextField(blank=True)),
+                ('header_image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='GalleryPage',
+            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')),
+                ('introduction', models.TextField(blank=True, help_text='Text to describe the index page')),
+                ('choices', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wagtailcore.Collection')),
+                ('image', models.ForeignKey(blank=True, help_text='Location listing image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='HomePage',
+            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')),
+                ('body', wagtail.wagtailcore.fields.StreamField((('heading_block', wagtail.wagtailcore.blocks.StructBlock((('heading_text', wagtail.wagtailcore.blocks.CharBlock(classname='title', required=True)), ('size', wagtail.wagtailcore.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a header size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))))), ('paragraph_block', wagtail.wagtailcore.blocks.RichTextBlock(icon='fa-paragraph', template='blocks/paragraph_block.html')), ('image_block', wagtail.wagtailcore.blocks.StructBlock((('image', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('attribution', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('block_quote', wagtail.wagtailcore.blocks.StructBlock((('attribute_name', wagtail.wagtailcore.blocks.CharBlock(blank=True, label='e.g. Guy Picciotto', required=False)),))), ('embed_block', wagtail.wagtailembeds.blocks.EmbedBlock(help_text='Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks', icon='fa-s15', template='blocks/embed_block.html'))), blank=True, verbose_name='Home page detail')),
+                ('image', models.ForeignKey(blank=True, help_text='Location image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='People',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('first_name', models.CharField(max_length=254, verbose_name='First name')),
+                ('last_name', models.CharField(max_length=254, verbose_name='Last name')),
+                ('job_title', models.CharField(max_length=254, verbose_name='Job title')),
+                ('image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'verbose_name_plural': 'People',
+                'verbose_name': 'Person',
+            },
+        ),
+        migrations.AddField(
+            model_name='formfield',
+            name='page',
+            field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='form_fields', to='base.FormPage'),
+        ),
+    ]

+ 30 - 0
bakerydemo/base/migrations/0002_auto_20170210_1217.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 12:17
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('locations', '0001_initial'),
+        ('base', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='aboutlocationrelationship',
+            name='locations',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='about_location_relationship', to='locations.LocationPage'),
+        ),
+        migrations.AddField(
+            model_name='aboutlocationrelationship',
+            name='page',
+            field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='location_about_relationship', to='base.AboutPage'),
+        ),
+    ]

+ 23 - 0
bakerydemo/base/migrations/0003_footertext.py

@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 13:38
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import wagtail.wagtailcore.fields
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0002_auto_20170210_1217'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='FooterText',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('body', wagtail.wagtailcore.fields.RichTextField()),
+            ],
+        ),
+    ]

+ 19 - 0
bakerydemo/base/migrations/0004_auto_20170210_1420.py

@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 14:20
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('base', '0003_footertext'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='footertext',
+            options={'verbose_name_plural': 'Footer Text'},
+        ),
+    ]

+ 67 - 37
bakerydemo/base/models.py

@@ -1,26 +1,24 @@
 from __future__ import unicode_literals
 
 from django.db import models
-from django.db.utils import OperationalError
 
 from modelcluster.fields import ParentalKey
 from modelcluster.models import ClusterableModel
-from wagtail.wagtailcore.models import Page, Orderable, Collection
-from wagtail.wagtailsearch import index
-from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
-from wagtail.wagtailcore.fields import StreamField, RichTextField
+
+from wagtail.contrib.modeladmin.options import (
+    ModelAdmin, ModelAdminGroup, modeladmin_register)
 from wagtail.wagtailadmin.edit_handlers import (
-        FieldPanel,
-        InlinePanel,
-        FieldRowPanel,
-        StreamFieldPanel,
-        MultiFieldPanel,
-        PageChooserPanel
-        )
+    FieldPanel, FieldRowPanel, InlinePanel, MultiFieldPanel,
+    PageChooserPanel, StreamFieldPanel,
+)
+from wagtail.wagtailcore.fields import RichTextField, StreamField
+from wagtail.wagtailcore.models import Collection, Orderable, Page
+from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
+from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
+from wagtail.wagtailsearch import index
 from wagtail.wagtailsnippets.models import register_snippet
-from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
+
 from .blocks import BaseStreamBlock
-from wagtail.wagtailforms.models import AbstractEmailForm, AbstractFormField
 
 
 @register_snippet
@@ -44,14 +42,43 @@ class People(ClusterableModel):
         ImageChooserPanel('image')
     ]
 
+    search_fields = Page.search_fields + [
+        index.SearchField('first_name'),
+        index.SearchField('last_name'),
+    ]
+
+    @property
+    def thumb_image(self):
+        # fail silently if there is no profile pic or the rendition file can't
+        # be found. Note @richbrennan worked out how to do this...
+        try:
+            return self.image.get_rendition('fill-50x50').img_tag()
+        except:
+            return ''
+
     def __str__(self):
-        return self.first_name + " " + self.last_name
+        return '{} {}'.format(self.first_name, self.last_name)
 
     class Meta:
         verbose_name = 'Person'
         verbose_name_plural = 'People'
 
 
+@register_snippet
+class FooterText(models.Model):
+    body = RichTextField()
+
+    panels = [
+        FieldPanel('body'),
+    ]
+
+    def __str__(self):
+        return "Footer text"
+
+    class Meta:
+        verbose_name_plural = 'Footer Text'
+
+
 class AboutLocationRelationship(Orderable, models.Model):
     """
     This defines the relationship between the `LocationPage` within the `locations`
@@ -84,7 +111,7 @@ class AboutPage(Page):
 
     body = StreamField(
         BaseStreamBlock(), verbose_name="About page detail", blank=True
-        )
+    )
     # We've defined the StreamBlock() within blocks.py that we've imported on
     # line 12. Defining it in a different file gives us consistency across the
     # site, though StreamFields _can_ be created on a per model basis if you
@@ -94,10 +121,10 @@ class AboutPage(Page):
         ImageChooserPanel('image'),
         StreamFieldPanel('body'),
         InlinePanel(
-           'location_about_relationship',
-           label='Locations',
-           min_num=None
-           ),
+            'location_about_relationship',
+            label='Locations',
+            min_num=None
+        ),
     ]
 
     # parent_page_types = [
@@ -112,22 +139,6 @@ class AboutPage(Page):
     # api_fields = ['image', 'body']
 
 
-def getImageCollections():
-    # We return all collections to a list that don't have the name root.
-    try:
-        collection_images = [(
-            collection.id, collection.name
-            ) for collection in Collection.objects.all().exclude(
-            name='Root'
-            )]
-        return collection_images
-    except:
-        return [('', '')]
-
-    def __str__(self):
-        return self.title
-
-
 class HomePage(Page):
     """
     The Home Page
@@ -143,7 +154,7 @@ class HomePage(Page):
 
     body = StreamField(
         BaseStreamBlock(), verbose_name="Home page detail", blank=True
-        )
+    )
 
     content_panels = Page.content_panels + [
         ImageChooserPanel('image'),
@@ -223,3 +234,22 @@ class FormPage(AbstractEmailForm):
             FieldPanel('subject'),
         ], "Email"),
     ]
+
+
+class PeopleModelAdmin(ModelAdmin):
+    model = People
+    menu_label = 'People'  # ditch this to use verbose_name_plural from model
+    menu_icon = 'fa-people'  # change as required
+    list_display = ('first_name', 'last_name', 'job_title', 'thumb_image')
+
+
+class MyModelAdminGroup(ModelAdminGroup):
+    menu_label = 'WagtailBakery'
+    menu_icon = 'folder-open-inverse'  # change as required
+    menu_order = 200  # will put in 3rd place (000 being 1st, 100 2nd)
+    items = (PeopleModelAdmin,)
+
+
+# When using a ModelAdminGroup class to group several ModelAdmin classes together,
+# you only need to register the ModelAdminGroup class with Wagtail:
+modeladmin_register(MyModelAdminGroup)

+ 13 - 2
bakerydemo/base/templatetags/navigation_tags.py

@@ -1,8 +1,10 @@
 from django import template
-from django.template import Template
-from django.utils.http import urlencode
+
 from wagtail.wagtailcore.models import Page
 
+from bakerydemo.base.models import FooterText
+
+
 register = template.Library()
 # https://docs.djangoproject.com/en/1.9/howto/custom-template-tags/
 
@@ -87,3 +89,12 @@ def breadcrumbs(context):
         'ancestors': ancestors,
         'request': context['request'],
     }
+
+
+@register.inclusion_tag('base/include/footer.html', takes_context=True)
+def get_footer_text(context):
+    footer_text = FooterText.objects.first().body
+
+    return {
+        'footer_text': footer_text,
+    }

+ 82 - 0
bakerydemo/blog/migrations/0001_initial.py

@@ -0,0 +1,82 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 12:17
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.contrib.taggit
+import modelcluster.fields
+import wagtail.contrib.wagtailroutablepage.models
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+import wagtail.wagtailembeds.blocks
+import wagtail.wagtailimages.blocks
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('wagtailcore', '0032_add_bulk_delete_page_permission'),
+        ('wagtailimages', '0017_reduce_focal_point_key_max_length'),
+        ('base', '0001_initial'),
+        ('taggit', '0002_auto_20150616_2121'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='BlogIndexPage',
+            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')),
+                ('introduction', models.TextField(blank=True, help_text='Text to describe the index page')),
+                ('image', models.ForeignKey(blank=True, help_text='Location listing image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=(wagtail.contrib.wagtailroutablepage.models.RoutablePageMixin, 'wagtailcore.page'),
+        ),
+        migrations.CreateModel(
+            name='BlogPage',
+            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')),
+                ('date_published', models.DateField(blank=True, null=True, verbose_name='Date article published')),
+                ('body', wagtail.wagtailcore.fields.StreamField((('heading_block', wagtail.wagtailcore.blocks.StructBlock((('heading_text', wagtail.wagtailcore.blocks.CharBlock(classname='title', required=True)), ('size', wagtail.wagtailcore.blocks.ChoiceBlock(blank=True, choices=[('', 'Select a header size'), ('h2', 'H2'), ('h3', 'H3'), ('h4', 'H4')], required=False))))), ('paragraph_block', wagtail.wagtailcore.blocks.RichTextBlock(icon='fa-paragraph', template='blocks/paragraph_block.html')), ('image_block', wagtail.wagtailcore.blocks.StructBlock((('image', wagtail.wagtailimages.blocks.ImageChooserBlock(required=True)), ('caption', wagtail.wagtailcore.blocks.CharBlock(required=False)), ('attribution', wagtail.wagtailcore.blocks.CharBlock(required=False))))), ('block_quote', wagtail.wagtailcore.blocks.StructBlock((('attribute_name', wagtail.wagtailcore.blocks.CharBlock(blank=True, label='e.g. Guy Picciotto', required=False)),))), ('embed_block', wagtail.wagtailembeds.blocks.EmbedBlock(help_text='Insert an embed URL e.g https://www.youtube.com/embed/SGJFWirQ3ks', icon='fa-s15', template='blocks/embed_block.html'))), blank=True, verbose_name='Blog post')),
+                ('image', models.ForeignKey(blank=True, help_text='Location image', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='BlogPageTag',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('content_object', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='tagged_items', to='blog.BlogPage')),
+                ('tag', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blog_blogpagetag_items', to='taggit.Tag')),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+        migrations.CreateModel(
+            name='BlogPeopleRelationship',
+            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', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='blog_person_relationship', to='blog.BlogPage')),
+                ('people', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='person_blog_relationship', to='base.People')),
+            ],
+            options={
+                'abstract': False,
+                'ordering': ['sort_order'],
+            },
+        ),
+        migrations.AddField(
+            model_name='blogpage',
+            name='tags',
+            field=modelcluster.contrib.taggit.ClusterTaggableManager(blank=True, help_text='A comma-separated list of tags.', through='blog.BlogPageTag', to='taggit.Tag', verbose_name='Tags'),
+        ),
+    ]

+ 77 - 32
bakerydemo/blog/models.py

@@ -1,30 +1,31 @@
 from __future__ import unicode_literals
 
+from django.contrib import messages
 from django.db import models
+from django.shortcuts import redirect, render
 
-from modelcluster.fields import ParentalKey
 from modelcluster.contrib.taggit import ClusterTaggableManager
-from taggit.models import TaggedItemBase
-from wagtail.wagtailcore.models import Page, Orderable, Collection
-from wagtail.wagtailsearch import index
-from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
-from wagtail.wagtailcore.fields import StreamField, RichTextField
+from modelcluster.fields import ParentalKey
+
+from taggit.models import Tag, TaggedItemBase
+
+from wagtail.contrib.wagtailroutablepage.models import RoutablePageMixin, route
 from wagtail.wagtailadmin.edit_handlers import (
-        FieldPanel,
-        InlinePanel,
-        FieldRowPanel,
-        StreamFieldPanel,
-        MultiFieldPanel
-        )
+    FieldPanel, InlinePanel, StreamFieldPanel,
+)
+from wagtail.wagtailcore.fields import StreamField
+from wagtail.wagtailcore.models import Page, Orderable
+from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
+from wagtail.wagtailsearch import index
 from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel
+
 from bakerydemo.base.blocks import BaseStreamBlock
 
 
 class BlogPeopleRelationship(Orderable, models.Model):
     """
-    This defines the relationship between the `LocationPage` within the `locations`
-    app and the About page below allowing us to add locations to the about
-    section.
+    This defines the relationship between the `People` within the `base`
+    app and the BlogPage below allowing us to add people to a BlogPage.
     """
     page = ParentalKey(
         'BlogPage', related_name='blog_person_relationship'
@@ -43,7 +44,7 @@ class BlogPageTag(TaggedItemBase):
 
 class BlogPage(Page):
     """
-    The About Page
+    A Blog Page (Post)
     """
     image = models.ForeignKey(
         'wagtailimages.Image',
@@ -59,8 +60,8 @@ class BlogPage(Page):
     date_published = models.DateField("Date article published", blank=True, null=True)
 
     body = StreamField(
-        BaseStreamBlock(), verbose_name="About page detail", blank=True
-        )
+        BaseStreamBlock(), verbose_name="Blog post", blank=True
+    )
 
     content_panels = Page.content_panels + [
         ImageChooserPanel('image'),
@@ -72,20 +73,37 @@ class BlogPage(Page):
         FieldPanel('tags'),
     ]
 
+    search_fields = Page.search_fields + [
+        index.SearchField('title'),
+        index.SearchField('body'),
+    ]
+
     def authors(self):
+        """
+        Returns the BlogPage's related People
+        """
         authors = [
-             n.people for n in self.blog_person_relationship.all()
+            n.people for n in self.blog_person_relationship.all()
         ]
 
         return authors
 
-    # def tags(self):
-    #     tags = self.tags.all()
-    #     return tags
-
-    parent_page_types = [
-       'BlogIndexPage'
-    ]
+    @property
+    def get_tags(self):
+        """
+        Returns the BlogPage's related list of Tags.
+        Each Tag is modified to include a url attribute
+        """
+        tags = self.tags.all()
+        for tag in tags:
+            tag.url = '/'+'/'.join(s.strip('/') for s in [
+                self.get_parent().url,
+                'tags',
+                tag.slug
+            ])
+        return tags
+
+    parent_page_types = ['BlogIndexPage']
 
     # Defining what content type can sit under the parent
     # The empty array means that no children can be placed under the
@@ -95,10 +113,14 @@ class BlogPage(Page):
     # api_fields = ['image', 'body']
 
 
-class BlogIndexPage(Page):
-    """
+class BlogIndexPage(RoutablePageMixin, Page):
     """
+    Index page for blogs.
+    We need to alter the page model's context to return the child page objects - the
+    BlogPage - so that it works as an index page
 
+    The RoutablePageMixin is used to allow for a custom sub-URL
+    """
     image = models.ForeignKey(
         'wagtailimages.Image',
         null=True,
@@ -123,14 +145,37 @@ class BlogIndexPage(Page):
 
     # Defining what content type can sit under the parent. Since it's a blank
     # array no subpage can be added
-    subpage_types = [
-        'BlogPage'
-    ]
+    subpage_types = ['BlogPage']
 
     def get_context(self, request):
         context = super(BlogIndexPage, self).get_context(request)
-        context['posts'] = BlogPage.objects.descendant_of(
+        context['blogs'] = BlogPage.objects.descendant_of(
             self).live().order_by(
             '-first_published_at')
         return context
+
+    @route('^tags/$', name='tag_archive')
+    @route('^tags/(\w+)/$', name='tag_archive')
+    def tag_archive(self, request, tag=None):
+        """
+        A Custom view that utilizes Tags. This view will
+        return all related BlogPages for a given Tag or redirect back to
+        the BlogIndexPage
+        """
+        try:
+            tag = Tag.objects.get(slug=tag)
+        except Tag.DoesNotExist:
+            if tag:
+                msg = 'There are no blog posts tagged with "{}"'.format(tag)
+                messages.add_message(request, messages.INFO, msg)
+            return redirect(self.url)
+
+        blogs = BlogPage.objects.filter(tags=tag).live().descendant_of(self)
+
+        context = {
+            'title': 'Posts tagged with: {}'.format(tag.name),
+            'blogs': blogs
+        }
+        return render(request, 'blog/blog_index_page.html', context)
+
     # api_fields = ['introduction']

+ 77 - 0
bakerydemo/breads/migrations/0001_initial.py

@@ -0,0 +1,77 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 12:17
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('wagtailcore', '0032_add_bulk_delete_page_permission'),
+        ('wagtailimages', '0017_reduce_focal_point_key_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='BreadPage',
+            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')),
+                ('description', wagtail.wagtailcore.fields.StreamField((('heading', wagtail.wagtailcore.blocks.CharBlock(classname='full title')), ('paragraph', wagtail.wagtailcore.blocks.RichTextBlock())))),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='BreadsIndexPage',
+            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')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='BreadType',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=255)),
+            ],
+            options={
+                'verbose_name_plural': 'Bread types',
+            },
+        ),
+        migrations.CreateModel(
+            name='Country',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=100)),
+            ],
+            options={
+                'verbose_name_plural': 'Countries of Origin',
+            },
+        ),
+        migrations.AddField(
+            model_name='breadpage',
+            name='bread_type',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='breads.BreadType'),
+        ),
+        migrations.AddField(
+            model_name='breadpage',
+            name='image',
+            field=models.ForeignKey(blank=True, help_text='Landscape mode only; horizontal width between 1000px and 3000px.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image'),
+        ),
+        migrations.AddField(
+            model_name='breadpage',
+            name='origin',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='breads.Country'),
+        ),
+    ]

+ 16 - 16
bakerydemo/breads/models.py

@@ -1,21 +1,21 @@
 from django.db import models
 
-from wagtail.wagtailcore.models import Page
-from wagtail.wagtailcore.fields import StreamField
 from wagtail.wagtailadmin.edit_handlers import FieldPanel, StreamFieldPanel
+from wagtail.wagtailcore.fields import StreamField
+from wagtail.wagtailcore.models import Page
 
+from wagtail.wagtailcore import blocks
 from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
 from wagtail.wagtailsearch import index
-from wagtail.wagtailcore import blocks
 from wagtail.wagtailsnippets.models import register_snippet
 
 
 @register_snippet
 class Country(models.Model):
-    '''
+    """
     Standard Django model to store set of countries of origin.
     Exposed in the Wagtail admin via Snippets.
-    '''
+    """
 
     title = models.CharField(max_length=100)
 
@@ -28,9 +28,9 @@ class Country(models.Model):
 
 @register_snippet
 class BreadType(models.Model):
-    '''
+    """
     Standard Django model used as a Snippet in the BreadPage model.
-    '''
+    """
 
     title = models.CharField(max_length=255)
 
@@ -46,16 +46,16 @@ class BreadType(models.Model):
 
 
 class BreadPage(Page):
-    '''
+    """
     Detail view for a specific bread
-    '''
+    """
 
     origin = models.ForeignKey(
         Country,
         on_delete=models.SET_NULL,
         null=True,
         blank=True,
-        )
+    )
     description = StreamField([
         ('heading', blocks.CharBlock(classname="full title")),
         ('paragraph', blocks.RichTextBlock()),
@@ -89,17 +89,17 @@ class BreadPage(Page):
         index.SearchField('description'),
     ]
 
-    parent_page_types = [
-       'BreadsIndexPage'
-    ]
+    parent_page_types = ['BreadsIndexPage']
+
+    api_fields = ['title', 'bread_type', 'origin', 'image']
 
 
 class BreadsIndexPage(Page):
-    '''
+    """
     Index page for breads. We don't have any fields within our model but we need
-    to alter the page models context to return the child page objects - the
+    to alter the page model's context to return the child page objects - the
     BreadPage - so that it works as an index page
-    '''
+    """
 
     subpage_types = ['BreadPage']
 

+ 62 - 0
bakerydemo/locations/migrations/0001_initial.py

@@ -0,0 +1,62 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-10 12:17
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import modelcluster.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('wagtailcore', '0032_add_bulk_delete_page_permission'),
+        ('wagtailimages', '0017_reduce_focal_point_key_max_length'),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='LocationOperatingHours',
+            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)),
+                ('day', models.CharField(choices=[('MON', 'MON'), ('TUE', 'TUE'), ('WED', 'WED'), ('THU', 'THU'), ('FRI', 'FRI'), ('SAT', 'SAT'), ('SUN', 'SUN')], default='MON', max_length=3)),
+                ('opening_time', models.TimeField()),
+                ('closing_time', models.TimeField()),
+            ],
+            options={
+                'abstract': False,
+                'ordering': ['sort_order'],
+            },
+        ),
+        migrations.CreateModel(
+            name='LocationPage',
+            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')),
+                ('address', models.TextField()),
+                ('lat_long', models.CharField(help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182)                    Right click Google Maps and click 'What's Here'", max_length=36)),
+                ('image', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailimages.Image')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.CreateModel(
+            name='LocationsIndexPage',
+            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')),
+            ],
+            options={
+                'abstract': False,
+            },
+            bases=('wagtailcore.page',),
+        ),
+        migrations.AddField(
+            model_name='locationoperatinghours',
+            name='location',
+            field=modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='hours_of_operation', to='locations.LocationPage'),
+        ),
+    ]

+ 16 - 14
bakerydemo/locations/models.py

@@ -9,9 +9,9 @@ from wagtail.wagtailsearch import index
 
 
 class OperatingHours(models.Model):
-    '''
+    """
     Django model to capture operating hours for a Location
-    '''
+    """
     MONDAY = 'MON'
     TUESDAY = 'TUE'
     WEDNESDAY = 'WED'
@@ -52,9 +52,9 @@ class OperatingHours(models.Model):
 
 
 class LocationOperatingHours(Orderable, OperatingHours):
-    '''
+    """
     Operating Hours entry for a Location
-    '''
+    """
     location = ParentalKey(
         'LocationPage',
         related_name='hours_of_operation'
@@ -62,9 +62,9 @@ class LocationOperatingHours(Orderable, OperatingHours):
 
 
 class LocationsIndexPage(Page):
-    '''
+    """
     Index page for locations
-    '''
+    """
 
     subpage_types = ['LocationPage']
 
@@ -77,9 +77,9 @@ class LocationsIndexPage(Page):
 
 
 class LocationPage(Page):
-    '''
+    """
     Detail for a specific location
-    '''
+    """
 
     address = models.TextField()
     image = models.ForeignKey(
@@ -93,8 +93,6 @@ class LocationPage(Page):
         max_length=36,
         help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182) \
                    Right click Google Maps and click 'What\'s Here'"
-
-
     )
 
     # Search index configuration
@@ -111,12 +109,16 @@ class LocationPage(Page):
     ]
 
     def __str__(self):
-        return self.name
+        return self.title
 
     def opening_hours(self):
         hours = self.hours_of_operation.all()
         return hours
 
-    parent_page_types = [
-       'LocationsIndexPage'
-    ]
+    def get_context(self, request):
+        context = super(LocationPage, self).get_context(request)
+        context['lat'] = self.lat_long.split(",")[0]
+        context['long'] = self.lat_long.split(",")[1]
+        return context
+
+    parent_page_types = ['LocationsIndexPage']

+ 0 - 0
bakerydemo/search/__init__.py


+ 57 - 0
bakerydemo/search/views.py

@@ -0,0 +1,57 @@
+from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
+from django.shortcuts import render
+
+from wagtail.wagtailcore.models import Page
+from wagtail.wagtailsearch.models import Query
+
+from bakerydemo.blog.models import BlogPage
+from bakerydemo.breads.models import BreadPage
+from bakerydemo.locations.models import LocationPage
+
+
+def search(request):
+    # Search
+    search_query = request.GET.get('query', None)
+    if search_query:
+        """
+        Because we can't use ElasticSearch for the demo, we use the native db search.
+        But native DB search can't search specific fields in our models on a `Page` query.
+        So for demo purposes ONLY, we hard-code in the model names we want to search.
+        In production, use ElasticSearch and a simplified search query, per
+        http://docs.wagtail.io/en/v1.8.1/topics/search/searching.html
+        """
+
+        blog_results = BlogPage.objects.live().search(search_query)
+        blog_page_ids = [p.page_ptr.id for p in blog_results]
+
+        bread_results = BreadPage.objects.live().search(search_query)
+        bread_page_ids = [p.page_ptr.id for p in bread_results]
+
+        location_results = LocationPage.objects.live().search(search_query)
+        location_result_ids = [p.page_ptr.id for p in location_results]
+
+        page_ids = blog_page_ids + bread_page_ids + location_result_ids
+        search_results = Page.objects.live().filter(id__in=page_ids)
+
+        query = Query.get(search_query)
+
+        # Record hit
+        query.add_hit()
+
+    else:
+        search_results = Page.objects.none()
+
+    # Pagination
+    page = request.GET.get('page', 1)
+    paginator = Paginator(search_results, 10)
+    try:
+        search_results = paginator.page(page)
+    except PageNotAnInteger:
+        search_results = paginator.page(1)
+    except EmptyPage:
+        search_results = paginator.page(paginator.num_pages)
+
+    return render(request, 'search/search_results.html', {
+        'search_query': search_query,
+        'search_results': search_results,
+    })

+ 5 - 2
bakerydemo/settings/base.py

@@ -35,7 +35,7 @@ INSTALLED_APPS = [
     'bakerydemo.blog',
     'bakerydemo.breads',
     'bakerydemo.locations',
-    # 'bakerydemo.search',
+    'bakerydemo.search',
 
     'wagtail.contrib.wagtailsearchpromotions',
     'wagtail.wagtailforms',
@@ -50,7 +50,9 @@ INSTALLED_APPS = [
     'wagtail.wagtailadmin',
     'wagtail.contrib.modeladmin',
     'wagtail.wagtailcore',
-
+    'wagtail.contrib.wagtailapi',
+    
+    'rest_framework',
     'modelcluster',
     'taggit',
     'wagtailfontawesome',
@@ -168,6 +170,7 @@ WAGTAILSEARCH_BACKENDS = {
     },
 }
 
+
 # Wagtail settings
 
 WAGTAIL_SITE_NAME = "bakerydemo"

+ 2 - 10
bakerydemo/settings/dev.py

@@ -1,19 +1,11 @@
 from .base import *
 
-
-# SECURITY WARNING: don't run with debug turned on in production!
 DEBUG = True
-TEMPLATES[0]['OPTIONS']['debug'] = True
-
-# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = 'CHANGEME!!!'
-
-INTERNAL_IPS = ('127.0.0.1', '10.0.2.2')
-
-BASE_URL = 'http://localhost:8000'
 
 EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
 
+# BASE_URL required for notification emails
+BASE_URL = 'http://localhost:8000'
 
 try:
     from .local import *

+ 0 - 175
bakerydemo/settings/production.py

@@ -1,175 +0,0 @@
-import os
-import dj_database_url
-
-from .base import *
-
-# Do not set SECRET_KEY, Postgres or LDAP password or any other sensitive data here.
-# Instead, use environment variables or create a local.py file on the server.
-
-# Disable debug mode
-DEBUG = False
-TEMPLATES[0]['OPTIONS']['debug'] = False
-
-
-# Configuration from environment variables
-# Alternatively, you can set these in a local.py file on the server
-
-env = os.environ.copy()
-
-# On Torchbox servers, many environment variables are prefixed with "CFG_"
-for key, value in os.environ.items():
-    if key.startswith('CFG_'):
-        env[key[4:]] = value
-
-
-# Basic configuration
-
-APP_NAME = env.get('APP_NAME', 'bakerydemo')
-
-if 'SECRET_KEY' in env:
-    SECRET_KEY = env['SECRET_KEY']
-
-if 'ALLOWED_HOSTS' in env:
-    ALLOWED_HOSTS = env['ALLOWED_HOSTS'].split(',')
-
-if 'PRIMARY_HOST' in env:
-    BASE_URL = 'http://%s/' % env['PRIMARY_HOST']
-
-if 'SERVER_EMAIL' in env:
-    SERVER_EMAIL = env['SERVER_EMAIL']
-
-if 'CACHE_PURGE_URL' in env:
-    INSTALLED_APPS += ( 'wagtail.contrib.wagtailfrontendcache', )
-    WAGTAILFRONTENDCACHE = {
-        'default': {
-            'BACKEND': 'wagtail.contrib.wagtailfrontendcache.backends.HTTPBackend',
-            'LOCATION': env['CACHE_PURGE_URL'],
-        },
-    }
-
-if 'STATIC_URL' in env:
-    STATIC_URL = env['STATIC_URL']
-
-if 'STATIC_DIR' in env:
-    STATIC_ROOT = env['STATIC_DIR']
-
-if 'MEDIA_URL' in env:
-    MEDIA_URL = env['MEDIA_URL']
-
-if 'MEDIA_DIR' in env:
-    MEDIA_ROOT = env['MEDIA_DIR']
-
-
-# Database
-
-if 'DATABASE_URL' in os.environ:
-    DATABASES = {'default': dj_database_url.config()}
-
-else:
-    DATABASES = {
-        'default': {
-            'ENGINE': 'django.db.backends.postgresql_psycopg2',
-            'NAME': env.get('PGDATABASE', APP_NAME),
-            'CONN_MAX_AGE': 600,  # number of seconds database connections should persist for
-
-            # User, host and port can be configured by the PGUSER, PGHOST and
-            # PGPORT environment variables (these get picked up by libpq).
-        }
-    }
-
-
-# Elasticsearch
-
-if 'ELASTICSEARCH_URL' in env:
-    WAGTAILSEARCH_BACKENDS = {
-        'default': {
-            'BACKEND': 'wagtail.wagtailsearch.backends.elasticsearch.ElasticSearch',
-            'URLS': [env['ELASTICSEARCH_URL']],
-            'INDEX': APP_NAME,
-            'ATOMIC_REBUILD': True,
-        },
-    }
-
-
-# Logging
-
-LOGGING = {
-    'version': 1,
-    'disable_existing_loggers': False,
-    'handlers': {
-        'mail_admins': {
-            'level': 'ERROR',
-            'class': 'django.utils.log.AdminEmailHandler',
-        },
-    },
-    'formatters': {
-        'default': {
-            'verbose': '[%(asctime)s] (%(process)d/%(thread)d) %(name)s %(levelname)s: %(message)s'
-        }
-    },
-    'loggers': {
-        'bakerydemo': {
-            'handlers':     [],
-            'level':        'INFO',
-            'propagate':    False,
-            'formatter':    'verbose',
-        },
-        'wagtail': {
-            'handlers':     [],
-            'level':        'INFO',
-            'propagate':    False,
-            'formatter':    'verbose',
-        },
-        'django.request': {
-            'handlers':     ['mail_admins'],
-            'level':        'ERROR',
-            'propagate':    False,
-            'formatter':    'verbose',
-        },
-        'django.security': {
-            'handlers':     ['mail_admins'],
-            'level':        'ERROR',
-            'propagate':    False,
-            'formatter':    'verbose',
-        },
-    },
-}
-
-
-if 'LOG_DIR' in env:
-    # bakerydemo log
-    LOGGING['handlers']['bakerydemo_file'] = {
-        'level':        'INFO',
-        'class':        'cloghandler.ConcurrentRotatingFileHandler',
-        'filename':     os.path.join(env['LOG_DIR'], 'bakerydemo.log'),
-        'maxBytes':     5242880, # 5MB
-        'backupCount':  5
-    }
-    LOGGING['loggers']['wagtail']['handlers'].append('bakerydemo_file')
-
-    # Wagtail log
-    LOGGING['handlers']['wagtail_file'] = {
-        'level':        'INFO',
-        'class':        'cloghandler.ConcurrentRotatingFileHandler',
-        'filename':     os.path.join(env['LOG_DIR'], 'wagtail.log'),
-        'maxBytes':     5242880, # 5MB
-        'backupCount':  5
-    }
-    LOGGING['loggers']['wagtail']['handlers'].append('wagtail_file')
-
-    # Error log
-    LOGGING['handlers']['errors_file'] = {
-        'level':        'ERROR',
-        'class':        'cloghandler.ConcurrentRotatingFileHandler',
-        'filename':     os.path.join(env['LOG_DIR'], 'error.log'),
-        'maxBytes':     5242880, # 5MB
-        'backupCount':  5
-    }
-    LOGGING['loggers']['django.request']['handlers'].append('errors_file')
-    LOGGING['loggers']['django.security']['handlers'].append('errors_file')
-
-
-try:
-    from .local import *
-except ImportError:
-    pass

+ 4 - 4
bakerydemo/templates/about_page.html

@@ -2,12 +2,12 @@
 {% load wagtailimages_tags %}
 
 {% block content %}
-	{{ page.title }}
+    {{ page.title }}
 
     <div class="image">
         {% image page.image width-500 as photo %}
               <img src="{{ photo.url }}" width="{{ photo.width }}" height="{{ photo.height }}" alt="{{ photo.alt }}" />
-	</div>
+    </div>
 
-	{{ page.body }}
-{% endblock content %}
+    {{ page.body }}
+{% endblock content %}

+ 12 - 2
bakerydemo/templates/base.html

@@ -20,7 +20,7 @@
                 {% endif %}
             {% endblock %}
         </title>
-        <meta name="description" content="">
+        <meta name="description" content="{% if self.search_description %}{{ self.search_description }}{% endif %}">
         <meta name="viewport" content="width=device-width, initial-scale=1">
 
         <!-- Bootstrap Core CSS -->
@@ -69,6 +69,16 @@
         </div>
     </div>
 
+    {% if messages %}
+    <div>
+        <ul class="messages">
+            {% for message in messages %}
+            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
+            {% endfor %}
+        </ul>
+    </div>
+    {% endif %}
+
     <div class="container">
         <div class="row">
             <div class="col-lg-7 col-lg-offset-2">
@@ -137,7 +147,7 @@
                             </a>
                         </li>
                     </ul>
-                    <p class="copyright text-center text-muted">Copyright &copy; Your Website 2016</p>
+                    <p class="copyright text-center text-muted">{% get_footer_text %}</p>
                 </div>
             </div>
         </div>

+ 1 - 1
bakerydemo/templates/base/about_page.html

@@ -10,4 +10,4 @@
     </div>
 
     {{ page.body }}
-{% endblock content %}
+{% endblock content %}

+ 11 - 14
bakerydemo/templates/base/form_page.html

@@ -1,15 +1,12 @@
+{% extends "base.html" %}
 {% load wagtailcore_tags %}
-<html>
-    <head>
-        <title>{{ page.title }}</title>
-    </head>
-    <body>
-        <h1>{{ page.title }}</h1>
-        {{ page.intro|richtext }}
-        <form action="{% pageurl page %}" method="POST">
-            {% csrf_token %}
-            {{ form.as_p }}
-            <input type="submit">
-        </form>
-    </body>
-</html>
+
+{% block content %}
+    {{ page.intro|richtext }}
+
+    <form action="{% pageurl page %}" method="POST">
+        {% csrf_token %}
+        {{ form.as_p }}
+        <input type="submit">
+    </form>
+{% endblock content %}

+ 7 - 9
bakerydemo/templates/base/form_page_landing.html

@@ -1,9 +1,7 @@
-<html>
-    <head>
-        <title>{{ page.title }}</title>
-    </head>
-    <body>
-        <h1>{{ page.title }}</h1>
-        <h2>Thanks for your submission!</h2>
-    </body>
-</html>
+{% extends "base.html" %}
+{% load wagtailcore_tags %}
+
+{% block content %}
+    <h1>{{ page.title }}</h1>
+    {{ page.thank_you_text|richtext }}
+{% endblock content %}

+ 4 - 0
bakerydemo/templates/base/include/footer.html

@@ -0,0 +1,4 @@
+{% load wagtailcore_tags %}
+
+
+{{ footer_text|richtext }}

+ 8 - 8
bakerydemo/templates/blocks/heading_block.html

@@ -1,15 +1,15 @@
 {% if self.size == 'h2' %}
-	    <h2>{{ self.heading_text }}</h2>
+        <h2>{{ self.heading_text }}</h2>
 
-	{% elif self.size == 'h3' %}
-	    <h3>{{ self.heading_text }}</h3>
+    {% elif self.size == 'h3' %}
+        <h3>{{ self.heading_text }}</h3>
 
-	{% elif self.size == 'h4' %}
-	    <h4>{{ self.heading_text }}</h4>
+    {% elif self.size == 'h4' %}
+        <h4>{{ self.heading_text }}</h4>
 
 {% endif %}
 
 {% comment %}
-	Content is coming from the StandardBlock StreamField
-	class within `blocks.py`
-{% endcomment %}
+    Content is coming from the StandardBlock StreamField
+    class within `blocks.py`
+{% endcomment %}

+ 2 - 4
bakerydemo/templates/blocks/image_block.html

@@ -1,8 +1,6 @@
 {% load wagtailimages_tags %}
 
-
-
 <figure>
-  {% image self.image fill-600x600 %}
-      <figcaption>{{ self.caption }} - {{ self.attribution }}</figcaption>
+    {% image self.image fill-600x600 %}
+    <figcaption>{{ self.caption }} - {{ self.attribution }}</figcaption>
 </figure>

+ 1 - 1
bakerydemo/templates/blocks/paragraph_block.html

@@ -1 +1 @@
-        {{ self }}
+{{ self }}

+ 9 - 3
bakerydemo/templates/blog/blog_index_page.html

@@ -3,8 +3,14 @@
 
 {% block content %}
     {{ page.title }}
+    {% for blog in blogs %}
+        <div>
+            <h2><a href="{{ blog.full_url }}">{{ blog.title }}</a></h2>
+            {{ blog.body|truncatewords_html:10 }}
 
-    {% for post in posts %}
-        <div><a href="{{ post.slug }}">{{ post.title }}</a></div>
+            {% for tag in blog.get_tags  %}
+                <a href="{{ tag.url }}">{{ tag }}</a>
+            {% endfor %}
+        </div>
     {% endfor %}
-{% endblock content %}
+{% endblock content %}

+ 4 - 4
bakerydemo/templates/blog/blog_page.html

@@ -7,15 +7,15 @@
       {% image self.image fill-600x600 %}
     </figure>
 
-    {% for tag in page.tags.all  %}
-        {{ tag }}
+    {% for tag in page.get_tags  %}
+        <a href="{{ tag.url }}">{{ tag }}</a>
     {% endfor %}
 
     <date>{{ page.date_published }}</date>
-    
+
     {% for author in page.authors %}
         <li>{{ author }}</li>
     {% endfor %}
 
     {{ page.body }}
-{% endblock content %}
+{% endblock content %}

+ 1 - 2
bakerydemo/templates/breads/breads_index_page.html

@@ -3,8 +3,7 @@
 
 {% block content %}
     {{ page.title }}
-
     {% for bread in breads %}
         <div><a href="{{ bread.slug }}">{{ bread.title }}</a></div>
     {% endfor %}
-{% endblock content %}
+{% endblock content %}

+ 38 - 2
bakerydemo/templates/locations/location_page.html

@@ -2,14 +2,50 @@
 {% load wagtailimages_tags %}
 
 {% block content %}
+
+    <style>
+      /* Needed for Google map embed */
+      #map {
+        height: 100%;
+      }
+      html, body {
+        height: 100%;
+        margin: 0;
+        padding: 0;
+      }
+    </style>
+
     <h1>{{ page.title }}</h1>
     <figure>
       {% image self.image fill-600x600 %}
     </figure>
     <p>{{ page.address }}</p>
     <p>{{ page.lat_long }}</p>
-    
+
+    <div id="map"></div>
+    <script>
+      var map;
+      function initMap() {
+        map = new google.maps.Map(document.getElementById('map'), {
+          center: {
+              lat: {{lat}},
+              lng: {{long}}
+          },
+          zoom: 8
+        });
+        var marker = new google.maps.Marker({
+          position: {
+              lat: {{lat}},
+              lng: {{long}}
+          },
+          map: map,
+          title: '{{page.title}}'
+        });
+      }
+    </script>
+    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD31CT9P9KxvNUJOwDq2kcFEIG8ADgaFgw&callback=initMap" async defer></script>
+
     {% for hours in page.opening_hours %}
         <li>{{ hours }}</li>
     {% endfor %}
-{% endblock content %}
+{% endblock content %}

+ 14 - 0
bakerydemo/templates/search/search_box.html

@@ -0,0 +1,14 @@
+<form action="{% url 'search' %}" class="form-inline" method="get">
+    <div class="row">
+        <div class="col-sm-12">
+            <div class="input-group">
+                <input type="text" name="query" class="form-control" />
+                <div class="input-group-btn">
+                    <button type="submit" class="btn btn-primary">
+                        <i class="glyphicon glyphicon-search"></i> Search
+                    </button>
+                </div>
+            </div>
+        </div>
+    </div>
+</form>

+ 33 - 0
bakerydemo/templates/search/search_results.html

@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+{% load wagtailcore_tags %}
+
+{% block title %}Search{% if search_results %} results{% endif %}{% endblock %}
+
+{% block content %}
+    <h1>
+        Search results{% if request.GET.query %} for “{{ request.GET.query }}”{% endif %}
+    </h1>
+    {% if search_results %}
+        <ul>
+            {% for result in search_results %}
+                <li>
+                    <h4>
+                        {% if result.specific.content_type.model == "blogpage" %}
+                            Blog post:
+                        {% elif result.specific.content_type.model == "locationpage" %}
+                            Location:
+                        {% else %}
+                            Bread
+                        {% endif %}
+                        <a href="{% pageurl result.specific %}">{{ result.specific }}</a>
+                    </h4>
+                        {% if result.specific.search_description %}{{ result.specific.search_description|safe }}{% endif %}
+                </li>
+            {% endfor %}
+        </ul>
+    {% elif search_query %}
+        No results found
+    {% else %}
+        You didn’t search for anything!
+    {% endif %}
+{% endblock %}

+ 0 - 4
bakerydemo/templates/tags/sidebar_menu.html

@@ -2,13 +2,10 @@
 {% get_site_root as site_root %}
 
 {% if calling_page|has_protocol_parent and calling_page.content_type.model == 'standardpage' %}
-
   <div class="off-canvas position-left reveal-for-large bla" id="offCanvasLeft" data-off-canvas>
     {% protocol_menu calling_page=calling_page %}
   </div>
-
 {% elif ancestor.has_children %}
-
   <div class="off-canvas position-left reveal-for-large" id="offCanvasLeft" data-off-canvas>
     <nav class="sidebar-nav">
       <h3>In this section</h3>
@@ -32,5 +29,4 @@
       </ul>
     </nav>
   </div>
-
 {% endif %}

+ 1 - 2
bakerydemo/templates/tags/top_menu.html

@@ -1,7 +1,6 @@
 {% load navigation_tags wagtailcore_tags %}
 {% get_site_root as site_root %}
 
-
 <nav class="navbar navbar-default navbar-custom">
     <div class="container-fluid">
         <ul class="nav navbar-nav">
@@ -19,4 +18,4 @@
         </ul>
       </div>
     </div>
-</nav>
+</nav>

+ 1 - 0
bakerydemo/templates/tags/top_menu_children.html

@@ -1,4 +1,5 @@
 {% load navigation_tags wagtailcore_tags %}
+
 <ul class="dropdown-menu">
   {% for child in menuitems_children %}
     <li><a href="{% pageurl child %}">{{ child.title }}</a></li>

+ 4 - 2
bakerydemo/urls.py

@@ -6,8 +6,9 @@ from wagtail.wagtailadmin import urls as wagtailadmin_urls
 from wagtail.wagtaildocs import urls as wagtaildocs_urls
 from wagtail.wagtailcore import urls as wagtail_urls
 
-# from bakerydemo.search import views as search_views
+from bakerydemo.search import views as search_views
 
+from wagtail.contrib.wagtailapi import urls as wagtailapi_urls
 
 urlpatterns = [
     url(r'^django-admin/', include(admin.site.urls)),
@@ -15,7 +16,8 @@ urlpatterns = [
     url(r'^admin/', include(wagtailadmin_urls)),
     url(r'^documents/', include(wagtaildocs_urls)),
 
-    # url(r'^search/$', search_views.search, name='search'),
+    url(r'^search/$', search_views.search, name='search'),
+    url(r'^api/', include(wagtailapi_urls)),
 
 ]
 

+ 67 - 2
readme.md

@@ -1,2 +1,67 @@
-bakerydemo
-==================
+Wagtail demo project
+=======================
+
+This is a demonstration project for [Wagtail CMS](http://wagtail.io).
+
+*We do __not__ recommend using this project to start your own site*. This project is only to provide some examples of
+implementing common features, it is not an exemplar of Django or Wagtail best practice.
+
+If you're reasonably new to Python/Django, we suggest you run this project on a Virtual Machine using Vagrant, which
+helps  resolve common software dependency issues. However for more experienced developers, instructions to start this
+project without Vagrant follow below.
+
+Once you're familiar with the examples in this project and you want to start a real site, we strongly recommend running
+the ``wagtail start`` command in a fresh virtual environment, explained in the
+[Wagtail CMS Documentation](http://wagtail.readthedocs.org/en/latest/getting_started/).
+
+Setup with Vagrant
+------------------
+
+### Dependencies
+* [VirtualBox](https://www.virtualbox.org/)
+* [Vagrant 1.5+](http://www.vagrantup.com)
+
+### Installation
+Run the following commands:
+
+    git clone https://github.com/torchbox/bakerydemo.git
+    cd wagtaildemo
+    vagrant up
+    vagrant ssh
+      (then, within the SSH session:)
+    ./manage.py migrate
+    ./manage.py load_initial_data
+    ./manage.py runserver 0.0.0.0:8000
+
+The demo site will now be accessible at [http://localhost:8000/](http://localhost:8000/) and the Wagtail admin
+interface at [http://localhost:8000/admin/](http://localhost:8000/admin/).
+
+Log into the admin with the credentials ``admin / changeme``.
+
+Setup without Vagrant
+-----
+Don't want to set up a whole VM to try out Wagtail? No problem.
+
+### Dependencies
+* [PIP](https://github.com/pypa/pip)
+
+### Installation
+
+With PIP installed run the following commands:
+
+    git clone https://github.com/torchbox/bakerydemo.git
+    cd wagtaildemo
+    pip install -r requirements.txt
+    ./manage.py migrate
+    ./manage.py load_initial_data
+    ./manage.py runserver
+
+Log into the admin with the credentials ``admin / changeme``.
+
+### Note on demo search:
+
+Because we can't (easily) use ElasticSearch for this demo, we use wagtail's native DB search.
+However, native DB search can't search specific fields in our models on a generalized `Page` query.
+So for demo purposes ONLY, we hard-code the model names we want to search into `search.views`, which is
+not ideal. In production, use ElasticSearch and a simplified search query, per
+http://docs.wagtail.io/en/v1.8.1/topics/search/searching.html

+ 2 - 1
vagrant/provision.sh

@@ -32,8 +32,9 @@ su - vagrant -c "$PIP install -r $PROJECT_DIR/requirements.txt"
 chmod a+x $PROJECT_DIR/manage.py
 
 
-# Run syncdb/migrate/update_index
+# Run syncdb/migrate/load_initial_data/update_index
 su - vagrant -c "$PYTHON $PROJECT_DIR/manage.py migrate --noinput && \
+                 $PYTHON $PROJECT_DIR/manage.py load_initial_data && \
                  $PYTHON $PROJECT_DIR/manage.py update_index"
 
 

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä