Switch to unified view

a/Allura/allura/model/filesystem.py b/Allura/allura/model/filesystem.py
1
from mimetypes import guess_type
1
from datetime import datetime
2
from datetime import datetime
2
3
4
import Image
3
import pymongo
5
import pymongo
4
from gridfs import GridFS
6
from gridfs import GridFS
7
import tg
5
8
6
from .session import project_doc_session
9
from .session import project_doc_session
7
10
8
from ming.base import build_mongometa
11
from ming.base import build_mongometa
9
from ming import schema
12
from ming import schema
...
...
11
from ming.orm.property import FieldProperty, ForeignIdProperty, RelationProperty
14
from ming.orm.property import FieldProperty, ForeignIdProperty, RelationProperty
12
from ming.orm.mapped_class import MappedClass, MappedClassMeta
15
from ming.orm.mapped_class import MappedClass, MappedClassMeta
13
16
14
from .session import project_orm_session
17
from .session import project_orm_session
15
18
19
SUPPORTED_BY_PIL=set([
20
        'image/jpg',
21
        'image/png',
22
        'image/jpeg',
23
        'image/gif'])
24
16
class File(MappedClass):
25
class File(MappedClass):
17
    class __mongometa__:
26
    class __mongometa__:
18
        session = project_orm_session
27
        session = project_orm_session
19
        name = 'fs.files' # must always end in '.files'
28
        name = 'fs.files' # must always end in '.files'
20
        indexes = [
29
        indexes = [
21
            'metadata.filename' ] # actual "stored" filename
30
            'metadata.filename' ] # actual "stored" filename
22
31
23
    _id=FieldProperty(schema.ObjectId)
32
    _id=FieldProperty(schema.ObjectId)
24
    # We don't actually store the filename here, just a randomish hash
33
    # We don't actually store the filename here, just a randomish hash
25
    filename=FieldProperty(str, if_missing=lambda:str(pymongo.bson.ObjectId()))
34
    filename=FieldProperty(str, if_missing=lambda:str(pymongo.bson.ObjectId()))
35
    # There is much weirdness in gridfs; the fp.content_type property is stored
36
    # in the contentType field
26
    contentType=FieldProperty(str)
37
    contentType=FieldProperty(str)
27
    length=FieldProperty(int)
38
    length=FieldProperty(int)
28
    chunkSize=FieldProperty(int)
39
    chunkSize=FieldProperty(int)
29
    uploadDate=FieldProperty(datetime)
40
    uploadDate=FieldProperty(datetime)
30
    aliases=FieldProperty([str])
41
    aliases=FieldProperty([str])
...
...
73
    @classmethod
84
    @classmethod
74
    def create(cls, content_type=None, **meta_kwargs):
85
    def create(cls, content_type=None, **meta_kwargs):
75
        fn = str(pymongo.bson.ObjectId())
86
        fn = str(pymongo.bson.ObjectId())
76
        fp = cls._fs().open(fn, 'w', collection=cls._grid_coll_name())
87
        fp = cls._fs().open(fn, 'w', collection=cls._grid_coll_name())
77
        fp.content_type = content_type
88
        fp.content_type = content_type
78
        fp.metadata = dict(meta_kwargs)
89
        fp.metadata =dict(meta_kwargs)
79
        return fp
90
        return fp
91
92
    @classmethod
93
    def save_image(cls, filename, fp,
94
                   content_type=None,
95
                   thumbnail_size=None,
96
                   thumbnail_meta=None,
97
                   square=False,
98
                   save_original=False,
99
                   original_meta=None):
100
        if content_type is None:
101
            content_type = guess_type(filename)
102
            if content_type[0]: content_type = content_type[0]
103
            else: content_type = 'application/octet-stream'
104
        if not content_type.lower() in SUPPORTED_BY_PIL:
105
            return None, None
106
        thumbnail_meta = thumbnail_meta or {}
107
        image = Image.open(fp)
108
        format = image.format
109
        if save_original:
110
            original_meta = original_meta or {}
111
            with cls.create(content_type=content_type,
112
                            filename=filename,
113
                            **original_meta) as fp_w:
114
                filename = fp_w.name
115
                if 'transparency' in image.info:
116
                    image.save(fp_w, format, transparency=image.info['transparency'])
117
                else:
118
                    image.save(fp_w, format)
119
            original = cls.query.get(filename=fp_w.name)
120
        else:
121
            original=None
122
        if square:
123
            height = image.size[0]
124
            width = image.size[1]
125
            sz = max(width, height)
126
            if 'transparency' in image.info:
127
                new_image = Image.new('RGBA', (sz,sz))
128
            else:
129
                new_image = Image.new('RGB', (sz,sz), 'white')
130
            if height < width:
131
                # image is wider than tall, so center horizontally
132
                new_image.paste(image, ((width-height)/2, 0))
133
            elif height > width:
134
                # image is taller than wide, so center vertically
135
                new_image.paste(image, (0, (height-width)/2))
136
            image = new_image
137
        if thumbnail_size:
138
            image.thumbnail(thumbnail_size, Image.ANTIALIAS)
139
        with cls.create(content_type=content_type,
140
                        filename=filename,
141
                        **thumbnail_meta) as fp_w:
142
            if 'transparency' in image.info:
143
                image.save(fp_w, format, transparency=image.info['transparency'])
144
            else:
145
                image.save(fp_w, format)
146
        thumbnail=cls.query.get(filename=fp_w.name)
147
        return original, thumbnail
148
        
149
    def is_image(self):
150
        if not self.contentType:
151
            return False
152
        return self.contentType.lower() in SUPPORTED_BY_PIL
80
153
81
    def open(self, mode='r'):
154
    def open(self, mode='r'):
82
        return self._fs().open(self.filename, mode, collection=self._grid_coll_name())
155
        return self._fs().open(self.filename, mode, collection=self._grid_coll_name())
83
156
84
    def delete(self):
157
    def delete(self):
85
        self.remove(self.filename)
158
        self.remove(self.filename)
159