Tag intersections.
[wolnelektury.git] / src / wolnelektury / views.py
1 # This file is part of Wolnelektury, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
3 #
4 from datetime import date, datetime
5 from urllib.parse import quote_plus
6 import feedparser
7 from allauth.socialaccount.views import SignupView
8
9 from django.conf import settings
10 from django.contrib import auth
11 from django.contrib.auth.decorators import login_required
12 from django.contrib.auth.forms import AuthenticationForm
13 from django.contrib.auth.views import LoginView
14 from django.core.cache import cache
15 from django.views.generic import FormView
16 from django.http import HttpResponse, HttpResponseRedirect
17 from django.shortcuts import render
18 from django.utils.translation import gettext_lazy as _
19 from django.views.decorators.cache import never_cache
20
21 from ajaxable.utils import AjaxableFormView
22 from ajaxable.utils import placeholdized
23 from catalogue.models import Book, Collection, Tag, Fragment
24 import club.models
25 from social.utils import get_or_choose_cite
26 from wolnelektury.forms import RegistrationForm, SocialSignupForm, WLAuthenticationForm
27
28
29 def main_page_2022(request):
30     ctx = {}
31     ctx['last_published'] = Book.objects.exclude(cover_clean='').filter(findable=True, parent=None).order_by('-created_at')[:10]
32     ctx['recommended_collection'] = Collection.objects.filter(listed=True, role='recommend').order_by('?').first()
33     ctx['ambassadors'] = club.models.Ambassador.objects.all().order_by('?')
34     return render(request, '2022/main_page.html', ctx)
35
36 @never_cache
37 def main_page(request):
38     if request.EXPERIMENTS['layout'].value:
39         return main_page_2022(request)
40
41     ctx = {
42         'last_published': Book.objects.exclude(cover_thumb='').filter(findable=True, parent=None).order_by('-created_at')[:6],
43         'theme_books': [],
44     }
45
46     # FIXME: find this theme and books properly.
47     if Fragment.objects.exists():
48         while True:
49             ctx['theme'] = Tag.objects.filter(category='theme').order_by('?')[:1][0]
50             tf = Fragment.tagged.with_any([ctx['theme']]).select_related('book').filter(book__findable=True).order_by('?')[:100]
51             if not tf:
52                 continue
53             ctx['theme_fragment'] = tf[0]
54             for f in tf:
55                 if f.book not in ctx['theme_books']:
56                     ctx['theme_books'].append(f.book)
57                 if len(ctx['theme_books']) == 3:
58                     break
59             break
60
61     # Choose collections for main.
62     ctx['collections'] = Collection.objects.filter(listed=True).order_by('?')[:4]
63
64     best = []
65     best_places = 5
66     recommended_collection = None
67     for recommended in Collection.objects.filter(listed=True, role='recommend').order_by('?'):
68         if recommended_collection is None:
69             recommended_collection = recommended
70         books = list(recommended.get_books().exclude(id__in=[b.id for b in best]).order_by('?')[:best_places])
71         best.extend(books)
72         best_places -= len(books)
73         if not best_places:
74             break
75     ctx['recommended_collection'] = recommended_collection
76     if best_places:
77         best.extend(
78             list(
79                 Book.objects.filter(findable=True).exclude(id__in=[b.id for b in best]).order_by('?')[:best_places]
80             )
81         )
82     ctx['best'] = best
83
84     return render(request, "main_page.html", ctx)
85
86
87 class WLLoginView(LoginView):
88     form_class = WLAuthenticationForm
89
90
91 wl_login_view = WLLoginView.as_view()
92
93
94 class LoginFormView(AjaxableFormView):
95     form_class = AuthenticationForm
96     template = "auth/login.html"
97     placeholdize = True
98     title = _('Sign in')
99     submit = _('Sign in')
100     ajax_redirect = True
101
102     def __call__(self, request):
103         if request.EXPERIMENTS['layout'].value:
104             return wl_login_view(request)
105
106         if request.user.is_authenticated:
107             return self.redirect_or_refresh(
108                 request, '/',
109                 message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
110         return super(LoginFormView, self).__call__(request)
111
112     def success(self, form, request):
113         auth.login(request, form.get_user())
114
115
116 class WLRegisterView(FormView):
117     form_class = RegistrationForm
118     template_name = 'registration/register.html'
119
120 wl_register_view = WLRegisterView.as_view()
121
122
123 class RegisterFormView(AjaxableFormView):
124     form_class = RegistrationForm
125     template = "auth/register.html"
126     placeholdize = True
127     title = _('Register')
128     submit = _('Register')
129     ajax_redirect = True
130     form_prefix = 'register'
131     honeypot = True
132
133     def __call__(self, request):
134         if request.EXPERIMENTS['layout'].value:
135             return wl_register_view(request)
136
137         if request.user.is_authenticated:
138             return self.redirect_or_refresh(
139                 request, '/',
140                 message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
141         return super(RegisterFormView, self).__call__(request)
142
143     def success(self, form, request):
144         form.save()
145         user = auth.authenticate(
146             username=form.cleaned_data['username'],
147             password=form.cleaned_data['password1']
148         )
149         auth.login(request, user)
150
151
152 class LoginRegisterFormView(LoginFormView):
153     template = 'auth/login_register.html'
154     title = _('You have to be logged in to continue')
155
156     def extra_context(self, request, obj):
157         return {
158             "register_form": placeholdized(RegistrationForm(prefix='register')),
159             "register_submit": _('Register'),
160         }
161
162
163 @never_cache
164 def logout_then_redirect(request):
165     auth.logout(request)
166     return HttpResponseRedirect(quote_plus(request.GET.get('next', '/'), safe='/?='))
167
168
169 @never_cache
170 def clock(request):
171     """ Provides server UTC time for jquery.countdown,
172     in a format suitable for Date.parse()
173     """
174     return HttpResponse(datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S UTC'))
175
176
177 def publish_plan(request):
178     cache_key = "publish_plan"
179     plan = cache.get(cache_key)
180
181     if plan is None:
182         plan = []
183         try:
184             feed = feedparser.parse(settings.PUBLISH_PLAN_FEED)
185         except:
186             pass
187         else:
188             for i in range(len(feed['entries'])):
189                 plan.append({
190                     'title': feed['entries'][i].title,
191                     'link': feed['entries'][i].link,
192                     })
193         cache.set(cache_key, plan, 1800)
194
195     return render(request, "publish_plan.html", {'plan': plan})
196
197
198 @login_required
199 def user_settings(request):
200     return render(request, "user.html")
201
202
203 def widget(request):
204     return render(request, 'widget.html')
205
206
207 class SocialSignupView(SignupView):
208     form_class = SocialSignupForm
209
210
211 def exception_test(request):
212     msg = request.GET.get('msg')
213     if msg:
214         raise Exception('Exception test: %s' % msg)
215     else:
216         raise Exception('Exception test')
217
218
219 def post_test(request):
220     return render(request, 'post_test.html', {'action': '/api/reading/jego-zasady/complete/'})