584 lines
17 KiB
Python
584 lines
17 KiB
Python
"""public views (no need to log in)"""
|
|
|
|
# pylint: disable=R6301
|
|
from itertools import chain
|
|
from operator import attrgetter
|
|
|
|
from django.conf import settings
|
|
from django.shortcuts import get_object_or_404
|
|
from django.contrib.postgres.aggregates import ArrayAgg
|
|
from django.contrib.postgres.search import SearchRank, SearchVector
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.core.mail import EmailMessage
|
|
from django.core.paginator import Paginator
|
|
from django.db.models import Count
|
|
from django.shortcuts import render, redirect
|
|
from django.utils import timezone
|
|
from django.views import View
|
|
|
|
from blogs import forms, models
|
|
from blogs.utilities import get_blog_info, get_webfinger_subscribe_uri
|
|
|
|
|
|
class HomeFeed(View):
|
|
"""the home feed when someone visits the site"""
|
|
|
|
def get(self, request):
|
|
"""display home page"""
|
|
|
|
latest = models.Article.objects.order_by("-pubdate")[:10]
|
|
|
|
data = {"title": "Latest blog posts", "latest": latest}
|
|
return render(request, "index.html", data)
|
|
|
|
|
|
class Blogs(View):
|
|
"""browse the list of blogs"""
|
|
|
|
def get(self, request, category=None):
|
|
"""here they are"""
|
|
|
|
if category:
|
|
|
|
blogs = models.Blog.objects.filter(approved=True, active=True, category=category).order_by(
|
|
"-updateddate"
|
|
)
|
|
|
|
else:
|
|
|
|
blogs = models.Blog.objects.filter(approved=True, active=True).order_by(
|
|
"-updateddate"
|
|
)
|
|
|
|
for blog in blogs:
|
|
blog.category_name = models.Category(blog.category).label
|
|
data = {"title": "Blogs and websites", "blogs": blogs}
|
|
return render(request, "browse/blogs.html", data)
|
|
|
|
|
|
class Conferences(View):
|
|
"""browse the list of conferences"""
|
|
|
|
def get(self, request, category=None):
|
|
"""here they are"""
|
|
now = timezone.now()
|
|
|
|
if category:
|
|
cons = models.Event.objects.filter(approved=True, start_date__gte=now, category=category).order_by(
|
|
"start_date"
|
|
)
|
|
else:
|
|
cons = models.Event.objects.filter(approved=True, start_date__gte=now).order_by(
|
|
"start_date"
|
|
)
|
|
|
|
for con in cons:
|
|
con.category_name = models.Category(con.category).label
|
|
con.call_for_papers = con.cfp.filter(closing_date__gte=timezone.now()).order_by("-closing_date").last()
|
|
|
|
data = {"title": "Upcoming events", "cons": cons}
|
|
return render(request, "browse/events.html", data)
|
|
|
|
|
|
class CallsForPapers(View):
|
|
"""browse the list of CFPs"""
|
|
|
|
def get(self, request):
|
|
"""here they are"""
|
|
now = timezone.now()
|
|
cfps = models.CallForPapers.objects.filter(
|
|
approved=True, closing_date__gte=now
|
|
).order_by("closing_date")
|
|
data = {"title": "Calls for Papers open now", "cfps": cfps}
|
|
return render(request, "browse/cfp.html", data)
|
|
|
|
|
|
class Groups(View):
|
|
"""browse the list of groups"""
|
|
|
|
def get(self, request, category=None):
|
|
"""here they are"""
|
|
|
|
if category:
|
|
groups = models.Group.objects.filter(approved=True, category=category).order_by("name")
|
|
|
|
else:
|
|
groups = models.Group.objects.filter(approved=True).order_by("name")
|
|
|
|
for group in groups:
|
|
group.category_name = models.Category(group.category).label
|
|
group.reg_type = models.utils.GroupType(group.type).label
|
|
data = {"title": "Groups and discussion lists", "groups": groups}
|
|
return render(request, "browse/groups.html", data)
|
|
|
|
|
|
class Newsletters(View):
|
|
"""browse the list of groups"""
|
|
|
|
def get(self, request):
|
|
"""here they are"""
|
|
news = models.Newsletter.objects.filter(approved=True).order_by("name")
|
|
for letter in news:
|
|
letter.category_name = models.Category(letter.category).label
|
|
data = {"title": "Newsletters", "news": news}
|
|
return render(request, "browse/newsletters.html", data)
|
|
|
|
|
|
class RegisterBlog(View):
|
|
"""register a blog"""
|
|
|
|
def get(self, request):
|
|
"""the registration page with a form"""
|
|
|
|
form = forms.RegisterBlogForm()
|
|
data = {"title": "Register your blog", "form": form}
|
|
return render(request, "blogs/register.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive POSTED RegisterBlogForm"""
|
|
|
|
form = forms.RegisterBlogForm(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
blog_info = get_blog_info(form.cleaned_data["url"])
|
|
except KeyError:
|
|
return render(
|
|
request,
|
|
"blogs/register.html",
|
|
{"title": "Complete blog registration", "form": form},
|
|
)
|
|
|
|
data = {
|
|
"title": "Complete blog registration",
|
|
"form": form,
|
|
}
|
|
|
|
if blog_info:
|
|
data["blog_info"] = blog_info
|
|
|
|
else:
|
|
data[
|
|
"error"
|
|
] = "Could not auto-discover your feed info, please enter manually"
|
|
|
|
return render(request, "blogs/confirm-register.html", data)
|
|
|
|
data = {"title": "Register your blog", "form": form}
|
|
return render(request, "blogs/register.html", data)
|
|
|
|
|
|
class ConfirmBlogRegistration(View):
|
|
"""submit validated and pre-filled registration form"""
|
|
|
|
def get(self, request):
|
|
"""the confirm registration page"""
|
|
|
|
form = forms.ConfirmBlogForm(request.POST)
|
|
data = {"title": "Complete blog registration", "form": form}
|
|
|
|
return render(request, "blogs/confirm-register.html", data)
|
|
|
|
def post(self, request):
|
|
"""the final form has been submitted!"""
|
|
|
|
form = forms.ConfirmBlogForm(request.POST)
|
|
if form.is_valid():
|
|
blog = form.save()
|
|
send_email("blog", blog)
|
|
return redirect("/thankyou/blog")
|
|
|
|
data = {"title": "Oops!", "form": form}
|
|
return render(request, "blogs/confirm-register.html", data)
|
|
|
|
|
|
class RegisterConference(View):
|
|
"""register a event"""
|
|
|
|
def get(self, request):
|
|
"""the registration page with a form"""
|
|
|
|
form = forms.RegisterConferenceForm()
|
|
data = {"title": "Register your event", "form": form}
|
|
return render(request, "events/register.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive form"""
|
|
|
|
form = forms.RegisterConferenceForm(request.POST)
|
|
if form.is_valid():
|
|
conf = form.save()
|
|
send_email("event", conf)
|
|
return redirect("/thankyou/conference")
|
|
|
|
|
|
class RegisterCallForPapers(View):
|
|
"""register a RegisterCallForPapers"""
|
|
|
|
def get(self, request):
|
|
"""the registration page with a form"""
|
|
|
|
form = forms.RegisterCallForPapersForm()
|
|
data = {"title": "Register your Call For Papers", "form": form}
|
|
|
|
return render(request, "events/cfp.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive POSTED RegisterCallForPapersForm"""
|
|
|
|
form = forms.RegisterCallForPapersForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
cfp = form.save()
|
|
send_email("Call for Papers", cfp)
|
|
return redirect("/thankyou/Call For Papers")
|
|
|
|
data = {
|
|
"title": "Register your Call For Papers",
|
|
"form": form,
|
|
"errors": True,
|
|
}
|
|
|
|
return render(request, "events/cfp.html", data)
|
|
|
|
|
|
class RegisterGroup(View):
|
|
"""register a group"""
|
|
|
|
def get(self, request):
|
|
"""the registration page with a form"""
|
|
|
|
form = forms.RegisterGroupForm()
|
|
data = {"title": "Register your group", "form": form}
|
|
|
|
return render(request, "register-group.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive POSTED form"""
|
|
|
|
form = forms.RegisterGroupForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
group = form.save()
|
|
send_email("group", group)
|
|
return redirect("/thankyou/group")
|
|
|
|
data = {"title": "Oops!", "form": form, "errors": True}
|
|
|
|
return render(request, "register-group.html", data)
|
|
|
|
|
|
class RegisterNewsletter(View):
|
|
"""register a newsletter"""
|
|
|
|
def get(self, request):
|
|
"""the registration page with a form"""
|
|
|
|
form = forms.RegisterNewsletterForm()
|
|
data = {"title": "Register your newsletter", "form": form}
|
|
|
|
return render(request, "register-newsletter.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive POSTED form"""
|
|
|
|
form = forms.RegisterNewsletterForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
newsletter = form.save()
|
|
send_email("newsletter", newsletter)
|
|
return redirect("/thankyou/newsletter")
|
|
|
|
data = {"title": "Oops!", "form": form, "errors": True}
|
|
|
|
return render(request, "register-newsletter.html", data)
|
|
|
|
|
|
class Search(View):
|
|
"""search functions"""
|
|
|
|
def get(self, request, articles=None):
|
|
"""display search page"""
|
|
|
|
query = request.GET.get("q")
|
|
|
|
article_vector = (
|
|
SearchVector("tagnames", weight="A")
|
|
+ SearchVector("title", weight="B")
|
|
+ SearchVector("description", weight="C")
|
|
)
|
|
|
|
articles = (
|
|
models.Article.objects.annotate(
|
|
tagnames=ArrayAgg("tags__name", distinct=True), # see above: this prevents many-to-many objects like tags causing the same article to appear in the results multiple times
|
|
rank=SearchRank(article_vector, query)
|
|
)
|
|
.filter(rank__gte=0.1)
|
|
.order_by("-rank")
|
|
)
|
|
|
|
conference_vector = SearchVector("name", weight="A") + SearchVector(
|
|
"description", weight="C"
|
|
)
|
|
|
|
events = (
|
|
models.Event.objects.filter(approved=True)
|
|
.annotate(rank=SearchRank(conference_vector, query))
|
|
.filter(rank__gte=0.1)
|
|
.order_by("-rank")
|
|
)
|
|
|
|
cfp_vector = SearchVector("name", weight="B") + SearchVector(
|
|
"details", weight="C"
|
|
)
|
|
|
|
cfps = (
|
|
models.CallForPapers.objects.filter(approved=True)
|
|
.annotate(rank=SearchRank(cfp_vector, query))
|
|
.filter(rank__gte=0.1)
|
|
.order_by("-rank")
|
|
)
|
|
|
|
news_vector = SearchVector("name", weight="A") + SearchVector(
|
|
"description", weight="C"
|
|
)
|
|
|
|
newsletters = (
|
|
models.Newsletter.objects.filter(approved=True)
|
|
.annotate(rank=SearchRank(news_vector, query))
|
|
.filter(rank__gte=0.1)
|
|
.order_by("-rank")
|
|
)
|
|
|
|
group_vector = SearchVector("name", weight="A") + SearchVector(
|
|
"description", weight="C"
|
|
)
|
|
|
|
groups = (
|
|
models.Group.objects.filter(approved=True)
|
|
.annotate(rank=SearchRank(group_vector, query))
|
|
.filter(rank__gte=0.1)
|
|
.order_by("-rank")
|
|
)
|
|
|
|
combined = sorted(
|
|
chain(articles, events, cfps, newsletters, groups),
|
|
key=attrgetter("rank"),
|
|
reverse=True,
|
|
)
|
|
|
|
for item in combined:
|
|
if hasattr(item, "category"):
|
|
item.category_name = models.Category(item.category).label
|
|
if hasattr(item, "event"):
|
|
item.category = models.Category(item.event.category)
|
|
item.category_name = models.Category(item.event.category).label
|
|
|
|
paginator = Paginator(combined, 10)
|
|
page_number = request.GET.get("page")
|
|
paged = paginator.get_page(page_number)
|
|
|
|
data = {"title": "Search Aus GLAMR", "items": paged, "query": query}
|
|
|
|
return render(request, "search.html", data)
|
|
|
|
|
|
class Browse(View):
|
|
"""browse by clicking on a tag"""
|
|
|
|
def get(self, request):
|
|
"""display browse results"""
|
|
|
|
query = request.GET.get("q")
|
|
results = models.Article.objects.filter(tags__name=query).order_by("-pubdate")
|
|
trending = models.Tag.objects.annotate(count=Count("articles")).order_by(
|
|
"-count"
|
|
)[:10]
|
|
|
|
paginator = Paginator(results, 10)
|
|
page_number = request.GET.get("page")
|
|
paged = paginator.get_page(page_number)
|
|
|
|
data = {
|
|
"title": f"Articles tagged '{query}'",
|
|
"trending": trending,
|
|
"items": paged,
|
|
"query": query,
|
|
}
|
|
|
|
return render(request, "browse/tags.html", data)
|
|
|
|
|
|
class Subscribe(View):
|
|
"""Subscribe page showing RSS feed link and mastodon follow form"""
|
|
|
|
def get(self, request):
|
|
"""display subscribe page"""
|
|
|
|
return render(request, "subscribe.html", {})
|
|
|
|
def post(self, request):
|
|
"""subscribe to Mastodon account"""
|
|
|
|
form = forms.SubscribeViaMastodon(request.POST)
|
|
if form.is_valid():
|
|
try:
|
|
username = form.cleaned_data["username"]
|
|
uri = get_webfinger_subscribe_uri(username)
|
|
|
|
if uri:
|
|
return redirect(uri)
|
|
|
|
form.add_error(
|
|
"username", "Enter a valid username e.g. @example@ausglam.space"
|
|
)
|
|
|
|
except KeyError:
|
|
pass
|
|
|
|
return render(request, "subscribe.html", {"form": form})
|
|
|
|
|
|
class SubscribeEmail(View):
|
|
"""Subscribe to weekly emails"""
|
|
|
|
def get(self, request):
|
|
"""display subscribe page"""
|
|
|
|
form = forms.SubscribeEmailForm()
|
|
data = {"title": "Get weekly email updates", "form": form}
|
|
|
|
return render(request, "subscribe-email.html", data)
|
|
|
|
def post(self, request):
|
|
"""subscribe to Mastodon account"""
|
|
|
|
form = forms.SubscribeEmailForm(request.POST)
|
|
if form.is_valid():
|
|
user = form.save()
|
|
user.send_confirmation_email()
|
|
return redirect("/thankyou/email%20address")
|
|
|
|
return render(request, "subscribe.html", {"form": form})
|
|
|
|
|
|
class ConfirmEmail(View):
|
|
"""Hit this when click to confirm email subscription"""
|
|
|
|
def get(self, request, token, user_id):
|
|
"""display confirmation page"""
|
|
|
|
user = get_object_or_404(models.Subscriber, id=user_id, token=token)
|
|
|
|
user.confirmed = True
|
|
user.save()
|
|
|
|
return render(request, "confirm-email.html", {"title": "Confirmed!"})
|
|
|
|
|
|
class UnsubscribeEmail(View):
|
|
"""Hit this when click to confirm email subscription"""
|
|
|
|
def get(self, request, token, user_id):
|
|
"""unsubscribe conf page"""
|
|
|
|
user = get_object_or_404(models.Subscriber, id=user_id, token=token)
|
|
user.delete()
|
|
|
|
return render(request, "unsubscribe.html", {"title": "Sorry to see you go"})
|
|
|
|
|
|
class Contribute(View):
|
|
"""help page"""
|
|
|
|
def get(self, request):
|
|
"""display contribute page"""
|
|
|
|
data = {
|
|
"title": "Contribute",
|
|
}
|
|
return render(request, "contribute.html", data)
|
|
|
|
|
|
class Help(View):
|
|
"""help page"""
|
|
|
|
def get(self, request):
|
|
"""display help page"""
|
|
|
|
data = {
|
|
"title": "Help",
|
|
}
|
|
return render(request, "help.html", data)
|
|
|
|
|
|
class Contact(View):
|
|
"""contact Hugh"""
|
|
|
|
def get(self, request):
|
|
"""the contact page with a form"""
|
|
|
|
form = forms.ContactForm()
|
|
data = {"title": "Get in touch", "form": form}
|
|
|
|
return render(request, "contact.html", data)
|
|
|
|
def post(self, request):
|
|
"""receive POSTED form"""
|
|
|
|
form = forms.ContactForm(request.POST)
|
|
|
|
if form.is_valid():
|
|
from_email = form.cleaned_data["from_email"]
|
|
subject = form.cleaned_data["subject"]
|
|
message = form.cleaned_data["message"]
|
|
send_contact_email(from_email, subject, message)
|
|
return redirect("/thankyou/message")
|
|
|
|
data = {"title": "Oops!", "form": form, "errors": True}
|
|
|
|
return render(request, "contact.html", data)
|
|
|
|
|
|
class Thankyou(View):
|
|
"""thankyou for registering page"""
|
|
|
|
def get(self, request, register_type):
|
|
"""display thankyou page"""
|
|
|
|
data = {"title": "Thanks!", "register_type": register_type}
|
|
return render(request, "thanks.html", data)
|
|
|
|
|
|
def send_email(instance, obj):
|
|
"""send an email alert to admin"""
|
|
|
|
html_message = f"<html><body>\
|
|
<p>A new {instance} has been registered:</p>\
|
|
<strong>{obj.title if hasattr(obj, 'title') else obj.name}</strong></p>\
|
|
<p>To approve or reject visit <a href='https://{settings.DOMAIN}/admin'>{settings.DOMAIN}/admin</a></p>\
|
|
</body></html>"
|
|
|
|
msg = EmailMessage(
|
|
f"📥 Someone has registered a new {instance}!",
|
|
html_message,
|
|
settings.DEFAULT_FROM_EMAIL,
|
|
[settings.ADMIN_EMAIL],
|
|
)
|
|
msg.content_subtype = "html"
|
|
msg.send()
|
|
|
|
|
|
def send_contact_email(from_email, subject, message):
|
|
"""email the message"""
|
|
|
|
html_message = f"<html><body><p>{message}</p></body></html>"
|
|
|
|
msg = EmailMessage(
|
|
f"Message via Aus GLAMR: {subject}",
|
|
html_message,
|
|
f"{from_email}",
|
|
[settings.ADMIN_EMAIL],
|
|
)
|
|
msg.content_subtype = "html"
|
|
msg.send()
|