--- a/Allura/allura/model/filesystem.py
+++ b/Allura/allura/model/filesystem.py
@@ -1,7 +1,10 @@
+from mimetypes import guess_type
from datetime import datetime
+import Image
import pymongo
from gridfs import GridFS
+import tg
from .session import project_doc_session
@@ -13,6 +16,12 @@
from .session import project_orm_session
+SUPPORTED_BY_PIL=set([
+ 'image/jpg',
+ 'image/png',
+ 'image/jpeg',
+ 'image/gif'])
+
class File(MappedClass):
class __mongometa__:
session = project_orm_session
@@ -23,6 +32,8 @@
_id=FieldProperty(schema.ObjectId)
# We don't actually store the filename here, just a randomish hash
filename=FieldProperty(str, if_missing=lambda:str(pymongo.bson.ObjectId()))
+ # There is much weirdness in gridfs; the fp.content_type property is stored
+ # in the contentType field
contentType=FieldProperty(str)
length=FieldProperty(int)
chunkSize=FieldProperty(int)
@@ -75,11 +86,74 @@
fn = str(pymongo.bson.ObjectId())
fp = cls._fs().open(fn, 'w', collection=cls._grid_coll_name())
fp.content_type = content_type
- fp.metadata = dict(meta_kwargs)
+ fp.metadata =dict(meta_kwargs)
return fp
+
+ @classmethod
+ def save_image(cls, filename, fp,
+ content_type=None,
+ thumbnail_size=None,
+ thumbnail_meta=None,
+ square=False,
+ save_original=False,
+ original_meta=None):
+ if content_type is None:
+ content_type = guess_type(filename)
+ if content_type[0]: content_type = content_type[0]
+ else: content_type = 'application/octet-stream'
+ if not content_type.lower() in SUPPORTED_BY_PIL:
+ return None, None
+ thumbnail_meta = thumbnail_meta or {}
+ image = Image.open(fp)
+ format = image.format
+ if save_original:
+ original_meta = original_meta or {}
+ with cls.create(content_type=content_type,
+ filename=filename,
+ **original_meta) as fp_w:
+ filename = fp_w.name
+ if 'transparency' in image.info:
+ image.save(fp_w, format, transparency=image.info['transparency'])
+ else:
+ image.save(fp_w, format)
+ original = cls.query.get(filename=fp_w.name)
+ else:
+ original=None
+ if square:
+ height = image.size[0]
+ width = image.size[1]
+ sz = max(width, height)
+ if 'transparency' in image.info:
+ new_image = Image.new('RGBA', (sz,sz))
+ else:
+ new_image = Image.new('RGB', (sz,sz), 'white')
+ if height < width:
+ # image is wider than tall, so center horizontally
+ new_image.paste(image, ((width-height)/2, 0))
+ elif height > width:
+ # image is taller than wide, so center vertically
+ new_image.paste(image, (0, (height-width)/2))
+ image = new_image
+ if thumbnail_size:
+ image.thumbnail(thumbnail_size, Image.ANTIALIAS)
+ with cls.create(content_type=content_type,
+ filename=filename,
+ **thumbnail_meta) as fp_w:
+ if 'transparency' in image.info:
+ image.save(fp_w, format, transparency=image.info['transparency'])
+ else:
+ image.save(fp_w, format)
+ thumbnail=cls.query.get(filename=fp_w.name)
+ return original, thumbnail
+
+ def is_image(self):
+ if not self.contentType:
+ return False
+ return self.contentType.lower() in SUPPORTED_BY_PIL
def open(self, mode='r'):
return self._fs().open(self.filename, mode, collection=self._grid_coll_name())
def delete(self):
self.remove(self.filename)
+