##// END OF EJS Templates
If trying to get thread diff from a GET request, send a message instead of failing with exception
neko259 -
r1425:acc799d1 default
parent child Browse files
Show More
@@ -1,297 +1,301 b''
1 from collections import OrderedDict
1 from collections import OrderedDict
2 import json
2 import json
3 import logging
3 import logging
4
4
5 from django.db import transaction
5 from django.db import transaction
6 from django.db.models import Count
6 from django.db.models import Count
7 from django.http import HttpResponse
7 from django.http import HttpResponse
8 from django.shortcuts import get_object_or_404
8 from django.shortcuts import get_object_or_404
9 from django.core import serializers
9 from django.core import serializers
10 from django.template.context_processors import csrf
10 from django.template.context_processors import csrf
11 from django.views.decorators.csrf import csrf_protect
11 from django.views.decorators.csrf import csrf_protect
12
12
13 from boards.abstracts.settingsmanager import get_settings_manager,\
13 from boards.abstracts.settingsmanager import get_settings_manager,\
14 FAV_THREAD_NO_UPDATES
14 FAV_THREAD_NO_UPDATES
15
15
16 from boards.forms import PostForm, PlainErrorList
16 from boards.forms import PostForm, PlainErrorList
17 from boards.models import Post, Thread, Tag
17 from boards.models import Post, Thread, Tag
18 from boards.models.thread import STATUS_ARCHIVE
18 from boards.models.thread import STATUS_ARCHIVE
19 from boards.utils import datetime_to_epoch
19 from boards.utils import datetime_to_epoch
20 from boards.views.thread import ThreadView
20 from boards.views.thread import ThreadView
21 from boards.models.user import Notification
21 from boards.models.user import Notification
22 from boards.mdx_neboard import Parser
22 from boards.mdx_neboard import Parser
23
23
24
24
25 __author__ = 'neko259'
25 __author__ = 'neko259'
26
26
27 PARAMETER_TRUNCATED = 'truncated'
27 PARAMETER_TRUNCATED = 'truncated'
28 PARAMETER_TAG = 'tag'
28 PARAMETER_TAG = 'tag'
29 PARAMETER_OFFSET = 'offset'
29 PARAMETER_OFFSET = 'offset'
30 PARAMETER_DIFF_TYPE = 'type'
30 PARAMETER_DIFF_TYPE = 'type'
31 PARAMETER_POST = 'post'
31 PARAMETER_POST = 'post'
32 PARAMETER_UPDATED = 'updated'
32 PARAMETER_UPDATED = 'updated'
33 PARAMETER_LAST_UPDATE = 'last_update'
33 PARAMETER_LAST_UPDATE = 'last_update'
34 PARAMETER_THREAD = 'thread'
34 PARAMETER_THREAD = 'thread'
35 PARAMETER_UIDS = 'uids'
35 PARAMETER_UIDS = 'uids'
36
36
37 DIFF_TYPE_HTML = 'html'
37 DIFF_TYPE_HTML = 'html'
38 DIFF_TYPE_JSON = 'json'
38 DIFF_TYPE_JSON = 'json'
39
39
40 STATUS_OK = 'ok'
40 STATUS_OK = 'ok'
41 STATUS_ERROR = 'error'
41 STATUS_ERROR = 'error'
42
42
43 logger = logging.getLogger(__name__)
43 logger = logging.getLogger(__name__)
44
44
45
45
46 @transaction.atomic
46 @transaction.atomic
47 def api_get_threaddiff(request):
47 def api_get_threaddiff(request):
48 """
48 """
49 Gets posts that were changed or added since time
49 Gets posts that were changed or added since time
50 """
50 """
51
51
52 thread_id = request.POST.get(PARAMETER_THREAD)
52 thread_id = request.POST.get(PARAMETER_THREAD)
53 uids_str = request.POST.get(PARAMETER_UIDS).strip()
53 uids_str = request.POST.get(PARAMETER_UIDS)
54 uids = uids_str.split(' ')
54
55 if not thread_id or not uids_str:
56 return HttpResponse(content='Invalid request.')
57
58 uids = uids_str.strip().split(' ')
55
59
56 opening_post = get_object_or_404(Post, id=thread_id)
60 opening_post = get_object_or_404(Post, id=thread_id)
57 thread = opening_post.get_thread()
61 thread = opening_post.get_thread()
58
62
59 json_data = {
63 json_data = {
60 PARAMETER_UPDATED: [],
64 PARAMETER_UPDATED: [],
61 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
65 PARAMETER_LAST_UPDATE: None, # TODO Maybe this can be removed already?
62 }
66 }
63 posts = Post.objects.filter(threads__in=[thread]).exclude(uid__in=uids)
67 posts = Post.objects.filter(threads__in=[thread]).exclude(uid__in=uids)
64
68
65 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
69 diff_type = request.GET.get(PARAMETER_DIFF_TYPE, DIFF_TYPE_HTML)
66
70
67 for post in posts:
71 for post in posts:
68 json_data[PARAMETER_UPDATED].append(post.get_post_data(
72 json_data[PARAMETER_UPDATED].append(post.get_post_data(
69 format_type=diff_type, request=request))
73 format_type=diff_type, request=request))
70 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
74 json_data[PARAMETER_LAST_UPDATE] = str(thread.last_edit_time)
71
75
72 # If the tag is favorite, update the counter
76 # If the tag is favorite, update the counter
73 settings_manager = get_settings_manager(request)
77 settings_manager = get_settings_manager(request)
74 favorite = settings_manager.thread_is_fav(opening_post)
78 favorite = settings_manager.thread_is_fav(opening_post)
75 if favorite:
79 if favorite:
76 settings_manager.add_or_read_fav_thread(opening_post)
80 settings_manager.add_or_read_fav_thread(opening_post)
77
81
78 return HttpResponse(content=json.dumps(json_data))
82 return HttpResponse(content=json.dumps(json_data))
79
83
80
84
81 @csrf_protect
85 @csrf_protect
82 def api_add_post(request, opening_post_id):
86 def api_add_post(request, opening_post_id):
83 """
87 """
84 Adds a post and return the JSON response for it
88 Adds a post and return the JSON response for it
85 """
89 """
86
90
87 opening_post = get_object_or_404(Post, id=opening_post_id)
91 opening_post = get_object_or_404(Post, id=opening_post_id)
88
92
89 logger.info('Adding post via api...')
93 logger.info('Adding post via api...')
90
94
91 status = STATUS_OK
95 status = STATUS_OK
92 errors = []
96 errors = []
93
97
94 if request.method == 'POST':
98 if request.method == 'POST':
95 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
99 form = PostForm(request.POST, request.FILES, error_class=PlainErrorList)
96 form.session = request.session
100 form.session = request.session
97
101
98 if form.need_to_ban:
102 if form.need_to_ban:
99 # Ban user because he is suspected to be a bot
103 # Ban user because he is suspected to be a bot
100 # _ban_current_user(request)
104 # _ban_current_user(request)
101 status = STATUS_ERROR
105 status = STATUS_ERROR
102 if form.is_valid():
106 if form.is_valid():
103 post = ThreadView().new_post(request, form, opening_post,
107 post = ThreadView().new_post(request, form, opening_post,
104 html_response=False)
108 html_response=False)
105 if not post:
109 if not post:
106 status = STATUS_ERROR
110 status = STATUS_ERROR
107 else:
111 else:
108 logger.info('Added post #%d via api.' % post.id)
112 logger.info('Added post #%d via api.' % post.id)
109 else:
113 else:
110 status = STATUS_ERROR
114 status = STATUS_ERROR
111 errors = form.as_json_errors()
115 errors = form.as_json_errors()
112
116
113 response = {
117 response = {
114 'status': status,
118 'status': status,
115 'errors': errors,
119 'errors': errors,
116 }
120 }
117
121
118 return HttpResponse(content=json.dumps(response))
122 return HttpResponse(content=json.dumps(response))
119
123
120
124
121 def get_post(request, post_id):
125 def get_post(request, post_id):
122 """
126 """
123 Gets the html of a post. Used for popups. Post can be truncated if used
127 Gets the html of a post. Used for popups. Post can be truncated if used
124 in threads list with 'truncated' get parameter.
128 in threads list with 'truncated' get parameter.
125 """
129 """
126
130
127 post = get_object_or_404(Post, id=post_id)
131 post = get_object_or_404(Post, id=post_id)
128 truncated = PARAMETER_TRUNCATED in request.GET
132 truncated = PARAMETER_TRUNCATED in request.GET
129
133
130 return HttpResponse(content=post.get_view(truncated=truncated, need_op_data=True))
134 return HttpResponse(content=post.get_view(truncated=truncated, need_op_data=True))
131
135
132
136
133 def api_get_threads(request, count):
137 def api_get_threads(request, count):
134 """
138 """
135 Gets the JSON thread opening posts list.
139 Gets the JSON thread opening posts list.
136 Parameters that can be used for filtering:
140 Parameters that can be used for filtering:
137 tag, offset (from which thread to get results)
141 tag, offset (from which thread to get results)
138 """
142 """
139
143
140 if PARAMETER_TAG in request.GET:
144 if PARAMETER_TAG in request.GET:
141 tag_name = request.GET[PARAMETER_TAG]
145 tag_name = request.GET[PARAMETER_TAG]
142 if tag_name is not None:
146 if tag_name is not None:
143 tag = get_object_or_404(Tag, name=tag_name)
147 tag = get_object_or_404(Tag, name=tag_name)
144 threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
148 threads = tag.get_threads().exclude(status=STATUS_ARCHIVE)
145 else:
149 else:
146 threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
150 threads = Thread.objects.exclude(status=STATUS_ARCHIVE)
147
151
148 if PARAMETER_OFFSET in request.GET:
152 if PARAMETER_OFFSET in request.GET:
149 offset = request.GET[PARAMETER_OFFSET]
153 offset = request.GET[PARAMETER_OFFSET]
150 offset = int(offset) if offset is not None else 0
154 offset = int(offset) if offset is not None else 0
151 else:
155 else:
152 offset = 0
156 offset = 0
153
157
154 threads = threads.order_by('-bump_time')
158 threads = threads.order_by('-bump_time')
155 threads = threads[offset:offset + int(count)]
159 threads = threads[offset:offset + int(count)]
156
160
157 opening_posts = []
161 opening_posts = []
158 for thread in threads:
162 for thread in threads:
159 opening_post = thread.get_opening_post()
163 opening_post = thread.get_opening_post()
160
164
161 # TODO Add tags, replies and images count
165 # TODO Add tags, replies and images count
162 post_data = opening_post.get_post_data(include_last_update=True)
166 post_data = opening_post.get_post_data(include_last_update=True)
163 post_data['status'] = thread.get_status()
167 post_data['status'] = thread.get_status()
164
168
165 opening_posts.append(post_data)
169 opening_posts.append(post_data)
166
170
167 return HttpResponse(content=json.dumps(opening_posts))
171 return HttpResponse(content=json.dumps(opening_posts))
168
172
169
173
170 # TODO Test this
174 # TODO Test this
171 def api_get_tags(request):
175 def api_get_tags(request):
172 """
176 """
173 Gets all tags or user tags.
177 Gets all tags or user tags.
174 """
178 """
175
179
176 # TODO Get favorite tags for the given user ID
180 # TODO Get favorite tags for the given user ID
177
181
178 tags = Tag.objects.get_not_empty_tags()
182 tags = Tag.objects.get_not_empty_tags()
179
183
180 term = request.GET.get('term')
184 term = request.GET.get('term')
181 if term is not None:
185 if term is not None:
182 tags = tags.filter(name__contains=term)
186 tags = tags.filter(name__contains=term)
183
187
184 tag_names = [tag.name for tag in tags]
188 tag_names = [tag.name for tag in tags]
185
189
186 return HttpResponse(content=json.dumps(tag_names))
190 return HttpResponse(content=json.dumps(tag_names))
187
191
188
192
189 # TODO The result can be cached by the thread last update time
193 # TODO The result can be cached by the thread last update time
190 # TODO Test this
194 # TODO Test this
191 def api_get_thread_posts(request, opening_post_id):
195 def api_get_thread_posts(request, opening_post_id):
192 """
196 """
193 Gets the JSON array of thread posts
197 Gets the JSON array of thread posts
194 """
198 """
195
199
196 opening_post = get_object_or_404(Post, id=opening_post_id)
200 opening_post = get_object_or_404(Post, id=opening_post_id)
197 thread = opening_post.get_thread()
201 thread = opening_post.get_thread()
198 posts = thread.get_replies()
202 posts = thread.get_replies()
199
203
200 json_data = {
204 json_data = {
201 'posts': [],
205 'posts': [],
202 'last_update': None,
206 'last_update': None,
203 }
207 }
204 json_post_list = []
208 json_post_list = []
205
209
206 for post in posts:
210 for post in posts:
207 json_post_list.append(post.get_post_data())
211 json_post_list.append(post.get_post_data())
208 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
212 json_data['last_update'] = datetime_to_epoch(thread.last_edit_time)
209 json_data['posts'] = json_post_list
213 json_data['posts'] = json_post_list
210
214
211 return HttpResponse(content=json.dumps(json_data))
215 return HttpResponse(content=json.dumps(json_data))
212
216
213
217
214 def api_get_notifications(request, username):
218 def api_get_notifications(request, username):
215 last_notification_id_str = request.GET.get('last', None)
219 last_notification_id_str = request.GET.get('last', None)
216 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
220 last_id = int(last_notification_id_str) if last_notification_id_str is not None else None
217
221
218 posts = Notification.objects.get_notification_posts(username=username,
222 posts = Notification.objects.get_notification_posts(username=username,
219 last=last_id)
223 last=last_id)
220
224
221 json_post_list = []
225 json_post_list = []
222 for post in posts:
226 for post in posts:
223 json_post_list.append(post.get_post_data())
227 json_post_list.append(post.get_post_data())
224 return HttpResponse(content=json.dumps(json_post_list))
228 return HttpResponse(content=json.dumps(json_post_list))
225
229
226
230
227 def api_get_post(request, post_id):
231 def api_get_post(request, post_id):
228 """
232 """
229 Gets the JSON of a post. This can be
233 Gets the JSON of a post. This can be
230 used as and API for external clients.
234 used as and API for external clients.
231 """
235 """
232
236
233 post = get_object_or_404(Post, id=post_id)
237 post = get_object_or_404(Post, id=post_id)
234
238
235 json = serializers.serialize("json", [post], fields=(
239 json = serializers.serialize("json", [post], fields=(
236 "pub_time", "_text_rendered", "title", "text", "image",
240 "pub_time", "_text_rendered", "title", "text", "image",
237 "image_width", "image_height", "replies", "tags"
241 "image_width", "image_height", "replies", "tags"
238 ))
242 ))
239
243
240 return HttpResponse(content=json)
244 return HttpResponse(content=json)
241
245
242
246
243 def api_get_preview(request):
247 def api_get_preview(request):
244 raw_text = request.POST['raw_text']
248 raw_text = request.POST['raw_text']
245
249
246 parser = Parser()
250 parser = Parser()
247 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
251 return HttpResponse(content=parser.parse(parser.preparse(raw_text)))
248
252
249
253
250 def api_get_new_posts(request):
254 def api_get_new_posts(request):
251 """
255 """
252 Gets favorite threads and unread posts count.
256 Gets favorite threads and unread posts count.
253 """
257 """
254 posts = list()
258 posts = list()
255
259
256 include_posts = 'include_posts' in request.GET
260 include_posts = 'include_posts' in request.GET
257
261
258 settings_manager = get_settings_manager(request)
262 settings_manager = get_settings_manager(request)
259 fav_threads = settings_manager.get_fav_threads()
263 fav_threads = settings_manager.get_fav_threads()
260 fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys())\
264 fav_thread_ops = Post.objects.filter(id__in=fav_threads.keys())\
261 .order_by('-pub_time').prefetch_related('thread')
265 .order_by('-pub_time').prefetch_related('thread')
262
266
263 ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
267 ops = [{'op': op, 'last_id': fav_threads[str(op.id)]} for op in fav_thread_ops]
264 if include_posts:
268 if include_posts:
265 new_post_threads = Thread.objects.get_new_posts(ops)
269 new_post_threads = Thread.objects.get_new_posts(ops)
266 if new_post_threads:
270 if new_post_threads:
267 thread_ids = {thread.id: thread for thread in new_post_threads}
271 thread_ids = {thread.id: thread for thread in new_post_threads}
268 else:
272 else:
269 thread_ids = dict()
273 thread_ids = dict()
270
274
271 for op in fav_thread_ops:
275 for op in fav_thread_ops:
272 fav_thread_dict = dict()
276 fav_thread_dict = dict()
273
277
274 op_thread = op.get_thread()
278 op_thread = op.get_thread()
275 if op_thread.id in thread_ids:
279 if op_thread.id in thread_ids:
276 thread = thread_ids[op_thread.id]
280 thread = thread_ids[op_thread.id]
277 new_post_count = thread.new_post_count
281 new_post_count = thread.new_post_count
278 fav_thread_dict['newest_post_link'] = thread.get_replies()\
282 fav_thread_dict['newest_post_link'] = thread.get_replies()\
279 .filter(id__gt=fav_threads[str(op.id)])\
283 .filter(id__gt=fav_threads[str(op.id)])\
280 .first().get_absolute_url(thread=thread)
284 .first().get_absolute_url(thread=thread)
281 else:
285 else:
282 new_post_count = 0
286 new_post_count = 0
283 fav_thread_dict['new_post_count'] = new_post_count
287 fav_thread_dict['new_post_count'] = new_post_count
284
288
285 fav_thread_dict['id'] = op.id
289 fav_thread_dict['id'] = op.id
286
290
287 fav_thread_dict['post_url'] = op.get_link_view()
291 fav_thread_dict['post_url'] = op.get_link_view()
288 fav_thread_dict['title'] = op.title
292 fav_thread_dict['title'] = op.title
289
293
290 posts.append(fav_thread_dict)
294 posts.append(fav_thread_dict)
291 else:
295 else:
292 fav_thread_dict = dict()
296 fav_thread_dict = dict()
293 fav_thread_dict['new_post_count'] = \
297 fav_thread_dict['new_post_count'] = \
294 Thread.objects.get_new_post_count(ops)
298 Thread.objects.get_new_post_count(ops)
295 posts.append(fav_thread_dict)
299 posts.append(fav_thread_dict)
296
300
297 return HttpResponse(content=json.dumps(posts))
301 return HttpResponse(content=json.dumps(posts))
General Comments 0
You need to be logged in to leave comments. Login now