OLD | NEW |
1 # Copyright: 2012 MoinMoin:CheerXiao | 1 # Copyright: 2012 MoinMoin:CheerXiao |
2 # Copyright: 2009 MoinMoin:ThomasWaldmann | 2 # Copyright: 2009 MoinMoin:ThomasWaldmann |
3 # Copyright: 2009-2011 MoinMoin:ReimarBauer | 3 # Copyright: 2009-2011 MoinMoin:ReimarBauer |
4 # Copyright: 2009 MoinMoin:ChristopherDenter | 4 # Copyright: 2009 MoinMoin:ChristopherDenter |
5 # Copyright: 2008,2009 MoinMoin:BastianBlank | 5 # Copyright: 2008,2009 MoinMoin:BastianBlank |
6 # Copyright: 2010 MoinMoin:ValentinJaniaut | 6 # Copyright: 2010 MoinMoin:ValentinJaniaut |
7 # Copyright: 2010 MoinMoin:DiogenesAugusto | 7 # Copyright: 2010 MoinMoin:DiogenesAugusto |
8 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. | 8 # License: GNU GPL v2 (or any later version), see LICENSE.txt for details. |
9 | 9 |
10 """ | 10 """ |
11 MoinMoin - item contents | 11 MoinMoin - item contents |
12 | 12 |
13 Classes handling the content part of items (ie. minus metadata). The | 13 Classes handling the content part of items (ie. minus metadata). The |
14 content part is sometimes called the "data" part in other places, but is | 14 content part is sometimes called the "data" part in other places, but is |
15 always called content in this module to avoid confusion. | 15 always called content in this module to avoid confusion. |
16 | 16 |
17 Each class in this module corresponds to a contenttype value. | 17 Each class in this module corresponds to a contenttype value. |
18 """ | 18 """ |
19 | 19 |
20 import os, re, base64 | 20 import os, re, base64 |
21 import tarfile | 21 import tarfile |
22 import zipfile | 22 import zipfile |
23 import tempfile | |
24 from StringIO import StringIO | 23 from StringIO import StringIO |
25 from array import array | 24 from array import array |
26 from collections import namedtuple | 25 from collections import namedtuple |
27 from operator import attrgetter | 26 from operator import attrgetter |
28 | 27 |
29 from flask import current_app as app | 28 from flask import current_app as app |
30 from flask import g as flaskg | 29 from flask import g as flaskg |
31 from flask import request, url_for, Response, abort, escape | 30 from flask import request, url_for, Response, abort, escape |
32 | 31 |
33 from flatland import Form, String | 32 from flatland import Form, String |
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
404 def get_member(self, name): | 403 def get_member(self, name): |
405 """ | 404 """ |
406 return a file-like object with the member file data | 405 return a file-like object with the member file data |
407 | 406 |
408 :param name: name of the data in the container file | 407 :param name: name of the data in the container file |
409 """ | 408 """ |
410 self.rev.data.seek(0) | 409 self.rev.data.seek(0) |
411 tf = tarfile.open(fileobj=self.rev.data, mode='r') | 410 tf = tarfile.open(fileobj=self.rev.data, mode='r') |
412 return tf.extractfile(name) | 411 return tf.extractfile(name) |
413 | 412 |
414 def put_member(self, name, content, content_length, expected_members): | 413 def put_member(self, tf, name, content, content_length, expected_members): |
415 """ | 414 """ |
416 puts a new member file into a temporary tar container. | 415 puts a new member file into a temporary tar container. |
417 If all expected members have been put, it saves the tar container | 416 If all expected members have been put, it saves the tar container |
418 to a new item revision. | 417 to a new item revision. |
419 | 418 |
420 :param name: name of the data in the container file | 419 :param name: name of the data in the container file |
421 :param content: the data to store into the tar file (str or file-like) | 420 :param content: the data to store into the tar file (str or file-like) |
422 :param content_length: byte-length of content (for str, None can be give
n) | 421 :param content_length: byte-length of content (for str, None can be give
n) |
423 :param expected_members: set of expected member file names | 422 :param expected_members: set of expected member file names |
424 """ | 423 """ |
425 if not name in expected_members: | 424 if not name in expected_members: |
426 raise StorageError("tried to add unexpected member {0!r} to containe
r item {1!r}".format(name, self.name)) | 425 raise StorageError("tried to add unexpected member {0!r} to containe
r item {1!r}".format(name, self.name)) |
427 if isinstance(name, unicode): | 426 if isinstance(name, unicode): |
428 name = name.encode('utf-8') | 427 name = name.encode('utf-8') |
429 temp_fname = os.path.join(tempfile.gettempdir(), 'TarContainer_' + | 428 |
430 cache_key(usage='TarContainer', name=self.name
)) | |
431 tf = tarfile.TarFile(temp_fname, mode='a') | |
432 ti = tarfile.TarInfo(name) | 429 ti = tarfile.TarInfo(name) |
433 if isinstance(content, str): | 430 if isinstance(content, str): |
434 if content_length is None: | 431 if content_length is None: |
435 content_length = len(content) | 432 content_length = len(content) |
436 content = StringIO(content) # we need a file obj | 433 content = StringIO(content) # we need a file obj |
437 elif not hasattr(content, 'read'): | 434 elif not hasattr(content, 'read'): |
438 logging.error("unsupported content object: {0!r}".format(content)) | 435 logging.error("unsupported content object: {0!r}".format(content)) |
439 raise StorageError("unsupported content object: {0!r}".format(conten
t)) | 436 raise StorageError("unsupported content object: {0!r}".format(conten
t)) |
440 assert content_length >= 0 # we don't want -1 interpreted as 4G-1 | 437 assert content_length >= 0 # we don't want -1 interpreted as 4G-1 |
441 ti.size = content_length | 438 ti.size = content_length |
442 tf.addfile(ti, content) | 439 tf.addfile(ti, content) |
443 tf_members = set(tf.getnames()) | 440 tf_members = set(tf.getnames()) |
444 tf.close() | 441 ······ |
445 if tf_members - expected_members: | 442 if tf_members - expected_members: |
446 msg = "found unexpected members in container item {0!r}".format(self
.name) | 443 msg = "found unexpected members in container item {0!r}".format(self
.name) |
447 logging.error(msg) | 444 logging.error(msg) |
448 os.remove(temp_fname) | |
449 raise StorageError(msg) | 445 raise StorageError(msg) |
450 if tf_members == expected_members: | |
451 # everything we expected has been added to the tar file, save the co
ntainer as revision | |
452 meta = {CONTENTTYPE: self.contenttype} | |
453 data = open(temp_fname, 'rb') | |
454 self.item._save(meta, data, name=self.name, action=u'SAVE', comment=
'') | |
455 data.close() | |
456 os.remove(temp_fname) | |
457 | |
458 | 446 |
459 @register | 447 @register |
460 class ApplicationXTar(TarMixin, Application): | 448 class ApplicationXTar(TarMixin, Application): |
461 """ | 449 """ |
462 Tar items | 450 Tar items |
463 """ | 451 """ |
464 contenttype = 'application/x-tar' | 452 contenttype = 'application/x-tar' |
465 display_name = 'TAR' | 453 display_name = 'TAR' |
466 | 454 |
467 | 455 |
(...skipping 688 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1156 display_name = 'SVGDRAW' | 1144 display_name = 'SVGDRAW' |
1157 | 1145 |
1158 class ModifyForm(Draw.ModifyForm): | 1146 class ModifyForm(Draw.ModifyForm): |
1159 template = "modify_svg-edit.html" | 1147 template = "modify_svg-edit.html" |
1160 help = "" | 1148 help = "" |
1161 | 1149 |
1162 def handle_post(self): | 1150 def handle_post(self): |
1163 # called from modify UI/POST | 1151 # called from modify UI/POST |
1164 png_upload = request.values.get('png_data') | 1152 png_upload = request.values.get('png_data') |
1165 svg_upload = request.values.get('filepath') | 1153 svg_upload = request.values.get('filepath') |
1166 filename = request.form['filename'] | |
1167 png_content = png_upload.decode('base_64') | 1154 png_content = png_upload.decode('base_64') |
1168 png_content = base64.urlsafe_b64decode(png_content.split(',')[1]) | 1155 png_content = base64.urlsafe_b64decode(png_content.split(',')[1]) |
1169 svg_content = svg_upload.decode('base_64') | 1156 svg_content = svg_upload.decode('base_64') |
1170 content_length = None | 1157 content_length = None |
1171 self.put_member("drawing.svg", svg_content, content_length, | 1158 # XXX works only for svg-edit currently on gae |
| 1159 tarbuffer = StringIO() |
| 1160 tf = tarfile.TarFile(fileobj=tarbuffer, mode='w') |
| 1161 |
| 1162 self.put_member(tf, "drawing.svg", svg_content, content_length, |
1172 expected_members=set(['drawing.svg', 'drawing.png'])) | 1163 expected_members=set(['drawing.svg', 'drawing.png'])) |
1173 self.put_member("drawing.png", png_content, content_length, | 1164 self.put_member(tf, "drawing.png", png_content, content_length, |
1174 expected_members=set(['drawing.svg', 'drawing.png'])) | 1165 expected_members=set(['drawing.svg', 'drawing.png'])) |
1175 | 1166 |
| 1167 meta = {CONTENTTYPE: self.contenttype} |
| 1168 self.item._save(meta, tarbuffer, name=self.name, action=u'SAVE', comment
='') |
| 1169 tarbuffer.close() |
| 1170 tf.close() |
| 1171 |
1176 def _render_data(self): | 1172 def _render_data(self): |
1177 # TODO: this could be a converter -> dom, then transcluding this kind | 1173 # TODO: this could be a converter -> dom, then transcluding this kind |
1178 # of items and also rendering them with the code in base class could wor
k | 1174 # of items and also rendering them with the code in base class could wor
k |
1179 item_name = self.name | 1175 item_name = self.name |
1180 drawing_url = url_for('frontend.get_item', item_name=item_name, member='
drawing.svg', rev=self.rev.revid) | 1176 drawing_url = url_for('frontend.get_item', item_name=item_name, member='
drawing.svg', rev=self.rev.revid) |
1181 png_url = url_for('frontend.get_item', item_name=item_name, member='draw
ing.png', rev=self.rev.revid) | 1177 png_url = url_for('frontend.get_item', item_name=item_name, member='draw
ing.png', rev=self.rev.revid) |
1182 return Markup(u'<img src="{0}" alt="{1}" />'.format(png_url, drawing_url
)) | 1178 return Markup(u'<img src="{0}" alt="{1}" />'.format(png_url, drawing_url
)) |
OLD | NEW |