瀏覽代碼

Merge branch 'master' into 41-abstract-base-page

David Ray 8 年之前
父節點
當前提交
23cdf1f82f

+ 26 - 0
bakerydemo/locations/migrations/0003_auto_20170214_2220.py

@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-14 22:20
+from __future__ import unicode_literals
+
+import django.core.validators
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0002_auto_20170211_2229'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='day',
+            field=models.CharField(choices=[('Mon', 'Mon'), ('Tue', 'Tue'), ('Wed', 'Weds'), ('Thu', 'Thu'), ('Fri', 'Fri'), ('Sat', 'Sat'), ('Sun', 'Sun')], default='Mon', max_length=4),
+        ),
+        migrations.AlterField(
+            model_name='locationpage',
+            name='lat_long',
+            field=models.CharField(help_text="Comma separated lat/long. (Ex. 64.144367, -21.939182)                    Right click Google Maps and select 'What's Here'", max_length=36, validators=[django.core.validators.RegexValidator(code='invalid_lat_long', message='Lat Long must be a comma-separated numeric lat and long', regex='^(\\-?\\d+(\\.\\d+)?),\\s*(\\-?\\d+(\\.\\d+)?)$')]),
+        ),
+    ]

+ 29 - 0
bakerydemo/locations/migrations/0004_auto_20170215_1334.py

@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-15 13:34
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+import wagtail.wagtailembeds.blocks
+import wagtail.wagtailimages.blocks
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0003_auto_20170214_2220'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='locationpage',
+            name='body',
+            field=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((('text', wagtail.wagtailcore.blocks.TextBlock()), ('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'),
+        ),
+        migrations.AddField(
+            model_name='locationpage',
+            name='introduction',
+            field=models.TextField(blank=True, help_text='Text to describe the index page'),
+        ),
+    ]

+ 21 - 0
bakerydemo/locations/migrations/0005_locationoperatinghours_closed.py

@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-15 13:38
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0004_auto_20170215_1334'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='locationoperatinghours',
+            name='closed',
+            field=models.BooleanField(default='False', help_text='Tick if location is closed', verbose_name='Closed?'),
+            preserve_default=False,
+        ),
+    ]

+ 30 - 0
bakerydemo/locations/migrations/0006_auto_20170215_1408.py

@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-15 14:08
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0005_locationoperatinghours_closed'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='closed',
+            field=models.BooleanField(help_text='Tick if location is closed on this day', verbose_name='Closed?'),
+        ),
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='closing_time',
+            field=models.TimeField(blank=True),
+        ),
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='opening_time',
+            field=models.TimeField(blank=True),
+        ),
+    ]

+ 25 - 0
bakerydemo/locations/migrations/0007_auto_20170215_1411.py

@@ -0,0 +1,25 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-15 14:11
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0006_auto_20170215_1408'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='closing_time',
+            field=models.TimeField(blank=True, null=True),
+        ),
+        migrations.AlterField(
+            model_name='locationoperatinghours',
+            name='opening_time',
+            field=models.TimeField(blank=True, null=True),
+        ),
+    ]

+ 16 - 0
bakerydemo/locations/migrations/0008_merge_20170218_0921.py

@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-18 09:21
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0007_auto_20170215_1411'),
+        ('locations', '0005_locationsindexpage_introduction'),
+    ]
+
+    operations = [
+    ]

+ 24 - 0
bakerydemo/locations/migrations/0009_auto_20170219_0942.py

