94 строки
2.7 KiB
Python
94 строки
2.7 KiB
Python
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
# file, you can obtain one at http://mozilla.org/MPL/2.0/.
|
|
import os
|
|
|
|
import commonmark
|
|
from django.conf import settings
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import render
|
|
from django.utils.functional import cached_property
|
|
from pkg_resources import parse_version
|
|
|
|
|
|
class News:
|
|
"""
|
|
Encapsulate the rendering of the news document ``NEWS.md``.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.path = os.path.join(settings.BASE_DIR, "NEWS.md")
|
|
self.parser = commonmark.Parser()
|
|
self.renderer = commonmark.HtmlRenderer()
|
|
self.cookie_name = "news_current"
|
|
|
|
@cached_property
|
|
def ast(self):
|
|
"""
|
|
Return (and cache for repeated querying) the Markdown AST
|
|
of the ``NEWS.md`` file.
|
|
"""
|
|
with open(self.path, "r") as news_file:
|
|
content = news_file.read()
|
|
return self.parser.parse(content)
|
|
|
|
@cached_property
|
|
def latest(self):
|
|
"""
|
|
Return the latest version found in the ``NEWS.md`` file.
|
|
"""
|
|
version = self.ast.first_child.literal
|
|
if not version:
|
|
version = self.ast.first_child.first_child.literal
|
|
if not version:
|
|
version = "0.0"
|
|
return version
|
|
|
|
def render(self):
|
|
"Render the ``NEWS.md`` file as a HTML."
|
|
return self.renderer.render(self.ast)
|
|
|
|
def update(self, request, response):
|
|
"Set the cookie for the given request with the latest seen version."
|
|
if not self.uptodate(request):
|
|
response.set_cookie(
|
|
self.cookie_name, self.latest, secure=True, httponly=True
|
|
)
|
|
|
|
def current(self, request):
|
|
"Return the latest seen version or nothing."
|
|
return request.COOKIES.get(self.cookie_name) or ""
|
|
|
|
def uptodate(self, request):
|
|
"Return whether the current is newer than the last seen version."
|
|
return parse_version(self.latest) <= parse_version(self.current(request))
|
|
|
|
|
|
def list_news(request):
|
|
"""
|
|
View to list all news and optionally render only part of the
|
|
template for AJAX requests.
|
|
"""
|
|
news = News()
|
|
|
|
if request.is_ajax():
|
|
response = HttpResponse(news.render())
|
|
else:
|
|
context = {"news": news}
|
|
response = render(request, "atmo/news/list.html", context)
|
|
|
|
news.update(request, response)
|
|
return response
|
|
|
|
|
|
def check_news(request):
|
|
"""
|
|
View to check if the current user has seen the latest "News" section
|
|
and return either `'ok'` or `'meh'` as a string.
|
|
"""
|
|
news = News()
|
|
if news.uptodate(request):
|
|
return HttpResponse("ok")
|
|
else:
|
|
return HttpResponse("meh")
|