Show More
@@ -0,0 +1,20 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | # Generated by Django 1.9.5 on 2016-05-11 09:23 | |||
|
3 | from __future__ import unicode_literals | |||
|
4 | ||||
|
5 | from django.db import migrations, models | |||
|
6 | ||||
|
7 | ||||
|
8 | class Migration(migrations.Migration): | |||
|
9 | ||||
|
10 | dependencies = [ | |||
|
11 | ('boards', '0044_globalid_content'), | |||
|
12 | ] | |||
|
13 | ||||
|
14 | operations = [ | |||
|
15 | migrations.AddField( | |||
|
16 | model_name='post', | |||
|
17 | name='version', | |||
|
18 | field=models.IntegerField(default=1), | |||
|
19 | ), | |||
|
20 | ] |
@@ -1,6 +1,7 b'' | |||||
1 | from django.contrib import admin |
|
1 | from django.contrib import admin | |
2 | from django.utils.translation import ugettext_lazy as _ |
|
2 | from django.utils.translation import ugettext_lazy as _ | |
3 | from django.core.urlresolvers import reverse |
|
3 | from django.core.urlresolvers import reverse | |
|
4 | from django.db.models import F | |||
4 | from boards.models import Post, Tag, Ban, Thread, Banner, PostImage, KeyPair, GlobalId |
|
5 | from boards.models import Post, Tag, Ban, Thread, Banner, PostImage, KeyPair, GlobalId | |
5 |
|
6 | |||
6 |
|
7 | |||
@@ -12,7 +13,8 b' class PostAdmin(admin.ModelAdmin):' | |||||
12 | search_fields = ('id', 'title', 'text', 'poster_ip') |
|
13 | search_fields = ('id', 'title', 'text', 'poster_ip') | |
13 | exclude = ('referenced_posts', 'refmap', 'images', 'global_id') |
|
14 | exclude = ('referenced_posts', 'refmap', 'images', 'global_id') | |
14 | readonly_fields = ('poster_ip', 'threads', 'thread', 'linked_images', |
|
15 | readonly_fields = ('poster_ip', 'threads', 'thread', 'linked_images', | |
15 |
'attachments', 'uid', 'url', 'pub_time', 'opening', 'linked_global_id' |
|
16 | 'attachments', 'uid', 'url', 'pub_time', 'opening', 'linked_global_id', | |
|
17 | 'version') | |||
16 |
|
18 | |||
17 | def ban_poster(self, request, queryset): |
|
19 | def ban_poster(self, request, queryset): | |
18 | bans = 0 |
|
20 | bans = 0 | |
@@ -54,6 +56,11 b' class PostAdmin(admin.ModelAdmin):' | |||||
54 | args=[global_id.id]), str(global_id)) |
|
56 | args=[global_id.id]), str(global_id)) | |
55 | linked_global_id.allow_tags = True |
|
57 | linked_global_id.allow_tags = True | |
56 |
|
58 | |||
|
59 | def save_model(self, request, obj, form, change): | |||
|
60 | obj.increment_version() | |||
|
61 | obj.save() | |||
|
62 | obj.clear_cache() | |||
|
63 | ||||
57 | actions = ['ban_poster', 'ban_with_hiding'] |
|
64 | actions = ['ban_poster', 'ban_with_hiding'] | |
58 |
|
65 | |||
59 |
|
66 | |||
@@ -96,6 +103,14 b' class ThreadAdmin(admin.ModelAdmin):' | |||||
96 | def save_related(self, request, form, formsets, change): |
|
103 | def save_related(self, request, form, formsets, change): | |
97 | super().save_related(request, form, formsets, change) |
|
104 | super().save_related(request, form, formsets, change) | |
98 | form.instance.refresh_tags() |
|
105 | form.instance.refresh_tags() | |
|
106 | ||||
|
107 | def save_model(self, request, obj, form, change): | |||
|
108 | op = obj.get_opening_post() | |||
|
109 | op.increment_version() | |||
|
110 | op.save(update_fields=['version']) | |||
|
111 | obj.save() | |||
|
112 | op.clear_cache() | |||
|
113 | ||||
99 | list_display = ('id', 'op', 'title', 'reply_count', 'status', 'ip', |
|
114 | list_display = ('id', 'op', 'title', 'reply_count', 'status', 'ip', | |
100 | 'display_tags') |
|
115 | 'display_tags') | |
101 | list_filter = ('bump_time', 'status') |
|
116 | list_filter = ('bump_time', 'status') |
@@ -12,9 +12,11 b' class Command(BaseCommand):' | |||||
12 | @transaction.atomic |
|
12 | @transaction.atomic | |
13 | def handle(self, *args, **options): |
|
13 | def handle(self, *args, **options): | |
14 | count = 0 |
|
14 | count = 0 | |
15 |
for global_id in GlobalId.objects. |
|
15 | for global_id in GlobalId.objects.exclude(content__isnull=True).exclude( | |
|
16 | content=''): | |||
16 | if global_id.is_local() and global_id.content is not None: |
|
17 | if global_id.is_local() and global_id.content is not None: | |
17 | global_id.content = None |
|
18 | global_id.content = None | |
18 | global_id.save() |
|
19 | global_id.save() | |
|
20 | global_id.signature_set.all().delete() | |||
19 | count += 1 |
|
21 | count += 1 | |
20 | print('Invalidated {} caches.'.format(count)) |
|
22 | print('Invalidated {} caches.'.format(count)) |
@@ -5,7 +5,7 b' import httplib2' | |||||
5 | from django.core.management import BaseCommand |
|
5 | from django.core.management import BaseCommand | |
6 |
|
6 | |||
7 | from boards.models import GlobalId |
|
7 | from boards.models import GlobalId | |
8 | from boards.models.post.sync import SyncManager |
|
8 | from boards.models.post.sync import SyncManager, TAG_ID, TAG_VERSION | |
9 |
|
9 | |||
10 | __author__ = 'neko259' |
|
10 | __author__ = 'neko259' | |
11 |
|
11 | |||
@@ -27,7 +27,7 b' class Command(BaseCommand):' | |||||
27 | def handle(self, *args, **options): |
|
27 | def handle(self, *args, **options): | |
28 | url = options.get('url') |
|
28 | url = options.get('url') | |
29 |
|
29 | |||
30 |
|
|
30 | list_url = url + 'api/sync/list/' | |
31 | get_url = url + 'api/sync/get/' |
|
31 | get_url = url + 'api/sync/get/' | |
32 | file_url = url[:-1] |
|
32 | file_url = url[:-1] | |
33 |
|
33 | |||
@@ -52,8 +52,8 b' class Command(BaseCommand):' | |||||
52 | raise Exception('Invalid global ID') |
|
52 | raise Exception('Invalid global ID') | |
53 | else: |
|
53 | else: | |
54 | h = httplib2.Http() |
|
54 | h = httplib2.Http() | |
55 |
xml = GlobalId.objects.generate_request_ |
|
55 | xml = GlobalId.objects.generate_request_list() | |
56 |
response, content = h.request( |
|
56 | response, content = h.request(list_url, method="POST", body=xml) | |
57 |
|
57 | |||
58 | print(content.decode() + '\n') |
|
58 | print(content.decode() + '\n') | |
59 |
|
59 | |||
@@ -64,11 +64,16 b' class Command(BaseCommand):' | |||||
64 |
|
64 | |||
65 | models = root.findall('models')[0] |
|
65 | models = root.findall('models')[0] | |
66 | for model in models: |
|
66 | for model in models: | |
67 | global_id, exists = GlobalId.from_xml_element(model) |
|
67 | tag_id = model.find(TAG_ID) | |
68 | if not exists: |
|
68 | global_id, exists = GlobalId.from_xml_element(tag_id) | |
69 | print(global_id) |
|
69 | tag_version = model.find(TAG_VERSION) | |
|
70 | if tag_version is not None: | |||
|
71 | version = int(tag_version.text) or 1 | |||
|
72 | else: | |||
|
73 | version = 1 | |||
|
74 | if not exists or global_id.post.version < version: | |||
70 | ids_to_sync.append(global_id) |
|
75 | ids_to_sync.append(global_id) | |
71 | print() |
|
76 | print('Starting sync...') | |
72 |
|
77 | |||
73 | if len(ids_to_sync) > 0: |
|
78 | if len(ids_to_sync) > 0: | |
74 | limit = options.get('split_query', len(ids_to_sync)) |
|
79 | limit = options.get('split_query', len(ids_to_sync)) |
@@ -11,7 +11,7 b' from boards.utils import datetime_to_epo' | |||||
11 | from django.core.exceptions import ObjectDoesNotExist |
|
11 | from django.core.exceptions import ObjectDoesNotExist | |
12 | from django.core.urlresolvers import reverse |
|
12 | from django.core.urlresolvers import reverse | |
13 | from django.db import models |
|
13 | from django.db import models | |
14 | from django.db.models import TextField, QuerySet |
|
14 | from django.db.models import TextField, QuerySet, F | |
15 | from django.template.defaultfilters import truncatewords, striptags |
|
15 | from django.template.defaultfilters import truncatewords, striptags | |
16 | from django.template.loader import render_to_string |
|
16 | from django.template.loader import render_to_string | |
17 |
|
17 | |||
@@ -104,6 +104,7 b' class Post(models.Model, Viewable):' | |||||
104 | tripcode = models.CharField(max_length=50, blank=True, default='') |
|
104 | tripcode = models.CharField(max_length=50, blank=True, default='') | |
105 | opening = models.BooleanField(db_index=True) |
|
105 | opening = models.BooleanField(db_index=True) | |
106 | hidden = models.BooleanField(default=False) |
|
106 | hidden = models.BooleanField(default=False) | |
|
107 | version = models.IntegerField(default=1) | |||
107 |
|
108 | |||
108 | def __str__(self): |
|
109 | def __str__(self): | |
109 | return 'P#{}/{}'.format(self.id, self.get_title()) |
|
110 | return 'P#{}/{}'.format(self.id, self.get_title()) | |
@@ -337,8 +338,11 b' class Post(models.Model, Viewable):' | |||||
337 |
|
338 | |||
338 | replacements = dict() |
|
339 | replacements = dict() | |
339 | for post_id in REGEX_REPLY.findall(self.get_raw_text()): |
|
340 | for post_id in REGEX_REPLY.findall(self.get_raw_text()): | |
340 | absolute_post_id = str(Post.objects.get(id=post_id).global_id) |
|
341 | try: | |
341 | replacements[post_id] = absolute_post_id |
|
342 | absolute_post_id = str(Post.objects.get(id=post_id).global_id) | |
|
343 | replacements[post_id] = absolute_post_id | |||
|
344 | except Post.DoesNotExist: | |||
|
345 | pass | |||
342 |
|
346 | |||
343 | text = self.get_raw_text() or '' |
|
347 | text = self.get_raw_text() or '' | |
344 | for key in replacements: |
|
348 | for key in replacements: | |
@@ -379,3 +383,14 b' class Post(models.Model, Viewable):' | |||||
379 |
|
383 | |||
380 | def set_hidden(self, hidden): |
|
384 | def set_hidden(self, hidden): | |
381 | self.hidden = hidden |
|
385 | self.hidden = hidden | |
|
386 | ||||
|
387 | def increment_version(self): | |||
|
388 | self.version = F('version') + 1 | |||
|
389 | ||||
|
390 | def clear_cache(self): | |||
|
391 | global_id = self.global_id | |||
|
392 | if global_id is not None and global_id.is_local()\ | |||
|
393 | and global_id.content is not None: | |||
|
394 | global_id.content = None | |||
|
395 | global_id.save() | |||
|
396 | global_id.signature_set.all().delete() |
@@ -32,6 +32,7 b" TAG_TAG = 'tag'" | |||||
32 | TAG_ATTACHMENT_REFS = 'attachment-refs' |
|
32 | TAG_ATTACHMENT_REFS = 'attachment-refs' | |
33 | TAG_ATTACHMENT_REF = 'attachment-ref' |
|
33 | TAG_ATTACHMENT_REF = 'attachment-ref' | |
34 | TAG_TRIPCODE = 'tripcode' |
|
34 | TAG_TRIPCODE = 'tripcode' | |
|
35 | TAG_VERSION = 'version' | |||
35 |
|
36 | |||
36 | TYPE_GET = 'get' |
|
37 | TYPE_GET = 'get' | |
37 |
|
38 | |||
@@ -126,6 +127,8 b' class SyncManager:' | |||||
126 | SyncManager._attachment_to_xml( |
|
127 | SyncManager._attachment_to_xml( | |
127 | attachments_tag, attachment_refs, file.file.file, |
|
128 | attachments_tag, attachment_refs, file.file.file, | |
128 | file.hash, file.file.url) |
|
129 | file.hash, file.file.url) | |
|
130 | version_tag = et.SubElement(content_tag, TAG_VERSION) | |||
|
131 | version_tag.text = str(post.version) | |||
129 |
|
132 | |||
130 | global_id.content = et.tostring(content_tag, ENCODING_UNICODE) |
|
133 | global_id.content = et.tostring(content_tag, ENCODING_UNICODE) | |
131 | global_id.save() |
|
134 | global_id.save() | |
@@ -227,11 +230,12 b' class SyncManager:' | |||||
227 | title=title, text=text, pub_time=pub_time, |
|
230 | title=title, text=text, pub_time=pub_time, | |
228 | opening_post=opening_post, tags=tags, |
|
231 | opening_post=opening_post, tags=tags, | |
229 | global_id=global_id, files=files, tripcode=tripcode) |
|
232 | global_id=global_id, files=files, tripcode=tripcode) | |
|
233 | print('Parsed post {}'.format(global_id)) | |||
230 | else: |
|
234 | else: | |
231 | raise SyncException(EXCEPTION_NODE.format(tag_status.text)) |
|
235 | raise SyncException(EXCEPTION_NODE.format(tag_status.text)) | |
232 |
|
236 | |||
233 | @staticmethod |
|
237 | @staticmethod | |
234 |
def generate_response_ |
|
238 | def generate_response_list(): | |
235 | response = et.Element(TAG_RESPONSE) |
|
239 | response = et.Element(TAG_RESPONSE) | |
236 |
|
240 | |||
237 | status = et.SubElement(response, TAG_STATUS) |
|
241 | status = et.SubElement(response, TAG_STATUS) | |
@@ -240,8 +244,11 b' class SyncManager:' | |||||
240 | models = et.SubElement(response, TAG_MODELS) |
|
244 | models = et.SubElement(response, TAG_MODELS) | |
241 |
|
245 | |||
242 | for post in Post.objects.prefetch_related('global_id').all(): |
|
246 | for post in Post.objects.prefetch_related('global_id').all(): | |
243 |
tag_ |
|
247 | tag_model = et.SubElement(models, TAG_MODEL) | |
|
248 | tag_id = et.SubElement(tag_model, TAG_ID) | |||
244 | post.global_id.to_xml_element(tag_id) |
|
249 | post.global_id.to_xml_element(tag_id) | |
|
250 | tag_version = et.SubElement(tag_model, TAG_VERSION) | |||
|
251 | tag_version.text = str(post.version) | |||
245 |
|
252 | |||
246 | return et.tostring(response, ENCODING_UNICODE) |
|
253 | return et.tostring(response, ENCODING_UNICODE) | |
247 |
|
254 |
@@ -8,7 +8,7 b" TAG_REQUEST = 'request'" | |||||
8 | TAG_ID = 'id' |
|
8 | TAG_ID = 'id' | |
9 |
|
9 | |||
10 | TYPE_GET = 'get' |
|
10 | TYPE_GET = 'get' | |
11 |
TYPE_ |
|
11 | TYPE_LIST = 'list' | |
12 |
|
12 | |||
13 | ATTR_VERSION = 'version' |
|
13 | ATTR_VERSION = 'version' | |
14 | ATTR_TYPE = 'type' |
|
14 | ATTR_TYPE = 'type' | |
@@ -39,13 +39,13 b' class GlobalIdManager(models.Manager):' | |||||
39 |
|
39 | |||
40 | return et.tostring(request, 'unicode') |
|
40 | return et.tostring(request, 'unicode') | |
41 |
|
41 | |||
42 |
def generate_request_ |
|
42 | def generate_request_list(self): | |
43 | """ |
|
43 | """ | |
44 | Form a pull request from a list of ModelId objects. |
|
44 | Form a pull request from a list of ModelId objects. | |
45 | """ |
|
45 | """ | |
46 |
|
46 | |||
47 | request = et.Element(TAG_REQUEST) |
|
47 | request = et.Element(TAG_REQUEST) | |
48 |
request.set(ATTR_TYPE, TYPE_ |
|
48 | request.set(ATTR_TYPE, TYPE_LIST) | |
49 | request.set(ATTR_VERSION, '1.0') |
|
49 | request.set(ATTR_VERSION, '1.0') | |
50 |
|
50 | |||
51 | model = et.SubElement(request, TAG_MODEL) |
|
51 | model = et.SubElement(request, TAG_MODEL) |
@@ -96,17 +96,24 b' function addQuickReply(postId) {' | |||||
96 | } |
|
96 | } | |
97 |
|
97 | |||
98 | function addQuickQuote() { |
|
98 | function addQuickQuote() { | |
|
99 | var textAreaJq = $('textarea'); | |||
|
100 | ||||
|
101 | var quoteButton = $("#quote-button"); | |||
|
102 | var postId = quoteButton.attr('data-post-id'); | |||
|
103 | if (postId != null && getForm().prev().attr('id') != postId) { | |||
|
104 | addQuickReply(postId); | |||
|
105 | } | |||
|
106 | ||||
99 | var textToAdd = ''; |
|
107 | var textToAdd = ''; | |
100 | var textAreaJq = $('textarea'); |
|
|||
101 | var selection = window.getSelection().toString(); |
|
108 | var selection = window.getSelection().toString(); | |
102 | if (selection.length == 0) { |
|
109 | if (selection.length == 0) { | |
103 |
selection = |
|
110 | selection = quoteButton.attr('data-text'); | |
104 | } |
|
111 | } | |
105 | if (selection.length > 0) { |
|
112 | if (selection.length > 0) { | |
106 | textToAdd += '[quote]' + selection + '[/quote]\n'; |
|
113 | textToAdd += '[quote]' + selection + '[/quote]\n'; | |
107 | } |
|
114 | } | |
108 |
|
115 | |||
109 | textAreaJq.val(textAreaJq.val()+ textToAdd); |
|
116 | textAreaJq.val(textAreaJq.val() + textToAdd); | |
110 |
|
117 | |||
111 | textAreaJq.focus(); |
|
118 | textAreaJq.focus(); | |
112 |
|
119 | |||
@@ -128,6 +135,15 b' function showQuoteButton() {' | |||||
128 |
|
135 | |||
129 | var text = window.getSelection().toString(); |
|
136 | var text = window.getSelection().toString(); | |
130 | quoteButton.attr('data-text', text); |
|
137 | quoteButton.attr('data-text', text); | |
|
138 | ||||
|
139 | var rect = window.getSelection().getRangeAt(0).getBoundingClientRect(); | |||
|
140 | var element = $(document.elementFromPoint(rect.x, rect.y)); | |||
|
141 | var postParent = element.parents('.post'); | |||
|
142 | if (postParent.length > 0) { | |||
|
143 | quoteButton.attr('data-post-id', postParent.attr('id')); | |||
|
144 | } else { | |||
|
145 | quoteButton.attr('data-post-id', null); | |||
|
146 | } | |||
131 | } else { |
|
147 | } else { | |
132 | quoteButton.hide(); |
|
148 | quoteButton.hide(); | |
133 | } |
|
149 | } |
@@ -71,6 +71,7 b' class KeyTest(TestCase):' | |||||
71 | '<text>[post]%s[/post]</text>' |
|
71 | '<text>[post]%s[/post]</text>' | |
72 | '<thread><id key="%s" local-id="%d" type="%s" /></thread>' |
|
72 | '<thread><id key="%s" local-id="%d" type="%s" /></thread>' | |
73 | '<pub-time>%s</pub-time>' |
|
73 | '<pub-time>%s</pub-time>' | |
|
74 | '<version>%s</version>' | |||
74 | '</content>' % ( |
|
75 | '</content>' % ( | |
75 | key.public_key, |
|
76 | key.public_key, | |
76 | reply_post.id, |
|
77 | reply_post.id, | |
@@ -80,6 +81,7 b' class KeyTest(TestCase):' | |||||
80 | post.id, |
|
81 | post.id, | |
81 | key.key_type, |
|
82 | key.key_type, | |
82 | str(reply_post.get_pub_time_str()), |
|
83 | str(reply_post.get_pub_time_str()), | |
|
84 | post.version, | |||
83 | ) in response, |
|
85 | ) in response, | |
84 | 'Wrong XML generated for the GET response.') |
|
86 | 'Wrong XML generated for the GET response.') | |
85 |
|
87 |
@@ -43,6 +43,7 b' class SyncTest(TestCase):' | |||||
43 | '<text>%s</text>' |
|
43 | '<text>%s</text>' | |
44 | '<tags><tag>%s</tag></tags>' |
|
44 | '<tags><tag>%s</tag></tags>' | |
45 | '<pub-time>%s</pub-time>' |
|
45 | '<pub-time>%s</pub-time>' | |
|
46 | '<version>%s</version>' | |||
46 | '</content>' % ( |
|
47 | '</content>' % ( | |
47 | post.global_id.key, |
|
48 | post.global_id.key, | |
48 | post.global_id.local_id, |
|
49 | post.global_id.local_id, | |
@@ -51,6 +52,7 b' class SyncTest(TestCase):' | |||||
51 | post.get_sync_text(), |
|
52 | post.get_sync_text(), | |
52 | post.get_thread().get_tags().first().name, |
|
53 | post.get_thread().get_tags().first().name, | |
53 | post.get_pub_time_str(), |
|
54 | post.get_pub_time_str(), | |
|
55 | post.version, | |||
54 | ) in response, |
|
56 | ) in response, | |
55 | 'Wrong response generated for the GET request.') |
|
57 | 'Wrong response generated for the GET request.') | |
56 |
|
58 | |||
@@ -90,6 +92,7 b' class SyncTest(TestCase):' | |||||
90 | '<text>%s</text>' |
|
92 | '<text>%s</text>' | |
91 | '<tags><tag>%s</tag></tags>' |
|
93 | '<tags><tag>%s</tag></tags>' | |
92 | '<pub-time>%s</pub-time>' |
|
94 | '<pub-time>%s</pub-time>' | |
|
95 | '<version>%s</version>' | |||
93 | '</content>' % ( |
|
96 | '</content>' % ( | |
94 | post.global_id.key, |
|
97 | post.global_id.key, | |
95 | post.global_id.local_id, |
|
98 | post.global_id.local_id, | |
@@ -98,5 +101,6 b' class SyncTest(TestCase):' | |||||
98 | post.get_sync_text(), |
|
101 | post.get_sync_text(), | |
99 | post.get_thread().get_tags().first().name, |
|
102 | post.get_thread().get_tags().first().name, | |
100 | post.get_pub_time_str(), |
|
103 | post.get_pub_time_str(), | |
|
104 | post.version, | |||
101 | ) in response, |
|
105 | ) in response, | |
102 | 'Wrong response generated for the GET request.') |
|
106 | 'Wrong response generated for the GET request.') |
@@ -1,5 +1,4 b'' | |||||
1 | from django.conf.urls import url |
|
1 | from django.conf.urls import url | |
2 | #from django.views.i18n import javascript_catalog |
|
|||
3 |
|
2 | |||
4 | import neboard |
|
3 | import neboard | |
5 |
|
4 | |||
@@ -9,10 +8,9 b' from boards.views import api, tag_thread' | |||||
9 | settings, all_tags, feed |
|
8 | settings, all_tags, feed | |
10 | from boards.views.authors import AuthorsView |
|
9 | from boards.views.authors import AuthorsView | |
11 | from boards.views.notifications import NotificationView |
|
10 | from boards.views.notifications import NotificationView | |
12 | from boards.views.search import BoardSearchView |
|
|||
13 | from boards.views.static import StaticPageView |
|
11 | from boards.views.static import StaticPageView | |
14 | from boards.views.preview import PostPreviewView |
|
12 | from boards.views.preview import PostPreviewView | |
15 |
from boards.views.sync import get_post_sync_data, response_get, response_ |
|
13 | from boards.views.sync import get_post_sync_data, response_get, response_list | |
16 | from boards.views.random import RandomImageView |
|
14 | from boards.views.random import RandomImageView | |
17 | from boards.views.tag_gallery import TagGalleryView |
|
15 | from boards.views.tag_gallery import TagGalleryView | |
18 | from boards.views.translation import cached_javascript_catalog |
|
16 | from boards.views.translation import cached_javascript_catalog | |
@@ -78,9 +76,8 b' urlpatterns = [' | |||||
78 | url(r'^api/new_posts/$', api.api_get_new_posts, name='new_posts'), |
|
76 | url(r'^api/new_posts/$', api.api_get_new_posts, name='new_posts'), | |
79 |
|
77 | |||
80 | # Sync protocol API |
|
78 | # Sync protocol API | |
81 |
url(r'^api/sync/ |
|
79 | url(r'^api/sync/list/$', response_list, name='api_sync_list'), | |
82 |
url(r'^api/sync/get/$', response_get, name='api_sync_ |
|
80 | url(r'^api/sync/get/$', response_get, name='api_sync_get'), | |
83 | # TODO 'get' request |
|
|||
84 |
|
81 | |||
85 | # Notifications |
|
82 | # Notifications | |
86 | url(r'^notifications/(?P<username>\w+)/$', NotificationView.as_view(), name='notifications'), |
|
83 | url(r'^notifications/(?P<username>\w+)/$', NotificationView.as_view(), name='notifications'), |
@@ -1,18 +1,17 b'' | |||||
1 | import xml.etree.ElementTree as et |
|
1 | import xml.etree.ElementTree as et | |
2 | import xml.dom.minidom |
|
|||
3 |
|
2 | |||
4 | from django.http import HttpResponse, Http404 |
|
3 | from django.http import HttpResponse, Http404 | |
5 | from boards.models import GlobalId, Post |
|
4 | from boards.models import GlobalId, Post | |
6 | from boards.models.post.sync import SyncManager |
|
5 | from boards.models.post.sync import SyncManager | |
7 |
|
6 | |||
8 |
|
7 | |||
9 |
def response_ |
|
8 | def response_list(request): | |
10 | request_xml = request.body |
|
9 | request_xml = request.body | |
11 |
|
10 | |||
12 | if request_xml is None: |
|
11 | if request_xml is None or len(request_xml) == 0: | |
13 | return HttpResponse(content='Use the API') |
|
12 | return HttpResponse(content='Use the API') | |
14 |
|
13 | |||
15 |
response_xml = SyncManager.generate_response_ |
|
14 | response_xml = SyncManager.generate_response_list() | |
16 |
|
15 | |||
17 | return HttpResponse(content=response_xml) |
|
16 | return HttpResponse(content=response_xml) | |
18 |
|
17 | |||
@@ -25,7 +24,7 b' def response_get(request):' | |||||
25 |
|
24 | |||
26 | request_xml = request.body |
|
25 | request_xml = request.body | |
27 |
|
26 | |||
28 | if request_xml is None: |
|
27 | if request_xml is None or len(request_xml) == 0: | |
29 | return HttpResponse(content='Use the API') |
|
28 | return HttpResponse(content='Use the API') | |
30 |
|
29 | |||
31 | posts = [] |
|
30 | posts = [] | |
@@ -50,13 +49,7 b' def get_post_sync_data(request, post_id)' | |||||
50 |
|
49 | |||
51 | xml_str = SyncManager.generate_response_get([post]) |
|
50 | xml_str = SyncManager.generate_response_get([post]) | |
52 |
|
51 | |||
53 | xml_repr = xml.dom.minidom.parseString(xml_str) |
|
|||
54 | xml_repr = xml_repr.toprettyxml() |
|
|||
55 |
|
||||
56 | content = '=Global ID=\n%s\n\n=XML=\n%s' \ |
|
|||
57 | % (post.global_id, xml_repr) |
|
|||
58 |
|
||||
59 | return HttpResponse( |
|
52 | return HttpResponse( | |
60 |
content_type='text/ |
|
53 | content_type='text/xml; charset=utf-8', | |
61 |
content= |
|
54 | content=xml_str, | |
62 | ) No newline at end of file |
|
55 | ) |
@@ -43,7 +43,7 b' Parameters:' | |||||
43 | * ``last_update``: last update timestamp |
|
43 | * ``last_update``: last update timestamp | |
44 | * ``last_post``: last added post id |
|
44 | * ``last_post``: last added post id | |
45 |
|
45 | |||
46 | Get the diff of the thread in the `O`` |
|
46 | Get the diff of the thread in the ``O`` | |
47 | format. 2 formats are available: ``html`` (used in AJAX thread update) and |
|
47 | format. 2 formats are available: ``html`` (used in AJAX thread update) and | |
48 | ``json``. The default format is ``html``. Return list format: |
|
48 | ``json``. The default format is ``html``. Return list format: | |
49 |
|
49 |
@@ -61,15 +61,15 b' responsible for validating it.' | |||||
61 |
|
61 | |||
62 | The server is required to return the status of request. See 3.2 for details. |
|
62 | The server is required to return the status of request. See 3.2 for details. | |
63 |
|
63 | |||
64 |
### 3.1.1 |
|
64 | ### 3.1.1 list ### | |
65 |
|
65 | |||
66 |
" |
|
66 | "list" request gets the desired model id list by the given filter (e.g. thread, tags, | |
67 | author) |
|
67 | author) | |
68 |
|
68 | |||
69 | Sample request is as follows: |
|
69 | Sample request is as follows: | |
70 |
|
70 | |||
71 | <?xml version="1.1" encoding="UTF-8" ?> |
|
71 | <?xml version="1.1" encoding="UTF-8" ?> | |
72 |
<request version="1.0" type=" |
|
72 | <request version="1.0" type="list"> | |
73 | <model version="1.0" name="post"> |
|
73 | <model version="1.0" name="post"> | |
74 | <timestamp_from>0</timestamp_from> |
|
74 | <timestamp_from>0</timestamp_from> | |
75 | <timestamp_to>0</timestamp_to> |
|
75 | <timestamp_to>0</timestamp_to> | |
@@ -95,10 +95,17 b' Sample response:' | |||||
95 | <response> |
|
95 | <response> | |
96 | <status>success</status> |
|
96 | <status>success</status> | |
97 | <models> |
|
97 | <models> | |
98 | <id key="id1" type="ecdsa" local-id="1" /> |
|
98 | <model> | |
99 |
<id key="id1" type="ecdsa" local-id=" |
|
99 | <id key="id1" type="ecdsa" local-id="1"> | |
100 | <id key="id2" type="ecdsa" local-id="1" /> |
|
100 | <version>1</version> | |
101 | <id key="id2" type="ecdsa" local-id="5" /> |
|
101 | </model> | |
|
102 | <model> | |||
|
103 | <id key="id1" type="ecdsa" local-id="2" /> | |||
|
104 | </model> | |||
|
105 | <model> | |||
|
106 | <id key="id2" type="ecdsa" local-id="1" /> | |||
|
107 | <some-valuable-attr>value</some-valuable-attr> | |||
|
108 | </model> | |||
102 | </models> |
|
109 | </models> | |
103 | </response> |
|
110 | </response> | |
104 |
|
111 |
@@ -13,6 +13,7 b'' | |||||
13 | * title -- text field. |
|
13 | * title -- text field. | |
14 | * text -- text field. |
|
14 | * text -- text field. | |
15 | * pub-time -- timestamp (TBD: Define format). |
|
15 | * pub-time -- timestamp (TBD: Define format). | |
|
16 | * version -- when post content changes, version should be incremented. | |||
16 |
|
17 | |||
17 | # 2.2 Optional fields # |
|
18 | # 2.2 Optional fields # | |
18 |
|
19 |
General Comments 0
You need to be logged in to leave comments.
Login now