This was a very small update, I'm hoping to rebase after this to solve some
[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 \
8394febb 38 not request.user.has_privilege(u'active'):
d43b472a
CAW
39 return redirect(
40 request, 'mediagoblin.user_pages.user_home',
5a4e3ff1 41 user=request.user.username)
8394febb 42 elif not request.user or not request.user.has_privilege(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
6bba33d7 75 if UserBan.query.filter(UserBan.user_id==user_id).count():
76 return render_user_banned(request)
8394febb 77 elif not request.user.has_privilege(privilege_name):
9b8ef022 78 raise Forbidden()
79
80 return controller(request, *args, **kwargs)
81
82 return wrapper
3fb96fc9 83 return user_has_privilege_decorator
9b8ef022 84
53c5e0b0 85
502073f2
JW
86def user_may_delete_media(controller):
87 """
53c5e0b0 88 Require user ownership of the MediaEntry to delete.
502073f2 89 """
1e03504e 90 @wraps(controller)
502073f2 91 def wrapper(request, *args, **kwargs):
461dd971 92 uploader_id = kwargs['media'].uploader
8394febb 93 if not (request.user.has_privilege(u'admin') or
5c2b8486 94 request.user.id == uploader_id):
cfa92229 95 raise Forbidden()
502073f2
JW
96
97 return controller(request, *args, **kwargs)
98
1e03504e 99 return wrapper
502073f2 100
3eb6fc4f 101
be5be115
AW
102def user_may_alter_collection(controller):
103 """
104 Require user ownership of the Collection to modify.
105 """
106 @wraps(controller)
107 def wrapper(request, *args, **kwargs):
44082b12
RE
108 creator_id = request.db.User.query.filter_by(
109 username=request.matchdict['user']).first().id
8394febb 110 if not (request.user.has_privilege(u'admin') or
5c2b8486 111 request.user.id == creator_id):
cfa92229 112 raise Forbidden()
be5be115
AW
113
114 return controller(request, *args, **kwargs)
115
116 return wrapper
117
118
3eb6fc4f
BK
119def uses_pagination(controller):
120 """
121 Check request GET 'page' key for wrong values
122 """
1e03504e 123 @wraps(controller)
3eb6fc4f
BK
124 def wrapper(request, *args, **kwargs):
125 try:
1301a8ad 126 page = int(request.GET.get('page', 1))
3eb6fc4f 127 if page < 0:
de12b4e7 128 return render_404(request)
3eb6fc4f 129 except ValueError:
de12b4e7 130 return render_404(request)
3eb6fc4f 131
439e37f7 132 return controller(request, page=page, *args, **kwargs)
3eb6fc4f 133
1e03504e 134 return wrapper
724933b1
CAW
135
136
01674e10 137def get_user_media_entry(controller):
724933b1
CAW
138 """
139 Pass in a MediaEntry based off of a url component
140 """
1e03504e 141 @wraps(controller)
724933b1 142 def wrapper(request, *args, **kwargs):
7f4e42b0 143 user = User.query.filter_by(username=request.matchdict['user']).first()
01674e10 144 if not user:
7f4e42b0
SS
145 raise NotFound()
146
7de20e52
CAW
147 media = None
148
149 # might not be a slug, might be an id, but whatever
150 media_slug = request.matchdict['media']
151
e4e50a27
CAW
152 # if it starts with id: it actually isn't a slug, it's an id.
153 if media_slug.startswith(u'id:'):
154 try:
155 media = MediaEntry.query.filter_by(
156 id=int(media_slug[3:]),
157 state=u'processed',
158 uploader=user.id).first()
159 except ValueError:
160 raise NotFound()
7de20e52
CAW
161 else:
162 # no magical id: stuff? It's a slug!
163 media = MediaEntry.query.filter_by(
697c74c2 164 slug=media_slug,
7de20e52
CAW
165 state=u'processed',
166 uploader=user.id).first()
724933b1 167
724933b1 168 if not media:
7de20e52 169 # Didn't find anything? Okay, 404.
7f4e42b0 170 raise NotFound()
724933b1
CAW
171
172 return controller(request, media=media, *args, **kwargs)
173
1e03504e 174 return wrapper
aba81c9f 175
243c3843 176
be5be115
AW
177def get_user_collection(controller):
178 """
179 Pass in a Collection based off of a url component
180 """
181 @wraps(controller)
182 def wrapper(request, *args, **kwargs):
44082b12
RE
183 user = request.db.User.query.filter_by(
184 username=request.matchdict['user']).first()
be5be115
AW
185
186 if not user:
187 return render_404(request)
188
44082b12
RE
189 collection = request.db.Collection.query.filter_by(
190 slug=request.matchdict['collection'],
191 creator=user.id).first()
be5be115
AW
192
193 # Still no collection? Okay, 404.
194 if not collection:
195 return render_404(request)
196
197 return controller(request, collection=collection, *args, **kwargs)
198
199 return wrapper
200
201
202def get_user_collection_item(controller):
203 """
204 Pass in a CollectionItem based off of a url component
205 """
206 @wraps(controller)
207 def wrapper(request, *args, **kwargs):
44082b12
RE
208 user = request.db.User.query.filter_by(
209 username=request.matchdict['user']).first()
be5be115
AW
210
211 if not user:
212 return render_404(request)
213
44082b12
RE
214 collection_item = request.db.CollectionItem.query.filter_by(
215 id=request.matchdict['collection_item']).first()
be5be115
AW
216
217 # Still no collection item? Okay, 404.
218 if not collection_item:
219 return render_404(request)
220
221 return controller(request, collection_item=collection_item, *args, **kwargs)
222
223 return wrapper
224
225
aba81c9f
E
226def get_media_entry_by_id(controller):
227 """
228 Pass in a MediaEntry based off of a url component
229 """
1e03504e 230 @wraps(controller)
aba81c9f 231 def wrapper(request, *args, **kwargs):
71717fd5 232 media = MediaEntry.query.filter_by(
461dd971 233 id=request.matchdict['media_id'],
71717fd5 234 state=u'processed').first()
aba81c9f
E
235 # Still no media? Okay, 404.
236 if not media:
de12b4e7 237 return render_404(request)
aba81c9f 238
461dd971
E
239 given_username = request.matchdict.get('user')
240 if given_username and (given_username != media.get_uploader.username):
241 return render_404(request)
242
aba81c9f
E
243 return controller(request, media=media, *args, **kwargs)
244
1e03504e 245 return wrapper
f91dcc9d
SS
246
247
5adb906a
RE
248def allow_registration(controller):
249 """ Decorator for if registration is enabled"""
250 @wraps(controller)
251 def wrapper(request, *args, **kwargs):
252 if not mgg.app_config["allow_registration"]:
253 messages.add_message(
254 request,
255 messages.WARNING,
256 _('Sorry, registration is disabled on this instance.'))
257 return redirect(request, "index")
258
259 return controller(request, *args, **kwargs)
260
261 return wrapper
262
30a9fe7c 263def get_media_comment_by_id(controller):
264 """
265 Pass in a MediaComment based off of a url component
266 """
267 @wraps(controller)
268 def wrapper(request, *args, **kwargs):
269 comment = MediaComment.query.filter_by(
270 id=request.matchdict['comment']).first()
271 # Still no media? Okay, 404.
272 if not comment:
273 return render_404(request)
274
275 return controller(request, comment=comment, *args, **kwargs)
276
277 return wrapper
278
279
5adb906a
RE
280def auth_enabled(controller):
281 """Decorator for if an auth plugin is enabled"""
282 @wraps(controller)
283 def wrapper(request, *args, **kwargs):
284 if not mgg.app.auth:
285 messages.add_message(
286 request,
287 messages.WARNING,
288 _('Sorry, authentication is disabled on this instance.'))
289 return redirect(request, 'index')
52a355b2 290 return controller(request, *args, **kwargs)
291
292 return wrapper
30a9fe7c 293
f91dcc9d
SS
294def get_workbench(func):
295 """Decorator, passing in a workbench as kwarg which is cleaned up afterwards"""
f91dcc9d
SS
296 @wraps(func)
297 def new_func(*args, **kwargs):
bd6fe977 298 with mgg.workbench_manager.create() as workbench:
f91dcc9d 299 return func(*args, workbench=workbench, **kwargs)
f91dcc9d 300 return new_func
9b8ef022 301
6bba33d7 302def require_admin_or_moderator_login(controller):
9b8ef022 303 """
6bba33d7 304 Require an login from an administrator or a moderator.
9b8ef022 305 """
306 @wraps(controller)
307 def new_controller_func(request, *args, **kwargs):
308 if request.user and \
8394febb 309 not request.user.has_privilege(u'admin',u'moderator'):
6bba33d7 310
9b8ef022 311 raise Forbidden()
312 elif not request.user:
313 next_url = urljoin(
314 request.urlgen('mediagoblin.auth.login',
315 qualified=True),
316 request.url)
317
318 return redirect(request, 'mediagoblin.auth.login',
319 next=next_url)
320
321 return controller(request, *args, **kwargs)
322
323 return new_controller_func
324
6bba33d7 325def user_not_banned(controller):
326 """
327 Requires that the user has not been banned. Otherwise redirects to the page
328 explaining why they have been banned
329 """
330 @wraps(controller)
331 def wrapper(request, *args, **kwargs):
332 if request.user:
333 user_banned = UserBan.query.get(request.user.id)
334 if user_banned:
335 return render_user_banned(request)
336 return controller(request, *args, **kwargs)
337
338 return wrapper
339