##// END OF EJS Templates
If there is no post update time, use pub time
neko259 -
r2098:c4ac6a57 default
parent child Browse files
Show More
@@ -1,389 +1,395 b''
1 import logging
1 import logging
2 import xml.etree.ElementTree as et
2 import xml.etree.ElementTree as et
3
3
4 from django.db import transaction
4 from django.db import transaction
5 from django.utils.dateparse import parse_datetime
5 from django.utils.dateparse import parse_datetime
6
6
7 from boards.abstracts.exceptions import SyncException
7 from boards.abstracts.exceptions import SyncException
8 from boards.abstracts.sync_filters import ThreadFilter, TagsFilter, \
8 from boards.abstracts.sync_filters import ThreadFilter, TagsFilter, \
9 TimestampFromFilter
9 TimestampFromFilter
10 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
10 from boards.models import KeyPair, GlobalId, Signature, Post, Tag
11 from boards.models.attachment.downloaders import download
11 from boards.models.attachment.downloaders import download
12 from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \
12 from boards.models.signature import TAG_REQUEST, ATTR_TYPE, TYPE_GET, \
13 ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST
13 ATTR_VERSION, TAG_MODEL, ATTR_NAME, TAG_ID, TYPE_LIST
14 from boards.utils import get_file_mimetype, get_file_hash
14 from boards.utils import get_file_mimetype, get_file_hash
15
15
16 EXCEPTION_NODE = 'Sync node returned an error: {}.'
16 EXCEPTION_NODE = 'Sync node returned an error: {}.'
17 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
17 EXCEPTION_DOWNLOAD = 'File was not downloaded.'
18 EXCEPTION_HASH = 'File hash does not match attachment hash.'
18 EXCEPTION_HASH = 'File hash does not match attachment hash.'
19 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
19 EXCEPTION_SIGNATURE = 'Invalid model signature for {}.'
20 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
20 EXCEPTION_AUTHOR_SIGNATURE = 'Model {} has no author signature.'
21 EXCEPTION_THREAD = 'No thread exists for post {}'
21 EXCEPTION_THREAD = 'No thread exists for post {}'
22 ENCODING_UNICODE = 'unicode'
22 ENCODING_UNICODE = 'unicode'
23
23
24 TAG_MODEL = 'model'
24 TAG_MODEL = 'model'
25 TAG_REQUEST = 'request'
25 TAG_REQUEST = 'request'
26 TAG_RESPONSE = 'response'
26 TAG_RESPONSE = 'response'
27 TAG_ID = 'id'
27 TAG_ID = 'id'
28 TAG_STATUS = 'status'
28 TAG_STATUS = 'status'
29 TAG_MODELS = 'models'
29 TAG_MODELS = 'models'
30 TAG_TITLE = 'title'
30 TAG_TITLE = 'title'
31 TAG_TEXT = 'text'
31 TAG_TEXT = 'text'
32 TAG_THREAD = 'thread'
32 TAG_THREAD = 'thread'
33 TAG_PUB_TIME = 'pub-time'
33 TAG_PUB_TIME = 'pub-time'
34 TAG_UPDATE_TIME = 'update-time'
34 TAG_UPDATE_TIME = 'update-time'
35 TAG_SIGNATURES = 'signatures'
35 TAG_SIGNATURES = 'signatures'
36 TAG_SIGNATURE = 'signature'
36 TAG_SIGNATURE = 'signature'
37 TAG_CONTENT = 'content'
37 TAG_CONTENT = 'content'
38 TAG_ATTACHMENTS = 'attachments'
38 TAG_ATTACHMENTS = 'attachments'
39 TAG_ATTACHMENT = 'attachment'
39 TAG_ATTACHMENT = 'attachment'
40 TAG_TAGS = 'tags'
40 TAG_TAGS = 'tags'
41 TAG_TAG = 'tag'
41 TAG_TAG = 'tag'
42 TAG_ATTACHMENT_REFS = 'attachment-refs'
42 TAG_ATTACHMENT_REFS = 'attachment-refs'
43 TAG_ATTACHMENT_REF = 'attachment-ref'
43 TAG_ATTACHMENT_REF = 'attachment-ref'
44 TAG_TRIPCODE = 'tripcode'
44 TAG_TRIPCODE = 'tripcode'
45 TAG_VERSION = 'version'
45 TAG_VERSION = 'version'
46
46
47 TYPE_GET = 'get'
47 TYPE_GET = 'get'
48
48
49 ATTR_VERSION = 'version'
49 ATTR_VERSION = 'version'
50 ATTR_TYPE = 'type'
50 ATTR_TYPE = 'type'
51 ATTR_NAME = 'name'
51 ATTR_NAME = 'name'
52 ATTR_VALUE = 'value'
52 ATTR_VALUE = 'value'
53 ATTR_MIMETYPE = 'mimetype'
53 ATTR_MIMETYPE = 'mimetype'
54 ATTR_KEY = 'key'
54 ATTR_KEY = 'key'
55 ATTR_REF = 'ref'
55 ATTR_REF = 'ref'
56 ATTR_URL = 'url'
56 ATTR_URL = 'url'
57 ATTR_ID_TYPE = 'id-type'
57 ATTR_ID_TYPE = 'id-type'
58
58
59 ID_TYPE_MD5 = 'md5'
59 ID_TYPE_MD5 = 'md5'
60 ID_TYPE_URL = 'url'
60 ID_TYPE_URL = 'url'
61
61
62 STATUS_SUCCESS = 'success'
62 STATUS_SUCCESS = 'success'
63
63
64 CURRENT_MODEL_VERSION = '1.1'
64 CURRENT_MODEL_VERSION = '1.1'
65
65
66
66
67 logger = logging.getLogger('boards.sync')
67 logger = logging.getLogger('boards.sync')
68
68
69
69
70 class SyncManager:
70 class SyncManager:
71 @staticmethod
71 @staticmethod
72 def generate_response_get(model_list: list):
72 def generate_response_get(model_list: list):
73 response = et.Element(TAG_RESPONSE)
73 response = et.Element(TAG_RESPONSE)
74
74
75 status = et.SubElement(response, TAG_STATUS)
75 status = et.SubElement(response, TAG_STATUS)
76 status.text = STATUS_SUCCESS
76 status.text = STATUS_SUCCESS
77
77
78 models = et.SubElement(response, TAG_MODELS)
78 models = et.SubElement(response, TAG_MODELS)
79
79
80 for post in model_list:
80 for post in model_list:
81 model = et.SubElement(models, TAG_MODEL)
81 model = et.SubElement(models, TAG_MODEL)
82 model.set(ATTR_NAME, 'post')
82 model.set(ATTR_NAME, 'post')
83
83
84 global_id = post.global_id
84 global_id = post.global_id
85
85
86 attachments = post.attachments.all()
86 attachments = post.attachments.all()
87 if global_id.content:
87 if global_id.content:
88 model.append(et.fromstring(global_id.content))
88 model.append(et.fromstring(global_id.content))
89 if len(attachments) > 0:
89 if len(attachments) > 0:
90 internal_attachments = False
90 internal_attachments = False
91 for attachment in attachments:
91 for attachment in attachments:
92 if attachment.is_internal():
92 if attachment.is_internal():
93 internal_attachments = True
93 internal_attachments = True
94 break
94 break
95
95
96 if internal_attachments:
96 if internal_attachments:
97 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
97 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
98 for file in attachments:
98 for file in attachments:
99 SyncManager._attachment_to_xml(
99 SyncManager._attachment_to_xml(
100 None, attachment_refs, file)
100 None, attachment_refs, file)
101 else:
101 else:
102 content_tag = et.SubElement(model, TAG_CONTENT)
102 content_tag = et.SubElement(model, TAG_CONTENT)
103
103
104 tag_id = et.SubElement(content_tag, TAG_ID)
104 tag_id = et.SubElement(content_tag, TAG_ID)
105 global_id.to_xml_element(tag_id)
105 global_id.to_xml_element(tag_id)
106
106
107 title = et.SubElement(content_tag, TAG_TITLE)
107 title = et.SubElement(content_tag, TAG_TITLE)
108 title.text = post.title
108 title.text = post.title
109
109
110 text = et.SubElement(content_tag, TAG_TEXT)
110 text = et.SubElement(content_tag, TAG_TEXT)
111 text.text = post.get_sync_text()
111 text.text = post.get_sync_text()
112
112
113 thread = post.get_thread()
113 thread = post.get_thread()
114 if post.is_opening():
114 if post.is_opening():
115 tag_tags = et.SubElement(content_tag, TAG_TAGS)
115 tag_tags = et.SubElement(content_tag, TAG_TAGS)
116 for tag in thread.get_tags():
116 for tag in thread.get_tags():
117 tag_tag = et.SubElement(tag_tags, TAG_TAG)
117 tag_tag = et.SubElement(tag_tags, TAG_TAG)
118 tag_tag.text = tag.get_name()
118 tag_tag.text = tag.get_name()
119 else:
119 else:
120 tag_thread = et.SubElement(content_tag, TAG_THREAD)
120 tag_thread = et.SubElement(content_tag, TAG_THREAD)
121 thread_id = et.SubElement(tag_thread, TAG_ID)
121 thread_id = et.SubElement(tag_thread, TAG_ID)
122 thread.get_opening_post().global_id.to_xml_element(thread_id)
122 thread.get_opening_post().global_id.to_xml_element(thread_id)
123
123
124 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
124 pub_time = et.SubElement(content_tag, TAG_PUB_TIME)
125 pub_time.text = str(post.get_pub_time_str())
125 pub_time.text = str(post.get_pub_time_str())
126
126
127 update_time = et.SubElement(content_tag, TAG_UPDATE_TIME)
127 update_time = et.SubElement(content_tag, TAG_UPDATE_TIME)
128 update_time.text = str(post.last_edit_time)
128 update_time.text = str(post.last_edit_time)
129
129
130 if post.tripcode:
130 if post.tripcode:
131 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
131 tripcode = et.SubElement(content_tag, TAG_TRIPCODE)
132 tripcode.text = post.tripcode
132 tripcode.text = post.tripcode
133
133
134 if len(attachments) > 0:
134 if len(attachments) > 0:
135 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
135 attachments_tag = et.SubElement(content_tag, TAG_ATTACHMENTS)
136
136
137 internal_attachments = False
137 internal_attachments = False
138 for attachment in attachments:
138 for attachment in attachments:
139 if attachment.is_internal():
139 if attachment.is_internal():
140 internal_attachments = True
140 internal_attachments = True
141 break
141 break
142
142
143 if internal_attachments:
143 if internal_attachments:
144 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
144 attachment_refs = et.SubElement(model, TAG_ATTACHMENT_REFS)
145 else:
145 else:
146 attachment_refs = None
146 attachment_refs = None
147
147
148 for file in attachments:
148 for file in attachments:
149 SyncManager._attachment_to_xml(
149 SyncManager._attachment_to_xml(
150 attachments_tag, attachment_refs, file)
150 attachments_tag, attachment_refs, file)
151
151
152 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
152 global_id.content = et.tostring(content_tag, ENCODING_UNICODE)
153 global_id.save()
153 global_id.save()
154
154
155 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
155 signatures_tag = et.SubElement(model, TAG_SIGNATURES)
156 post_signatures = global_id.signature_set.all()
156 post_signatures = global_id.signature_set.all()
157 if post_signatures:
157 if post_signatures:
158 signatures = post_signatures
158 signatures = post_signatures
159 else:
159 else:
160 key = KeyPair.objects.get(public_key=global_id.key)
160 key = KeyPair.objects.get(public_key=global_id.key)
161 signature = Signature(
161 signature = Signature(
162 key_type=key.key_type,
162 key_type=key.key_type,
163 key=key.public_key,
163 key=key.public_key,
164 signature=key.sign(global_id.content),
164 signature=key.sign(global_id.content),
165 global_id=global_id,
165 global_id=global_id,
166 )
166 )
167 signature.save()
167 signature.save()
168 signatures = [signature]
168 signatures = [signature]
169 for signature in signatures:
169 for signature in signatures:
170 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
170 signature_tag = et.SubElement(signatures_tag, TAG_SIGNATURE)
171 signature_tag.set(ATTR_TYPE, signature.key_type)
171 signature_tag.set(ATTR_TYPE, signature.key_type)
172 signature_tag.set(ATTR_VALUE, signature.signature)
172 signature_tag.set(ATTR_VALUE, signature.signature)
173 signature_tag.set(ATTR_KEY, signature.key)
173 signature_tag.set(ATTR_KEY, signature.key)
174
174
175 return et.tostring(response, ENCODING_UNICODE)
175 return et.tostring(response, ENCODING_UNICODE)
176
176
177 @staticmethod
177 @staticmethod
178 def parse_response_get(response_xml, hostname):
178 def parse_response_get(response_xml, hostname):
179 tag_root = et.fromstring(response_xml)
179 tag_root = et.fromstring(response_xml)
180 tag_status = tag_root.find(TAG_STATUS)
180 tag_status = tag_root.find(TAG_STATUS)
181 if STATUS_SUCCESS == tag_status.text:
181 if STATUS_SUCCESS == tag_status.text:
182 tag_models = tag_root.find(TAG_MODELS)
182 tag_models = tag_root.find(TAG_MODELS)
183 for tag_model in tag_models:
183 for tag_model in tag_models:
184 SyncManager.parse_post(tag_model, hostname)
184 SyncManager.parse_post(tag_model, hostname)
185 else:
185 else:
186 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
186 raise SyncException(EXCEPTION_NODE.format(tag_status.text))
187
187
188 @staticmethod
188 @staticmethod
189 @transaction.atomic
189 @transaction.atomic
190 def parse_post(tag_model, hostname):
190 def parse_post(tag_model, hostname):
191 tag_content = tag_model.find(TAG_CONTENT)
191 tag_content = tag_model.find(TAG_CONTENT)
192
192
193 content_str = et.tostring(tag_content, ENCODING_UNICODE)
193 content_str = et.tostring(tag_content, ENCODING_UNICODE)
194
194
195 tag_id = tag_content.find(TAG_ID)
195 tag_id = tag_content.find(TAG_ID)
196 global_id, exists = GlobalId.from_xml_element(tag_id)
196 global_id, exists = GlobalId.from_xml_element(tag_id)
197 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
197 signatures = SyncManager._verify_model(global_id, content_str, tag_model)
198
198
199 update_time = tag_content.find(TAG_UPDATE_TIME).text
199 pub_time = tag_content.find(TAG_PUB_TIME).text
200
201 tag_update_time = tag_content.find(TAG_UPDATE_TIME)
202 if tag_update_time:
203 update_time = tag_content.find(TAG_UPDATE_TIME).text
204 else:
205 update_time = pub_time
206
200 is_old = exists and global_id.post.last_edit_time < parse_datetime(update_time)
207 is_old = exists and global_id.post.last_edit_time < parse_datetime(update_time)
201 if exists and not is_old:
208 if exists and not is_old:
202 logger.debug('Post {} exists and is up to date.'.format(global_id))
209 logger.debug('Post {} exists and is up to date.'.format(global_id))
203 else:
210 else:
204 global_id.content = content_str
211 global_id.content = content_str
205 global_id.save()
212 global_id.save()
206 for signature in signatures:
213 for signature in signatures:
207 signature.global_id = global_id
214 signature.global_id = global_id
208 signature.save()
215 signature.save()
209
216
210 title = tag_content.find(TAG_TITLE).text or ''
217 title = tag_content.find(TAG_TITLE).text or ''
211 text = tag_content.find(TAG_TEXT).text or ''
218 text = tag_content.find(TAG_TEXT).text or ''
212 tripcode_tag = tag_content.find(TAG_TRIPCODE)
219 tripcode_tag = tag_content.find(TAG_TRIPCODE)
213 if tripcode_tag is not None:
220 if tripcode_tag is not None:
214 tripcode = tripcode_tag.text or ''
221 tripcode = tripcode_tag.text or ''
215 else:
222 else:
216 tripcode = ''
223 tripcode = ''
217
224
218 pub_time = tag_content.find(TAG_PUB_TIME).text
219 thread = tag_content.find(TAG_THREAD)
225 thread = tag_content.find(TAG_THREAD)
220 tags = []
226 tags = []
221 if thread:
227 if thread:
222 thread_id = thread.find(TAG_ID)
228 thread_id = thread.find(TAG_ID)
223 op_global_id, exists = GlobalId.from_xml_element(thread_id)
229 op_global_id, exists = GlobalId.from_xml_element(thread_id)
224 if exists:
230 if exists:
225 opening_post = Post.objects.get(global_id=op_global_id)
231 opening_post = Post.objects.get(global_id=op_global_id)
226 else:
232 else:
227 raise Exception(EXCEPTION_THREAD.format(global_id))
233 raise Exception(EXCEPTION_THREAD.format(global_id))
228 else:
234 else:
229 opening_post = None
235 opening_post = None
230 tag_tags = tag_content.find(TAG_TAGS)
236 tag_tags = tag_content.find(TAG_TAGS)
231 for tag_tag in tag_tags:
237 for tag_tag in tag_tags:
232 tag, created = Tag.objects.get_or_create(
238 tag, created = Tag.objects.get_or_create(
233 aliases__name=tag_tag.text)
239 aliases__name=tag_tag.text)
234 tags.append(tag)
240 tags.append(tag)
235
241
236 # TODO Check that the replied posts are already present
242 # TODO Check that the replied posts are already present
237 # before adding new ones
243 # before adding new ones
238
244
239 files = []
245 files = []
240 urls = []
246 urls = []
241 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
247 tag_attachments = tag_content.find(TAG_ATTACHMENTS) or list()
242 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
248 tag_refs = tag_model.find(TAG_ATTACHMENT_REFS)
243 for attachment in tag_attachments:
249 for attachment in tag_attachments:
244 if attachment.get(ATTR_ID_TYPE) == ID_TYPE_URL:
250 if attachment.get(ATTR_ID_TYPE) == ID_TYPE_URL:
245 urls.append(attachment.text)
251 urls.append(attachment.text)
246 else:
252 else:
247 tag_ref = tag_refs.find("{}[@ref='{}']".format(
253 tag_ref = tag_refs.find("{}[@ref='{}']".format(
248 TAG_ATTACHMENT_REF, attachment.text))
254 TAG_ATTACHMENT_REF, attachment.text))
249 url = tag_ref.get(ATTR_URL)
255 url = tag_ref.get(ATTR_URL)
250 attached_file = download(hostname + url, validate=False)
256 attached_file = download(hostname + url, validate=False)
251 if attached_file is None:
257 if attached_file is None:
252 raise SyncException(EXCEPTION_DOWNLOAD)
258 raise SyncException(EXCEPTION_DOWNLOAD)
253
259
254 hash = get_file_hash(attached_file)
260 hash = get_file_hash(attached_file)
255 if hash != attachment.text:
261 if hash != attachment.text:
256 raise SyncException(EXCEPTION_HASH)
262 raise SyncException(EXCEPTION_HASH)
257
263
258 files.append(attached_file)
264 files.append(attached_file)
259
265
260 if is_old:
266 if is_old:
261 post = global_id.post
267 post = global_id.post
262 Post.objects.update_post(
268 Post.objects.update_post(
263 post, title=title, text=text, pub_time=pub_time,
269 post, title=title, text=text, pub_time=pub_time,
264 tags=tags, files=files, file_urls=urls,
270 tags=tags, files=files, file_urls=urls,
265 tripcode=tripcode, version=version, last_edit_time=update_time)
271 tripcode=tripcode, version=version, last_edit_time=update_time)
266 logger.debug('Parsed updated post {}'.format(global_id))
272 logger.debug('Parsed updated post {}'.format(global_id))
267 else:
273 else:
268 Post.objects.import_post(
274 Post.objects.import_post(
269 title=title, text=text, pub_time=pub_time,
275 title=title, text=text, pub_time=pub_time,
270 opening_post=opening_post, tags=tags,
276 opening_post=opening_post, tags=tags,
271 global_id=global_id, files=files,
277 global_id=global_id, files=files,
272 file_urls=urls, tripcode=tripcode, last_edit_time=update_time)
278 file_urls=urls, tripcode=tripcode, last_edit_time=update_time)
273 logger.debug('Parsed new post {}'.format(global_id))
279 logger.debug('Parsed new post {}'.format(global_id))
274
280
275 @staticmethod
281 @staticmethod
276 def generate_response_list(filters):
282 def generate_response_list(filters):
277 response = et.Element(TAG_RESPONSE)
283 response = et.Element(TAG_RESPONSE)
278
284
279 status = et.SubElement(response, TAG_STATUS)
285 status = et.SubElement(response, TAG_STATUS)
280 status.text = STATUS_SUCCESS
286 status.text = STATUS_SUCCESS
281
287
282 models = et.SubElement(response, TAG_MODELS)
288 models = et.SubElement(response, TAG_MODELS)
283
289
284 posts = Post.objects.prefetch_related('global_id')
290 posts = Post.objects.prefetch_related('global_id')
285 for post_filter in filters:
291 for post_filter in filters:
286 posts = post_filter.filter(posts)
292 posts = post_filter.filter(posts)
287
293
288 for post in posts:
294 for post in posts:
289 tag_model = et.SubElement(models, TAG_MODEL)
295 tag_model = et.SubElement(models, TAG_MODEL)
290 tag_id = et.SubElement(tag_model, TAG_ID)
296 tag_id = et.SubElement(tag_model, TAG_ID)
291 post.global_id.to_xml_element(tag_id)
297 post.global_id.to_xml_element(tag_id)
292 update_time = et.SubElement(tag_model, TAG_UPDATE_TIME)
298 update_time = et.SubElement(tag_model, TAG_UPDATE_TIME)
293 update_time.text = str(post.last_edit_time)
299 update_time.text = str(post.last_edit_time)
294
300
295 return et.tostring(response, ENCODING_UNICODE)
301 return et.tostring(response, ENCODING_UNICODE)
296
302
297 @staticmethod
303 @staticmethod
298 def _verify_model(global_id, content_str, tag_model):
304 def _verify_model(global_id, content_str, tag_model):
299 """
305 """
300 Verifies all signatures for a single model.
306 Verifies all signatures for a single model.
301 """
307 """
302
308
303 signatures = []
309 signatures = []
304
310
305 tag_signatures = tag_model.find(TAG_SIGNATURES)
311 tag_signatures = tag_model.find(TAG_SIGNATURES)
306 has_author_signature = False
312 has_author_signature = False
307 for tag_signature in tag_signatures:
313 for tag_signature in tag_signatures:
308 signature_type = tag_signature.get(ATTR_TYPE)
314 signature_type = tag_signature.get(ATTR_TYPE)
309 signature_value = tag_signature.get(ATTR_VALUE)
315 signature_value = tag_signature.get(ATTR_VALUE)
310 signature_key = tag_signature.get(ATTR_KEY)
316 signature_key = tag_signature.get(ATTR_KEY)
311
317
312 if global_id.key_type == signature_type and\
318 if global_id.key_type == signature_type and\
313 global_id.key == signature_key:
319 global_id.key == signature_key:
314 has_author_signature = True
320 has_author_signature = True
315
321
316 signature = Signature(key_type=signature_type,
322 signature = Signature(key_type=signature_type,
317 key=signature_key,
323 key=signature_key,
318 signature=signature_value)
324 signature=signature_value)
319
325
320 if not KeyPair.objects.verify(signature, content_str):
326 if not KeyPair.objects.verify(signature, content_str):
321 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
327 raise SyncException(EXCEPTION_SIGNATURE.format(content_str))
322
328
323 signatures.append(signature)
329 signatures.append(signature)
324 if not has_author_signature:
330 if not has_author_signature:
325 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
331 raise SyncException(EXCEPTION_AUTHOR_SIGNATURE.format(content_str))
326
332
327 return signatures
333 return signatures
328
334
329 @staticmethod
335 @staticmethod
330 def _attachment_to_xml(tag_attachments, tag_refs, attachment):
336 def _attachment_to_xml(tag_attachments, tag_refs, attachment):
331 if tag_attachments is not None:
337 if tag_attachments is not None:
332 attachment_tag = et.SubElement(tag_attachments, TAG_ATTACHMENT)
338 attachment_tag = et.SubElement(tag_attachments, TAG_ATTACHMENT)
333 if attachment.is_internal():
339 if attachment.is_internal():
334 mimetype = get_file_mimetype(attachment.file.file)
340 mimetype = get_file_mimetype(attachment.file.file)
335 attachment_tag.set(ATTR_MIMETYPE, mimetype)
341 attachment_tag.set(ATTR_MIMETYPE, mimetype)
336 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_MD5)
342 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_MD5)
337 attachment_tag.text = attachment.hash
343 attachment_tag.text = attachment.hash
338 else:
344 else:
339 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_URL)
345 attachment_tag.set(ATTR_ID_TYPE, ID_TYPE_URL)
340 attachment_tag.text = attachment.url
346 attachment_tag.text = attachment.url
341
347
342 if tag_refs is not None and attachment.is_internal():
348 if tag_refs is not None and attachment.is_internal():
343 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
349 attachment_ref = et.SubElement(tag_refs, TAG_ATTACHMENT_REF)
344 attachment_ref.set(ATTR_REF, attachment.hash)
350 attachment_ref.set(ATTR_REF, attachment.hash)
345 attachment_ref.set(ATTR_URL, attachment.file.url)
351 attachment_ref.set(ATTR_URL, attachment.file.url)
346
352
347 @staticmethod
353 @staticmethod
348 def generate_request_get(global_id_list: list):
354 def generate_request_get(global_id_list: list):
349 """
355 """
350 Form a get request from a list of ModelId objects.
356 Form a get request from a list of ModelId objects.
351 """
357 """
352
358
353 request = et.Element(TAG_REQUEST)
359 request = et.Element(TAG_REQUEST)
354 request.set(ATTR_TYPE, TYPE_GET)
360 request.set(ATTR_TYPE, TYPE_GET)
355 request.set(ATTR_VERSION, '1.0')
361 request.set(ATTR_VERSION, '1.0')
356
362
357 model = et.SubElement(request, TAG_MODEL)
363 model = et.SubElement(request, TAG_MODEL)
358 model.set(ATTR_VERSION, '1.0')
364 model.set(ATTR_VERSION, '1.0')
359 model.set(ATTR_NAME, 'post')
365 model.set(ATTR_NAME, 'post')
360
366
361 for global_id in global_id_list:
367 for global_id in global_id_list:
362 tag_id = et.SubElement(model, TAG_ID)
368 tag_id = et.SubElement(model, TAG_ID)
363 global_id.to_xml_element(tag_id)
369 global_id.to_xml_element(tag_id)
364
370
365 return et.tostring(request, 'unicode')
371 return et.tostring(request, 'unicode')
366
372
367 @staticmethod
373 @staticmethod
368 def generate_request_list(opening_post=None, tags=list(),
374 def generate_request_list(opening_post=None, tags=list(),
369 timestamp_from=None):
375 timestamp_from=None):
370 """
376 """
371 Form a pull request from a list of ModelId objects.
377 Form a pull request from a list of ModelId objects.
372 """
378 """
373
379
374 request = et.Element(TAG_REQUEST)
380 request = et.Element(TAG_REQUEST)
375 request.set(ATTR_TYPE, TYPE_LIST)
381 request.set(ATTR_TYPE, TYPE_LIST)
376 request.set(ATTR_VERSION, '1.0')
382 request.set(ATTR_VERSION, '1.0')
377
383
378 model = et.SubElement(request, TAG_MODEL)
384 model = et.SubElement(request, TAG_MODEL)
379 model.set(ATTR_VERSION, CURRENT_MODEL_VERSION)
385 model.set(ATTR_VERSION, CURRENT_MODEL_VERSION)
380 model.set(ATTR_NAME, 'post')
386 model.set(ATTR_NAME, 'post')
381
387
382 if opening_post:
388 if opening_post:
383 ThreadFilter().add_filter(model, opening_post)
389 ThreadFilter().add_filter(model, opening_post)
384 if tags:
390 if tags:
385 TagsFilter().add_filter(model, tags)
391 TagsFilter().add_filter(model, tags)
386 if timestamp_from:
392 if timestamp_from:
387 TimestampFromFilter().add_filter(model, timestamp_from)
393 TimestampFromFilter().add_filter(model, timestamp_from)
388
394
389 return et.tostring(request, 'unicode')
395 return et.tostring(request, 'unicode')
General Comments 0
You need to be logged in to leave comments. Login now