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