]> 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']
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']
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).order_by('-creation_timestamp')[:5]
88         context['recent_changes'] = Note.objects.filter(published=True).order_by('-last_update_timestamp')
89         context['index'] = Note.objects.filter(slug='index').first()
90         return context
91
92
93 class ArchivesView(ListView):
94     model = Note
95
96     def get_queryset(self):
97         qs = super().get_queryset()
98         if not self.request.user.is_staff:
99             qs = qs.filter(published=True)
100         return qs
101
102
103 class ListOnTagView(ListView):
104     model = Note
105
106     def get_queryset(self):
107         qs = Note.objects.filter(tags__name__in=[self.kwargs['tag']])
108         if not self.request.user.is_staff:
109             qs = qs.filter(published=True)
110         return qs
111
112
113 class Atom1FeedWithBaseXml(Atom1Feed):
114     def root_attributes(self):
115         root_attributes = super().root_attributes()
116         scheme, netloc, path, params, query, fragment = urllib.parse.urlparse(self.feed['feed_url'])
117         root_attributes['xml:base'] = urllib.parse.urlunparse((scheme, netloc, '/', params, query, fragment))
118         return root_attributes
119
120
121 class AtomFeed(Feed):
122     title = settings.SITE_TITLE
123     link = '/'
124     feed_type = Atom1FeedWithBaseXml
125     author_name = settings.SITE_AUTHOR
126
127     def get_object(self, request, *args, **kwargs):
128         self.sub = kwargs.get('sub', 'default')
129         return super().get_object(request, *args, **kwargs)
130
131     def items(self):
132         qs = Note.objects.filter(published=True)
133         if self.sub == 'default':
134             pass
135         elif self.sub == 'gnome-en':
136             qs = qs.filter(tags__name__in=['gnome']).filter(tags__name__in=['lang-en'])
137         else:
138             qs = qs.filter(tags__name__in=[self.sub])
139         return qs.select_related()[:20]
140
141     def item_description(self, item):
142         return item.text
143
144     def item_guid(self, item):
145         for tag in item.tags.all():
146             if tag.name.startswith('old-post-id-'):
147                 return 'http://www.0d.be/posts/%s' % tag.name.split('-')[-1]
148         return 'https://www.0d.be' + item.get_absolute_url()
149
150     def item_title(self, item):
151         return item.title
152
153     def item_pubdate(self, item):
154         return item.creation_timestamp
155
156
157 @csrf_exempt
158 def ajax_upload(request, *args, **kwargs):
159     upload = request.FILES['upload']
160     upload_path = 'uploads'
161     if os.path.splitext(upload.name.lower())[-1] in ('.jpg', '.jpeg', '.png', '.gif', '.svg'):
162         upload_path = 'images'
163     saved_path = default_storage.save('%s/%s' % (upload_path, upload.name), upload)
164     url = '/media/' + saved_path
165     response = {'url': url, 'filename': upload.name}
166     if upload_path == 'images':
167         if default_storage.size(saved_path) > 500_000 and not upload.name.endswith('.svg'):
168             response['orig_url'] = url
169             try:
170                 response['url'] = get_thumbnail(saved_path, '1000', upscale=False).url
171             except OSError:
172                 pass
173     return JsonResponse(response)
174
175
176 @csrf_exempt
177 def ajax_new_page(request, *args, **kwargs):
178     title = request.POST['title']
179     slug = slugify(title)
180     note, created = Note.objects.get_or_create(slug=slug)
181     if created:
182         note.title = title
183         note.text = '<p>...</p>'
184         note.save()
185     return JsonResponse(
186         {
187             'url': '/%s/' % note.slug,
188             'request_id': request.POST['request_id'],
189         }
190     )
191
192
193 def ajax_search(request, *args, **kwargs):
194     vector = SearchVector('title', weight='A', config='french') + SearchVector(
195         'plain_text', weight='B', config='french'
196     )
197     query = SearchQuery(request.GET.get('q', ''), config='french')
198     results = (
199         Note.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.1).order_by('-rank')[:10]
200     )
201     return JsonResponse(
202         {'data': [{'title': x.title, 'rank': x.rank, 'url': x.get_absolute_url()} for x in results]}
203     )