Add created/changed datetime support, and improve metadata on stores.

* Added schema.org metadata for stores.
This commit is contained in:
2013-04-11 18:11:58 +01:00
parent f3a2e2c8bd
commit 934062aa46
4 changed files with 259 additions and 72 deletions

View File

@@ -4,6 +4,7 @@ from django.shortcuts import render_to_response
from django import forms from django import forms
from django.db import transaction from django.db import transaction
from django.db.models import Count from django.db.models import Count
from django.utils.timezone import now
from django.contrib import admin from django.contrib import admin
from django.contrib.contenttypes.generic import GenericStackedInline from django.contrib.contenttypes.generic import GenericStackedInline
from .models import Chain, Store, Address, Brand, ClaimRequest, Link, LinkType, County, Country from .models import Chain, Store, Address, Brand, ClaimRequest, Link, LinkType, County, Country
@@ -16,7 +17,7 @@ class LinkInlineAdmin(GenericStackedInline):
class ChainAdmin(admin.ModelAdmin): class ChainAdmin(admin.ModelAdmin):
list_filter = ['active'] list_filter = ['active']
list_display = ['name'] list_display = ['name', 'active', 'changed']
prepopulated_fields = {"slug": ("name",)} prepopulated_fields = {"slug": ("name",)}
search_fields = ['name'] search_fields = ['name']
inlines = [ inlines = [
@@ -25,7 +26,7 @@ class ChainAdmin(admin.ModelAdmin):
class StoreAdmin(admin.ModelAdmin): class StoreAdmin(admin.ModelAdmin):
list_filter = ['chain', 'active'] list_filter = ['chain', 'active']
list_display = ['name', 'store_type', 'active'] list_display = ['name', 'store_type', 'active', 'changed']
prepopulated_fields = {"slug": ("name",)} prepopulated_fields = {"slug": ("name",)}
search_fields = ['name'] search_fields = ['name']
inlines = [ inlines = [
@@ -39,7 +40,7 @@ class StoreAdmin(admin.ModelAdmin):
def set_active(self, request, queryset): def set_active(self, request, queryset):
with transaction.commit_on_success(): with transaction.commit_on_success():
queryset.update(active=True) queryset.update(active=True, changed=now())
self.message_user(request, "Successfully set %d stores to active." % queryset.count()) self.message_user(request, "Successfully set %d stores to active." % queryset.count())
set_active.short_description = 'Set selected stores active.' set_active.short_description = 'Set selected stores active.'
@@ -53,6 +54,7 @@ class StoreAdmin(admin.ModelAdmin):
with transaction.commit_on_success(): with transaction.commit_on_success():
for store in queryset: for store in queryset:
store.brands.add(brand) store.brands.add(brand)
queryset.update(changed=now())
if count > 1: if count > 1:
plural = 's' plural = 's'
else: else:
@@ -61,7 +63,7 @@ class StoreAdmin(admin.ModelAdmin):
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
if not form: if not form:
form = self.AddBrandForm(initial={'_selected_action': queryset.values_list('id', flat=True)}) form = self.AddBrandForm(initial={'_selected_action': queryset.values_list('id', flat=True)})
return render_to_response('admin/add_brand.html', {'stores': queryset, 'brand_form': form }, RequestContext(request)) return render_to_response('admin/add_brand.html', {'stores': queryset, 'brand_form': form}, RequestContext(request))
add_brand.short_description = "Add brand to the selected stores" add_brand.short_description = "Add brand to the selected stores"
@@ -77,9 +79,7 @@ class StoreAdmin(admin.ModelAdmin):
count = queryset.count() count = queryset.count()
chain = form.cleaned_data['chain'] chain = form.cleaned_data['chain']
with transaction.commit_on_success(): with transaction.commit_on_success():
for store in queryset: queryset.update(chain=chain, changed=now())
store.chain = chain
store.save()
if count > 1: if count > 1:
plural = 's' plural = 's'
else: else:
@@ -101,12 +101,13 @@ class ClaimAdmin(admin.ModelAdmin):
def approve_request(self, request, queryset): def approve_request(self, request, queryset):
qs = queryset.filter(status=ClaimRequest.CLAIM_STATUS_PENDING) qs = queryset.filter(status=ClaimRequest.CLAIM_STATUS_PENDING)
for obj in qs: with transaction.commit_on_success():
obj.status = ClaimRequest.CLAIM_STATUS_APPROVED for obj in qs:
target = obj.generic_obj obj.status = ClaimRequest.CLAIM_STATUS_APPROVED
target.editor = obj.user target = obj.generic_obj
target.save() target.editor = obj.user
obj.save() target.save()
obj.save()
if qs.count() == 1: if qs.count() == 1:
message_bit = "1 request was" message_bit = "1 request was"
else: else:
@@ -139,9 +140,7 @@ class CountyAdmin(admin.ModelAdmin):
if form.is_valid(): if form.is_valid():
country = form.cleaned_data['country'] country = form.cleaned_data['country']
with transaction.commit_on_success(): with transaction.commit_on_success():
for county in queryset: queryset.update(country=country)
county.country = country
county.save()
self.message_user(request, "Successfully set %d counties to %s." % (queryset.count(), country)) self.message_user(request, "Successfully set %d counties to %s." % (queryset.count(), country))
return HttpResponseRedirect(request.get_full_path()) return HttpResponseRedirect(request.get_full_path())
if not form: if not form:

View File

@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
from django.utils.timezone import now
from south.db import db
from south.v2 import SchemaMigration
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Chain.created'
db.add_column(u'stores_chain', 'created',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Chain.changed'
db.add_column(u'stores_chain', 'changed',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Store.created'
db.add_column(u'stores_store', 'created',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Store.changed'
db.add_column(u'stores_store', 'changed',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Chain.created'
db.delete_column(u'stores_chain', 'created')
# Deleting field 'Chain.changed'
db.delete_column(u'stores_chain', 'changed')
# Deleting field 'Store.created'
db.delete_column(u'stores_store', 'created')
# Deleting field 'Store.changed'
db.delete_column(u'stores_store', 'changed')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.address': {
'Meta': {'object_name': 'Address'},
'address1': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'address2': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'address3': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'city': ('django.db.models.fields.CharField', [], {'max_length': "'50'"}),
'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'addresses'", 'to': "orm['stores.Country']"}),
'county': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'addresses'", 'null': 'True', 'to': "orm['stores.County']"}),
'geo_latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'geo_longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'postcode': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'})
},
'stores.brand': {
'Meta': {'ordering': "['name']", 'object_name': 'Brand'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.chain': {
'Meta': {'ordering': "['name']", 'object_name': 'Chain'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_chains'", 'null': 'True', 'to': u"orm['auth.User']"}),
'head_office': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stores.Address']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.claimrequest': {
'Meta': {'object_name': 'ClaimRequest'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claims'", 'to': u"orm['auth.User']"})
},
'stores.country': {
'Meta': {'ordering': "['name']", 'object_name': 'Country'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'iso_code': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.county': {
'Meta': {'ordering': "['name']", 'object_name': 'County'},
'country': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'counties'", 'null': 'True', 'to': "orm['stores.Country']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.link': {
'Meta': {'object_name': 'Link'},
'account_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'account_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['stores.LinkType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"})
},
'stores.linktype': {
'Meta': {'ordering': "['name']", 'object_name': 'LinkType'},
'icon': ('django.db.models.fields.CharField', [], {'default': "'icon-globe'", 'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'url_format': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'stores.store': {
'Meta': {'ordering': "['name']", 'object_name': 'Store'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stores'", 'to': "orm['stores.Address']"}),
'brands': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['stores.Brand']"}),
'chain': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'to': "orm['stores.Chain']"}),
'changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_stores'", 'null': 'True', 'to': u"orm['auth.User']"}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'phone': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'store_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['stores']

View File

@@ -1,6 +1,7 @@
import re import re
from django.db import models from django.db import models
from django.core.urlresolvers import reverse_lazy from django.core.urlresolvers import reverse_lazy
from django.utils.timezone import now
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.gis.geos import Point from django.contrib.gis.geos import Point
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@@ -21,9 +22,13 @@ class Chain(models.Model):
long_description = models.TextField('Description', null=True, blank=True) long_description = models.TextField('Description', null=True, blank=True)
links = generic.GenericRelation('stores.Link', content_type_field='object_type') links = generic.GenericRelation('stores.Link', content_type_field='object_type')
created = models.DateTimeField('Created Date/Time', default=now)
changed = models.DateTimeField('Changed Date/Time', default=now)
def save(self, **kwargs): def save(self, **kwargs):
if self.slug == '': if self.slug == '':
self.slug = re.sub(r'\W+', '-', str(self.name).lower()) self.slug = re.sub(r'\W+', '-', str(self.name).lower())
self.changed = now()
return super(Chain, self).save(**kwargs) return super(Chain, self).save(**kwargs)
def get_absolute_url(self): def get_absolute_url(self):
@@ -65,6 +70,9 @@ class Store(models.Model):
long_description = models.TextField('Description', null=True, blank=True, help_text="Full description of the store, including any marketing material. Markdown supported.") long_description = models.TextField('Description', null=True, blank=True, help_text="Full description of the store, including any marketing material. Markdown supported.")
brands = models.ManyToManyField('stores.Brand', related_name='stores', null=True, blank=True, help_text="Brands that are sold by this store.") brands = models.ManyToManyField('stores.Brand', related_name='stores', null=True, blank=True, help_text="Brands that are sold by this store.")
created = models.DateTimeField('Created Date/Time', default=now)
changed = models.DateTimeField('Changed Date/Time', default=now)
def get_full_address(self): def get_full_address(self):
if self.address: if self.address:
return self.address.full_address return self.address.full_address
@@ -76,6 +84,7 @@ class Store(models.Model):
def save(self, **kwargs): def save(self, **kwargs):
if not self.slug or self.slug == '': if not self.slug or self.slug == '':
self.slug = re.sub(r'\W+', '-', str(self.name).lower()) self.slug = re.sub(r'\W+', '-', str(self.name).lower())
self.changed = now()
return super(Store, self).save(**kwargs) return super(Store, self).save(**kwargs)
def get_absolute_url(self): def get_absolute_url(self):

View File

@@ -9,7 +9,7 @@
{% block style %} {% block style %}
<style type="text/css" xmlns="http://www.w3.org/1999/html"> <style type="text/css" xmlns="http://www.w3.org/1999/html">
.vcard ul { .store-details ul {
list-style-type: none; list-style-type: none;
} }
#map-canvas-store { #map-canvas-store {
@@ -45,69 +45,76 @@ $(document).ready(function(){initialize_map_store()});
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="page-header"> <div itemscope itemtype="http://schema.org/Place">
<h1>{{ store.name }}</h1> <div class="page-header">
</div> <h1 itemtype="name">{{ store.name }}</h1>
<div class="row-fluid">
<div class="span8">
{% if store.long_description %}
{{ store.long_description|markdown }}
{% else %}
<p>No description / write up available for this store at the moment.</p>
{% endif %}
<p>{% flag_button %}</p>
{% if store.brands.count %}
<h3>Brands Stocked</h3>
<ul>
{% for brand in store.brands.all %}
<li>{% if brand.website %}<a href="{{ brand.website }}">{{ brand }}</a>{% else %}{{ brand }}{% endif %}</li>
{% endfor %}
</ul>
{% endif %}
</div> </div>
<div class="span4">
{% if user.is_authenticated and not store.editor or user.is_superuser %}
<p>
{% switch claim_support %}{% if not store.editor %}<a href="{% url "store-claim" store.slug %}" class="btn btn-small">Claim Store</a>{% endif %}{% endswitch %}
{% if is_editor %}<a href="{% url "store-update" store.slug %}" class="btn btn-small">Edit Store</a>{% endif %}
{% if user.is_superuser %}<a href="{% url "admin:stores_store_change" store.pk %}" class="btn btn-small">Edit in Admin</a>{% endif %}
</p>
{% endif %}
{% if store.chain %}<p><b>Chain</b>: <a href="{% url "chain-detail" store.chain.slug %}">{{ store.chain }}</a></p>{% endif %}
<p><b>Type</b>: {{ store.get_store_type_display }}</p>
<div class="vcard">
<h3>Address</h3>
<ul class="adr">
<li class="fn">{{ store.name }}</li>
<li class="street-address">{{ store.address.address1 }}</li>
{% if store.address.address2 %}<li>{{ store.address.address2 }}</li>{% endif %}
{% if store.address.address3 %}<li>{{ store.address.address3 }}</li>{% endif %}
<li class="locality">{{ store.address.city }}</li>
{% if store.address.county %}<li class="region">{{ store.address.county }}</li>{% endif %}
<li class="postal-code">{{ store.address.postcode }}</li>
<li class="country-name">{{ store.address.country }}</li>
</ul>
<h3>Contact Details</h3> <div class="row-fluid">
<div class="span8">
{% if store.long_description %}
<div itemprop="description">
{{ store.long_description|markdown }}
</div>
{% else %}
<p>No description / write up available for this store at the moment.</p>
{% endif %}
<p>{% flag_button %}</p>
{% if store.brands.count %}
<h3>Brands Stocked</h3>
<ul> <ul>
{% if store.website %}<li>Website: <a class="url" target="_new" href="{{ store.website }}">{{ store.website }}</a></li>{% endif %} {% for brand in store.brands.all %}
{% if store.email %}<li>Email: <a class="email" href="mailto:{{ store.email }}">{{ store.email }}</a></li>{% endif %} <li>{% if brand.website %}<a href="{{ brand.website }}">{{ brand }}</a>{% else %}{{ brand }}{% endif %}</li>
{% if store.phone %}<li>Phone: <span class="tel">{{ store.phone }}</span></li>{% endif %}
{% for link in store.links.all %}
<li>{{ link.to_html|safe }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %}
</div> </div>
<div class="span4">
{% if user.is_authenticated and not store.editor or user.is_superuser %}
<p>
{% switch claim_support %}{% if not store.editor %}<a href="{% url "store-claim" store.slug %}" class="btn btn-small">Claim Store</a>{% endif %}{% endswitch %}
{% if is_editor %}<a href="{% url "store-update" store.slug %}" class="btn btn-small">Edit Store</a>{% endif %}
{% if user.is_superuser %}<a href="{% url "admin:stores_store_change" store.pk %}" class="btn btn-small">Edit in Admin</a>{% endif %}
</p>
{% endif %}
{% if store.chain %}<p><b>Chain</b>: <a href="{% url "chain-detail" store.chain.slug %}">{{ store.chain }}</a></p>{% endif %}
<p><b>Type</b>: {{ store.get_store_type_display }}</p>
<div class="store-details">
<h3>Address</h3>
<ul itemscope itemtype="http://schema.org/PostalAddress">
<li itemprop="name">{{ store.name }}</li>
<li itemprop="streetAddress">{{ store.address.address1 }}</li>
{% if store.address.address2 %}<li>{{ store.address.address2 }}</li>{% endif %}
{% if store.address.address3 %}<li>{{ store.address.address3 }}</li>{% endif %}
<li itemprop="addressLocality">{{ store.address.city }}</li>
{% if store.address.county %}<li itemprop="addressRegion">{{ store.address.county }}</li>{% endif %}
<li itemprop="postalCode">{{ store.address.postcode }}</li>
<li itemprop="addressCountry">{{ store.address.country }}</li>
</ul>
<div id="map-canvas-store" style="width: 300px; height: 300px;" class="map"> <h3>Contact Details</h3>
<noscript> <ul itemscope itemtype="http://schema.org/ContactPoint">
<img alt="Map of {{ store.address.full_address }}" src="https://maps.google.com/maps/api/staticmap?center={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&zoom=16&markers={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&size=300x300&sensor=false"> {% if store.website %}<li>Website: <a target="_new" href="{{ store.website }}">{{ store.website }}</a></li>{% endif %}
</noscript> {% if store.email %}<li>Email: <a itemprop="email" href="mailto:{{ store.email }}">{{ store.email }}</a></li>{% endif %}
{% if store.phone %}<li>Phone: <span itemprop="telephone">{{ store.phone }}</span></li>{% endif %}
{% for link in store.links.all %}
<li>{{ link.to_html|safe }}</li>
{% endfor %}
</ul>
<div id="map-canvas-store" style="width: 300px; height: 300px;" class="map">
<noscript>
<img alt="Map of {{ store.address.full_address }}" src="https://maps.google.com/maps/api/staticmap?center={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&zoom=16&markers={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&size=300x300&sensor=false">
</noscript>
</div>
<p class="muted">Last Updated: {{ store.changed }}</p>
<p class="hidden" itemprop="geocoordinates"><span class="latitude">{{ store.address.geo_latitude }}</span>, <span class="longitude">{{ store.address.geo_longitude }}</span></p>
</div>
</div> </div>
{% flag_form store %}
</div> </div>
{% flag_form store %}
</div> </div>
{% endblock %} {% endblock %}