mirror of https://github.com/django/django.git
parent
491419b5ff
commit
74b3467646
|
@ -1,5 +1,4 @@
|
||||||
# Geo-enabled Sitemap classes.
|
# Geo-enabled Sitemap classes.
|
||||||
from django.contrib.gis.sitemaps.georss import GeoRSSSitemap
|
|
||||||
from django.contrib.gis.sitemaps.kml import KMLSitemap, KMZSitemap
|
from django.contrib.gis.sitemaps.kml import KMLSitemap, KMZSitemap
|
||||||
|
|
||||||
__all__ = ['GeoRSSSitemap', 'KMLSitemap', 'KMZSitemap']
|
__all__ = ['KMLSitemap', 'KMZSitemap']
|
||||||
|
|
|
@ -1,55 +0,0 @@
|
||||||
from django.core import urlresolvers
|
|
||||||
from django.contrib.sitemaps import Sitemap
|
|
||||||
|
|
||||||
|
|
||||||
class GeoRSSSitemap(Sitemap):
|
|
||||||
"""
|
|
||||||
A minimal hook to produce sitemaps for GeoRSS feeds.
|
|
||||||
"""
|
|
||||||
def __init__(self, feed_dict, slug_dict=None):
|
|
||||||
"""
|
|
||||||
This sitemap object initializes on a feed dictionary (as would be passed
|
|
||||||
to `django.contrib.gis.views.feed`) and a slug dictionary.
|
|
||||||
If the slug dictionary is not defined, then it's assumed the keys provide
|
|
||||||
the URL parameter to the feed. However, if you have a complex feed (e.g.,
|
|
||||||
you override `get_object`, then you'll need to provide a slug dictionary.
|
|
||||||
The slug dictionary should have the same keys as the feed dictionary, but
|
|
||||||
each value in the slug dictionary should be a sequence of slugs that may
|
|
||||||
be used for valid feeds. For example, let's say we have a feed that
|
|
||||||
returns objects for a specific ZIP code in our feed dictionary:
|
|
||||||
|
|
||||||
feed_dict = {'zipcode' : ZipFeed}
|
|
||||||
|
|
||||||
Then we would use a slug dictionary with a list of the zip code slugs
|
|
||||||
corresponding to feeds you want listed in the sitemap:
|
|
||||||
|
|
||||||
slug_dict = {'zipcode' : ['77002', '77054']}
|
|
||||||
"""
|
|
||||||
# Setting up.
|
|
||||||
self.feed_dict = feed_dict
|
|
||||||
self.locations = []
|
|
||||||
if slug_dict is None:
|
|
||||||
slug_dict = {}
|
|
||||||
# Getting the feed locations.
|
|
||||||
for section in feed_dict.keys():
|
|
||||||
if slug_dict.get(section, False):
|
|
||||||
for slug in slug_dict[section]:
|
|
||||||
self.locations.append('%s/%s' % (section, slug))
|
|
||||||
else:
|
|
||||||
self.locations.append(section)
|
|
||||||
|
|
||||||
def get_urls(self, page=1, site=None):
|
|
||||||
"""
|
|
||||||
This method is overrridden so the appropriate `geo_format` attribute
|
|
||||||
is placed on each URL element.
|
|
||||||
"""
|
|
||||||
urls = Sitemap.get_urls(self, page=page, site=site)
|
|
||||||
for url in urls:
|
|
||||||
url['geo_format'] = 'georss'
|
|
||||||
return urls
|
|
||||||
|
|
||||||
def items(self):
|
|
||||||
return self.locations
|
|
||||||
|
|
||||||
def location(self, obj):
|
|
||||||
return urlresolvers.reverse('django.contrib.gis.views.feed', args=(obj,))
|
|
|
@ -42,12 +42,12 @@ class KMLSitemap(Sitemap):
|
||||||
raise TypeError('KML Sources must be a model or a 3-tuple.')
|
raise TypeError('KML Sources must be a model or a 3-tuple.')
|
||||||
return kml_sources
|
return kml_sources
|
||||||
|
|
||||||
def get_urls(self, page=1, site=None):
|
def get_urls(self, page=1, site=None, protocol=None):
|
||||||
"""
|
"""
|
||||||
This method is overrridden so the appropriate `geo_format` attribute
|
This method is overrridden so the appropriate `geo_format` attribute
|
||||||
is placed on each URL element.
|
is placed on each URL element.
|
||||||
"""
|
"""
|
||||||
urls = Sitemap.get_urls(self, page=page, site=site)
|
urls = Sitemap.get_urls(self, page=page, site=site, protocol=protocol)
|
||||||
for url in urls:
|
for url in urls:
|
||||||
url['geo_format'] = self.geo_format
|
url['geo_format'] = self.geo_format
|
||||||
return urls
|
return urls
|
||||||
|
|
|
@ -1,77 +1,11 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import Http404
|
||||||
from django.template import loader
|
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
|
||||||
from django.core import urlresolvers
|
|
||||||
from django.core.paginator import EmptyPage, PageNotAnInteger
|
|
||||||
from django.contrib.gis.db.models.fields import GeometryField
|
from django.contrib.gis.db.models.fields import GeometryField
|
||||||
|
from django.contrib.gis.shortcuts import render_to_kml, render_to_kmz
|
||||||
from django.db import connections, DEFAULT_DB_ALIAS
|
from django.db import connections, DEFAULT_DB_ALIAS
|
||||||
from django.db.models.fields import FieldDoesNotExist
|
from django.db.models.fields import FieldDoesNotExist
|
||||||
from django.utils import six
|
|
||||||
from django.utils.deprecation import RemovedInDjango18Warning
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
from django.contrib.gis.shortcuts import render_to_kml, render_to_kmz
|
|
||||||
|
|
||||||
|
|
||||||
def index(request, sitemaps):
|
|
||||||
"""
|
|
||||||
This view generates a sitemap index that uses the proper view
|
|
||||||
for resolving geographic section sitemap URLs.
|
|
||||||
"""
|
|
||||||
warnings.warn("Geo Sitemaps are deprecated. Use plain sitemaps from "
|
|
||||||
"django.contrib.sitemaps instead", RemovedInDjango18Warning, stacklevel=2)
|
|
||||||
current_site = get_current_site(request)
|
|
||||||
sites = []
|
|
||||||
protocol = request.scheme
|
|
||||||
for section, site in sitemaps.items():
|
|
||||||
if callable(site):
|
|
||||||
pages = site().paginator.num_pages
|
|
||||||
else:
|
|
||||||
pages = site.paginator.num_pages
|
|
||||||
sitemap_url = urlresolvers.reverse('django.contrib.gis.sitemaps.views.sitemap', kwargs={'section': section})
|
|
||||||
sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
|
|
||||||
|
|
||||||
if pages > 1:
|
|
||||||
for page in range(2, pages + 1):
|
|
||||||
sites.append('%s://%s%s?p=%s' % (protocol, current_site.domain, sitemap_url, page))
|
|
||||||
xml = loader.render_to_string('sitemap_index.xml', {'sitemaps': sites})
|
|
||||||
return HttpResponse(xml, content_type='application/xml')
|
|
||||||
|
|
||||||
|
|
||||||
def sitemap(request, sitemaps, section=None):
|
|
||||||
"""
|
|
||||||
This view generates a sitemap with additional geographic
|
|
||||||
elements defined by Google.
|
|
||||||
"""
|
|
||||||
warnings.warn("Geo Sitemaps are deprecated. Use plain sitemaps from "
|
|
||||||
"django.contrib.sitemaps instead", RemovedInDjango18Warning, stacklevel=2)
|
|
||||||
maps, urls = [], []
|
|
||||||
if section is not None:
|
|
||||||
if section not in sitemaps:
|
|
||||||
raise Http404(_("No sitemap available for section: %r") % section)
|
|
||||||
maps.append(sitemaps[section])
|
|
||||||
else:
|
|
||||||
maps = list(six.itervalues(sitemaps))
|
|
||||||
|
|
||||||
page = request.GET.get("p", 1)
|
|
||||||
current_site = get_current_site(request)
|
|
||||||
for site in maps:
|
|
||||||
try:
|
|
||||||
if callable(site):
|
|
||||||
urls.extend(site().get_urls(page=page, site=current_site))
|
|
||||||
else:
|
|
||||||
urls.extend(site.get_urls(page=page, site=current_site))
|
|
||||||
except EmptyPage:
|
|
||||||
raise Http404(_("Page %s empty") % page)
|
|
||||||
except PageNotAnInteger:
|
|
||||||
raise Http404(_("No page '%s'") % page)
|
|
||||||
xml = loader.render_to_string('gis/sitemaps/geo_sitemap.xml', {'urlset': urls})
|
|
||||||
return HttpResponse(xml, content_type='application/xml')
|
|
||||||
|
|
||||||
|
|
||||||
def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB_ALIAS):
|
def kml(request, label, model, field_name=None, compress=False, using=DEFAULT_DB_ALIAS):
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{% autoescape off %}<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:geo="http://www.google.com/geo/schemas/sitemap/1.0">
|
|
||||||
{% spaceless %}
|
|
||||||
{% for url in urlset %}
|
|
||||||
<url>
|
|
||||||
<loc>{{ url.location|escape }}</loc>
|
|
||||||
{% if url.lastmod %}<lastmod>{{ url.lastmod|date:"Y-m-d" }}</lastmod>{% endif %}
|
|
||||||
{% if url.changefreq %}<changefreq>{{ url.changefreq }}</changefreq>{% endif %}
|
|
||||||
{% if url.priority %}<priority>{{ url.priority }}</priority>{% endif %}
|
|
||||||
{% if url.geo_format %}<geo:geo>
|
|
||||||
<geo:format>{{ url.geo_format }}</geo:format>
|
|
||||||
</geo:geo>{% endif %}
|
|
||||||
</url>
|
|
||||||
{% endfor %}
|
|
||||||
{% endspaceless %}
|
|
||||||
</urlset>
|
|
||||||
{% endautoescape %}
|
|
|
@ -1,10 +1,8 @@
|
||||||
from django.contrib.gis.sitemaps import GeoRSSSitemap, KMLSitemap, KMZSitemap
|
from django.contrib.gis.sitemaps import KMLSitemap, KMZSitemap
|
||||||
|
|
||||||
from .feeds import feed_dict
|
|
||||||
from .models import City, Country
|
from .models import City, Country
|
||||||
|
|
||||||
|
|
||||||
sitemaps = {'kml': KMLSitemap([City, Country]),
|
sitemaps = {'kml': KMLSitemap([City, Country]),
|
||||||
'kmz': KMZSitemap([City, Country]),
|
'kmz': KMZSitemap([City, Country]),
|
||||||
'georss': GeoRSSSitemap(feed_dict),
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ from __future__ import unicode_literals
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from unittest import skipUnless
|
from unittest import skipUnless
|
||||||
from xml.dom import minidom
|
from xml.dom import minidom
|
||||||
import os
|
|
||||||
import zipfile
|
import zipfile
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -11,16 +10,14 @@ from django.contrib.gis.geos import HAS_GEOS
|
||||||
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
from django.contrib.gis.tests.utils import HAS_SPATIAL_DB
|
||||||
from django.contrib.sites.models import Site
|
from django.contrib.sites.models import Site
|
||||||
from django.test import TestCase, modify_settings
|
from django.test import TestCase, modify_settings
|
||||||
from django.test.utils import IgnoreDeprecationWarningsMixin
|
|
||||||
from django.utils._os import upath
|
|
||||||
|
|
||||||
if HAS_GEOS:
|
if HAS_GEOS:
|
||||||
from .models import City, Country
|
from .models import City, Country
|
||||||
|
|
||||||
|
|
||||||
@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sites'})
|
@modify_settings(INSTALLED_APPS={'append': ['django.contrib.sites', 'django.contrib.sitemaps']})
|
||||||
@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
|
@skipUnless(HAS_GEOS and HAS_SPATIAL_DB, "Geos and spatial db are required.")
|
||||||
class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
class GeoSitemapTest(TestCase):
|
||||||
|
|
||||||
urls = 'django.contrib.gis.tests.geoapp.urls'
|
urls = 'django.contrib.gis.tests.geoapp.urls'
|
||||||
|
|
||||||
|
@ -34,36 +31,19 @@ class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
expected = set(expected)
|
expected = set(expected)
|
||||||
self.assertEqual(actual, expected)
|
self.assertEqual(actual, expected)
|
||||||
|
|
||||||
def test_geositemap_index(self):
|
|
||||||
"Tests geographic sitemap index."
|
|
||||||
# Getting the geo index.
|
|
||||||
from django.contrib import sitemaps
|
|
||||||
template_dirs = settings.TEMPLATE_DIRS + (
|
|
||||||
os.path.join(os.path.dirname(upath(sitemaps.__file__)), 'templates'),)
|
|
||||||
with self.settings(TEMPLATE_DIRS=template_dirs):
|
|
||||||
doc = minidom.parseString(self.client.get('/sitemap.xml').content)
|
|
||||||
index = doc.firstChild
|
|
||||||
self.assertEqual(index.getAttribute('xmlns'), 'http://www.sitemaps.org/schemas/sitemap/0.9')
|
|
||||||
self.assertEqual(3, len(index.getElementsByTagName('sitemap')))
|
|
||||||
|
|
||||||
def test_geositemap_kml(self):
|
def test_geositemap_kml(self):
|
||||||
"Tests KML/KMZ geographic sitemaps."
|
"Tests KML/KMZ geographic sitemaps."
|
||||||
for kml_type in ('kml', 'kmz'):
|
for kml_type in ('kml', 'kmz'):
|
||||||
doc = minidom.parseString(self.client.get('/sitemaps/%s.xml' % kml_type).content)
|
doc = minidom.parseString(self.client.get('/sitemaps/%s.xml' % kml_type).content)
|
||||||
|
|
||||||
# Ensuring the right sitemaps namespaces are present.
|
# Ensuring the right sitemaps namespace is present.
|
||||||
urlset = doc.firstChild
|
urlset = doc.firstChild
|
||||||
self.assertEqual(urlset.getAttribute('xmlns'), 'http://www.sitemaps.org/schemas/sitemap/0.9')
|
self.assertEqual(urlset.getAttribute('xmlns'), 'http://www.sitemaps.org/schemas/sitemap/0.9')
|
||||||
self.assertEqual(urlset.getAttribute('xmlns:geo'), 'http://www.google.com/geo/schemas/sitemap/1.0')
|
|
||||||
|
|
||||||
urls = urlset.getElementsByTagName('url')
|
urls = urlset.getElementsByTagName('url')
|
||||||
self.assertEqual(2, len(urls)) # Should only be 2 sitemaps.
|
self.assertEqual(2, len(urls)) # Should only be 2 sitemaps.
|
||||||
for url in urls:
|
for url in urls:
|
||||||
self.assertChildNodes(url, ['loc', 'geo:geo'])
|
self.assertChildNodes(url, ['loc'])
|
||||||
# Making sure the 'geo:format' element was properly set.
|
|
||||||
geo_elem = url.getElementsByTagName('geo:geo')[0]
|
|
||||||
geo_format = geo_elem.getElementsByTagName('geo:format')[0]
|
|
||||||
self.assertEqual(kml_type, geo_format.childNodes[0].data)
|
|
||||||
|
|
||||||
# Getting the relative URL since we don't have a real site.
|
# Getting the relative URL since we don't have a real site.
|
||||||
kml_url = url.getElementsByTagName('loc')[0].childNodes[0].data.split('http://example.com')[1]
|
kml_url = url.getElementsByTagName('loc')[0].childNodes[0].data.split('http://example.com')[1]
|
||||||
|
@ -84,25 +64,3 @@ class GeoSitemapTest(IgnoreDeprecationWarningsMixin, TestCase):
|
||||||
elif 'country' in kml_url:
|
elif 'country' in kml_url:
|
||||||
model = Country
|
model = Country
|
||||||
self.assertEqual(model.objects.count(), len(kml_doc.getElementsByTagName('Placemark')))
|
self.assertEqual(model.objects.count(), len(kml_doc.getElementsByTagName('Placemark')))
|
||||||
|
|
||||||
def test_geositemap_georss(self):
|
|
||||||
"Tests GeoRSS geographic sitemaps."
|
|
||||||
from .feeds import feed_dict
|
|
||||||
|
|
||||||
doc = minidom.parseString(self.client.get('/sitemaps/georss.xml').content)
|
|
||||||
|
|
||||||
# Ensuring the right sitemaps namespaces are present.
|
|
||||||
urlset = doc.firstChild
|
|
||||||
self.assertEqual(urlset.getAttribute('xmlns'), 'http://www.sitemaps.org/schemas/sitemap/0.9')
|
|
||||||
self.assertEqual(urlset.getAttribute('xmlns:geo'), 'http://www.google.com/geo/schemas/sitemap/1.0')
|
|
||||||
|
|
||||||
# Making sure the correct number of feed URLs were included.
|
|
||||||
urls = urlset.getElementsByTagName('url')
|
|
||||||
self.assertEqual(len(feed_dict), len(urls))
|
|
||||||
|
|
||||||
for url in urls:
|
|
||||||
self.assertChildNodes(url, ['loc', 'geo:geo'])
|
|
||||||
# Making sure the 'geo:format' element was properly set to 'georss'.
|
|
||||||
geo_elem = url.getElementsByTagName('geo:geo')[0]
|
|
||||||
geo_format = geo_elem.getElementsByTagName('geo:format')[0]
|
|
||||||
self.assertEqual('georss', geo_format.childNodes[0].data)
|
|
||||||
|
|
|
@ -10,9 +10,11 @@ urlpatterns = patterns('',
|
||||||
(r'^feeds/(?P<url>.*)/$', 'django.contrib.gis.views.feed', {'feed_dict': feed_dict}),
|
(r'^feeds/(?P<url>.*)/$', 'django.contrib.gis.views.feed', {'feed_dict': feed_dict}),
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns += patterns('django.contrib.gis.sitemaps.views',
|
urlpatterns += patterns('django.contrib.sitemaps.views',
|
||||||
(r'^sitemap.xml$', 'index', {'sitemaps': sitemaps}),
|
|
||||||
(r'^sitemaps/(?P<section>\w+)\.xml$', 'sitemap', {'sitemaps': sitemaps}),
|
(r'^sitemaps/(?P<section>\w+)\.xml$', 'sitemap', {'sitemaps': sitemaps}),
|
||||||
|
)
|
||||||
|
|
||||||
|
urlpatterns += patterns('django.contrib.gis.sitemaps.views',
|
||||||
(r'^sitemaps/kml/(?P<label>\w+)/(?P<model>\w+)/(?P<field_name>\w+)\.kml$', 'kml'),
|
(r'^sitemaps/kml/(?P<label>\w+)/(?P<model>\w+)/(?P<field_name>\w+)\.kml$', 'kml'),
|
||||||
(r'^sitemaps/kml/(?P<label>\w+)/(?P<model>\w+)/(?P<field_name>\w+)\.kmz$', 'kmz'),
|
(r'^sitemaps/kml/(?P<label>\w+)/(?P<model>\w+)/(?P<field_name>\w+)\.kmz$', 'kmz'),
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,10 +2,9 @@
|
||||||
Geographic Sitemaps
|
Geographic Sitemaps
|
||||||
===================
|
===================
|
||||||
|
|
||||||
Google's sitemap protocol used to include geospatial content support. [#]_
|
KML is an XML language focused on geographic visualization [#]_.``KMLSitemap``
|
||||||
This included the addition of the ``<url>`` child element
|
and its compressed counterpart ``KMZSitemap`` allow you to present geolocated
|
||||||
``<geo:geo>``, which tells Google that the content located at the URL is
|
data in a machine-readable format.
|
||||||
geographic in nature. This is now obsolete.
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
=======
|
=======
|
||||||
|
@ -19,8 +18,5 @@ Reference
|
||||||
``KMZSitemap``
|
``KMZSitemap``
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
``GeoRSSSitemap``
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
.. rubric:: Footnotes
|
.. rubric:: Footnotes
|
||||||
.. [#] Google, Inc., `What is a Geo Sitemap? <https://support.google.com/webmasters/answer/94555>`_.
|
.. [#] http://www.opengeospatial.org/standards/kml
|
||||||
|
|
Loading…
Reference in New Issue