Moving forward.
[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     ctx['widget'] = settings.WIDGETS.get(request.GET.get('w'))
35     return render(request, '2022/main_page.html', ctx)
36
37 @never_cache
38 def main_page(request):
39     if request.GET.get('w') in settings.WIDGETS:
40         request.EXPERIMENTS['layout'].override(True)
41
42     if request.EXPERIMENTS['layout'].value:
43         return main_page_2022(request)
44
45     ctx = {
46         'last_published': Book.objects.exclude(cover_thumb='').filter(findable=True, parent=None).order_by('-created_at')[:6],
47         'theme_books': [],
48     }
49
50     # FIXME: find this theme and books properly.
51     if Fragment.objects.exists():
52         while True:
53             ctx['theme'] = Tag.objects.filter(category='theme').order_by('?')[:1][0]
54             tf = Fragment.tagged.with_any([ctx['theme']]).select_related('book').filter(book__findable=True).order_by('?')[:100]
55             if not tf:
56                 continue
57             ctx['theme_fragment'] = tf[0]
58             for f in tf:
59                 if f.book not in ctx['theme_books']:
60                     ctx['theme_books'].append(f.book)
61                 if len(ctx['theme_books']) == 3:
62                     break
63             break
64
65     # Choose collections for main.
66     ctx['collections'] = Collection.objects.filter(listed=True).order_by('?')[:4]
67
68     best = []
69     best_places = 5
70     recommended_collection = None
71     for recommended in Collection.objects.filter(listed=True, role='recommend').order_by('?'):
72         if recommended_collection is None:
73             recommended_collection = recommended
74         books = list(recommended.get_books().exclude(id__in=[b.id for b in best]).order_by('?')[:best_places])
75         best.extend(books)
76         best_places -= len(books)
77         if not best_places:
78             break
79     ctx['recommended_collection'] = recommended_collection
80     if best_places:
81         best.extend(
82             list(
83                 Book.objects.filter(findable=True).exclude(id__in=[b.id for b in best]).order_by('?')[:best_places]
84             )
85         )
86     ctx['best'] = best
87
88     return render(request, "main_page.html", ctx)
89
90
91 class WLLoginView(LoginView):
92     form_class = WLAuthenticationForm
93
94
95 wl_login_view = WLLoginView.as_view()
96
97
98 class LoginFormView(AjaxableFormView):
99     form_class = AuthenticationForm
100     template = "auth/login.html"
101     placeholdize = True
102     title = _('Sign in')
103     submit = _('Sign in')
104     ajax_redirect = True
105
106     def __call__(self, request):
107         if request.EXPERIMENTS['layout'].value:
108             return wl_login_view(request)
109
110         if request.user.is_authenticated:
111             return self.redirect_or_refresh(
112                 request, '/',
113                 message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
114         return super(LoginFormView, self).__call__(request)
115
116     def success(self, form, request):
117         auth.login(request, form.get_user())
118
119
120 class WLRegisterView(FormView):
121     form_class = RegistrationForm
122     template_name = 'registration/register.html'
123
124     def form_valid(self, form):
125         form.save()
126         user = auth.authenticate(
127             username=form.cleaned_data['username'],
128             password=form.cleaned_data['password1']
129         )
130         auth.login(self.request, user)
131         return HttpResponseRedirect(quote_plus(self.request.GET.get('next', '/'), safe='/?='))
132
133 wl_register_view = WLRegisterView.as_view()
134
135
136 class RegisterFormView(AjaxableFormView):
137     form_class = RegistrationForm
138     template = "auth/register.html"
139     placeholdize = True
140     title = _('Register')
141     submit = _('Register')
142     ajax_redirect = True
143     form_prefix = 'register'
144     honeypot = True
145
146     def __call__(self, request):
147         if request.EXPERIMENTS['layout'].value:
148             return wl_register_view(request)
149
150         if request.user.is_authenticated:
151             return self.redirect_or_refresh(
152                 request, '/',
153                 message=_('Already logged in as user %(user)s', ) % {'user': request.user.username})
154         return super(RegisterFormView, self).__call__(request)
155
156     def success(self, form, request):
157         form.save()
158         user = auth.authenticate(
159             username=form.cleaned_data['username'],
160             password=form.cleaned_data['password1']
161         )
162         auth.login(request, user)
163
164
165 class LoginRegisterFormView(LoginFormView):
166     template = 'auth/login_register.html'
167     title = _('You have to be logged in to continue')
168
169     def extra_context(self, request, obj):
170         return {
171             "register_form": placeholdized(RegistrationForm(prefix='register')),
172             "register_submit": _('Register'),
173         }
174
175
176 @never_cache
177 def logout_then_redirect(request):
178     auth.logout(request)
179     return HttpResponseRedirect(quote_plus(request.GET.get('next', '/'), safe='/?='))
180
181
182 @never_cache
183 def clock(request):
184     """ Provides server UTC time for jquery.countdown,
185     in a format suitable for Date.parse()
186     """
187     return HttpResponse(datetime.utcnow().strftime('%Y/%m/%d %H:%M:%S UTC'))
188
189
190 def publish_plan(request):
191     cache_key = "publish_plan"
192     plan = cache.get(cache_key)
193
194     if plan is None:
195         plan = []
196         try:
197             feed = feedparser.parse(settings.PUBLISH_PLAN_FEED)
198         except:
199             pass
200         else:
201             for i in range(len(feed['entries'])):
202                 plan.append({
203                     'title': feed['entries'][i].title,
204                     'link': feed['entries'][i].link,
205                     })
206         cache.set(cache_key, plan, 1800)
207
208     return render(request, "publish_plan.html", {'plan': plan})
209
210
211 @login_required
212 def user_settings(request):
213     return render(request, "user.html")
214
215
216 def widget(request):
217     return render(request, 'widget.html')
218
219
220 class SocialSignupView(SignupView):
221     form_class = SocialSignupForm
222
223
224 def exception_test(request):
225     msg = request.GET.get('msg')
226     if msg:
227         raise Exception('Exception test: %s' % msg)
228     else:
229         raise Exception('Exception test')
230
231
232 def post_test(request):
233     return render(request, 'post_test.html', {'action': '/api/reading/jego-zasady/complete/'})