docker setup
This commit is contained in:
		| @ -0,0 +1,263 @@ | ||||
| import warnings | ||||
| from urllib.parse import urlencode | ||||
| from urllib.request import urlopen | ||||
|  | ||||
| from django.apps import apps as django_apps | ||||
| from django.conf import settings | ||||
| from django.core import paginator | ||||
| from django.core.exceptions import ImproperlyConfigured | ||||
| from django.urls import NoReverseMatch, reverse | ||||
| from django.utils import translation | ||||
| from django.utils.deprecation import RemovedInDjango50Warning | ||||
|  | ||||
| PING_URL = "https://www.google.com/webmasters/tools/ping" | ||||
|  | ||||
|  | ||||
| class SitemapNotFound(Exception): | ||||
|     pass | ||||
|  | ||||
|  | ||||
| def ping_google(sitemap_url=None, ping_url=PING_URL, sitemap_uses_https=True): | ||||
|     """ | ||||
|     Alert Google that the sitemap for the current site has been updated. | ||||
|     If sitemap_url is provided, it should be an absolute path to the sitemap | ||||
|     for this site -- e.g., '/sitemap.xml'. If sitemap_url is not provided, this | ||||
|     function will attempt to deduce it by using urls.reverse(). | ||||
|     """ | ||||
|     sitemap_full_url = _get_sitemap_full_url(sitemap_url, sitemap_uses_https) | ||||
|     params = urlencode({"sitemap": sitemap_full_url}) | ||||
|     urlopen("%s?%s" % (ping_url, params)) | ||||
|  | ||||
|  | ||||
| def _get_sitemap_full_url(sitemap_url, sitemap_uses_https=True): | ||||
|     if not django_apps.is_installed("django.contrib.sites"): | ||||
|         raise ImproperlyConfigured( | ||||
|             "ping_google requires django.contrib.sites, which isn't installed." | ||||
|         ) | ||||
|  | ||||
|     if sitemap_url is None: | ||||
|         try: | ||||
|             # First, try to get the "index" sitemap URL. | ||||
|             sitemap_url = reverse("django.contrib.sitemaps.views.index") | ||||
|         except NoReverseMatch: | ||||
|             try: | ||||
|                 # Next, try for the "global" sitemap URL. | ||||
|                 sitemap_url = reverse("django.contrib.sitemaps.views.sitemap") | ||||
|             except NoReverseMatch: | ||||
|                 pass | ||||
|  | ||||
|     if sitemap_url is None: | ||||
|         raise SitemapNotFound( | ||||
|             "You didn't provide a sitemap_url, and the sitemap URL couldn't be " | ||||
|             "auto-detected." | ||||
|         ) | ||||
|  | ||||
|     Site = django_apps.get_model("sites.Site") | ||||
|     current_site = Site.objects.get_current() | ||||
|     scheme = "https" if sitemap_uses_https else "http" | ||||
|     return "%s://%s%s" % (scheme, current_site.domain, sitemap_url) | ||||
|  | ||||
|  | ||||
| class Sitemap: | ||||
|     # This limit is defined by Google. See the index documentation at | ||||
|     # https://www.sitemaps.org/protocol.html#index. | ||||
|     limit = 50000 | ||||
|  | ||||
|     # If protocol is None, the URLs in the sitemap will use the protocol | ||||
|     # with which the sitemap was requested. | ||||
|     protocol = 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): | ||||
|             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 get_languages_for_item(self, item): | ||||
|         """Languages for which this item is displayed.""" | ||||
|         return self._languages() | ||||
|  | ||||
|     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 item in self.items() | ||||
|                 for lang_code in self.get_languages_for_item(item) | ||||
|             ] | ||||
|             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) | ||||
|  | ||||
|     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 None and protocol is None: | ||||
|             warnings.warn( | ||||
|                 "The default sitemap protocol will be changed from 'http' to " | ||||
|                 "'https' in Django 5.0. Set Sitemap.protocol to silence this " | ||||
|                 "warning.", | ||||
|                 category=RemovedInDjango50Warning, | ||||
|                 stacklevel=2, | ||||
|             ) | ||||
|         # RemovedInDjango50Warning: when the deprecation ends, replace 'http' | ||||
|         # with 'https'. | ||||
|         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"): | ||||
|                 Site = django_apps.get_model("sites.Site") | ||||
|                 try: | ||||
|                     site = Site.objects.get_current() | ||||
|                 except Site.DoesNotExist: | ||||
|                     pass | ||||
|             if site is None: | ||||
|                 raise ImproperlyConfigured( | ||||
|                     "To use sitemaps, either enable the sites framework or pass " | ||||
|                     "a Site/RequestSite object in your view." | ||||
|                 ) | ||||
|         return site.domain | ||||
|  | ||||
|     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 get_latest_lastmod(self): | ||||
|         if not hasattr(self, "lastmod"): | ||||
|             return None | ||||
|         if callable(self.lastmod): | ||||
|             try: | ||||
|                 return max([self.lastmod(item) for item in self.items()], default=None) | ||||
|             except TypeError: | ||||
|                 return None | ||||
|         else: | ||||
|             return self.lastmod | ||||
|  | ||||
|     def _urls(self, page, protocol, domain): | ||||
|         urls = [] | ||||
|         latest_lastmod = None | ||||
|         all_items_lastmod = True  # track if all items have a lastmod | ||||
|  | ||||
|         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), | ||||
|                 "priority": str(priority if priority is not None else ""), | ||||
|                 "alternates": [], | ||||
|             } | ||||
|  | ||||
|             if self.i18n and self.alternates: | ||||
|                 item_languages = self.get_languages_for_item(item[0]) | ||||
|                 for lang_code in item_languages: | ||||
|                     loc = f"{protocol}://{domain}{self._location(item, lang_code)}" | ||||
|                     url_info["alternates"].append( | ||||
|                         { | ||||
|                             "location": loc, | ||||
|                             "lang_code": lang_code, | ||||
|                         } | ||||
|                     ) | ||||
|                 if self.x_default and settings.LANGUAGE_CODE in item_languages: | ||||
|                     lang_code = settings.LANGUAGE_CODE | ||||
|                     loc = f"{protocol}://{domain}{self._location(item, lang_code)}" | ||||
|                     loc = loc.replace(f"/{lang_code}/", "/", 1) | ||||
|                     url_info["alternates"].append( | ||||
|                         { | ||||
|                             "location": loc, | ||||
|                             "lang_code": "x-default", | ||||
|                         } | ||||
|                     ) | ||||
|  | ||||
|             urls.append(url_info) | ||||
|  | ||||
|         if all_items_lastmod and latest_lastmod: | ||||
|             self.latest_lastmod = latest_lastmod | ||||
|  | ||||
|         return urls | ||||
|  | ||||
|  | ||||
| class GenericSitemap(Sitemap): | ||||
|     priority = None | ||||
|     changefreq = None | ||||
|  | ||||
|     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 = 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. | ||||
|         return self.queryset.filter() | ||||
|  | ||||
|     def lastmod(self, item): | ||||
|         if self.date_field is not None: | ||||
|             return getattr(item, self.date_field) | ||||
|         return None | ||||
|  | ||||
|     def get_latest_lastmod(self): | ||||
|         if self.date_field is not None: | ||||
|             return ( | ||||
|                 self.queryset.order_by("-" + self.date_field) | ||||
|                 .values_list(self.date_field, flat=True) | ||||
|                 .first() | ||||
|             ) | ||||
|         return None | ||||
| @ -0,0 +1,8 @@ | ||||
| from django.apps import AppConfig | ||||
| from django.utils.translation import gettext_lazy as _ | ||||
|  | ||||
|  | ||||
| class SiteMapsConfig(AppConfig): | ||||
|     default_auto_field = "django.db.models.AutoField" | ||||
|     name = "django.contrib.sitemaps" | ||||
|     verbose_name = _("Site Maps") | ||||
| @ -0,0 +1,16 @@ | ||||
| from django.contrib.sitemaps import ping_google | ||||
| from django.core.management.base import BaseCommand | ||||
|  | ||||
|  | ||||
| class Command(BaseCommand): | ||||
|     help = "Ping Google with an updated sitemap, pass optional url of sitemap" | ||||
|  | ||||
|     def add_arguments(self, parser): | ||||
|         parser.add_argument("sitemap_url", nargs="?") | ||||
|         parser.add_argument("--sitemap-uses-http", action="store_true") | ||||
|  | ||||
|     def handle(self, *args, **options): | ||||
|         ping_google( | ||||
|             sitemap_url=options["sitemap_url"], | ||||
|             sitemap_uses_https=not options["sitemap_uses_http"], | ||||
|         ) | ||||
| @ -0,0 +1,16 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml"> | ||||
| {% spaceless %} | ||||
| {% for url in urlset %} | ||||
|   <url> | ||||
|     <loc>{{ url.location }}</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 %} | ||||
|     {% for alternate in url.alternates %} | ||||
|     <xhtml:link rel="alternate" hreflang="{{ alternate.lang_code }}" href="{{ alternate.location }}"/> | ||||
|     {% endfor %} | ||||
|   </url> | ||||
| {% endfor %} | ||||
| {% endspaceless %} | ||||
| </urlset> | ||||
| @ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> | ||||
| {% spaceless %} | ||||
| {% for site in sitemaps %} | ||||
|   <sitemap> | ||||
|     <loc>{{ site.location }}</loc> | ||||
|       {% if site.last_mod %} | ||||
|         <lastmod>{{ site.last_mod|date:"c" }}</lastmod> | ||||
|       {% endif %} | ||||
|   </sitemap> | ||||
| {% endfor %} | ||||
| {% endspaceless %} | ||||
| </sitemapindex> | ||||
| @ -0,0 +1,151 @@ | ||||
| import datetime | ||||
| import warnings | ||||
| from dataclasses import dataclass | ||||
| from functools import wraps | ||||
|  | ||||
| from django.contrib.sites.shortcuts import get_current_site | ||||
| from django.core.paginator import EmptyPage, PageNotAnInteger | ||||
| from django.http import Http404 | ||||
| from django.template.response import TemplateResponse | ||||
| from django.urls import reverse | ||||
| from django.utils import timezone | ||||
| from django.utils.deprecation import RemovedInDjango50Warning | ||||
| from django.utils.http import http_date | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class SitemapIndexItem: | ||||
|     location: str | ||||
|     last_mod: bool = None | ||||
|  | ||||
|     # RemovedInDjango50Warning | ||||
|     def __str__(self): | ||||
|         msg = ( | ||||
|             "Calling `__str__` on SitemapIndexItem is deprecated, use the `location` " | ||||
|             "attribute instead." | ||||
|         ) | ||||
|         warnings.warn(msg, RemovedInDjango50Warning, stacklevel=2) | ||||
|         return self.location | ||||
|  | ||||
|  | ||||
| def x_robots_tag(func): | ||||
|     @wraps(func) | ||||
|     def inner(request, *args, **kwargs): | ||||
|         response = func(request, *args, **kwargs) | ||||
|         response.headers["X-Robots-Tag"] = "noindex, noodp, noarchive" | ||||
|         return response | ||||
|  | ||||
|     return inner | ||||
|  | ||||
|  | ||||
| def _get_latest_lastmod(current_lastmod, new_lastmod): | ||||
|     """ | ||||
|     Returns the latest `lastmod` where `lastmod` can be either a date or a | ||||
|     datetime. | ||||
|     """ | ||||
|     if not isinstance(new_lastmod, datetime.datetime): | ||||
|         new_lastmod = datetime.datetime.combine(new_lastmod, datetime.time.min) | ||||
|     if timezone.is_naive(new_lastmod): | ||||
|         new_lastmod = timezone.make_aware(new_lastmod, datetime.timezone.utc) | ||||
|     return new_lastmod if current_lastmod is None else max(current_lastmod, new_lastmod) | ||||
|  | ||||
|  | ||||
| @x_robots_tag | ||||
| def index( | ||||
|     request, | ||||
|     sitemaps, | ||||
|     template_name="sitemap_index.xml", | ||||
|     content_type="application/xml", | ||||
|     sitemap_url_name="django.contrib.sitemaps.views.sitemap", | ||||
| ): | ||||
|     req_protocol = request.scheme | ||||
|     req_site = get_current_site(request) | ||||
|  | ||||
|     sites = []  # all sections' sitemap URLs | ||||
|     all_indexes_lastmod = True | ||||
|     latest_lastmod = None | ||||
|     for section, site in sitemaps.items(): | ||||
|         # For each section label, add links of all pages of its sitemap | ||||
|         # (usually generated by the `sitemap` view). | ||||
|         if callable(site): | ||||
|             site = site() | ||||
|         protocol = req_protocol if site.protocol is None else site.protocol | ||||
|         sitemap_url = reverse(sitemap_url_name, kwargs={"section": section}) | ||||
|         absolute_url = "%s://%s%s" % (protocol, req_site.domain, sitemap_url) | ||||
|         site_lastmod = site.get_latest_lastmod() | ||||
|         if all_indexes_lastmod: | ||||
|             if site_lastmod is not None: | ||||
|                 latest_lastmod = _get_latest_lastmod(latest_lastmod, site_lastmod) | ||||
|             else: | ||||
|                 all_indexes_lastmod = False | ||||
|         sites.append(SitemapIndexItem(absolute_url, site_lastmod)) | ||||
|         # Add links to all pages of the sitemap. | ||||
|         for page in range(2, site.paginator.num_pages + 1): | ||||
|             sites.append( | ||||
|                 SitemapIndexItem("%s?p=%s" % (absolute_url, page), site_lastmod) | ||||
|             ) | ||||
|     # If lastmod is defined for all sites, set header so as | ||||
|     # ConditionalGetMiddleware is able to send 304 NOT MODIFIED | ||||
|     if all_indexes_lastmod and latest_lastmod: | ||||
|         headers = {"Last-Modified": http_date(latest_lastmod.timestamp())} | ||||
|     else: | ||||
|         headers = None | ||||
|     return TemplateResponse( | ||||
|         request, | ||||
|         template_name, | ||||
|         {"sitemaps": sites}, | ||||
|         content_type=content_type, | ||||
|         headers=headers, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| @x_robots_tag | ||||
| def sitemap( | ||||
|     request, | ||||
|     sitemaps, | ||||
|     section=None, | ||||
|     template_name="sitemap.xml", | ||||
|     content_type="application/xml", | ||||
| ): | ||||
|     req_protocol = request.scheme | ||||
|     req_site = get_current_site(request) | ||||
|  | ||||
|     if section is not None: | ||||
|         if section not in sitemaps: | ||||
|             raise Http404("No sitemap available for section: %r" % section) | ||||
|         maps = [sitemaps[section]] | ||||
|     else: | ||||
|         maps = sitemaps.values() | ||||
|     page = request.GET.get("p", 1) | ||||
|  | ||||
|     lastmod = None | ||||
|     all_sites_lastmod = True | ||||
|     urls = [] | ||||
|     for site in maps: | ||||
|         try: | ||||
|             if callable(site): | ||||
|                 site = site() | ||||
|             urls.extend(site.get_urls(page=page, site=req_site, protocol=req_protocol)) | ||||
|             if all_sites_lastmod: | ||||
|                 site_lastmod = getattr(site, "latest_lastmod", None) | ||||
|                 if site_lastmod is not None: | ||||
|                     lastmod = _get_latest_lastmod(lastmod, site_lastmod) | ||||
|                 else: | ||||
|                     all_sites_lastmod = False | ||||
|         except EmptyPage: | ||||
|             raise Http404("Page %s empty" % page) | ||||
|         except PageNotAnInteger: | ||||
|             raise Http404("No page '%s'" % page) | ||||
|     # If lastmod is defined for all sites, set header so as | ||||
|     # ConditionalGetMiddleware is able to send 304 NOT MODIFIED | ||||
|     if all_sites_lastmod: | ||||
|         headers = {"Last-Modified": http_date(lastmod.timestamp())} if lastmod else None | ||||
|     else: | ||||
|         headers = None | ||||
|     return TemplateResponse( | ||||
|         request, | ||||
|         template_name, | ||||
|         {"urlset": urls}, | ||||
|         content_type=content_type, | ||||
|         headers=headers, | ||||
|     ) | ||||
		Reference in New Issue
	
	Block a user