|
@@ -60,32 +60,71 @@ class Sitemap:
|
|
|
# with which the sitemap was requested.
|
|
|
protocol = None
|
|
|
|
|
|
- def __get(self, name, obj, default=None):
|
|
|
+ # Enables generating URLs for all languages.
|
|
|
+ i18n = False
|
|
|
+
|
|
|
+ # Override list of languages to use.
|
|
|
+ languages = None
|
|
|
+
|
|
|
+ # Enables generating alternate/hreflang links.
|
|
|
+ alternates = False
|
|
|
+
|
|
|
+ # Add an alternate/hreflang link with value 'x-default'.
|
|
|
+ x_default = False
|
|
|
+
|
|
|
+ def _get(self, name, item, default=None):
|
|
|
try:
|
|
|
attr = getattr(self, name)
|
|
|
except AttributeError:
|
|
|
return default
|
|
|
if callable(attr):
|
|
|
- return attr(obj)
|
|
|
+ if self.i18n:
|
|
|
+ # Split the (item, lang_code) tuples again for the location,
|
|
|
+ # priority, lastmod and changefreq method calls.
|
|
|
+ item, lang_code = item
|
|
|
+ return attr(item)
|
|
|
return attr
|
|
|
|
|
|
- def items(self):
|
|
|
- return []
|
|
|
-
|
|
|
- def location(self, obj):
|
|
|
- return obj.get_absolute_url()
|
|
|
+ def _languages(self):
|
|
|
+ if self.languages is not None:
|
|
|
+ return self.languages
|
|
|
+ return [lang_code for lang_code, _ in settings.LANGUAGES]
|
|
|
+
|
|
|
+ def _items(self):
|
|
|
+ if self.i18n:
|
|
|
+ # Create (item, lang_code) tuples for all items and languages.
|
|
|
+ # This is necessary to paginate with all languages already considered.
|
|
|
+ items = [
|
|
|
+ (item, lang_code)
|
|
|
+ for lang_code in self._languages()
|
|
|
+ for item in self.items()
|
|
|
+ ]
|
|
|
+ return items
|
|
|
+ return self.items()
|
|
|
+
|
|
|
+ def _location(self, item, force_lang_code=None):
|
|
|
+ if self.i18n:
|
|
|
+ obj, lang_code = item
|
|
|
+ # Activate language from item-tuple or forced one before calling location.
|
|
|
+ with translation.override(force_lang_code or lang_code):
|
|
|
+ return self._get('location', item)
|
|
|
+ return self._get('location', item)
|
|
|
|
|
|
@property
|
|
|
def paginator(self):
|
|
|
- return paginator.Paginator(self.items(), self.limit)
|
|
|
+ return paginator.Paginator(self._items(), self.limit)
|
|
|
|
|
|
- def get_urls(self, page=1, site=None, protocol=None):
|
|
|
+ def items(self):
|
|
|
+ return []
|
|
|
+
|
|
|
+ def location(self, item):
|
|
|
+ return item.get_absolute_url()
|
|
|
+
|
|
|
+ def get_protocol(self, protocol=None):
|
|
|
# Determine protocol
|
|
|
- if self.protocol is not None:
|
|
|
- protocol = self.protocol
|
|
|
- if protocol is None:
|
|
|
- protocol = 'http'
|
|
|
+ return self.protocol or protocol or 'http'
|
|
|
|
|
|
+ def get_domain(self, site=None):
|
|
|
# Determine domain
|
|
|
if site is None:
|
|
|
if django_apps.is_installed('django.contrib.sites'):
|
|
@@ -99,43 +138,61 @@ class Sitemap:
|
|
|
"To use sitemaps, either enable the sites framework or pass "
|
|
|
"a Site/RequestSite object in your view."
|
|
|
)
|
|
|
- domain = site.domain
|
|
|
-
|
|
|
- if getattr(self, 'i18n', False):
|
|
|
- urls = []
|
|
|
- current_lang_code = translation.get_language()
|
|
|
- for lang_code, lang_name in settings.LANGUAGES:
|
|
|
- translation.activate(lang_code)
|
|
|
- urls += self._urls(page, protocol, domain)
|
|
|
- translation.activate(current_lang_code)
|
|
|
- else:
|
|
|
- urls = self._urls(page, protocol, domain)
|
|
|
+ return site.domain
|
|
|
|
|
|
- return urls
|
|
|
+ def get_urls(self, page=1, site=None, protocol=None):
|
|
|
+ protocol = self.get_protocol(protocol)
|
|
|
+ domain = self.get_domain(site)
|
|
|
+ return self._urls(page, protocol, domain)
|
|
|
|
|
|
def _urls(self, page, protocol, domain):
|
|
|
urls = []
|
|
|
latest_lastmod = None
|
|
|
all_items_lastmod = True # track if all items have a lastmod
|
|
|
- for item in self.paginator.page(page).object_list:
|
|
|
- loc = "%s://%s%s" % (protocol, domain, self.__get('location', item))
|
|
|
- priority = self.__get('priority', item)
|
|
|
- lastmod = self.__get('lastmod', item)
|
|
|
+
|
|
|
+ paginator_page = self.paginator.page(page)
|
|
|
+ for item in paginator_page.object_list:
|
|
|
+ loc = f'{protocol}://{domain}{self._location(item)}'
|
|
|
+ priority = self._get('priority', item)
|
|
|
+ lastmod = self._get('lastmod', item)
|
|
|
+
|
|
|
if all_items_lastmod:
|
|
|
all_items_lastmod = lastmod is not None
|
|
|
if (all_items_lastmod and
|
|
|
(latest_lastmod is None or lastmod > latest_lastmod)):
|
|
|
latest_lastmod = lastmod
|
|
|
+
|
|
|
url_info = {
|
|
|
'item': item,
|
|
|
'location': loc,
|
|
|
'lastmod': lastmod,
|
|
|
- 'changefreq': self.__get('changefreq', item),
|
|
|
+ 'changefreq': self._get('changefreq', item),
|
|
|
'priority': str(priority if priority is not None else ''),
|
|
|
}
|
|
|
+
|
|
|
+ if self.i18n and self.alternates:
|
|
|
+ alternates = []
|
|
|
+ for lang_code in self._languages():
|
|
|
+ loc = f'{protocol}://{domain}{self._location(item, lang_code)}'
|
|
|
+ alternates.append({
|
|
|
+ 'location': loc,
|
|
|
+ 'lang_code': lang_code,
|
|
|
+ })
|
|
|
+ if self.x_default:
|
|
|
+ lang_code = settings.LANGUAGE_CODE
|
|
|
+ loc = f'{protocol}://{domain}{self._location(item, lang_code)}'
|
|
|
+ loc = loc.replace(f'/{lang_code}/', '/', 1)
|
|
|
+ alternates.append({
|
|
|
+ 'location': loc,
|
|
|
+ 'lang_code': 'x-default',
|
|
|
+ })
|
|
|
+ url_info['alternates'] = alternates
|
|
|
+
|
|
|
urls.append(url_info)
|
|
|
+
|
|
|
if all_items_lastmod and latest_lastmod:
|
|
|
self.latest_lastmod = latest_lastmod
|
|
|
+
|
|
|
return urls
|
|
|
|
|
|
|
|
@@ -146,9 +203,9 @@ class GenericSitemap(Sitemap):
|
|
|
def __init__(self, info_dict, priority=None, changefreq=None, protocol=None):
|
|
|
self.queryset = info_dict['queryset']
|
|
|
self.date_field = info_dict.get('date_field')
|
|
|
- self.priority = priority
|
|
|
- self.changefreq = changefreq
|
|
|
- self.protocol = protocol
|
|
|
+ self.priority = self.priority or priority
|
|
|
+ self.changefreq = self.changefreq or changefreq
|
|
|
+ self.protocol = self.protocol or protocol
|
|
|
|
|
|
def items(self):
|
|
|
# Make sure to return a clone; we don't want premature evaluation.
|