Merge remote branch 'remotes/jwandborg/feature_400-resize_images_to_fit_page'
[mediagoblin.git] / mediagoblin / db / models.py
1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011 Free Software Foundation, Inc
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17 import datetime, uuid
18
19 from mongokit import Document
20
21 from mediagoblin import util
22 from mediagoblin.auth import lib as auth_lib
23 from mediagoblin import mg_globals
24 from mediagoblin.db import migrations
25 from mediagoblin.db.util import ASCENDING, DESCENDING, ObjectId
26 from mediagoblin.util import Pagination
27 from mediagoblin.util import DISPLAY_IMAGE_FETCHING_ORDER
28
29
30 ###################
31 # Custom validators
32 ###################
33
34 ########
35 # Models
36 ########
37
38
39 class User(Document):
40 __collection__ = 'users'
41
42 structure = {
43 'username': unicode,
44 'email': unicode,
45 'created': datetime.datetime,
46 'plugin_data': dict, # plugins can dump stuff here.
47 'pw_hash': unicode,
48 'email_verified': bool,
49 'status': unicode,
50 'verification_key': unicode,
51 'is_admin': bool,
52 'url' : unicode,
53 'bio' : unicode, # May contain markdown
54 'bio_html': unicode, # May contain plaintext, or HTML
55 }
56
57 required_fields = ['username', 'created', 'pw_hash', 'email']
58
59 default_values = {
60 'created': datetime.datetime.utcnow,
61 'email_verified': False,
62 'status': u'needs_email_verification',
63 'verification_key': lambda: unicode(uuid.uuid4()),
64 'is_admin': False}
65
66 def check_login(self, password):
67 """
68 See if a user can login with this password
69 """
70 return auth_lib.bcrypt_check_password(
71 password, self['pw_hash'])
72
73
74 class MediaEntry(Document):
75 __collection__ = 'media_entries'
76
77 structure = {
78 'uploader': ObjectId,
79 'title': unicode,
80 'slug': unicode,
81 'created': datetime.datetime,
82 'description': unicode, # May contain markdown/up
83 'description_html': unicode, # May contain plaintext, or HTML
84 'media_type': unicode,
85 'media_data': dict, # extra data relevant to this media_type
86 'plugin_data': dict, # plugins can dump stuff here.
87 'tags': [unicode],
88 'state': unicode,
89
90 # For now let's assume there can only be one main file queued
91 # at a time
92 'queued_media_file': [unicode],
93
94 # A dictionary of logical names to filepaths
95 'media_files': dict,
96
97 # The following should be lists of lists, in appropriate file
98 # record form
99 'attachment_files': list,
100
101 # This one should just be a single file record
102 'thumbnail_file': [unicode]}
103
104 required_fields = [
105 'uploader', 'created', 'media_type', 'slug']
106
107 default_values = {
108 'created': datetime.datetime.utcnow,
109 'state': u'unprocessed'}
110
111 def get_comments(self):
112 return self.db.MediaComment.find({
113 'media_entry': self['_id']}).sort('created', DESCENDING)
114
115 def get_display_media(self, media_map, fetch_order=DISPLAY_IMAGE_FETCHING_ORDER):
116 """
117 Find the best media for display.
118
119 Args:
120 - media_map: a dict like
121 {u'image_size': [u'dir1', u'dir2', u'image.jpg']}
122 - fetch_order: the order we should try fetching images in
123
124 Returns:
125 (media_size, media_path)
126 """
127 media_sizes = media_map.keys()
128
129 for media_size in DISPLAY_IMAGE_FETCHING_ORDER:
130 if media_size in media_sizes:
131 return media_map[media_size]
132
133 def main_mediafile(self):
134 pass
135
136 def generate_slug(self):
137 self['slug'] = util.slugify(self['title'])
138
139 duplicate = mg_globals.database.media_entries.find_one(
140 {'slug': self['slug']})
141
142 if duplicate:
143 self['slug'] = "%s-%s" % (self['_id'], self['slug'])
144
145 def url_for_self(self, urlgen):
146 """
147 Generate an appropriate url for ourselves
148
149 Use a slug if we have one, else use our '_id'.
150 """
151 uploader = self.uploader()
152
153 if self.get('slug'):
154 return urlgen(
155 'mediagoblin.user_pages.media_home',
156 user=uploader['username'],
157 media=self['slug'])
158 else:
159 return urlgen(
160 'mediagoblin.user_pages.media_home',
161 user=uploader['username'],
162 media=unicode(self['_id']))
163
164 def url_to_prev(self, urlgen):
165 """
166 Provide a url to the previous entry from this user, if there is one
167 """
168 cursor = self.db.MediaEntry.find({'_id' : {"$gt": self['_id']},
169 'uploader': self['uploader'],
170 'state': 'processed'}).sort(
171 '_id', ASCENDING).limit(1)
172 if cursor.count():
173 return urlgen('mediagoblin.user_pages.media_home',
174 user=self.uploader()['username'],
175 media=unicode(cursor[0]['slug']))
176
177 def url_to_next(self, urlgen):
178 """
179 Provide a url to the next entry from this user, if there is one
180 """
181 cursor = self.db.MediaEntry.find({'_id' : {"$lt": self['_id']},
182 'uploader': self['uploader'],
183 'state': 'processed'}).sort(
184 '_id', DESCENDING).limit(1)
185
186 if cursor.count():
187 return urlgen('mediagoblin.user_pages.media_home',
188 user=self.uploader()['username'],
189 media=unicode(cursor[0]['slug']))
190
191 def uploader(self):
192 return self.db.User.find_one({'_id': self['uploader']})
193
194
195 class MediaComment(Document):
196 __collection__ = 'media_comments'
197
198 structure = {
199 'media_entry': ObjectId,
200 'author': ObjectId,
201 'created': datetime.datetime,
202 'content': unicode,
203 'content_html': unicode}
204
205 required_fields = [
206 'media_entry', 'author', 'created', 'content']
207
208 default_values = {
209 'created': datetime.datetime.utcnow}
210
211 def media_entry(self):
212 return self.db.MediaEntry.find_one({'_id': self['media_entry']})
213
214 def author(self):
215 return self.db.User.find_one({'_id': self['author']})
216
217
218 REGISTER_MODELS = [
219 MediaEntry,
220 User,
221 MediaComment]
222
223
224 def register_models(connection):
225 """
226 Register all models in REGISTER_MODELS with this connection.
227 """
228 connection.register(REGISTER_MODELS)
229