@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.10.5 on 2017-02-19 09:42
+from __future__ import unicode_literals
+
+from django.db import migrations
+import wagtail.wagtailcore.blocks
+import wagtail.wagtailcore.fields
+import wagtail.wagtailembeds.blocks
+import wagtail.wagtailimages.blocks
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('locations', '0008_merge_20170218_0921'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='locationpage',
+            name='body',
+            field=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((('text', wagtail.wagtailcore.blocks.TextBlock()), ('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 this location'),
+        ),
+    ]

+ 75 - 23
bakerydemo/locations/models.py

@@ -1,57 +1,82 @@
+from datetime import datetime
+
+from django.conf import settings
 from django.core.validators import RegexValidator
 from django.db import models
 
 from modelcluster.fields import ParentalKey
 
-from wagtail.wagtailadmin.edit_handlers import FieldPanel, InlinePanel
+from wagtail.wagtailadmin.edit_handlers import (
+    FieldPanel,
+    InlinePanel,
+    StreamFieldPanel)
 from wagtail.wagtailcore.models import Orderable, Page
-from wagtail.wagtailimages.edit_handlers import ImageChooserPanel
+from wagtail.wagtailimages.edit_handlers import (
+    ImageChooserPanel,
+    )
+from wagtail.wagtailcore.fields import StreamField
 from wagtail.wagtailsearch import index
 
 from bakerydemo.base.models import BasePageFieldsMixin
+from bakerydemo.base.blocks import BaseStreamBlock
 
 
 class OperatingHours(models.Model):
     """
     Django model to capture operating hours for a Location
     """
-    MONDAY = 'MON'
-    TUESDAY = 'TUE'
-    WEDNESDAY = 'WED'
-    THURSDAY = 'THU'
-    FRIDAY = 'FRI'
-    SATURDAY = 'SAT'
-    SUNDAY = 'SUN'
+    MONDAY = 'Mon'
+    TUESDAY = 'Tue'
+    WEDNESDAY = 'Wed'
+    THURSDAY = 'Thu'
+    FRIDAY = 'Fri'
+    SATURDAY = 'Sat'
+    SUNDAY = 'Sun'
 
     DAY_CHOICES = (
-        (MONDAY, 'MON'),
-        (TUESDAY, 'TUE'),
-        (WEDNESDAY, 'WED'),
-        (THURSDAY, 'THU'),
-        (FRIDAY, 'FRI'),
-        (SATURDAY, 'SAT'),
-        (SUNDAY, 'SUN'),
+        (MONDAY, 'Mon'),
+        (TUESDAY, 'Tue'),
+        (WEDNESDAY, 'Weds'),
+        (THURSDAY, 'Thu'),
+        (FRIDAY, 'Fri'),
+        (SATURDAY, 'Sat'),
+        (SUNDAY, 'Sun'),
     )
 
     day = models.CharField(
-        max_length=3,
+        max_length=4,
         choices=DAY_CHOICES,
         default=MONDAY,
     )
-    opening_time = models.TimeField()
-    closing_time = models.TimeField()
+    opening_time = models.TimeField(
+        blank=True,
+        null=True)
+    closing_time = models.TimeField(
+        blank=True,
+        null=True)
+    closed = models.BooleanField(
+        "Closed?",
+        blank=True,
+        help_text='Tick if location is closed on this day'
+        )
 
     panels = [
         FieldPanel('day'),
         FieldPanel('opening_time'),
         FieldPanel('closing_time'),
+        FieldPanel('closed'),
     ]
 
     class Meta:
         abstract = True
 
     def __str__(self):
-        return '{}: {} - {}'.format(self.day, self.opening_time, self.closing_time)
+        return '{}: {} - {} {}'.format(
+            self.day,
+            self.opening_time.strftime('%H:%M'),
+            self.closing_time.strftime('%H:%M'),
+            settings.TIME_ZONE
+        )
 
 
 class LocationOperatingHours(Orderable, OperatingHours):
@@ -82,7 +107,9 @@ class LocationPage(Page):
     """
     Detail for a specific bakery location.
     """
-
+    introduction = models.TextField(
+        help_text='Text to describe the index page',
+        blank=True)
     address = models.TextField()
     image = models.ForeignKey(
         'wagtailimages.Image',
@@ -103,6 +130,13 @@ class LocationPage(Page):
             ),
         ]
     )
