Le repo des sources pour le site web des JM2L
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

601 line
25 KiB

  1. # -*- coding: utf8 -*-
  2. from pyramid.view import view_config, view_defaults
  3. from pyramid.response import Response
  4. from pyramid.exceptions import NotFound
  5. from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest
  6. from PIL import Image
  7. import re, os, shutil
  8. from os import path
  9. import mimetypes
  10. import magic
  11. import subprocess
  12. try:
  13. from StringIO import StringIO
  14. except ImportError:
  15. from io import StringIO
  16. # Database access imports
  17. from .models import User, Place, Tiers, Event, SallePhy
  18. from .blenderthumbnailer import blend_extract_thumb, write_png
  19. from jm2l.const import CurrentYear
  20. from slugify import slugify
  21. MIN_FILE_SIZE = 1 # bytes
  22. MAX_FILE_SIZE = 500000000 # bytes
  23. IMAGE_TYPES = re.compile('image/(gif|p?jpeg|(x-)?png)')
  24. ACCEPTED_MIMES = ['application/pdf',
  25. 'application/vnd.oasis.opendocument.text',
  26. 'application/vnd.oasis.opendocument.text-template',
  27. 'application/vnd.oasis.opendocument.graphics',
  28. 'application/vnd.oasis.opendocument.graphics-template',
  29. 'application/vnd.oasis.opendocument.presentation',
  30. 'application/vnd.oasis.opendocument.presentation-template',
  31. 'application/vnd.oasis.opendocument.spreadsheet',
  32. 'application/vnd.oasis.opendocument.spreadsheet-template',
  33. 'image/svg+xml',
  34. 'application/x-blender'
  35. ]
  36. ACCEPT_FILE_TYPES = IMAGE_TYPES
  37. THUMBNAIL_SIZE = 80
  38. EXPIRATION_TIME = 300 # seconds
  39. IMAGEPATH = ['images']
  40. DOCPATH = ['document']
  41. THUMBNAILPATH = ['images', 'thumbnails']
  42. # change the following to POST if DELETE isn't supported by the webserver
  43. DELETEMETHOD = "DELETE"
  44. mimetypes.init()
  45. class MediaPath():
  46. def get_all(self, media_table, linked_id, MediaType=None):
  47. filelist = list()
  48. curpath = self.get_mediapath(media_table, linked_id, None)
  49. thumbpath = os.path.join(curpath, 'thumbnails')
  50. if not os.path.isdir(curpath) or not os.path.isdir(thumbpath):
  51. return list()
  52. for f in os.listdir(curpath):
  53. filename, ext = os.path.splitext(f)
  54. if os.path.isdir(os.path.join(curpath, f)):
  55. continue
  56. if f.endswith('.type'):
  57. continue
  58. if f:
  59. ress_url = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ", "%20"))
  60. thumb_url = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ", "%20"))
  61. if MediaType is None:
  62. if os.path.exists(os.path.join(thumbpath, f + ".jpg")):
  63. filelist.append((ress_url, thumb_url + ".jpg"))
  64. else:
  65. filelist.append((ress_url, thumb_url))
  66. elif MediaType == 'Image' and len(os.path.splitext(filename)[1]) == 0:
  67. filelist.append((ress_url, thumb_url))
  68. elif MediaType == 'Other' and len(os.path.splitext(filename)[1]):
  69. filelist.append((ress_url, thumb_url))
  70. return filelist
  71. def get_list(self, media_table, linked_id, MediaType=None):
  72. filelist = list()
  73. curpath = self.get_mediapath(media_table, linked_id, None)
  74. if not os.path.isdir(curpath):
  75. return list()
  76. for f in os.listdir(curpath):
  77. if os.path.isdir(os.path.join(curpath, f)):
  78. continue
  79. if f.endswith('.type'):
  80. continue
  81. if f:
  82. filename, ext = os.path.splitext(f)
  83. tmpurl = '/image/%s/%d/%s' % (media_table, linked_id, f.replace(" ", "%20"))
  84. if MediaType is None:
  85. filelist.append(tmpurl)
  86. elif MediaType == 'Image' and ext.lower() in ['.gif', '.jpg', '.png', '.svg', '.jpeg']:
  87. filelist.append(tmpurl)
  88. elif MediaType == 'Other' and ext.lower() not in ['.gif', '.jpg', '.png', '.svg', '.jpeg']:
  89. filelist.append(tmpurl)
  90. return filelist
  91. def get_thumb(self, media_table, linked_id, MediaType=None):
  92. filelist = list()
  93. curpath = self.get_mediapath(media_table, linked_id, None)
  94. curpath = os.path.join(curpath, 'thumbnails')
  95. if not os.path.isdir(curpath):
  96. return list()
  97. for f in os.listdir(curpath):
  98. filename, ext = os.path.splitext(f)
  99. if os.path.isdir(os.path.join(curpath, f)):
  100. continue
  101. if f.endswith('.type'):
  102. continue
  103. if f:
  104. tmpurl = '/image/%s/%d/thumbnails/%s' % (media_table, linked_id, f.replace(" ", "%20"))
  105. if MediaType is None:
  106. filelist.append(tmpurl)
  107. elif MediaType == 'Image' and len(os.path.splitext(filename)[1]) == 0:
  108. filelist.append(tmpurl)
  109. elif MediaType == 'Other' and len(os.path.splitext(filename)[1]):
  110. filelist.append(tmpurl)
  111. return filelist
  112. def move_mediapath(self, media_table, from_id, to_id):
  113. """
  114. Move target media folder to follow database
  115. :param media_table: type of media
  116. :param from_id: source
  117. :param to_id: destination
  118. :return: Error if any
  119. """
  120. if media_table in ['tiers', 'place', 'salle', 'users']:
  121. src = IMAGEPATH + [media_table, from_id]
  122. dst = IMAGEPATH + [media_table, to_id]
  123. else:
  124. raise RuntimeError("Sorry, Media '%s' not supported yet for move." % media_table)
  125. src_path = os.path.join('jm2l/upload', *src)
  126. dst_path = os.path.join('jm2l/upload', *dst)
  127. if not os.path.isdir(src_path):
  128. # Nothing to do ...
  129. return False
  130. if os.path.isdir(dst_path):
  131. raise RuntimeError('Destination path already exist')
  132. shutil.move(src_path, dst_path)
  133. return True
  134. def get_mediapath(self, media_table, linked_id, name):
  135. """
  136. :param media_table: type of media
  137. :param linked_id: id of media
  138. :param name: filename
  139. :return: full relative path on server side
  140. """
  141. linked_id = str(linked_id)
  142. if media_table in ['tiers', 'place', 'salle']:
  143. # Retrieve Slug
  144. if media_table == 'tiers':
  145. slug = Tiers.by_id(linked_id).slug
  146. if media_table == 'place':
  147. slug = Place.by_id(linked_id).slug or slugify(Place.by_id(linked_id).name)
  148. if media_table == 'salle':
  149. slug = SallePhy.by_id(linked_id).slug
  150. p = IMAGEPATH + [media_table, slug]
  151. elif media_table == 'presse':
  152. # Use Year in linked_id
  153. p = IMAGEPATH + [media_table, linked_id]
  154. elif media_table == 'tasks':
  155. # Use Current Year
  156. p = IMAGEPATH + [str(CurrentYear), media_table, linked_id]
  157. elif media_table == 'poles':
  158. # Use Current Year
  159. p = IMAGEPATH + [str(CurrentYear), media_table, linked_id]
  160. elif media_table in ['RIB', 'Justif']:
  161. slug = User.by_id(linked_id).slug
  162. p = IMAGEPATH + ['users', slug, media_table]
  163. elif media_table in ['users', 'badge']:
  164. user = User.by_id(linked_id)
  165. if not user:
  166. raise HTTPNotFound()
  167. else:
  168. slug = user.slug
  169. p = IMAGEPATH + [media_table, slug]
  170. elif media_table == 'event':
  171. ev = Event.by_id(linked_id)
  172. slug = ev.slug
  173. year = ev.for_year
  174. p = IMAGEPATH + ['event', str(year), slug]
  175. if name:
  176. p += [name]
  177. TargetPath = os.path.join('jm2l/upload', *p)
  178. if not os.path.isdir(os.path.dirname(TargetPath)):
  179. try:
  180. os.makedirs(os.path.dirname(TargetPath))
  181. except OSError as e:
  182. if e.errno != 17:
  183. raise e
  184. return os.path.join('jm2l/upload', *p)
  185. def ExtMimeIcon(self, mime):
  186. if mime == 'application/pdf':
  187. return "/img/PDF.png"
  188. def check_blend_file(self, fileobj):
  189. head = fileobj.read(12)
  190. fileobj.seek(0)
  191. if head[:2] == b'\x1f\x8b': # gzip magic
  192. import zlib
  193. head = zlib.decompress(fileobj.read(), 31)[:12]
  194. fileobj.seek(0)
  195. if head.startswith(b'BLENDER'):
  196. return True
  197. def get_mimetype_from_file(self, fileobj):
  198. mimetype = magic.from_buffer(fileobj.read(1024), mime=True)
  199. fileobj.seek(0)
  200. # Check if the binary file is a blender file
  201. if (mimetype == "application/octet-stream" or mimetype == "application/x-gzip") and self.check_blend_file(
  202. fileobj):
  203. return "application/x-blender", True
  204. else:
  205. return mimetype, False
  206. def get_mimetype(self, name):
  207. """ This function return the mime-type based on .type file """
  208. try:
  209. with open(self.mediapath(name) + '.type', 'r', 16) as f:
  210. mime = f.read()
  211. return mime
  212. except IOError:
  213. return None
  214. @view_defaults(route_name='media_upload')
  215. class MediaUpload(MediaPath):
  216. def __init__(self, request):
  217. self.request = request
  218. self.media_table = self.request.matchdict.get('media_table')
  219. self.display_only = False
  220. if self.media_table.startswith('_'):
  221. self.display_only = True
  222. self.media_table = self.media_table[1:]
  223. self.linked_id = self.request.matchdict.get('uid')
  224. if not self.linked_id.isdigit():
  225. raise HTTPBadRequest('Wrong Parameter')
  226. request.response.headers['Access-Control-Allow-Origin'] = '*'
  227. request.response.headers['Access-Control-Allow-Methods'] = 'OPTIONS, HEAD, GET, POST, PUT, DELETE'
  228. def mediapath(self, name):
  229. return self.get_mediapath(self.media_table, self.linked_id, name)
  230. def validate(self, result, filecontent):
  231. """ Do some basic check to the uploaded file before to accept it """
  232. # Try to determine mime type from content uploaded
  233. found_mime = magic.from_buffer(filecontent.read(1024), mime=True)
  234. filecontent.seek(0)
  235. # Do a special statement for specific detected mime type
  236. if found_mime in ["application/octet-stream", "application/x-gzip"]:
  237. # Lets see if it's a bender file
  238. if self.check_blend_file(filecontent):
  239. found_mime = "application/x-blender"
  240. # MonKey Patch of content type
  241. result['type'] = found_mime
  242. # Reject mime type that don't match
  243. if found_mime != result['type']:
  244. result['error'] = 'L\'extension du fichier ne correspond pas à son contenu - '
  245. result['error'] += "( %s vs %s )" % (found_mime, result['type'])
  246. return False
  247. # Accept images and mime types listed
  248. if not found_mime in ACCEPTED_MIMES:
  249. if not (IMAGE_TYPES.match(found_mime)):
  250. result['error'] = 'Ce type fichier n\'est malheureusement pas supporté. '
  251. return False
  252. if result['size'] < MIN_FILE_SIZE:
  253. result['error'] = 'le fichier est trop petit'
  254. elif result['size'] > MAX_FILE_SIZE:
  255. result['error'] = 'le fichier est trop voluminueux'
  256. # elif not ACCEPT_FILE_TYPES.match(file['type']):
  257. # file['error'] = u'les type de fichiers acceptés sont png, jpg et gif'
  258. else:
  259. return True
  260. return False
  261. def get_file_size(self, fileobj):
  262. fileobj.seek(0, 2) # Seek to the end of the file
  263. size = fileobj.tell() # Get the position of EOF
  264. fileobj.seek(0) # Reset the file position to the beginning
  265. return size
  266. def thumbnailurl(self, name):
  267. return self.request.route_url('media_view', name='thumbnails',
  268. media_table=self.media_table,
  269. uid=self.linked_id) + '/' + name
  270. def thumbnailpath(self, name):
  271. origin = self.mediapath(name)
  272. TargetPath = os.path.join(os.path.dirname(origin), 'thumbnails', name)
  273. if not os.path.isdir(os.path.dirname(TargetPath)):
  274. os.makedirs(os.path.dirname(TargetPath))
  275. return TargetPath
  276. def createthumbnail(self, filename):
  277. image = Image.open(self.mediapath(filename))
  278. image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
  279. timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
  280. timage.paste(
  281. image,
  282. (int((THUMBNAIL_SIZE - image.size[0]) / 2), int((THUMBNAIL_SIZE - image.size[1]) / 2)))
  283. TargetFileName = self.thumbnailpath(filename)
  284. if not TargetFileName.endswith('png'):
  285. timage = timage.convert('RGB')
  286. timage.save(TargetFileName)
  287. return self.thumbnailurl(os.path.basename(TargetFileName))
  288. def pdfthumbnail(self, filename):
  289. TargetFileName = self.thumbnailpath(filename)
  290. Command = ["convert", "./%s[0]" % self.mediapath(filename), "./%s_.jpg" % TargetFileName]
  291. Result = subprocess.call(Command)
  292. if Result == 0:
  293. image = Image.open(TargetFileName + "_.jpg")
  294. pdf_indicator = Image.open("jm2l/static/img/PDF_Thumb_Stamp.png")
  295. image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
  296. timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
  297. # Add thumbnail
  298. timage.paste(
  299. image,
  300. (int((THUMBNAIL_SIZE - image.size[0]) / 2), int((THUMBNAIL_SIZE - image.size[1]) / 2)))
  301. # Stamp with PDF file type
  302. timage.paste(
  303. pdf_indicator,
  304. (timage.size[0] - 30, timage.size[1] - 30),
  305. pdf_indicator,
  306. )
  307. timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
  308. os.unlink(TargetFileName + "_.jpg")
  309. return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))
  310. return self.ExtMimeIcon('application/pdf')
  311. def svgthumbnail(self, filename):
  312. TargetFileName = self.thumbnailpath(filename)
  313. Command = ["convert", "./%s[0]" % self.mediapath(filename), "./%s_.jpg" % TargetFileName]
  314. Result = subprocess.call(Command)
  315. if Result == 0:
  316. image = Image.open(TargetFileName + "_.jpg")
  317. pdf_indicator = Image.open("jm2l/static/img/svg-icon.png")
  318. image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
  319. timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
  320. # Add thumbnail
  321. timage.paste(
  322. image,
  323. (int((THUMBNAIL_SIZE - image.size[0]) / 2), int((THUMBNAIL_SIZE - image.size[1]) / 2)))
  324. # Stamp with PDF file type
  325. timage.paste(
  326. pdf_indicator,
  327. (timage.size[0] - 30, timage.size[1] - 30),
  328. pdf_indicator,
  329. )
  330. timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
  331. os.unlink(TargetFileName + "_.jpg")
  332. return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))
  333. return self.ExtMimeIcon('image/svg+xml')
  334. def docthumbnail(self, filename):
  335. TargetFileName = self.thumbnailpath(filename)
  336. # let's take the thumbnail generated inside the document
  337. Command = ["unzip", "-p", self.mediapath(filename), "Thumbnails/thumbnail.png"]
  338. ThumbBytes = subprocess.check_output(Command)
  339. image = Image.open(StringIO.StringIO(ThumbBytes))
  340. image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
  341. # Use the correct stamp
  342. f, ext = os.path.splitext(filename)
  343. istamp = [('Writer', 'odt'),
  344. ('Impress', 'odp'),
  345. ('Calc', 'ods'),
  346. ('Draw', 'odg')]
  347. stampfilename = filter(lambda x, y: ext.endswith(y), istamp)
  348. stamp = Image.open("jm2l/static/img/%s-icon.png" % stampfilename[0][0])
  349. timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
  350. # Add thumbnail
  351. timage.paste(
  352. image,
  353. (int((THUMBNAIL_SIZE - image.size[0]) / 2), int((THUMBNAIL_SIZE - image.size[1]) / 2)))
  354. # Stamp with PDF file type
  355. timage.paste(
  356. stamp,
  357. (timage.size[0] - 30, timage.size[1] - 30),
  358. stamp,
  359. )
  360. timage.convert('RGB').save(TargetFileName + ".jpg", 'JPEG')
  361. return self.thumbnailurl(os.path.basename(TargetFileName + ".jpg"))
  362. def blendthumbnail(self, filename):
  363. blendfile = self.mediapath(filename)
  364. # Extract Thumb
  365. if 0:
  366. head = fileobj.read(12)
  367. fileobj.seek(0)
  368. if head[:2] == b'\x1f\x8b': # gzip magic
  369. import zlib
  370. head = zlib.decompress(fileobj.read(), 31)[:12]
  371. fileobj.seek(0)
  372. buf, width, height = blend_extract_thumb(blendfile)
  373. if buf:
  374. png = write_png(buf, width, height)
  375. TargetFileName = self.thumbnailpath(filename)
  376. image = Image.open(StringIO.StringIO(png))
  377. blender_indicator = Image.open("jm2l/static/img/Blender_Thumb_Stamp.png")
  378. image.thumbnail((THUMBNAIL_SIZE, THUMBNAIL_SIZE), Image.ANTIALIAS)
  379. timage = Image.new('RGBA', (THUMBNAIL_SIZE, THUMBNAIL_SIZE), (255, 255, 255, 0))
  380. # Add thumbnail
  381. timage.paste(
  382. image,
  383. (int((THUMBNAIL_SIZE - image.size[0]) / 2), int((THUMBNAIL_SIZE - image.size[1]) / 2)))
  384. # Stamp with Blender file type
  385. timage.paste(
  386. blender_indicator,
  387. (timage.size[0] - 30, timage.size[1] - 30),
  388. blender_indicator,
  389. )
  390. timage.save(TargetFileName + ".png")
  391. return self.thumbnailurl(os.path.basename(TargetFileName + ".png"))
  392. return self.ExtMimeIcon('application/x-blender')
  393. def fileinfo(self, name):
  394. filename = self.mediapath(name)
  395. f, ext = os.path.splitext(name)
  396. if ext != '.type' and os.path.isfile(filename):
  397. info = {}
  398. info['name'] = name
  399. info['size'] = os.path.getsize(filename)
  400. info['url'] = self.request.route_url('media_view',
  401. name=name,
  402. media_table=self.media_table,
  403. uid=self.linked_id)
  404. mime = self.get_mimetype(name)
  405. if IMAGE_TYPES.match(mime):
  406. info['thumbnailUrl'] = self.thumbnailurl(name)
  407. elif mime in ACCEPTED_MIMES:
  408. thumb = self.thumbnailpath("%s%s" % (f, ext))
  409. thumbext = ".jpg"
  410. if mime == "application/x-blender":
  411. thumbext = ".png"
  412. if os.path.exists(thumb + thumbext):
  413. info['thumbnailUrl'] = self.thumbnailurl(name) + thumbext
  414. else:
  415. info['thumbnailUrl'] = self.ExtMimeIcon(mime)
  416. else:
  417. info['thumbnailUrl'] = self.ExtMimeIcon(mime)
  418. if not self.display_only:
  419. info['deleteType'] = DELETEMETHOD
  420. info['deleteUrl'] = self.request.route_url('media_upload',
  421. sep='',
  422. name='',
  423. media_table=self.media_table,
  424. uid=self.linked_id) + '/' + name
  425. if DELETEMETHOD != 'DELETE':
  426. info['deleteUrl'] += '&_method=DELETE'
  427. return info
  428. else:
  429. return None
  430. @view_config(request_method='OPTIONS')
  431. def options(self):
  432. return Response(body='')
  433. @view_config(request_method='HEAD')
  434. def options(self):
  435. return Response(body='')
  436. @view_config(request_method='GET', renderer="json")
  437. def get(self):
  438. p = self.request.matchdict.get('name')
  439. if p:
  440. return self.fileinfo(p)
  441. else:
  442. filelist = []
  443. content = self.mediapath('')
  444. if content and path.exists(content):
  445. for f in os.listdir(content):
  446. n = self.fileinfo(f)
  447. if n:
  448. filelist.append(n)
  449. return {"files": filelist}
  450. @view_config(request_method='DELETE', xhr=True, accept="application/json", renderer='json')
  451. def delete(self):
  452. filename = self.request.matchdict.get('name')
  453. try:
  454. os.remove(self.mediapath(filename) + '.type')
  455. except IOError:
  456. pass
  457. except OSError:
  458. pass
  459. try:
  460. os.remove(self.thumbnailpath(filename))
  461. except IOError:
  462. pass
  463. except OSError:
  464. pass
  465. try:
  466. os.remove(self.thumbnailpath(filename + ".jpg"))
  467. except IOError:
  468. pass
  469. except OSError:
  470. pass
  471. try:
  472. os.remove(self.mediapath(filename))
  473. except IOError:
  474. return False
  475. return True
  476. @view_config(request_method='POST', xhr=True, accept="application/json", renderer='json')
  477. def post(self):
  478. if self.request.matchdict.get('_method') == "DELETE":
  479. return self.delete()
  480. results = []
  481. for name, fieldStorage in self.request.POST.items():
  482. # if isinstance(fieldStorage, unicode):
  483. # continue
  484. result = {}
  485. result['name'] = os.path.basename(fieldStorage.filename)
  486. result['type'] = fieldStorage.type
  487. result['size'] = self.get_file_size(fieldStorage.file)
  488. if self.validate(result, fieldStorage.file):
  489. filename, file_extension = os.path.splitext(result['name'])
  490. local_filename = slugify(filename) + file_extension
  491. # Keep mime-type in .type file
  492. with open(self.mediapath(local_filename) + '.type', 'w') as f:
  493. f.write(result['type'])
  494. # Store uploaded file
  495. fieldStorage.file.seek(0)
  496. with open(self.mediapath(local_filename), 'wb') as f:
  497. shutil.copyfileobj(fieldStorage.file, f)
  498. if re.match(IMAGE_TYPES, result['type']):
  499. result['thumbnailUrl'] = self.createthumbnail(local_filename)
  500. elif result['type'] == 'application/pdf':
  501. result['thumbnailUrl'] = self.pdfthumbnail(local_filename)
  502. elif result['type'] == 'image/svg+xml':
  503. result['thumbnailUrl'] = self.svgthumbnail(local_filename)
  504. elif result['type'].startswith('application/vnd'):
  505. result['thumbnailUrl'] = self.docthumbnail(local_filename)
  506. elif result['type'] == 'application/x-blender':
  507. result['thumbnailUrl'] = self.blendthumbnail(local_filename)
  508. else:
  509. result['thumbnailUrl'] = self.ExtMimeIcon(result['type'])
  510. result['deleteType'] = DELETEMETHOD
  511. result['deleteUrl'] = self.request.route_url('media_upload',
  512. sep='',
  513. name='',
  514. media_table=self.media_table,
  515. uid=self.linked_id) + '/' + local_filename
  516. result['url'] = self.request.route_url('media_view',
  517. media_table=self.media_table,
  518. uid=self.linked_id,
  519. name=local_filename)
  520. if DELETEMETHOD != 'DELETE':
  521. result['deleteUrl'] += '&_method=DELETE'
  522. results.append(result)
  523. return {"files": results}
  524. @view_defaults(route_name='media_view')
  525. class MediaView(MediaPath):
  526. def __init__(self, request):
  527. self.request = request
  528. self.media_table = self.request.matchdict.get('media_table')
  529. self.linked_id = self.request.matchdict.get('uid')
  530. def mediapath(self, name):
  531. return self.get_mediapath(self.media_table, self.linked_id, name)
  532. @view_config(request_method='GET', http_cache=(EXPIRATION_TIME, {'public': True}))
  533. def get(self):
  534. name = self.request.matchdict.get('name')
  535. self.request.response.content_type = self.get_mimetype(name)
  536. try:
  537. self.request.response.body_file = open(self.mediapath(name), 'rb', 10000)
  538. except IOError:
  539. raise NotFound
  540. return self.request.response
  541. ##return Response(app_iter=ImgHandle, content_type = 'image/png')