Ticket #8995: django-sitemap-protocol.diff

File django-sitemap-protocol.diff, 15.1 KB (added by John Hensley, 13 years ago)

Backward-compatible patch with docs

  • django/contrib/sitemaps/__init__.py

    commit dd6a1c57bf8d884ace441488b067145581a56559
    Author: John Hensley <john@fairviewcomputing.com>
    Date:   Fri Sep 30 10:39:22 2011 -0400
    
        Support specification of sitemap protocol.
    
    diff --git a/django/contrib/sitemaps/__init__.py b/django/contrib/sitemaps/__init__.py
    index 2ce919f..7aac8ad 100644
    a b class Sitemap(object):  
    4040    # http://sitemaps.org/protocol.php#index.
    4141    limit = 50000
    4242
     43    # If protocol is None, the sitemap's URLs will be constructed
     44    # using the protocol with which the sitemap was requested.
     45    protocol = None
     46
    4347    def __get(self, name, obj, default=None):
    4448        try:
    4549            attr = getattr(self, name)
    class Sitemap(object):  
    7175            if site is None:
    7276                raise ImproperlyConfigured("In order to use Sitemaps you must either use the sites framework or pass in a Site or RequestSite object in your view code.")
    7377
     78        if self.protocol is not None:
     79            protocol = self.protocol
     80        elif hasattr(self, 'request'):
     81            protocol = self.request.is_secure() and 'https' or 'http'
     82        else:
     83            protocol = 'http'
     84
    7485        urls = []
    7586        for item in self.paginator.page(page).object_list:
    76             loc = "http://%s%s" % (site.domain, self.__get('location', item))
     87            loc = "%s://%s%s" % (protocol, site.domain, self.__get('location', item))
    7788            priority = self.__get('priority', item, None)
    7889            url_info = {
    7990                'item':       item,
  • django/contrib/sitemaps/tests/__init__.py

    diff --git a/django/contrib/sitemaps/tests/__init__.py b/django/contrib/sitemaps/tests/__init__.py
    index c5b483c..be28c8f 100644
    a b  
    11from django.contrib.sitemaps.tests.basic import *
     2from django.contrib.sitemaps.tests.https import *
  • new file django/contrib/sitemaps/tests/https.py

    diff --git a/django/contrib/sitemaps/tests/https.py b/django/contrib/sitemaps/tests/https.py
    new file mode 100644
    index 0000000..4646215
    - +  
     1import os
     2from datetime import date
     3from django.conf import settings
     4from django.contrib.auth.models import User
     5from django.contrib.sitemaps import Sitemap, GenericSitemap
     6from django.contrib.sites.models import Site
     7from django.core.exceptions import ImproperlyConfigured
     8from django.test import TestCase
     9from django.utils.unittest import skipUnless
     10from django.utils.formats import localize
     11from django.utils.translation import activate, deactivate
     12
     13
     14class HTTPSSitemapTests(TestCase):
     15    urls = 'django.contrib.sitemaps.tests.urls_https'
     16
     17    def setUp(self):
     18        if Site._meta.installed:
     19            self.base_url = 'https://example.com'
     20        else:
     21            self.base_url = 'https://testserver'
     22        self.old_USE_L10N = settings.USE_L10N
     23        self.old_Site_meta_installed = Site._meta.installed
     24        self.old_TEMPLATE_DIRS = settings.TEMPLATE_DIRS
     25        self.old_Site_meta_installed = Site._meta.installed
     26        settings.TEMPLATE_DIRS = (
     27            os.path.join(os.path.dirname(__file__), 'templates'),
     28        )
     29        # Create a user that will double as sitemap content
     30        User.objects.create_user('testuser', 'test@example.com', 's3krit')
     31
     32    def tearDown(self):
     33        settings.USE_L10N = self.old_USE_L10N
     34        Site._meta.installed = self.old_Site_meta_installed
     35        settings.TEMPLATE_DIRS = self.old_TEMPLATE_DIRS
     36        Site._meta.installed = self.old_Site_meta_installed
     37
     38    def test_simple_sitemap_index(self):
     39        "A simple sitemap index can be rendered"
     40        # Retrieve the sitemap.
     41        response = self.client.get('/simple/index.xml')
     42        # Check for all the important bits:
     43        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     44<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     45<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
     46</sitemapindex>
     47""" % self.base_url)
     48
     49    def test_simple_sitemap_custom_index(self):
     50        "A simple sitemap index can be rendered with a custom template"
     51        # Retrieve the sitemap.
     52        response = self.client.get('/simple/custom-index.xml')
     53        # Check for all the important bits:
     54        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     55<!-- This is a customised template -->
     56<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     57<sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
     58</sitemapindex>
     59""" % self.base_url)
     60
     61    def test_simple_sitemap(self):
     62        "A simple sitemap can be rendered"
     63        # Retrieve the sitemap.
     64        response = self.client.get('/simple/sitemap.xml')
     65        # Check for all the important bits:
     66        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     67<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     68<url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
     69</urlset>
     70""" % (self.base_url, date.today().strftime('%Y-%m-%d')))
     71
     72    def test_simple_custom_sitemap(self):
     73        "A simple sitemap can be rendered with a custom template"
     74        # Retrieve the sitemap.
     75        response = self.client.get('/simple/custom-sitemap.xml')
     76        # Check for all the important bits:
     77        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     78<!-- This is a customised template -->
     79<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     80<url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
     81</urlset>
     82""" % (self.base_url, date.today().strftime('%Y-%m-%d')))
     83
     84    @skipUnless(settings.USE_I18N, "Internationalization is not enabled")
     85    def test_localized_priority(self):
     86        "The priority value should not be localized (Refs #14164)"
     87        # Localization should be active
     88        settings.USE_L10N = True
     89        activate('fr')
     90        self.assertEqual(u'0,3', localize(0.3))
     91
     92        # Retrieve the sitemap. Check that priorities
     93        # haven't been rendered in localized format
     94        response = self.client.get('/simple/sitemap.xml')
     95        self.assertContains(response, '<priority>0.5</priority>')
     96        self.assertContains(response, '<lastmod>%s</lastmod>' % date.today().strftime('%Y-%m-%d'))
     97        deactivate()
     98
     99    def test_generic_sitemap(self):
     100        "A minimal generic sitemap can be rendered"
     101        # Retrieve the sitemap.
     102        response = self.client.get('/generic/sitemap.xml')
     103
     104        expected = ''
     105        for username in User.objects.values_list("username", flat=True):
     106            expected += "<url><loc>%s/users/%s/</loc></url>" % (self.base_url, username)
     107        # Check for all the important bits:
     108        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     109<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     110%s
     111</urlset>
     112""" % expected)
     113
     114    @skipUnless("django.contrib.flatpages" in settings.INSTALLED_APPS, "django.contrib.flatpages app not installed.")
     115    def test_flatpage_sitemap(self):
     116        "Basic FlatPage sitemap test"
     117
     118        # Import FlatPage inside the test so that when django.contrib.flatpages
     119        # is not installed we don't get problems trying to delete Site
     120        # objects (FlatPage has an M2M to Site, Site.delete() tries to
     121        # delete related objects, but the M2M table doesn't exist.
     122        from django.contrib.flatpages.models import FlatPage
     123
     124        public = FlatPage.objects.create(
     125            url=u'/public/',
     126            title=u'Public Page',
     127            enable_comments=True,
     128            registration_required=False,
     129        )
     130        public.sites.add(settings.SITE_ID)
     131        private = FlatPage.objects.create(
     132            url=u'/private/',
     133            title=u'Private Page',
     134            enable_comments=True,
     135            registration_required=True
     136        )
     137        private.sites.add(settings.SITE_ID)
     138        response = self.client.get('/flatpages/sitemap.xml')
     139        # Public flatpage should be in the sitemap
     140        self.assertContains(response, '<loc>%s%s</loc>' % (self.base_url, public.url))
     141        # Private flatpage should not be in the sitemap
     142        self.assertNotContains(response, '<loc>%s%s</loc>' % (self.base_url, private.url))
     143
     144    def test_requestsite_sitemap(self):
     145        # Make sure hitting the flatpages sitemap without the sites framework
     146        # installed doesn't raise an exception
     147        Site._meta.installed = False
     148        # Retrieve the sitemap.
     149        response = self.client.get('/simple/sitemap.xml')
     150        # Check for all the important bits:
     151        self.assertEqual(response.content, """<?xml version="1.0" encoding="UTF-8"?>
     152<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
     153<url><loc>https://testserver/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
     154</urlset>
     155""" % date.today().strftime('%Y-%m-%d'))
     156
     157    @skipUnless("django.contrib.sites" in settings.INSTALLED_APPS, "django.contrib.sites app not installed.")
     158    def test_sitemap_get_urls_no_site_1(self):
     159        """
     160        Check we get ImproperlyConfigured if we don't pass a site object to
     161        Sitemap.get_urls and no Site objects exist
     162        """
     163        Site.objects.all().delete()
     164        self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
     165
     166    def test_sitemap_get_urls_no_site_2(self):
     167        """
     168        Check we get ImproperlyConfigured when we don't pass a site object to
     169        Sitemap.get_urls if Site objects exists, but the sites framework is not
     170        actually installed.
     171        """
     172        Site._meta.installed = False
     173        self.assertRaises(ImproperlyConfigured, Sitemap().get_urls)
     174
     175    def test_sitemap_item(self):
     176        """
     177        Check to make sure that the raw item is included with each
     178        Sitemap.get_url() url result.
     179        """
     180        user_sitemap = GenericSitemap({'queryset': User.objects.all()})
     181        def is_user(url):
     182            return isinstance(url['item'], User)
     183        item_in_url_info = all(map(is_user, user_sitemap.get_urls()))
     184        self.assertTrue(item_in_url_info)
  • new file django/contrib/sitemaps/tests/urls_https.py

    diff --git a/django/contrib/sitemaps/tests/urls_https.py b/django/contrib/sitemaps/tests/urls_https.py
    new file mode 100644
    index 0000000..7ff0a1d
    - +  
     1from datetime import datetime
     2from django.conf.urls.defaults import *
     3from django.contrib.sitemaps import Sitemap, GenericSitemap, FlatPageSitemap
     4from django.contrib.auth.models import User
     5
     6class HTTPSSimpleSitemap(Sitemap):
     7    changefreq = "never"
     8    priority = 0.5
     9    location = '/location/'
     10    lastmod = datetime.now()
     11    protocol = 'https'
     12
     13    def items(self):
     14        return [object()]
     15
     16class HTTPSGenericSitemap(GenericSitemap):
     17    protocol = 'https'
     18
     19class HTTPSFlatPageSitemap(FlatPageSitemap):
     20    protocol = 'https'
     21
     22simple_sitemaps = {
     23    'simple': HTTPSSimpleSitemap,
     24}
     25
     26generic_sitemaps = {
     27    'generic': HTTPSGenericSitemap({
     28        'queryset': User.objects.all()
     29    }),
     30}
     31
     32flatpage_sitemaps = {
     33    'flatpages': HTTPSFlatPageSitemap,
     34}
     35
     36urlpatterns = patterns('django.contrib.sitemaps.views',
     37    (r'^simple/index\.xml$', 'index', {'sitemaps': simple_sitemaps}),
     38    (r'^simple/custom-index\.xml$', 'index', {'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap_index.xml'}),
     39    (r'^simple/sitemap-(?P<section>.+)\.xml$', 'sitemap', {'sitemaps': simple_sitemaps}),
     40    (r'^simple/sitemap\.xml$', 'sitemap', {'sitemaps': simple_sitemaps}),
     41    (r'^simple/custom-sitemap\.xml$', 'sitemap', {'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap.xml'}),
     42    (r'^generic/sitemap\.xml$', 'sitemap', {'sitemaps': generic_sitemaps}),
     43    (r'^flatpages/sitemap\.xml$', 'sitemap', {'sitemaps': flatpage_sitemaps}),
     44)
  • django/contrib/sitemaps/views.py

    diff --git a/django/contrib/sitemaps/views.py b/django/contrib/sitemaps/views.py
    index d82146c..3696436 100644
    a b def index(request, sitemaps,  
    99        template_name='sitemap_index.xml', mimetype='application/xml'):
    1010    current_site = get_current_site(request)
    1111    sites = []
    12     protocol = request.is_secure() and 'https' or 'http'
     12    request_protocol = request.is_secure() and 'https' or 'http'
    1313    for section, site in sitemaps.items():
    14         site.request = request
    1514        if callable(site):
    16             pages = site().paginator.num_pages
     15            site = site()
     16        if site.protocol is not None:
     17            protocol = site.protocol
    1718        else:
    18             pages = site.paginator.num_pages
     19            protocol = request_protocol
     20        site.request = request
     21        pages = site.paginator.num_pages
    1922        sitemap_url = urlresolvers.reverse('django.contrib.sitemaps.views.sitemap', kwargs={'section': section})
    2023        sites.append('%s://%s%s' % (protocol, current_site.domain, sitemap_url))
    2124        if pages > 1:
    def sitemap(request, sitemaps, section=None,  
    4346            raise Http404("Page %s empty" % page)
    4447        except PageNotAnInteger:
    4548            raise Http404("No page '%s'" % page)
    46     return TemplateResponse(request, template_name, {'urlset': urls}, mimetype=mimetype)
    47  No newline at end of file
     49    return TemplateResponse(request, template_name, {'urlset': urls}, mimetype=mimetype)
  • docs/ref/contrib/sitemaps.txt

    diff --git a/docs/ref/contrib/sitemaps.txt b/docs/ref/contrib/sitemaps.txt
    index ecc5024..0ad28b2 100644
    a b Sitemap class reference  
    161161        the ``get_absolute_url()`` method on each object as returned by
    162162        :attr:`~Sitemap.items()`.
    163163
     164        To specify a protocol other than http use
     165        :attr:`~Sitemap.protocol` below.
     166
    164167    .. attribute:: Sitemap.lastmod
    165168
    166169        **Optional.** Either a method or attribute.
    Sitemap class reference  
    193196            * ``'yearly'``
    194197            * ``'never'``
    195198
     199    .. attribute:: Sitemap.protocol
     200
     201        **Optional.** The protocol to use in the sitemap URLs.
     202
     203        If specified, the protocol attribute will be used to construct the
     204        sitemap URLs. If not, the protocol with which the sitemap was
     205        requested will be used. If the sitemap is built outside the context
     206        of a request, the default is ``'http'``.
     207
     208        .. versionadded:: 1.4
     209
     210
    196211    .. method:: Sitemap.priority
    197212
    198213        **Optional.** Either a method or attribute.
  • docs/releases/1.4.txt

    diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
    index 29fd5ef..81e1b6e 100644
    a b Django 1.4 also includes several smaller improvements worth noting:  
    340340  be able to retrieve a translation string without displaying it but setting
    341341  a template context variable instead.
    342342
     343* The :doc:`Sitemaps <ref/contrib/sitemaps>` framework can now handle links
     344  that use https protocol using the new class attribute
     345  :attr:`Sitemap.protocol` attribute.
     346
    343347.. _backwards-incompatible-changes-1.4:
    344348
    345349Backwards incompatible changes in 1.4
Back to Top