]> git.0d.be Git - chloro.git/blob - chloro/phyll/views.py
do not include non-feed posts on homepage
[chloro.git] / chloro / phyll / views.py
1 # chloro - personal space
2 # Copyright (C) 2019  Frederic Peters
3 #
4 # This program is free software: you can redistribute it and/or modify it
5 # under the terms of the GNU Affero General Public License as published
6 # by the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 import os
18 import urllib.parse
19
20 from django.conf import settings
21 from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
22 from django.contrib.syndication.views import Feed
23 from django.core.exceptions import PermissionDenied
24 from django.core.files.storage import default_storage
25 from django.http import Http404, HttpResponse, JsonResponse
26 from django.utils.feedgenerator import Atom1Feed
27 from django.utils.text import slugify
28 from django.views import View
29 from django.views.decorators.csrf import csrf_exempt
30 from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView
31 from sorl.thumbnail.shortcuts import get_thumbnail
32
33 from .models import Note
34
35
36 class NoteView(DetailView):
37     model = Note
38
39     def get(self, request, *args, **kwargs):
40         note = self.get_object()
41         if kwargs.get('year'):
42             # check date does match
43             creation = self.get_object().creation_timestamp
44             if (creation.year, creation.month, creation.day) != (
45                 int(kwargs['year']),
46                 int(kwargs['month']),
47                 int(kwargs['day']),
48             ):
49                 raise Http404()
50         if not note.published and not request.user.is_staff:
51             raise PermissionDenied()
52         return super().get(request, *args, **kwargs)
53
54
55 class NoteEditView(UpdateView):
56     model = Note
57     fields = ['title', 'slug', 'text', 'tags', 'published', 'included_in_feed']
58
59
60 class NoteApiSaveView(View):
61     http_method_names = ['post']
62
63     def post(self, request, *args, **kwargs):
64         note = Note.objects.get(slug=kwargs['slug'])
65         note.text = request.POST['text']
66         note.save()
67         return HttpResponse('ok')
68
69
70 class NoteAddView(CreateView):
71     model = Note
72     fields = ['title', 'slug', 'text', 'tags', 'published', 'included_in_feed']
73
74
75 class NoteDeleteView(DeleteView):
76     model = Note
77
78     def get_success_url(self):
79         return '/'
80
81
82 class HomeView(TemplateView):
83     template_name = 'phyll/home.html'
84
85     def get_context_data(self, **kwargs):
86         context = super().get_context_data(**kwargs)
87         context['posts'] = Note.objects.filter(published=True, included_in_feed=True).order_by(
88             '-creation_timestamp'
89         )[:5]
90         context['recent_changes'] = Note.objects.filter(published=True, included_in_feed=True).order_by(
91             '-last_update_timestamp'
92         )
93         context['index'] = Note.objects.filter(slug='index').first()
94         return context
95
96
97 class ArchivesView(ListView):
98     model = Note
99
100     def get_queryset(self):
101         qs = super().get_queryset()
102         if not self.request.user.is_staff:
103             qs = qs.filter(published=True, included_in_feed=True)
104         return qs
105
106
107 class ListOnTagView(ListView):
108     model = Note
109
110     def get_queryset(self):
111         qs = Note.objects.filter(tags__name__in=[self.kwargs['tag']])
112         if not self.request.user.is_staff:
113             qs = qs.filter(published=True)
114         return qs
115
116
117 class Atom1FeedWithBaseXml(Atom1Feed):
118     def root_attributes(self):
119         root_attributes = super().root_attributes()
120         scheme, netloc, path, params, query, fragment = urllib.parse.urlparse(self.feed['feed_url'])
121         root_attributes['xml:base'] = urllib.parse.urlunparse((scheme, netloc, '/', params, query, fragment))
122         return root_attributes
123
124
125 class AtomFeed(Feed):
126     title = settings.SITE_TITLE
127     link = '/'
128     feed_type = Atom1FeedWithBaseXml
129     author_name = settings.SITE_AUTHOR
130
131     def get_object(self, request, *args, **kwargs):
132         self.sub = kwargs.get('sub', 'default')
133         return super().get_object(request, *args, **kwargs)
134
135     def items(self):
136         qs = Note.objects.filter(published=True, included_in_feed=True)
137         if self.sub == 'default':
138             pass
139         elif self.sub == 'gnome-en':
140             qs = qs.filter(tags__name__in=['gnome']).filter(tags__name__in=['lang-en'])
141         else:
142             qs = qs.filter(tags__name__in=[self.sub])
143         return qs.select_related()[:20]
144
145     def item_description(self, item):
146         return item.text
147
148     def item_guid(self, item):
149         for tag in item.tags.all():
150             if tag.name.startswith('old-post-id-'):
151                 return 'http://www.0d.be/posts/%s' % tag.name.split('-')[-1]
152         return 'https://www.0d.be' + item.get_absolute_url()
153
154     def item_title(self, item):
155         return item.title
156
157     def item_pubdate(self, item):
158         return item.creation_timestamp
159
160
161 @csrf_exempt
162 def ajax_upload(request, *args, **kwargs):
163     upload = request.FILES['upload']
164     upload_path = 'uploads'
165     if os.path.splitext(upload.name.lower())[-1] in ('.jpg', '.jpeg', '.png', '.gif', '.svg'):
166         upload_path = 'images'
167     saved_path = default_storage.save('%s/%s' % (upload_path, upload.name), upload)
168     url = '/media/' + saved_path
169     response = {'url': url, 'filename': upload.name}
170     if upload_path == 'images':
171         if default_storage.size(saved_path) > 500_000 and not upload.name.endswith('.svg'):
172             response['orig_url'] = url
173             try:
174                 response['url'] = get_thumbnail(saved_path, '1000', upscale=False).url
175             except OSError:
176                 pass
177     return JsonResponse(response)
178
179
180 @csrf_exempt
181 def ajax_new_page(request, *args, **kwargs):
182     title = request.POST['title']
183     slug = slugify(title)
184     note, created = Note.objects.get_or_create(slug=slug)
185     if created:
186         note.title = title
187         note.text = '<p>...</p>'
188         note.save()
189     return JsonResponse(
190         {
191             'url': '/%s/' % note.slug,
192             'request_id': request.POST['request_id'],
193         }
194     )
195
196
197 def ajax_search(request, *args, **kwargs):
198     vector = SearchVector('title', weight='A', config='french') + SearchVector(
199         'plain_text', weight='B', config='french'
200     )
201     query = SearchQuery(request.GET.get('q', ''), config='french')
202     results = (
203         Note.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.1).order_by('-rank')[:10]
204     )
205     return JsonResponse(
206         {'data': [{'title': x.title, 'rank': x.rank, 'url': x.get_absolute_url()} for x in results]}
207     )