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.

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