+    body = StreamField(
+        BaseStreamBlock(), verbose_name="About this location", 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
+    # have a use case for it
 
     # Search index configuration
     search_fields = Page.search_fields + [
@@ -111,19 +145,37 @@ class LocationPage(Page):
 
     # Editor panels configuration
     content_panels = Page.content_panels + [
+        FieldPanel('introduction', classname="full"),
+        StreamFieldPanel('body'),
         FieldPanel('address', classname="full"),
         FieldPanel('lat_long'),
         ImageChooserPanel('image'),
-        InlinePanel('hours_of_operation', label="Hours of Operation")
+        InlinePanel('hours_of_operation', label="Hours of Operation"),
     ]
 
     def __str__(self):
         return self.title
 
-    def opening_hours(self):
+    @property
+    def operating_hours(self):
         hours = self.hours_of_operation.all()
         return hours
 
+    def is_open(self):
+        # Determines if the location is currently open
+        now = datetime.now()
+        current_time = now.time()
+        current_day = now.strftime('%a').upper()
+        try:
+            self.operating_hours.get(
+                day=current_day,
+                opening_time__lte=current_time,
+                closing_time__gte=current_time
+            )
+            return True
+        except LocationOperatingHours.DoesNotExist:
+            return False
+
     def get_context(self, request):
         context = super(LocationPage, self).get_context(request)
         context['lat'] = self.lat_long.split(",")[0]

+ 78 - 1
bakerydemo/static/css/main.css

@@ -128,7 +128,7 @@ p {
   }
 
   p {
-    margin: 0 0 50px;
+    margin: 0 0 30px;
   }
 
 }
@@ -504,3 +504,80 @@ li.has-submenu a.allow-toggle {
 .navbar-toggle .icon-bar {
   background-color: #fff;
 }
+
+/* Location list page */
+.location-list-item {
+  text-align: center;
+  margin-bottom: 30px;
+}
+.location-list-title {
+  line-height: 270px;
+  height: 270px;
+  background-color: #eb7400;
+}
+.location-list-title img {
+  background-color: rgba(233,228,221,1);
+  height: 270px;
+  left: 0;
+  position: absolute;
+  top: 0;
+  width: 100%;
+}
+.location-list-title:hover img {
+  opacity: 0.3;
+}
+.location-list-title span.title {
+  color: white;
+  display: inline-block;
+  font-weight: 300;
+  position: relative;
+  text-shadow: 0px 0px 30px rgba(0, 0, 0, 1);
+}
+.location-list-title:hover span.title {
+  text-shadow: none;
+}
+.location-list-item address {
+  font-weight: 300;
+  font-family: 'Lato', sans-serif;
+  font-size: 1.4em;
+  padding: 10px 40px;
+}
+
+/* Location detail page */
+.template-location-page .intro {
+  margin-bottom: 0;
+}
+.location-opening h3 {
+  margin-top: 0;
+}
+span.day {
+  font-weight: bold;
+  font-family: 'Lato', sans-serif;
+}
+time.location-time {
+  display: block;
+}
+
+.map-container {
+  height: 550px;
+}
+
+.location-address {
+  background-color: rgba(233,228,221,1);
+  padding: 10px 30px;
+  margin-bottom: -200px;
+  position: relative;
+  z-index: 1;
+}
+
+/* No gutters */
+.row.no-gutters {
+  margin-right: 0;
+  margin-left: 0;
+}
+.row.no-gutters > [class^="col-"],
+.row.no-gutters > [class*=" col-"] {
+  padding-right: 0;
+  padding-left: 0;
+}
+

+ 1 - 1
bakerydemo/templates/base.html

@@ -4,7 +4,7 @@
     {% include "includes/head.html" %}
 {% endblock head %}
 
-<body>
+<body class="{% block body_class %}template-{{ self.get_verbose_name|slugify }}{% endblock %}">
 {% wagtailuserbar %}
 
 {% block header %}

+ 88 - 22
bakerydemo/templates/locations/location_page.html

@@ -1,38 +1,103 @@
 {% extends "base.html" %}
-{% load wagtailimages_tags %}
+{% load wagtailimages_tags navigation_tags %}
 
 {% block head-extra %}
-  <style>
-      /* Needed for Google map embed */
-      #map {
-        height: 100%;
-      }
-      .maps.embed-container {
-         pointer-events: none;
-      }
-      html, body {
-        height: 100%;
-        margin: 0;
-        padding: 0;
-      }
-  </style>
+    <style>
+        /* Following two selectors needed for Google map embed */
+        #map {
+          height: 100%;
+        }
+        html, body {
+          height: 100%;
+          margin: 0;
+          padding: 0;
+        }
+    </style>
 {% endblock head-extra %}
 
+
 {% block content-header %}
