Show More
@@ -0,0 +1,31 b'' | |||
|
1 | import xml.etree.ElementTree as et | |
|
2 | ||
|
3 | from boards.models import Post | |
|
4 | ||
|
5 | TAG_THREAD = 'thread' | |
|
6 | ||
|
7 | ||
|
8 | class PostFilter: | |
|
9 | def __init__(self, content=None): | |
|
10 | self.content = content | |
|
11 | ||
|
12 | def filter(self, posts): | |
|
13 | return posts | |
|
14 | ||
|
15 | def add_filter(self, model_tag, value): | |
|
16 | return model_tag | |
|
17 | ||
|
18 | ||
|
19 | class ThreadFilter(PostFilter): | |
|
20 | def filter(self, posts): | |
|
21 | op_id = self.content.text | |
|
22 | ||
|
23 | op = Post.objects.filter(opening=True, id=op_id).first() | |
|
24 | if op: | |
|
25 | return posts.filter(thread=op.get_thread()) | |
|
26 | else: | |
|
27 | return posts.none() | |
|
28 | ||
|
29 | def add_filter(self, model_tag, value): | |
|
30 | thread_tag = et.SubElement(model_tag, TAG_THREAD) | |
|
31 | thread_tag.text = str(value) |
@@ -0,0 +1,24 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | # Generated by Django 1.10.5 on 2017-01-23 14:20 | |
|
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', '0055_auto_20161229_1132'), | |
|
12 | ] | |
|
13 | ||
|
14 | operations = [ | |
|
15 | migrations.AlterModelOptions( | |
|
16 | name='attachment', | |
|
17 | options={'ordering': ('id',)}, | |
|
18 | ), | |
|
19 | migrations.AlterField( | |
|
20 | model_name='post', | |
|
21 | name='uid', | |
|
22 | field=models.TextField(), | |
|
23 | ), | |
|
24 | ] |
@@ -18,6 +18,7 b' PostingDelay = 30' | |||
|
18 | 18 | Autoban = false |
|
19 | 19 | DefaultTag = test |
|
20 | 20 | MaxFileCount = 1 |
|
21 | AdditionalSpoilerSpaces = false | |
|
21 | 22 | |
|
22 | 23 | [Messages] |
|
23 | 24 | # Thread bumplimit |
@@ -24,6 +24,8 b' class Command(BaseCommand):' | |||
|
24 | 24 | parser.add_argument('--split-query', type=int, default=1, |
|
25 | 25 | help='Split GET query into separate by the given' |
|
26 | 26 | ' number of posts in one') |
|
27 | parser.add_argument('--thread', type=int, | |
|
28 | help='Get posts of one specific thread') | |
|
27 | 29 | |
|
28 | 30 | def handle(self, *args, **options): |
|
29 | 31 | logger = logging.getLogger('boards.sync') |
@@ -45,7 +47,7 b' class Command(BaseCommand):' | |||
|
45 | 47 | global_id = GlobalId(key_type=key_type, key=key, |
|
46 | 48 | local_id=local_id) |
|
47 | 49 | |
|
48 |
xml = |
|
|
50 | xml = SyncManager.generate_request_get([global_id]) | |
|
49 | 51 | h = httplib2.Http() |
|
50 | 52 | response, content = h.request(get_url, method="POST", body=xml) |
|
51 | 53 | |
@@ -55,7 +57,8 b' class Command(BaseCommand):' | |||
|
55 | 57 | else: |
|
56 | 58 | logger.info('Running LIST request...') |
|
57 | 59 | h = httplib2.Http() |
|
58 |
xml = |
|
|
60 | xml = SyncManager.generate_request_list( | |
|
61 | opening_post=options.get('thread')) | |
|
59 | 62 | response, content = h.request(list_url, method="POST", body=xml) |
|
60 | 63 | logger.info('Processing response...') |
|
61 | 64 | |
@@ -83,7 +86,7 b' class Command(BaseCommand):' | |||
|
83 | 86 | if len(ids_to_sync) > 0: |
|
84 | 87 | limit = options.get('split_query', len(ids_to_sync)) |
|
85 | 88 | for offset in range(0, len(ids_to_sync), limit): |
|
86 |
xml = |
|
|
89 | xml = SyncManager.generate_request_get(ids_to_sync[offset:offset + limit]) | |
|
87 | 90 | h = httplib2.Http() |
|
88 | 91 | logger.info('Running GET request...') |
|
89 | 92 | response, content = h.request(get_url, method="POST", body=xml) |
@@ -10,6 +10,7 b' from django.core.exceptions import Objec' | |||
|
10 | 10 | from django.core.urlresolvers import reverse |
|
11 | 11 | |
|
12 | 12 | import boards |
|
13 | from boards import settings | |
|
13 | 14 | |
|
14 | 15 | |
|
15 | 16 | __author__ = 'neko259' |
@@ -24,6 +25,7 b' LINE_BREAK_HTML = \'<div class="br"></div' | |||
|
24 | 25 | SPOILER_SPACE = ' ' |
|
25 | 26 | |
|
26 | 27 | MAX_SPOILER_MULTIPLIER = 2 |
|
28 | MAX_SPOILER_SPACE_COUNT = 20 | |
|
27 | 29 | |
|
28 | 30 | |
|
29 | 31 | class TextFormatter(): |
@@ -202,11 +204,15 b' def render_tag(tag_name, value, options,' | |||
|
202 | 204 | |
|
203 | 205 | |
|
204 | 206 | def render_spoiler(tag_name, value, options, parent, context): |
|
205 | text_len = len(value) | |
|
206 | space_count = random.randint(0, text_len * MAX_SPOILER_MULTIPLIER) | |
|
207 | side_spaces = SPOILER_SPACE * (space_count // 2) | |
|
208 | return '<span class="spoiler">{}{}{}</span>'.format(side_spaces, value, | |
|
209 | side_spaces) | |
|
207 | if settings.get_bool('Forms', 'AdditionalSpoilerSpaces'): | |
|
208 | text_len = len(value) | |
|
209 | space_count = min(random.randint(0, text_len * MAX_SPOILER_MULTIPLIER), | |
|
210 | MAX_SPOILER_SPACE_COUNT) | |
|
211 | side_spaces = SPOILER_SPACE * (space_count // 2) | |
|
212 | else: | |
|
213 | side_spaces = '' | |
|
214 | return '<span class="spoiler">{}{}{}</span>'.format(side_spaces, | |
|
215 | value, side_spaces) | |
|
210 | 216 | |
|
211 | 217 | |
|
212 | 218 | formatters = [ |
@@ -30,15 +30,15 b' class Downloader:' | |||
|
30 | 30 | return True |
|
31 | 31 | |
|
32 | 32 | @staticmethod |
|
33 | def download(url: str): | |
|
33 | def download(url: str, validate): | |
|
34 | 34 | # Verify content headers |
|
35 | 35 | response_head = requests.head(url, verify=False) |
|
36 | 36 | content_type = response_head.headers[HEADER_CONTENT_TYPE].split(';')[0] |
|
37 | if content_type in TYPE_URL_ONLY: | |
|
37 | if validate and content_type in TYPE_URL_ONLY: | |
|
38 | 38 | return None |
|
39 | 39 | |
|
40 | 40 | length_header = response_head.headers.get(HEADER_CONTENT_LENGTH) |
|
41 | if length_header: | |
|
41 | if validate and length_header: | |
|
42 | 42 | length = int(length_header) |
|
43 | 43 | validate_file_size(length) |
|
44 | 44 | # Get the actual content into memory |
@@ -63,7 +63,7 b' class Downloader:' | |||
|
63 | 63 | |
|
64 | 64 | class YouTubeDownloader(Downloader): |
|
65 | 65 | @staticmethod |
|
66 | def download(url: str): | |
|
66 | def download(url: str, validate): | |
|
67 | 67 | yt = YouTube() |
|
68 | 68 | yt.from_url(url) |
|
69 | 69 | videos = yt.filter(YOUTUBE_VIDEO_FORMAT) |
@@ -82,7 +82,7 b' class NothingDownloader(Downloader):' | |||
|
82 | 82 | return REGEX_MAGNET.match(url) |
|
83 | 83 | |
|
84 | 84 | @staticmethod |
|
85 | def download(url: str): | |
|
85 | def download(url: str, validate): | |
|
86 | 86 | return None |
|
87 | 87 | |
|
88 | 88 | |
@@ -93,9 +93,9 b' DOWNLOADERS = (' | |||
|
93 | 93 | ) |
|
94 | 94 | |
|
95 | 95 | |
|
96 | def download(url): | |
|
96 | def download(url, validate=True): | |
|
97 | 97 | for downloader in DOWNLOADERS: |
|
98 | 98 | if downloader.handles(url): |
|
99 | return downloader.download(url) | |
|
99 | return downloader.download(url, validate=validate) | |
|
100 | 100 | raise Exception('No downloader supports this URL.') |
|
101 | 101 |
@@ -90,7 +90,7 b' class Post(models.Model, Viewable):' | |||
|
90 | 90 | thread = models.ForeignKey('Thread', db_index=True, related_name='replies') |
|
91 | 91 | |
|
92 | 92 | url = models.TextField() |
|
93 |
uid = models.TextField( |
|
|
93 | uid = models.TextField() | |
|
94 | 94 | |
|
95 | 95 | # Global ID with author key. If the message was downloaded from another |
|
96 | 96 | # server, this indicates the server. |
@@ -1,9 +1,13 b'' | |||
|
1 | 1 | import xml.etree.ElementTree as et |
|
2 | 2 | import logging |
|
3 | from xml.etree import ElementTree | |
|
3 | 4 | |
|
4 | 5 | from boards.abstracts.exceptions import SyncException |
|
6 | from boards.abstracts.sync_filters import ThreadFilter | |
|
5 | 7 | from boards.models import KeyPair, GlobalId, Signature, Post, Tag |
|
6 | 8 | from boards.models.attachment.downloaders import download |
|
9 | from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \ | |
|
10 | ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST | |
|
7 | 11 | from boards.utils import get_file_mimetype, get_file_hash |
|
8 | 12 | from django.db import transaction |
|
9 | 13 | from django import forms |
@@ -238,7 +242,7 b' class SyncManager:' | |||
|
238 | 242 | TAG_ATTACHMENT_REF, attachment.text)) |
|
239 | 243 | url = tag_ref.get(ATTR_URL) |
|
240 | 244 | try: |
|
241 | attached_file = download(hostname + url) | |
|
245 | attached_file = download(hostname + url, validate=False) | |
|
242 | 246 | |
|
243 | 247 | if attached_file is None: |
|
244 | 248 | raise SyncException(EXCEPTION_DOWNLOAD) |
@@ -269,7 +273,7 b' class SyncManager:' | |||
|
269 | 273 | logger.debug('Parsed new post {}'.format(global_id)) |
|
270 | 274 | |
|
271 | 275 | @staticmethod |
|
272 | def generate_response_list(): | |
|
276 | def generate_response_list(filters): | |
|
273 | 277 | response = et.Element(TAG_RESPONSE) |
|
274 | 278 | |
|
275 | 279 | status = et.SubElement(response, TAG_STATUS) |
@@ -277,7 +281,11 b' class SyncManager:' | |||
|
277 | 281 | |
|
278 | 282 | models = et.SubElement(response, TAG_MODELS) |
|
279 | 283 | |
|
280 |
|
|
|
284 | posts = Post.objects.prefetch_related('global_id') | |
|
285 | for post_filter in filters: | |
|
286 | posts = post_filter.filter(posts) | |
|
287 | ||
|
288 | for post in posts: | |
|
281 | 289 | tag_model = et.SubElement(models, TAG_MODEL) |
|
282 | 290 | tag_id = et.SubElement(tag_model, TAG_ID) |
|
283 | 291 | post.global_id.to_xml_element(tag_id) |
@@ -334,4 +342,4 b' class SyncManager:' | |||
|
334 | 342 | if tag_refs is not None: |
|
335 | 343 | attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF) |
|
336 | 344 | attachment_ref.set(ATTR_REF, attachment.hash) |
|
337 | attachment_ref.set(ATTR_URL, attachment.file.url) | |
|
345 | attachment_ref.set(ATTR_URL, attachment.file.url) |
@@ -1,8 +1,9 b'' | |||
|
1 | 1 | import xml.etree.ElementTree as et |
|
2 | ||
|
2 | 3 | from django.db import models |
|
4 | ||
|
3 | 5 | from boards.models import KeyPair |
|
4 | 6 | |
|
5 | ||
|
6 | 7 | TAG_MODEL = 'model' |
|
7 | 8 | TAG_REQUEST = 'request' |
|
8 | 9 | TAG_ID = 'id' |
@@ -20,40 +21,6 b" ATTR_LOCAL_ID = 'local-id'" | |||
|
20 | 21 | |
|
21 | 22 | |
|
22 | 23 | class GlobalIdManager(models.Manager): |
|
23 | def generate_request_get(self, global_id_list: list): | |
|
24 | """ | |
|
25 | Form a get request from a list of ModelId objects. | |
|
26 | """ | |
|
27 | ||
|
28 | request = et.Element(TAG_REQUEST) | |
|
29 | request.set(ATTR_TYPE, TYPE_GET) | |
|
30 | request.set(ATTR_VERSION, '1.0') | |
|
31 | ||
|
32 | model = et.SubElement(request, TAG_MODEL) | |
|
33 | model.set(ATTR_VERSION, '1.0') | |
|
34 | model.set(ATTR_NAME, 'post') | |
|
35 | ||
|
36 | for global_id in global_id_list: | |
|
37 | tag_id = et.SubElement(model, TAG_ID) | |
|
38 | global_id.to_xml_element(tag_id) | |
|
39 | ||
|
40 | return et.tostring(request, 'unicode') | |
|
41 | ||
|
42 | def generate_request_list(self): | |
|
43 | """ | |
|
44 | Form a pull request from a list of ModelId objects. | |
|
45 | """ | |
|
46 | ||
|
47 | request = et.Element(TAG_REQUEST) | |
|
48 | request.set(ATTR_TYPE, TYPE_LIST) | |
|
49 | request.set(ATTR_VERSION, '1.0') | |
|
50 | ||
|
51 | model = et.SubElement(request, TAG_MODEL) | |
|
52 | model.set(ATTR_VERSION, '1.0') | |
|
53 | model.set(ATTR_NAME, 'post') | |
|
54 | ||
|
55 | return et.tostring(request, 'unicode') | |
|
56 | ||
|
57 | 24 | def global_id_exists(self, global_id): |
|
58 | 25 | """ |
|
59 | 26 | Checks if the same global id already exists in the system. |
@@ -23,47 +23,50 b' THUMB_SIZES = ((200, 150),)' | |||
|
23 | 23 | |
|
24 | 24 | @receiver(post_save, sender=Post) |
|
25 | 25 | def connect_replies(instance, **kwargs): |
|
26 | for reply_number in re.finditer(REGEX_REPLY, instance.get_raw_text()): | |
|
27 | post_id = reply_number.group(1) | |
|
26 | if not kwargs['update_fields']: | |
|
27 | for reply_number in re.finditer(REGEX_REPLY, instance.get_raw_text()): | |
|
28 | post_id = reply_number.group(1) | |
|
28 | 29 | |
|
29 | try: | |
|
30 | referenced_post = Post.objects.get(id=post_id) | |
|
30 | try: | |
|
31 | referenced_post = Post.objects.get(id=post_id) | |
|
31 | 32 | |
|
32 | if not referenced_post.referenced_posts.filter( | |
|
33 | id=instance.id).exists(): | |
|
34 | referenced_post.referenced_posts.add(instance) | |
|
35 | referenced_post.last_edit_time = instance.pub_time | |
|
36 | referenced_post.build_refmap() | |
|
37 | referenced_post.save(update_fields=['refmap', 'last_edit_time']) | |
|
38 | except Post.DoesNotExist: | |
|
39 | pass | |
|
33 | if not referenced_post.referenced_posts.filter( | |
|
34 | id=instance.id).exists(): | |
|
35 | referenced_post.referenced_posts.add(instance) | |
|
36 | referenced_post.last_edit_time = instance.pub_time | |
|
37 | referenced_post.build_refmap() | |
|
38 | referenced_post.save(update_fields=['refmap', 'last_edit_time']) | |
|
39 | except Post.DoesNotExist: | |
|
40 | pass | |
|
40 | 41 | |
|
41 | 42 | |
|
42 | 43 | @receiver(post_save, sender=Post) |
|
43 | 44 | @receiver(post_import_deps, sender=Post) |
|
44 | 45 | def connect_global_replies(instance, **kwargs): |
|
45 | for reply_number in re.finditer(REGEX_GLOBAL_REPLY, instance.get_raw_text()): | |
|
46 | key_type = reply_number.group(1) | |
|
47 |
key = reply_number.group( |
|
|
48 |
|
|
|
46 | if not kwargs['update_fields']: | |
|
47 | for reply_number in re.finditer(REGEX_GLOBAL_REPLY, instance.get_raw_text()): | |
|
48 | key_type = reply_number.group(1) | |
|
49 | key = reply_number.group(2) | |
|
50 | local_id = reply_number.group(3) | |
|
49 | 51 | |
|
50 | try: | |
|
51 | global_id = GlobalId.objects.get(key_type=key_type, key=key, | |
|
52 | local_id=local_id) | |
|
53 | referenced_post = Post.objects.get(global_id=global_id) | |
|
54 | referenced_post.referenced_posts.add(instance) | |
|
55 | referenced_post.last_edit_time = instance.pub_time | |
|
56 | referenced_post.build_refmap() | |
|
57 | referenced_post.save(update_fields=['refmap', 'last_edit_time']) | |
|
58 | except (GlobalId.DoesNotExist, Post.DoesNotExist): | |
|
59 | pass | |
|
52 | try: | |
|
53 | global_id = GlobalId.objects.get(key_type=key_type, key=key, | |
|
54 | local_id=local_id) | |
|
55 | referenced_post = Post.objects.get(global_id=global_id) | |
|
56 | referenced_post.referenced_posts.add(instance) | |
|
57 | referenced_post.last_edit_time = instance.pub_time | |
|
58 | referenced_post.build_refmap() | |
|
59 | referenced_post.save(update_fields=['refmap', 'last_edit_time']) | |
|
60 | except (GlobalId.DoesNotExist, Post.DoesNotExist): | |
|
61 | pass | |
|
60 | 62 | |
|
61 | 63 | |
|
62 | 64 | @receiver(post_save, sender=Post) |
|
63 | 65 | def connect_notifications(instance, **kwargs): |
|
64 | for reply_number in re.finditer(REGEX_NOTIFICATION, instance.get_raw_text()): | |
|
65 | user_name = reply_number.group(1).lower() | |
|
66 | Notification.objects.get_or_create(name=user_name, post=instance) | |
|
66 | if not kwargs['update_fields']: | |
|
67 | for reply_number in re.finditer(REGEX_NOTIFICATION, instance.get_raw_text()): | |
|
68 | user_name = reply_number.group(1).lower() | |
|
69 | Notification.objects.get_or_create(name=user_name, post=instance) | |
|
67 | 70 | |
|
68 | 71 | |
|
69 | 72 | @receiver(pre_save, sender=Post) |
@@ -57,40 +57,35 b' function showFormAfter(blockToInsertAfte' | |||
|
57 | 57 | } |
|
58 | 58 | |
|
59 | 59 | function addQuickReply(postId) { |
|
60 | // If we click "reply" on the same post, it means "cancel" | |
|
61 | if (getForm().prev().attr('id') == postId) { | |
|
62 | resetFormPosition(); | |
|
63 | } else { | |
|
64 | var blockToInsert = null; | |
|
65 | var textAreaJq = getPostTextarea(); | |
|
66 | var postLinkRaw = '[post]' + postId + '[/post]' | |
|
67 | var textToAdd = ''; | |
|
60 | var blockToInsert = null; | |
|
61 | var textAreaJq = getPostTextarea(); | |
|
62 | var postLinkRaw = '[post]' + postId + '[/post]' | |
|
63 | var textToAdd = ''; | |
|
68 | 64 | |
|
69 |
|
|
|
70 |
|
|
|
65 | if (postId != null) { | |
|
66 | var post = $('#' + postId); | |
|
71 | 67 | |
|
72 |
|
|
|
73 |
|
|
|
74 |
|
|
|
75 |
|
|
|
76 |
|
|
|
77 |
|
|
|
78 |
|
|
|
79 | } | |
|
80 | textToAdd += postLinkRaw + '\n'; | |
|
68 | // If this is not OP, add reflink to the post. If there already is | |
|
69 | // the same reflink, don't add it again. | |
|
70 | var postText = textAreaJq.val(); | |
|
71 | if (!post.is(':first-child') && postText.indexOf(postLinkRaw) < 0) { | |
|
72 | // Insert line break if none is present. | |
|
73 | if (postText.length > 0 && !postText.endsWith('\n') && !postText.endsWith('\r')) { | |
|
74 | textToAdd += '\n'; | |
|
81 | 75 | } |
|
76 | textToAdd += postLinkRaw + '\n'; | |
|
77 | } | |
|
82 | 78 | |
|
83 |
|
|
|
84 |
|
|
|
85 |
|
|
|
86 |
|
|
|
87 |
|
|
|
88 |
|
|
|
89 | ||
|
90 | textAreaJq.focus(); | |
|
79 | textAreaJq.val(textAreaJq.val()+ textToAdd); | |
|
80 | blockToInsert = post; | |
|
81 | } else { | |
|
82 | blockToInsert = $('.thread'); | |
|
83 | } | |
|
84 | showFormAfter(blockToInsert); | |
|
91 | 85 | |
|
92 | moveCaretToEnd(textAreaJq); | |
|
93 | } | |
|
86 | textAreaJq.focus(); | |
|
87 | ||
|
88 | moveCaretToEnd(textAreaJq); | |
|
94 | 89 | } |
|
95 | 90 | |
|
96 | 91 | function addQuickQuote() { |
@@ -98,7 +93,7 b' function addQuickQuote() {' | |||
|
98 | 93 | |
|
99 | 94 | var quoteButton = $("#quote-button"); |
|
100 | 95 | var postId = quoteButton.attr('data-post-id'); |
|
101 | if (postId != null && getForm().prev().attr('id') != postId) { | |
|
96 | if (postId != null) { | |
|
102 | 97 | addQuickReply(postId); |
|
103 | 98 | } |
|
104 | 99 |
@@ -1,4 +1,3 b'' | |||
|
1 | from base64 import b64encode | |
|
2 | 1 |
|
|
3 | 2 | |
|
4 | 3 | from django.test import TestCase |
@@ -38,7 +37,7 b' class KeyTest(TestCase):' | |||
|
38 | 37 | def test_request_get(self): |
|
39 | 38 | post = self._create_post_with_key() |
|
40 | 39 | |
|
41 |
request = |
|
|
40 | request = SyncManager.generate_request_get([post.global_id]) | |
|
42 | 41 | logger.debug(request) |
|
43 | 42 | |
|
44 | 43 | key = KeyPair.objects.get(primary=True) |
@@ -3,7 +3,7 b' from django.test import TestCase' | |||
|
3 | 3 | from boards.models import KeyPair, Post, Tag |
|
4 | 4 | from boards.models.post.sync import SyncManager |
|
5 | 5 | from boards.tests.mocks import MockRequest |
|
6 | from boards.views.sync import response_get | |
|
6 | from boards.views.sync import response_get, response_list | |
|
7 | 7 | |
|
8 | 8 | __author__ = 'neko259' |
|
9 | 9 | |
@@ -103,3 +103,112 b' class SyncTest(TestCase):' | |||
|
103 | 103 | post.version, |
|
104 | 104 | ) in response, |
|
105 | 105 | 'Wrong response generated for the GET request.') |
|
106 | ||
|
107 | def test_list_all(self): | |
|
108 | key = KeyPair.objects.generate_key(primary=True) | |
|
109 | tag = Tag.objects.create(name='tag1') | |
|
110 | post = Post.objects.create_post(title='test_title', | |
|
111 | text='test_text\rline two', | |
|
112 | tags=[tag]) | |
|
113 | post2 = Post.objects.create_post(title='test title 2', | |
|
114 | text='test text 2', | |
|
115 | tags=[tag]) | |
|
116 | ||
|
117 | request_all = MockRequest() | |
|
118 | request_all.body = ( | |
|
119 | '<request type="list" version="1.0">' | |
|
120 | '<model name="post" version="1.0">' | |
|
121 | '</model>' | |
|
122 | '</request>' | |
|
123 | ) | |
|
124 | ||
|
125 | response_all = response_list(request_all).content.decode() | |
|
126 | self.assertTrue( | |
|
127 | '<status>success</status>' | |
|
128 | '<models>' | |
|
129 | '<model>' | |
|
130 | '<id key="{}" local-id="{}" type="{}" />' | |
|
131 | '<version>{}</version>' | |
|
132 | '</model>' | |
|
133 | '<model>' | |
|
134 | '<id key="{}" local-id="{}" type="{}" />' | |
|
135 | '<version>{}</version>' | |
|
136 | '</model>' | |
|
137 | '</models>'.format( | |
|
138 | post.global_id.key, | |
|
139 | post.global_id.local_id, | |
|
140 | post.global_id.key_type, | |
|
141 | post.version, | |
|
142 | post2.global_id.key, | |
|
143 | post2.global_id.local_id, | |
|
144 | post2.global_id.key_type, | |
|
145 | post2.version, | |
|
146 | ) in response_all, | |
|
147 | 'Wrong response generated for the LIST request for all posts.') | |
|
148 | ||
|
149 | def test_list_existing_thread(self): | |
|
150 | key = KeyPair.objects.generate_key(primary=True) | |
|
151 | tag = Tag.objects.create(name='tag1') | |
|
152 | post = Post.objects.create_post(title='test_title', | |
|
153 | text='test_text\rline two', | |
|
154 | tags=[tag]) | |
|
155 | post2 = Post.objects.create_post(title='test title 2', | |
|
156 | text='test text 2', | |
|
157 | tags=[tag]) | |
|
158 | ||
|
159 | request_thread = MockRequest() | |
|
160 | request_thread.body = ( | |
|
161 | '<request type="list" version="1.0">' | |
|
162 | '<model name="post" version="1.0">' | |
|
163 | '<thread>{}</thread>' | |
|
164 | '</model>' | |
|
165 | '</request>'.format( | |
|
166 | post.id, | |
|
167 | ) | |
|
168 | ) | |
|
169 | ||
|
170 | response_thread = response_list(request_thread).content.decode() | |
|
171 | self.assertTrue( | |
|
172 | '<status>success</status>' | |
|
173 | '<models>' | |
|
174 | '<model>' | |
|
175 | '<id key="{}" local-id="{}" type="{}" />' | |
|
176 | '<version>{}</version>' | |
|
177 | '</model>' | |
|
178 | '</models>'.format( | |
|
179 | post.global_id.key, | |
|
180 | post.global_id.local_id, | |
|
181 | post.global_id.key_type, | |
|
182 | post.version, | |
|
183 | ) in response_thread, | |
|
184 | 'Wrong response generated for the LIST request for posts of ' | |
|
185 | 'existing thread.') | |
|
186 | ||
|
187 | def test_list_non_existing_thread(self): | |
|
188 | key = KeyPair.objects.generate_key(primary=True) | |
|
189 | tag = Tag.objects.create(name='tag1') | |
|
190 | post = Post.objects.create_post(title='test_title', | |
|
191 | text='test_text\rline two', | |
|
192 | tags=[tag]) | |
|
193 | post2 = Post.objects.create_post(title='test title 2', | |
|
194 | text='test text 2', | |
|
195 | tags=[tag]) | |
|
196 | ||
|
197 | request_thread = MockRequest() | |
|
198 | request_thread.body = ( | |
|
199 | '<request type="list" version="1.0">' | |
|
200 | '<model name="post" version="1.0">' | |
|
201 | '<thread>{}</thread>' | |
|
202 | '</model>' | |
|
203 | '</request>'.format( | |
|
204 | 0, | |
|
205 | ) | |
|
206 | ) | |
|
207 | ||
|
208 | response_thread = response_list(request_thread).content.decode() | |
|
209 | self.assertTrue( | |
|
210 | '<status>success</status>' | |
|
211 | '<models />' | |
|
212 | in response_thread, | |
|
213 | 'Wrong response generated for the LIST request for posts of ' | |
|
214 | 'non-existing thread.') |
@@ -1,17 +1,41 b'' | |||
|
1 | import logging | |
|
2 | ||
|
1 | 3 | import xml.etree.ElementTree as et |
|
2 | 4 | |
|
3 | 5 | from django.http import HttpResponse, Http404 |
|
6 | ||
|
7 | from boards.abstracts.sync_filters import ThreadFilter, TAG_THREAD | |
|
4 | 8 | from boards.models import GlobalId, Post |
|
5 | 9 | from boards.models.post.sync import SyncManager |
|
6 | 10 | |
|
7 | 11 | |
|
12 | logger = logging.getLogger('boards.sync') | |
|
13 | ||
|
14 | ||
|
15 | FILTERS = { | |
|
16 | TAG_THREAD: ThreadFilter, | |
|
17 | } | |
|
18 | ||
|
19 | ||
|
8 | 20 | def response_list(request): |
|
9 | 21 | request_xml = request.body |
|
10 | 22 | |
|
23 | filters = [] | |
|
24 | ||
|
11 | 25 | if request_xml is None or len(request_xml) == 0: |
|
12 | 26 | return HttpResponse(content='Use the API') |
|
27 | else: | |
|
28 | root_tag = et.fromstring(request_xml) | |
|
29 | model_tag = root_tag[0] | |
|
13 | 30 | |
|
14 | response_xml = SyncManager.generate_response_list() | |
|
31 | for tag_filter in model_tag: | |
|
32 | filter_name = tag_filter.tag | |
|
33 | model_filter = FILTERS.get(filter_name)(tag_filter) | |
|
34 | if not model_filter: | |
|
35 | logger.warning('Unavailable filter: {}'.format(filter_name)) | |
|
36 | filters.append(model_filter) | |
|
37 | ||
|
38 | response_xml = SyncManager.generate_response_list(filters) | |
|
15 | 39 | |
|
16 | 40 | return HttpResponse(content=response_xml) |
|
17 | 41 |
@@ -33,11 +33,11 b' 3. Setup a database in `neboard/settings' | |||
|
33 | 33 | |
|
34 | 34 | Depending on configured database and search engine, you need to install corresponding dependencies manually. |
|
35 | 35 | |
|
36 | Default database is *sqlite*, default search engine is *simple*. | |
|
36 | Default database is *sqlite*. If you want to change the database backend, refer to the django documentation for the correct settings. Please note that sqlite accepts only one connection at a time, so you won't be able to run 2 servers or a server and a sync at the same time. | |
|
37 | 37 | |
|
38 | 38 | 4. Setup SECRET_KEY to a secret value in `neboard/settings.py |
|
39 | 39 | 5. Run `./manage.py migrate` to apply all migrations |
|
40 |
6. Apply config changes to `boards/config/ |
|
|
40 | 6. Apply config changes to `boards/config/settings.ini`. You can see the default settings in `boards/config/default_config.ini`(do not delete or overwrite it). | |
|
41 | 41 | 7. If you want to use decetral engine, run `./manage.py generate_keypair` to generate keys |
|
42 | 42 | |
|
43 | 43 | # RUNNING # |
General Comments 0
You need to be logged in to leave comments.
Login now