This commit was the work I did fixing errors that cropped up from the merge.
[mediagoblin.git] / mediagoblin / decorators.py
CommitLineData
bb3eaf20 1# GNU MediaGoblin -- federated, autonomous media hosting
cf29e8a8 2# Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
bb3eaf20
CAW
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
1e03504e 17from functools import wraps
bb3eaf20 18
3a199328 19from urlparse import urljoin
7f4e42b0 20from werkzeug.exceptions import Forbidden, NotFound
059eaee4 21from werkzeug.urls import url_quote
bb3eaf20 22
f91dcc9d 23from mediagoblin import mg_globals as mgg
5adb906a 24from mediagoblin import messages
9d6e453f 25from mediagoblin.db.models import (MediaEntry, User, MediaComment,
26 UserBan, Privilege)
27from mediagoblin.tools.response import redirect, render_404, render_user_banned
5adb906a 28from mediagoblin.tools.translate import pass_to_ugettext as _
bb3eaf20
CAW
29
30
31def require_active_login(controller):
32 """
33 Require an active login from the user.
34 """
1e03504e 35 @wraps(controller)
bb3eaf20 36 def new_controller_func(request, *args, **kwargs):
a72c504b 37 if request.user and \
066d49b2 38 request.user.status == u'needs_email_verification':
d43b472a
CAW
39 return redirect(
40 request, 'mediagoblin.user_pages.user_home',
5a4e3ff1 41 user=request.user.username)
066d49b2 42 elif not request.user or request.user.status != u'active':
3a199328
JW
43 next_url = urljoin(
44 request.urlgen('mediagoblin.auth.login',
45 qualified=True),
46 request.url)
47
059eaee4 48 return redirect(request, 'mediagoblin.auth.login',
56c113c7 49 next=next_url)
bb3eaf20
CAW
50
51 return controller(request, *args, **kwargs)
52
1e03504e 53 return new_controller_func
3eb6fc4f 54
ad742028
SS
55def active_user_from_url(controller):
56 """Retrieve User() from <user> URL pattern and pass in as url_user=...
57
58 Returns a 404 if no such active user has been found"""
59 @wraps(controller)
60 def wrapper(request, *args, **kwargs):
61 user = User.query.filter_by(username=request.matchdict['user']).first()
62 if user is None:
63 return render_404(request)
64
65 return controller(request, *args, url_user=user, **kwargs)
66
67 return wrapper
68
3fb96fc9 69def user_has_privilege(privilege_name):
6bba33d7 70
3fb96fc9 71 def user_has_privilege_decorator(controller):
9b8ef022 72 @wraps(controller)
9b8ef022 73 def wrapper(request, *args, **kwargs):
74 user_id = request.user.id
3fb96fc9 75 privileges_of_user = Privilege.query.filter(
76 Privilege.all_users.any(
77 User.id==user_id))
6bba33d7 78 if UserBan.query.filter(UserBan.user_id==user_id).count():
79 return render_user_banned(request)
80 elif not privileges_of_user.filter(
3fb96fc9 81 Privilege.privilege_name==privilege_name).count():
9b8ef022 82 raise Forbidden()
83
84 return controller(request, *args, **kwargs)
85
86 return wrapper
3fb96fc9 87 return user_has_privilege_decorator
9b8ef022 88
53c5e0b0 89
502073f2
JW
90def user_may_delete_media(controller):
91 """
53c5e0b0 92 Require user ownership of the MediaEntry to delete.
502073f2 93 """
1e03504e 94 @wraps(controller)
502073f2 95 def wrapper(request, *args, **kwargs):
461dd971 96 uploader_id = kwargs['media'].uploader
bec591d8 97 if not (request.user.is_admin or
5c2b8486 98 request.user.id == uploader_id):
cfa92229 99 raise Forbidden()
502073f2
JW
100
101 return controller(request, *args, **kwargs)
102
1e03504e 103 return wrapper
502073f2 104
3eb6fc4f 105
be5be115
AW
106def user_may_alter_collection(controller):
107 """
108 Require user ownership of the Collection to modify.
109 """
110 @wraps(controller)
111 def wrapper(request, *args, **kwargs):
44082b12
RE
112 creator_id = request.db.User.query.filter_by(
113 username=request.matchdict['user']).first().id
be5be115 114 if not (request.user.is_admin or
5c2b8486 115 request.user.id == creator_id):
cfa92229 116 raise Forbidden()
be5be115
AW
117
118 return controller(request, *args, **kwargs)
119
120 return wrapper
121
122
3eb6fc4f
BK
123def uses_pagination(controller):
124 """
125 Check request GET 'page' key for wrong values
126 """
1e03504e 127 @wraps(controller)
3eb6fc4f
BK
128 def wrapper(request, *args, **kwargs):
129 try:
1301a8ad 130 page = int(request.GET.get('page', 1))
3eb6fc4f 131 if page < 0:
de12b4e7 132 return render_404(request)
3eb6fc4f 133 except ValueError:
de12b4e7 134 return render_404(request)
3eb6fc4f 135
439e37f7 136 return controller(request, page=page, *args, **kwargs)
3eb6fc4f 137
1e03504e 138 return wrapper
724933b1
CAW
139
140
01674e10 141def get_user_media_entry(controller):
724933b1
CAW
142 """
143 Pass in a MediaEntry based off of a url component
144 """
1e03504e 145 @wraps(controller)
724933b1 146 def wrapper(request, *args, **kwargs):
7f4e42b0 147 user = User.query.filter_by(username=request.matchdict['user']).first()
01674e10 148 if not user:
7f4e42b0
SS
149 raise NotFound()
150
7de20e52
CAW
151 media = None
152
153 # might not be a slug, might be an id, but whatever
154 media_slug = request.matchdict['media']
155
e4e50a27
CAW
156 # if it starts with id: it actually isn't a slug, it's an id.
157 if media_slug.startswith(u'id:'):
158 try:
159 media = MediaEntry.query.filter_by(
160 id=int(media_slug[3:]),
161 state=u'processed',
162 uploader=user.id).first()
163 except ValueError:
164 raise NotFound()
7de20e52
CAW
165 else:
166 # no magical id: stuff? It's a slug!
167 media = MediaEntry.query.filter_by(
697c74c2 168 slug=media_slug,
7de20e52
CAW
169 state=u'processed',
170 uploader=user.id).first()
724933b1 171
724933b1 172 if not media:
7de20e52 173 # Didn't find anything? Okay, 404.
7f4e42b0 174 raise NotFound()
724933b1
CAW
175
176 return controller(request, media=media, *args, **kwargs)
177
1e03504e 178 return wrapper
aba81c9f 179
243c3843 180
be5be115
AW
181def get_user_collection(controller):
182 """
183 Pass in a Collection based off of a url component
184 """
185 @wraps(controller)
186 def wrapper(request, *args, **kwargs):
44082b12
RE
187 user = request.db.User.query.filter_by(
188 username=request.matchdict['user']).first()
be5be115
AW
189
190 if not user:
191 return render_404(request)
192
44082b12
RE
193 collection = request.db.Collection.query.filter_by(
194 slug=request.matchdict['collection'],
195 creator=user.id).first()
be5be115
AW
196
197 # Still no collection? Okay, 404.
198 if not collection:
199 return render_404(request)
200
201 return controller(request, collection=collection, *args, **kwargs)
202
203 return wrapper
204
205
206def get_user_collection_item(controller):
207 """
208 Pass in a CollectionItem based off of a url component
209 """
210 @wraps(controller)
211 def wrapper(request, *args, **kwargs):
44082b12
RE
212 user = request.db.User.query.filter_by(
213 username=request.matchdict['user']).first()
be5be115
AW
214
215 if not user:
216 return render_404(request)
217
44082b12
RE
218 collection_item = request.db.CollectionItem.query.filter_by(
219 id=request.matchdict['collection_item']).first()
be5be115
AW
220
221 # Still no collection item? Okay, 404.
222 if not collection_item:
223 return render_404(request)
224
225 return controller(request, collection_item=collection_item, *args, **kwargs)
226
227 return wrapper
228
229
aba81c9f
E
230def get_media_entry_by_id(controller):
231 """
232 Pass in a MediaEntry based off of a url component
233 """
1e03504e 234 @wraps(controller)
aba81c9f 235 def wrapper(request, *args, **kwargs):
71717fd5 236 media = MediaEntry.query.filter_by(
461dd971 237 id=request.matchdict['media_id'],
71717fd5 238 state=u'processed').first()
aba81c9f
E
239 # Still no media? Okay, 404.
240 if not media:
de12b4e7 241 return render_404(request)
aba81c9f 242
461dd971
E
243 given_username = request.matchdict.get('user')
244 if given_username and (given_username != media.get_uploader.username):
245 return render_404(request)
246
aba81c9f
E
247 return controller(request, media=media, *args, **kwargs)
248
1e03504e 249 return wrapper
f91dcc9d
SS
250
251
5adb906a
RE
252def allow_registration(controller):
253 """ Decorator for if registration is enabled"""
254 @wraps(controller)
255 def wrapper(request, *args, **kwargs):
256 if not mgg.app_config["allow_registration"]:
257 messages.add_message(
258 request,
259 messages.WARNING,
260 _('Sorry, registration is disabled on this instance.'))
261 return redirect(request, "index")
262
263 return controller(request, *args, **kwargs)
264
265 return wrapper
266
30a9fe7c 267def get_media_comment_by_id(controller):
268 """
269 Pass in a MediaComment based off of a url component
270 """
271 @wraps(controller)
272 def wrapper(request, *args, **kwargs):
273 comment = MediaComment.query.filter_by(
274 id=request.matchdict['comment']).first()
275 # Still no media? Okay, 404.
276 if not comment:
277 return render_404(request)
278
279 return controller(request, comment=comment, *args, **kwargs)
280
281 return wrapper
282
283
5adb906a
RE
284def auth_enabled(controller):
285 """Decorator for if an auth plugin is enabled"""
286 @wraps(controller)
287 def wrapper(request, *args, **kwargs):
288 if not mgg.app.auth:
289 messages.add_message(
290 request,
291 messages.WARNING,
292 _('Sorry, authentication is disabled on this instance.'))
293 return redirect(request, 'index')
52a355b2 294 return controller(request, *args, **kwargs)
295
296 return wrapper
30a9fe7c 297
f91dcc9d
SS
298def get_workbench(func):
299 """Decorator, passing in a workbench as kwarg which is cleaned up afterwards"""
f91dcc9d
SS
300 @wraps(func)
301 def new_func(*args, **kwargs):
bd6fe977 302 with mgg.workbench_manager.create() as workbench:
f91dcc9d 303 return func(*args, workbench=workbench, **kwargs)
f91dcc9d 304 return new_func
9b8ef022 305
6bba33d7 306def require_admin_or_moderator_login(controller):
9b8ef022 307 """
6bba33d7 308 Require an login from an administrator or a moderator.
9b8ef022 309 """
310 @wraps(controller)
311 def new_controller_func(request, *args, **kwargs):
9d6e453f 312 admin_privilege = Privilege.query.filter(
313 Privilege.privilege_name==u'admin').one()
314 moderator_privilege = Privilege.query.filter(
315 Privilege.privilege_name==u'moderator').one()
9b8ef022 316 if request.user and \
6bba33d7 317 not admin_privilege in request.user.all_privileges and \
318 not moderator_privilege in request.user.all_privileges:
319
9b8ef022 320 raise Forbidden()
321 elif not request.user:
322 next_url = urljoin(
323 request.urlgen('mediagoblin.auth.login',
324 qualified=True),
325 request.url)
326
327 return redirect(request, 'mediagoblin.auth.login',
328 next=next_url)
329
330 return controller(request, *args, **kwargs)
331
332 return new_controller_func
333
6bba33d7 334def user_not_banned(controller):
335 """
336 Requires that the user has not been banned. Otherwise redirects to the page
337 explaining why they have been banned
338 """
339 @wraps(controller)
340 def wrapper(request, *args, **kwargs):
341 if request.user:
342 user_banned = UserBan.query.get(request.user.id)
343 if user_banned:
344 return render_user_banned(request)
345 return controller(request, *args, **kwargs)
346
347 return wrapper
348