-    <h1>{{ page.title }}</h1>
-    <figure>
-      {% image self.image fill-600x600 %}
-    </figure>
+{# @TODO This is identical to the header within blog_page.html. We should create an include #}
+{% image self.image fill-1920x600 as hero_img %}
+<div class="container-fluid hero" style="background-image:url('{{ hero_img.url }}')">
+<div class="hero-gradient-mask"></div>
+    <div class="container">
+        <div class="row">
+            <div class="col-md-7">
+                <h1>{{ page.title }}</h1>
+                <p class="stand-first">{{ page.subtitle }}</p>
+            </div>
+        </div>
+    </div>
+</div>
 {% endblock content-header %}
 
 {% block content-body %}
-    <p>{{ page.address|linebreaks }}</p>
+<div class="container">
+    <div class="row">
+        <div class="col-md-7">
+          <div class="row">
+            {% if page.introduction %}
+                <div class="intro col-md-7"><p>{{ page.introduction }}</p></div>
+            {% endif %}
+
+            {% if page.opening_hours %}
+                <div class="col-md-4 col-md-offset-1 location-opening">
+                <h3>Opening hours</h3>
+                {% for hours in page.opening_hours %}
+                  <time itemprop="openingHours" datetime="{{ hours }}" class="location-time">
+                    <span class="day">{{ hours.day }}</span>:
+                    <span class="hours">
+                      {% if hours.closed == True %}
+                        Closed
+                        {% else %}
+                        {% if hours.opening_time %}
+                          {{ hours.opening_time }}
+                        {% endif %} -
+                        {% if hours.closing_time %}
+                          {{ hours.closing_time }}
+                        {% endif %}
+                      {% endif %}
+                      </span></time>
+                {% endfor %}
+                </div>
+            {% endif %}
+          </div>
+        </div>
+    </div>
+</div>
+<div class="container-flex">
+  <div class="row">
+    <div class="col-md-2 col-md-offset-5 location-address">
+      <h3>Address</h3>
+        <address>{{ page.address|linebreaks }}</address>
+    </div>
+    {# @TODO align address to opening hours? #}
+  </div>
+
+  <div class="map-container">
     <div id="map" class="maps embed-container"></div>
+  </div>
+</div>
 
-    {% for hours in page.opening_hours %}
+    {% if page.is_open %}
+      Open Now!
+    {% else %}
+      Currently Closed
+    {% endif %}
+
+    {% for hours in page.operating_hours %}
         <li>{{ hours }}</li>
     {% endfor %}
 
+<div class="container">
+  <div class="row">
+    <div class="col-md-7 location-body">
+      {{ page.body }}
+    </div>
+  </div>
+</div>
+
     <script>
       var map;
       function initMap() {
@@ -41,7 +106,8 @@
               lat: {{lat}},
               lng: {{long}}
           },
-          zoom: 8
+          zoom: 15,
+          scrollwheel:  false
         });
         var marker = new google.maps.Marker({
           position: {

+ 29 - 11
bakerydemo/templates/locations/locations_index_page.html

@@ -1,18 +1,36 @@
 {% extends "base.html" %}
-{% load wagtailcore_tags %}
-{% load wagtailimages_tags %}
+{% load wagtailcore_tags navigation_tags wagtailimages_tags %}
 
 {% block content-header %}
-    {% include "base/include/header.html" %}
+<div class="container">
+    <div class="row">
+        <div class="col-md-12">
+            <h1>{{ page.title }}</h1>
+            <p>{{ page.introduction }}</p>
+        </div>
+    </div>
+</div>
 {% endblock content-header %}
 
 {% block content-body %}
-    {{ page.introduction }}
-
-    {% for location in locations %}
-        <div>
-            <a href="{% pageurl location %}">{{ location.title }}</a>
-            {% image location.image width-150 %}
-        </div>
-    {% endfor %}
+<div class="container">
+    <div class="row no-gutters">
+        {% for location in locations %}
+            <div class="col-md-6 location-list-item">
+                <a href="{% pageurl location %}">
+                <h1 class="location-list-title">
+                
+        
+                    {% image location.image fill-660x270-c75 as image %}
+                    <img src="{{ image.url }}" width="{{ image.width }}" height="{{ image.height }}" alt="{{ image.alt }}" class="" />
+                    
+                    <span class="title">{{ location.title }}</span>
+                
+                </h1></a>
+                    <address>{{ location.address }}</address>
+                    <a href="https://google.com/maps/?q={{ location.lat_long }}" class="btn">Map</a>
+            </div>
+        {% endfor %}
+    </div>
+</div>
 {% endblock content-body %}