Skip to content
Newer
Older
100644 706 lines (616 sloc) 23.1 KB
c3e0171 @peterbe adding a plog calendar
authored Mar 31, 2012
1 import time
e7c1f02 @peterbe caching works for the home page
authored Feb 26, 2012
2 import urllib
62c0d0e @peterbe related search works
authored Feb 8, 2012
3 import logging
dd35852 @peterbe initial commit
authored Jan 30, 2012
4 import datetime
5 import re
6 import functools
7 import json
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
8 import cgi
78f599e @peterbe inbound email should work now
authored Apr 7, 2012
9 from cStringIO import StringIO
62c0d0e @peterbe related search works
authored Feb 8, 2012
10 from collections import defaultdict
dd35852 @peterbe initial commit
authored Jan 31, 2012
11 from pprint import pprint
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
12 from django.contrib.sites.models import Site
13 from django.core.urlresolvers import reverse
14 from django.template import Context, loader
dd35852 @peterbe initial commit
authored Jan 31, 2012
15 from django import http
62c0d0e @peterbe related search works
authored Feb 8, 2012
16 from django.core.cache import cache
20a635c @peterbe blog archive
authored Feb 22, 2012
17 from django.views.decorators.cache import cache_page
d208cb1 @peterbe akismet and celery stuff
authored Feb 10, 2012
18 from django.db import transaction
7a826dc @peterbe adding new-comments feature
authored Mar 1, 2012
19 from django.shortcuts import render, get_object_or_404, redirect
dd35852 @peterbe initial commit
authored Jan 31, 2012
20 from django.template.loader import render_to_string
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
21 from django.core.mail import send_mail
62c0d0e @peterbe related search works
authored Feb 8, 2012
22 from django.views.decorators.http import require_POST
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
23 from django.contrib.auth.decorators import login_required
24 from django.template import Template
25 from django.conf import settings
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
26 from django.views.decorators.csrf import csrf_exempt
78f599e @peterbe inbound email should work now
authored Apr 7, 2012
27 from django.core.files import File
28 from postmark.inbound import PostmarkInbound
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
29 from .models import BlogItem, BlogComment, Category, BlogFile
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
30 from .utils import render_comment_text, valid_email, utc_now
43deed5 @peterbe re-org redisutils
authored Mar 1, 2012
31 from apps.redisutils import get_redis_connection
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
32 from apps.view_cache_utils import cache_page_with_prefix
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
33 from . import tasks
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
34 from . import utils
35 from .forms import BlogForm, BlogFileUpload
62c0d0e @peterbe related search works
authored Feb 8, 2012
36
37
38 ONE_HOUR = 60 * 60
39 ONE_DAY = ONE_HOUR * 24
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
40 ONE_WEEK = ONE_DAY * 7
dd35852 @peterbe initial commit
authored Jan 31, 2012
41
42 def json_view(f):
43 @functools.wraps(f)
44 def wrapper(*args, **kw):
45 response = f(*args, **kw)
46 if isinstance(response, http.HttpResponse):
47 return response
48 else:
49 return http.HttpResponse(json.dumps(response),
50 content_type='application/json')
51 return wrapper
52
53
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
54 def _blog_post_key_prefixer(request):
55 if request.method != 'GET':
56 return None
7a826dc @peterbe adding new-comments feature
authored Mar 1, 2012
57 if request.user.is_authenticated():
58 return None
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
59 prefix = urllib.urlencode(request.GET)
60 oid = request.path.split('/')[-1]
61 cache_key = 'latest_comment_add_date:%s' % oid
62 latest_date = cache.get(cache_key)
63 if latest_date is None:
64 try:
65 blogitem = BlogItem.objects.get(oid=oid)
66 except BlogItem.DoesNotExist:
67 # don't bother, something's really wrong
68 return None
69 latest_date = blogitem.modify_date
70 for c in (BlogComment.objects
71 .filter(blogitem=blogitem, add_date__gt=latest_date)
72 .order_by('-add_date')[:1]):
73 latest_date = c.add_date
74 latest_date = latest_date.strftime('%f')
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
75 cache.set(cache_key, latest_date, ONE_WEEK)
14e826c @peterbe fixes for str on cache values
authored Feb 29, 2012
76 prefix += str(latest_date)
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
77 return prefix
78
79
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
80 @cache_page_with_prefix(ONE_WEEK, _blog_post_key_prefixer)
dd35852 @peterbe initial commit
authored Jan 31, 2012
81 def blog_post(request, oid):
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
82 if oid.endswith('/'):
83 oid = oid[:-1]
dd35852 @peterbe initial commit
authored Jan 31, 2012
84 try:
85 post = BlogItem.objects.get(oid=oid)
86 except BlogItem.DoesNotExist:
87 try:
88 post = BlogItem.objects.get(oid__iexact=oid)
89 except BlogItem.DoesNotExist:
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
90 if oid == 'add':
91 return redirect(reverse('add_post'))
dd35852 @peterbe initial commit
authored Jan 31, 2012
92 raise http.Http404(oid)
4d11c8b @peterbe adding feed and upgrading bootstrap css
authored Feb 1, 2012
93
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
94 # this is temporarily here to see how often this is actually rendered
95 logging.info("PSEUDO-DEBUGGING cache miss on blog_post")
96
dd35852 @peterbe initial commit
authored Jan 31, 2012
97 data = {
98 'post': post,
99 }
70b05c6 @peterbe bug fix about previous and next
authored Feb 27, 2012
100 try:
101 data['previous_post'] = post.get_previous_by_pub_date()
102 except BlogItem.DoesNotExist:
103 data['previous_post'] = None
104 try:
105 data['next_post'] = post.get_next_by_pub_date(pub_date__lt=utc_now())
106 except BlogItem.DoesNotExist:
107 data['next_post'] = None
62c0d0e @peterbe related search works
authored Feb 8, 2012
108 data['related'] = get_related_posts(post)
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
109
dd35852 @peterbe initial commit
authored Jan 31, 2012
110 return render(request, 'plog/post.html', data)
111
62c0d0e @peterbe related search works
authored Feb 8, 2012
112
113 def get_related_posts(post):
114 cache_key = 'related_ids:%s' % post.pk
115 related_pks = cache.get(cache_key)
116 if related_pks is None:
117 related_pks = _get_related_pks(post, 10)
118 cache.set(cache_key, related_pks, ONE_DAY)
119
120 _posts = {} # cache of posts
121 for post in BlogItem.objects.filter(pk__in=related_pks):
122 # so we only need 1 query to fetch 10 items in the particular order
123 _posts[post.pk] = post
124 related = []
125 for pk in related_pks:
126 related.append(_posts[pk])
127 return related
128
129
130 def _get_related_pks(post, max_):
8b3f258 @peterbe get_redis_connection(reconnection_wrapped=True) experiment
authored Apr 11, 2012
131 redis = get_redis_connection(reconnection_wrapped=True)
62c0d0e @peterbe related search works
authored Feb 8, 2012
132 count_keywords = redis.get('kwcount')
133 if not count_keywords:
134 for p in (BlogItem.objects
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
135 .filter(pub_date__lt=utc_now())):
62c0d0e @peterbe related search works
authored Feb 8, 2012
136 for keyword in p.keywords:
137 redis.sadd('kw:%s' % keyword, p.pk)
138 redis.incr('kwcount')
139
140 _keywords = post.keywords
141 _related = defaultdict(int)
142 for i, keyword in enumerate(_keywords):
143 ids = redis.smembers('kw:%s' % keyword)
144 for pk in ids:
145 pk = int(pk)
146 if pk != post.pk:
147 _related[pk] += (len(_keywords) - i)
148 items = sorted(((v, k) for (k, v) in _related.items()), reverse=True)
149 return [y for (x, y) in items][:max_]
150
151
dd35852 @peterbe initial commit
authored Jan 31, 2012
152 def _render_comment(comment):
153 return render_to_string('plog/comment.html', {'comment': comment})
154
155
156 @json_view
157 def prepare_json(request):
158 data = {
159 'csrf_token': request.META["CSRF_COOKIE"],
160 'name': request.COOKIES.get('name',
161 request.COOKIES.get('__blogcomment_name')),
162 'email': request.COOKIES.get('email',
163 request.COOKIES.get('__blogcomment_email')),
164 }
ac091cd @peterbe fixed xsrf problem
authored Feb 27, 2012
165 # http://stackoverflow.com/a/7503362/205832
166 request.META['CSRF_COOKIE_USED'] = True
dd35852 @peterbe initial commit
authored Jan 31, 2012
167 return data
168
62c0d0e @peterbe related search works
authored Feb 8, 2012
169
170 @require_POST
dd35852 @peterbe initial commit
authored Jan 31, 2012
171 @json_view
172 def preview_json(request):
173 comment = request.POST.get('comment', u'').strip()
174 name = request.POST.get('name', u'').strip()
175 email = request.POST.get('email', u'').strip()
176 if not comment:
177 return {}
178
179 html = render_comment_text(comment.strip())
180 comment = {
181 'oid': 'preview-oid',
182 'name': name,
183 'email': email,
184 'rendered': html,
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
185 'add_date': utc_now(),
dd35852 @peterbe initial commit
authored Jan 31, 2012
186 }
187 html = render_to_string('plog/comment.html', {
188 'comment': comment,
189 'preview': True,
190 })
191 return {'html': html}
192
193
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
194 # Not using @json_view so I can use response.set_cookie first
62c0d0e @peterbe related search works
authored Feb 8, 2012
195 @require_POST
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
196 @transaction.commit_on_success
dd35852 @peterbe initial commit
authored Jan 31, 2012
197 def submit_json(request, oid):
198 post = get_object_or_404(BlogItem, oid=oid)
6bae2ca @peterbe static image content
authored Mar 2, 2012
199 if post.disallow_comments:
200 return http.HttpResponseBadRequest("No comments please")
dd35852 @peterbe initial commit
authored Jan 31, 2012
201 comment = request.POST['comment'].strip()
62c0d0e @peterbe related search works
authored Feb 8, 2012
202 if not comment:
203 return http.HttpResponseBadRequest("Missing comment")
dd35852 @peterbe initial commit
authored Jan 31, 2012
204 name = request.POST.get('name', u'').strip()
205 email = request.POST.get('email', u'').strip()
206 parent = request.POST.get('parent')
207 if parent:
208 parent = get_object_or_404(BlogComment, oid=parent)
62c0d0e @peterbe related search works
authored Feb 8, 2012
209 else:
210 parent = None # in case it was u''
dd35852 @peterbe initial commit
authored Jan 31, 2012
211
212 search = {'comment': comment}
213 if name:
214 search['name'] = name
215 if email:
216 search['email'] = email
217 if parent:
218 search['parent'] = parent
219
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
220 for blog_comment in BlogComment.objects.filter(**search):
dd35852 @peterbe initial commit
authored Jan 31, 2012
221 break
222 else:
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
223 blog_comment = BlogComment.objects.create(
dd35852 @peterbe initial commit
authored Jan 31, 2012
224 oid=BlogComment.next_oid(),
225 blogitem=post,
226 parent=parent,
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
227 approved=False,
dd35852 @peterbe initial commit
authored Jan 31, 2012
228 comment=comment,
229 name=name,
230 email=email,
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
231 ip_address=request.META.get('REMOTE_ADDR'),
232 user_agent=request.META.get('HTTP_USER_AGENT')
dd35852 @peterbe initial commit
authored Jan 31, 2012
233 )
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
234 if not settings.DEBUG:
235 tasks.akismet_rate.delay(blog_comment.pk)
236
6f03cab @peterbe fixes for automatically approved comments
authored Mar 19, 2012
237 if request.user.is_authenticated():
238 _approve_comment(blog_comment)
239 assert blog_comment.approved
240
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
241 tos = [x[1] for x in settings.ADMINS]
242 from_ = ['%s <%s>' % x for x in settings.ADMINS][0]
243 body = _get_comment_body(post, blog_comment)
244 send_mail("Peterbe.com: New comment on '%s'" % post.title,
245 body, from_, tos)
246
dd35852 @peterbe initial commit
authored Jan 31, 2012
247 html = render_to_string('plog/comment.html', {
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
248 'comment': blog_comment,
249 'preview': True,
dd35852 @peterbe initial commit
authored Jan 31, 2012
250 })
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
251 data = {'html': html, 'parent': parent and parent.oid or None}
252 response = http.HttpResponse(json.dumps(data), mimetype="application/json")
253 if name:
b1e7d35 @peterbe fixed bug with unicode name and cookie
authored Mar 1, 2012
254 if isinstance(name, unicode):
255 name = name.encode('utf-8')
d208cb1 @peterbe akismet and celery stuff
authored Feb 11, 2012
256 response.set_cookie('name', name)
257 if email:
258 response.set_cookie('email', email)
259 return response
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
260
261
262 @login_required
263 def approve_comment(request, oid, comment_oid):
264 blogitem = get_object_or_404(BlogItem, oid=oid)
265 blogcomment = get_object_or_404(BlogComment, oid=comment_oid)
266 if blogcomment.blogitem != blogitem:
267 raise http.Http404("bad rel")
268
6f03cab @peterbe fixes for automatically approved comments
authored Mar 19, 2012
269 _approve_comment(blogcomment)
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
270
271 if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest':
272 return http.HttpResponse('OK')
273 else:
274 url = blogitem.get_absolute_url()
275 if blogcomment.blogitem:
07740d8 @peterbe comment approval fixes
authored Mar 4, 2012
276 url += '#%s' % blogcomment.oid
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
277 return http.HttpResponse('''<html>Comment approved<br>
278 <a href="%s">%s</a>
279 </html>
280 ''' % (url, blogitem.title))
281
6f03cab @peterbe fixes for automatically approved comments
authored Mar 19, 2012
282 def _approve_comment(blogcomment):
283 blogcomment.approved = True
284 blogcomment.save()
285
286 if (blogcomment.parent and blogcomment.parent.email
287 and valid_email(blogcomment.parent.email)
288 and blogcomment.email != blogcomment.parent.email):
289 parent = blogcomment.parent
290 tos = [parent.email]
291 from_ = 'Peterbe.com <noreply+%s@peterbe.com>' % blogcomment.oid
292 body = _get_comment_reply_body(blogcomment.blogitem, blogcomment, parent)
293 subject = 'Peterbe.com: Reply to your comment'
294 send_mail(subject, body, from_, tos)
295
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
296
297 def _get_comment_body(blogitem, blogcomment):
298 base_url = 'http://%s' % Site.objects.get(pk=settings.SITE_ID).domain
299 approve_url = reverse('approve_comment', args=[blogitem.oid, blogcomment.oid])
300 delete_url = reverse('delete_comment', args=[blogitem.oid, blogcomment.oid])
301 message = template = loader.get_template('plog/comment_body.txt')
302 context = {
303 'post': blogitem,
304 'comment': blogcomment,
305 'approve_url': approve_url,
306 'delete_url': delete_url,
307 'base_url': base_url,
308 }
309 return template.render(Context(context)).strip()
310
311
312 def _get_comment_reply_body(blogitem, blogcomment, parent):
313 base_url = 'http://%s' % Site.objects.get(pk=settings.SITE_ID).domain
314 approve_url = reverse('approve_comment', args=[blogitem.oid, blogcomment.oid])
315 delete_url = reverse('delete_comment', args=[blogitem.oid, blogcomment.oid])
316 message = template = loader.get_template('plog/comment_reply_body.txt')
317 context = {
318 'post': blogitem,
319 'comment': blogcomment,
320 'parent': parent,
321 'base_url': base_url,
322 }
323 return template.render(Context(context)).strip()
324
325
326 @login_required
327 def delete_comment(request, oid, comment_oid):
4a15de1 @peterbe ability to delete comments
authored Feb 27, 2012
328 user = request.user
329 assert user.is_staff or user.is_superuser
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
330 blogitem = get_object_or_404(BlogItem, oid=oid)
4a15de1 @peterbe ability to delete comments
authored Feb 27, 2012
331 blogcomment = get_object_or_404(BlogComment, oid=comment_oid)
cbbd33a @peterbe admin, sitemaps and replies
authored Feb 14, 2012
332 if blogcomment.blogitem != blogitem:
333 raise http.Http404("bad rel")
334
335 blogcomment.delete()
336
337 return http.HttpResponse("Comment deleted")
20a635c @peterbe blog archive
authored Feb 22, 2012
338
339
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
340 def _plog_index_key_prefixer(request):
341 if request.method != 'GET':
342 return None
343 if request.user.is_authenticated():
344 return None
345 prefix = urllib.urlencode(request.GET)
346 cache_key = 'latest_post_modify_date'
347 latest_date = cache.get(cache_key)
348 if latest_date is None:
349 latest, = (BlogItem.objects
350 .order_by('-modify_date')
351 .values('modify_date')[:1])
352 latest_date = latest['modify_date'].strftime('%f')
353 cache.set(cache_key, latest_date, ONE_DAY)
354 prefix += str(latest_date)
355 return prefix
356
357 @cache_page_with_prefix(ONE_DAY, _plog_index_key_prefixer)
20a635c @peterbe blog archive
authored Feb 22, 2012
358 def plog_index(request):
3ac74ed @peterbe improved plog_index caching
authored Apr 19, 2012
359
360 # this is temporarily here to see how often this is actually rendered
361 logging.info("PSEUDO-DEBUGGING cache miss on plog_index")
362
20a635c @peterbe blog archive
authored Feb 22, 2012
363 groups = defaultdict(list)
e7c1f02 @peterbe caching works for the home page
authored Feb 27, 2012
364 now = utc_now()
20a635c @peterbe blog archive
authored Feb 22, 2012
365 group_dates = []
366
367 _categories = dict((x.pk, x.name) for x in
368 Category.objects.all())
369 blogitem_categories = defaultdict(list)
370 for cat_item in BlogItem.categories.through.objects.all():
371 blogitem_categories[cat_item.blogitem_id].append(
372 _categories[cat_item.category_id]
373 )
374 for item in (BlogItem.objects
375 .filter(pub_date__lt=now)
376 .values('pub_date', 'oid', 'title', 'pk')
377 .order_by('-pub_date')):
378 group = item['pub_date'].strftime('%Y.%m')
379 item['categories'] = blogitem_categories[item['pk']]
380 groups[group].append(item)
381 tup = (group, item['pub_date'].strftime('%B, %Y'))
382 if tup not in group_dates:
383 group_dates.append(tup)
384
385 data = {
386 'groups': groups,
387 'group_dates': group_dates,
388 }
389 return render(request, 'plog/index.html', data)
7a826dc @peterbe adding new-comments feature
authored Mar 1, 2012
390
391
07740d8 @peterbe comment approval fixes
authored Mar 5, 2012
392 def _new_comment_key_prefixer(request):
393 if request.method != 'GET':
394 return None
395 if request.user.is_authenticated():
396 return None
397 prefix = urllib.urlencode(request.GET)
398 cache_key = 'latest_comment_add_date'
399 latest_date = cache.get(cache_key)
400 if latest_date is None:
401 latest, = (BlogItem.objects
402 .order_by('-modify_date')
403 .values('modify_date')[:1])
404 latest_date = latest['modify_date'].strftime('%f')
405 cache.set(cache_key, latest_date, 60 * 60)
406 prefix += str(latest_date)
407 return prefix
408
409
410 @cache_page_with_prefix(ONE_HOUR, _new_comment_key_prefixer)
7a826dc @peterbe adding new-comments feature
authored Mar 1, 2012
411 def new_comments(request):
412 data = {}
413 comments = BlogComment.objects.all()
07740d8 @peterbe comment approval fixes
authored Mar 5, 2012
414 if not request.user.is_authenticated():
7a826dc @peterbe adding new-comments feature
authored Mar 1, 2012
415 comments = comments.filter(approved=True)
416
417 # legacy stuff that can be removed in march 2012
418 for c in comments.filter(blogitem__isnull=True):
419 if not c.parent:
420 c.delete()
421 else:
422 c.correct_blogitem_parent()
423
424 data['comments'] = (comments
425 .order_by('-add_date')
426 .select_related('blogitem')[:100])
427 return render(request, 'plog/new-comments.html', data)
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
428
429
430 @login_required
431 @transaction.commit_on_success
432 def add_post(request):
433 data = {}
434 user = request.user
435 assert user.is_staff or user.is_superuser
436 if request.method == 'POST':
437 form = BlogForm(data=request.POST)
438 if form.is_valid():
95d3e21 @peterbe fix for keywords
authored Mar 11, 2012
439 keywords = [x.strip() for x
440 in form.cleaned_data['keywords'].splitlines()
441 if x.strip()]
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
442 blogitem = BlogItem.objects.create(
443 oid=form.cleaned_data['oid'],
444 title=form.cleaned_data['title'],
445 text=form.cleaned_data['text'],
446 summary=form.cleaned_data['summary'],
447 display_format=form.cleaned_data['display_format'],
448 codesyntax=form.cleaned_data['codesyntax'],
449 url=form.cleaned_data['url'],
450 pub_date=form.cleaned_data['pub_date'],
95d3e21 @peterbe fix for keywords
authored Mar 12, 2012
451 keywords=keywords,
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
452 )
453 for category in form.cleaned_data['categories']:
454 blogitem.categories.add(category)
455 blogitem.save()
456 url = reverse('edit_post', args=[blogitem.oid])
457 return redirect(url)
458 else:
459 initial = {
460 'pub_date': utc_now() + datetime.timedelta(seconds=60 * 60),
461 'display_format': 'markdown',
462 }
463 form = BlogForm(initial=initial)
464 data['form'] = form
465 data['page_title'] = 'Add post'
5680b3b @peterbe bustage fix2
authored Mar 11, 2012
466 data['blogitem'] = None
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
467 return render(request, 'plog/edit.html', data)
468
469
470 @login_required
471 @transaction.commit_on_success
472 def edit_post(request, oid):
473 blogitem = get_object_or_404(BlogItem, oid=oid)
474 data = {}
475 user = request.user
476 assert user.is_staff or user.is_superuser
477 if request.method == 'POST':
478 form = BlogForm(instance=blogitem, data=request.POST)
479 if form.is_valid():
480 blogitem.oid = form.cleaned_data['oid']
481 blogitem.title = form.cleaned_data['title']
482 blogitem.text = form.cleaned_data['text']
483 blogitem.text_rendered = ''
484 blogitem.summary = form.cleaned_data['summary']
485 blogitem.display_format = form.cleaned_data['display_format']
486 blogitem.codesyntax = form.cleaned_data['codesyntax']
487 blogitem.pub_date = form.cleaned_data['pub_date']
95d3e21 @peterbe fix for keywords
authored Mar 12, 2012
488 keywords = [x.strip() for x
489 in form.cleaned_data['keywords'].splitlines()
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
490 if x.strip()]
491 blogitem.keywords = keywords
492 blogitem.categories.clear()
493 for category in form.cleaned_data['categories']:
494 blogitem.categories.add(category)
495 blogitem.save()
496
497 url = reverse('edit_post', args=[blogitem.oid])
498 return redirect(url)
499
500 else:
501 form = BlogForm(instance=blogitem)
502 data['form'] = form
503 data['page_title'] = 'Edit post'
504 data['blogitem'] = blogitem
78f599e @peterbe inbound email should work now
authored Apr 7, 2012
505 data['INBOUND_EMAIL_ADDRESS'] = settings.INBOUND_EMAIL_ADDRESS
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
506 return render(request, 'plog/edit.html', data)
507
508
509 @csrf_exempt
510 @login_required
511 @require_POST
512 def preview_post(request):
513 from django.template import Context
514 from django.template.loader import get_template
515
516 post_data = dict()
517 for key, value in request.POST.items():
518 if value:
519 post_data[key] = value
520 post_data['categories'] = request.POST.getlist('categories[]')
521 post_data['oid'] = 'doesntmatter'
522 post_data['keywords'] = []
523 form = BlogForm(data=post_data)
524 if not form.is_valid():
525 return http.HttpResponse(str(form.errors))
526
527 class MockPost(object):
528
529 def count_comments(self):
530 return 0
531
532 @property
533 def rendered(self):
534 if self.display_format == 'structuredtext':
535 return utils.stx_to_html(self.text, self.codesyntax)
536 else:
537 return utils.markdown_to_html(self.text, self.codesyntax)
538
539 post = MockPost()
540 post.title = form.cleaned_data['title']
541 post.text = form.cleaned_data['text']
542 post.display_format = form.cleaned_data['display_format']
543 post.codesyntax = form.cleaned_data['codesyntax']
544 post.url = form.cleaned_data['url']
545 post.pub_date = form.cleaned_data['pub_date']
546 post.categories = Category.objects.filter(pk__in=form.cleaned_data['categories'])
547 template = get_template("plog/_post.html")
548 context = Context({'post': post})
549 return http.HttpResponse(template.render(context))
550
551
552 @login_required
553 @transaction.commit_on_success
554 def add_file(request):
555 data = {}
556 user = request.user
557 assert user.is_staff or user.is_superuser
558 if request.method == 'POST':
559 form = BlogFileUpload(request.POST, request.FILES)
560 if form.is_valid():
561 instance = form.save()
562 url = reverse('edit_post', args=[instance.blogitem.oid])
563 return redirect(url)
564 else:
565 initial = {}
566 if request.REQUEST.get('oid'):
567 blogitem = get_object_or_404(BlogItem, oid=request.REQUEST.get('oid'))
568 initial['blogitem'] = blogitem
569 form = BlogFileUpload(initial=initial)
570 data['form'] = form
571 return render(request, 'plog/add_file.html', data)
572
573
574 @login_required
575 def post_thumbnails(request, oid):
576 blogitem = get_object_or_404(BlogItem, oid=oid)
577 blogfiles = (BlogFile.objects
578 .filter(blogitem=blogitem)
579 .order_by('-add_date'))
580 from sorl.thumbnail import get_thumbnail
581 html = ''
582 # XXX very rough and hacky code
583 for blogfile in blogfiles:
584 im = get_thumbnail(blogfile.file, '100x100', #crop='center',
585 quality=81)
586
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
587 full_im = get_thumbnail(blogfile.file, '1000x1000', #crop='center',
588 upscale=False, quality=100)
589
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
590 url_ = settings.STATIC_URL + im.url
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
591 full_url = settings.STATIC_URL + full_im.url
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
592 tag = ('<img src="%s" alt="%s" width="%s" height="%s">' %
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
593 (url_,
594 getattr(blogfile, 'title', blogitem.title),
595 im.width,
596 im.height))
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
597 html += tag
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
598 delete_url = reverse('delete_post_thumbnail') + '?id=%s' % blogfile.pk
599 whole_tag = ('<a href="%s" title="%s">%s</a>' %
af5d6ea @peterbe carousel pictures
authored Apr 6, 2012
600 (full_url,
601 getattr(blogfile, 'title', blogitem.title),
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
602 tag.replace('src="', 'class="floatright" src="')))
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
603 html += ' (%s, %s)' % (im.width, im.height)
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
604 html += ' <a href="%s">delete</a>' % delete_url
605 html += '<br><input value="%s" title="Full size 1200x1200">' % full_url
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
606 html += '<br><input value="%s">' % cgi.escape(tag).replace('"', '&quot;')
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
607 html += '<br><input value="%s">' % cgi.escape(whole_tag).replace('"', '&quot;')
c35fd13 @peterbe form for adding new posts with markdown display format
authored Mar 8, 2012
608 html += '<br>'
609
610 return http.HttpResponse(html)
00b2e1b @peterbe better for uploading blog pictures
authored Mar 27, 2012
611
612
613 @login_required
614 def delete_post_thumbnail(request):
615 pk = request.REQUEST.get('id')
616 assert pk
617 blogfile = get_object_or_404(BlogFile, pk=pk)
618 blogitem = blogfile.blogitem
619 edit_post_url = reverse('edit_post', args=[blogitem.oid])
620 blogfile.delete()
621 return http.HttpResponse(
622 'Blogfile deleted.<br><a href="%s">Edit \'%s\'</a>' %
623 (edit_post_url, blogitem.title)
624 )
c3e0171 @peterbe adding a plog calendar
authored Mar 31, 2012
625
626
627 def calendar(request):
628 data = {'page_title': 'Archive calendar'}
629 return render(request, 'plog/calendar.html', data)
630
631
632 @json_view
633 def calendar_data(request):
634 start = request.GET['start']
635 end = request.GET['end']
636 start = datetime.datetime.fromtimestamp(float(start))
637 end = datetime.datetime.fromtimestamp(float(end))
638 if not request.user.is_authenticated():
639 end = min(end, datetime.datetime.utcnow())
640 if end < start:
641 return []
642 assert start < end
643 assert (end - start).days < 50
644 start = utils.utcify(start)
645 end = utils.utcify(end)
646
647 qs = BlogItem.objects.filter(pub_date__gte=start, pub_date__lt=end)
648 items = []
649 for each in qs:
650 item = {
651 'title': each.title,
652 'start': time.mktime(each.pub_date.timetuple()),
653 'url': reverse('blog_post', args=[each.oid]),
654 'className': 'post',
655 }
656 items.append(item)
657
658 return items
22941bc @peterbe baby-steps on postmark inbound data
authored Apr 7, 2012
659
660
661 @require_POST
662 @csrf_exempt
663 def inbound_email(request):
664 raw_data = request.raw_post_data
78f599e @peterbe inbound email should work now
authored Apr 7, 2012
665 #filename = '/tmp/raw_data.%s.json' % (time.time(),)
666 #with open(filename, 'w') as f:
667 # f.write(raw_data)
668
22941bc @peterbe baby-steps on postmark inbound data
authored Apr 7, 2012
669 data = json.loads(raw_data)
78f599e @peterbe inbound email should work now
authored Apr 7, 2012
670 #logging.info(data)
671 #logging.info(filename)
672 #from pprint import pprint
673 #pprint(data)
674 inbound = PostmarkInbound(json=raw_data)
675 if not inbound.has_attachments():
676 m = "ERROR! No attachments"
677 logging.debug(m)
678 return http.HttpResponse(m)
679 try:
680 hashkey, subject = inbound.subject().split(':', 1)
681 except ValueError:
682 m = "ERROR! No hashkey defined in subject line"
683 logging.debug(m)
684 return http.HttpResponse(m)
685 try:
686 post = BlogItem.get_by_inbound_hashkey(hashkey)
687 except BlogItem.DoesNotExist:
688 m = "ERROR! Unrecognized hashkey"
689 logging.debug(m)
690 return http.HttpResponse(m)
691
692 attachments = inbound.attachments()
693 attachment = attachments[0]
694 blogfile = BlogFile(
695 blogitem=post,
696 title=subject.strip(),
697 )
698 content = StringIO(attachment.read())
699 f = File(content, name=attachment.name())
700 f.size = attachment.content_length()
701 blogfile.file.save(attachment.name(),
702 f,
703 save=True)
704 blogfile.save()
22941bc @peterbe baby-steps on postmark inbound data
authored Apr 7, 2012
705 return http.HttpResponse("OK\n")
Something went wrong with that request. Please try again.