Commit | Line | Data |
---|---|---|
7f4ebeed | 1 | # GNU MediaGoblin -- federated, autonomous media hosting |
cf29e8a8 | 2 | # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS. |
9a16e16f SS |
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 | ||
fb2fbe2c | 17 | import logging |
64712915 | 18 | import datetime |
5ab60299 | 19 | import json |
52359e91 | 20 | |
e49b7e02 BP |
21 | import six |
22 | ||
3a8c3a38 | 23 | from mediagoblin import messages, mg_globals |
64a456a4 JT |
24 | from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, Comment, |
25 | CollectionItem, LocalUser, Activity, \ | |
26 | GenericModelReference) | |
2a01c5e7 | 27 | from mediagoblin.plugins.api.tools import get_media_file_paths |
2e6ee596 E |
28 | from mediagoblin.tools.response import render_to_response, render_404, \ |
29 | redirect, redirect_obj | |
5ab60299 | 30 | from mediagoblin.tools.text import cleaned_markdown_conversion |
a789b713 | 31 | from mediagoblin.tools.translate import pass_to_ugettext as _ |
152a3bfa | 32 | from mediagoblin.tools.pagination import Pagination |
b9492011 | 33 | from mediagoblin.tools.federation import create_activity |
9074ee7c | 34 | from mediagoblin.user_pages import forms as user_forms |
52a355b2 | 35 | from mediagoblin.user_pages.lib import (send_comment_email, |
9d6e453f | 36 | add_media_to_collection, build_report_object) |
2d7b6bde JW |
37 | from mediagoblin.notifications import trigger_notification, \ |
38 | add_comment_subscription, mark_comment_notification_seen | |
2edd6b0b | 39 | from mediagoblin.tools.pluginapi import hook_transform |
f6249408 | 40 | |
50854db0 | 41 | from mediagoblin.decorators import (uses_pagination, get_user_media_entry, |
8e91df87 | 42 | get_media_entry_by_id, user_has_privilege, user_not_banned, |
6d1e55b2 | 43 | require_active_login, user_may_delete_media, user_may_alter_collection, |
30a9fe7c | 44 | get_user_collection, get_user_collection_item, active_user_from_url, |
6483b370 | 45 | get_optional_media_comment_by_id, allow_reporting) |
9a16e16f | 46 | |
00c39256 | 47 | from werkzeug.contrib.atom import AtomFeed |
2d7b6bde | 48 | from werkzeug.exceptions import MethodNotAllowed |
5ab60299 | 49 | from werkzeug.wrappers import Response |
1301a8ad | 50 | |
9074ee7c | 51 | |
fb2fbe2c JAN |
52 | _log = logging.getLogger(__name__) |
53 | _log.setLevel(logging.DEBUG) | |
54 | ||
6bba33d7 | 55 | @user_not_banned |
3eb6fc4f | 56 | @uses_pagination |
1301a8ad | 57 | def user_home(request, page): |
d88fcb03 JT |
58 | """'Homepage' of a LocalUser()""" |
59 | user = LocalUser.query.filter_by(username=request.matchdict['user']).first() | |
7acdbfd3 | 60 | if not user: |
de12b4e7 | 61 | return render_404(request) |
25625107 | 62 | elif not user.has_privilege(u'active'): |
990d3b69 CAW |
63 | return render_to_response( |
64 | request, | |
51b43180 | 65 | 'mediagoblin/user_pages/user_nonactive.html', |
990d3b69 | 66 | {'user': user}) |
9a16e16f | 67 | |
2fb36dac | 68 | cursor = MediaEntry.query.\ |
336508bb | 69 | filter_by(actor = user.id).order_by(MediaEntry.created.desc()) |
9a16e16f | 70 | |
1301a8ad | 71 | pagination = Pagination(page, cursor) |
ca3ca51c | 72 | media_entries = pagination() |
44e3e917 | 73 | |
336508bb | 74 | # if no data is available, return NotFound |
ae85ed0f | 75 | if media_entries == None: |
de12b4e7 | 76 | return render_404(request) |
243c3843 | 77 | |
5949be9a CAW |
78 | user_gallery_url = request.urlgen( |
79 | 'mediagoblin.user_pages.user_gallery', | |
5a4e3ff1 | 80 | user=user.username) |
5949be9a | 81 | |
9038c9f9 CAW |
82 | return render_to_response( |
83 | request, | |
c9c24934 E |
84 | 'mediagoblin/user_pages/user.html', |
85 | {'user': user, | |
5949be9a | 86 | 'user_gallery_url': user_gallery_url, |
c9c24934 E |
87 | 'media_entries': media_entries, |
88 | 'pagination': pagination}) | |
f6249408 | 89 | |
6bba33d7 | 90 | @user_not_banned |
64c2a400 | 91 | @active_user_from_url |
184f2240 | 92 | @uses_pagination |
64c2a400 | 93 | def user_gallery(request, page, url_user=None): |
d88fcb03 | 94 | """'Gallery' of a LocalUser()""" |
f2c0bf3e | 95 | tag = request.matchdict.get('tag', None) |
64c2a400 | 96 | cursor = MediaEntry.query.filter_by( |
0f3bf8d4 | 97 | actor=url_user.id, |
64c2a400 | 98 | state=u'processed').order_by(MediaEntry.created.desc()) |
af008743 | 99 | |
f2c0bf3e SS |
100 | # Filter potentially by tag too: |
101 | if tag: | |
102 | cursor = cursor.filter( | |
103 | MediaEntry.tags_helper.any( | |
38905733 | 104 | MediaTag.slug == request.matchdict['tag'])) |
f2c0bf3e | 105 | |
64c2a400 | 106 | # Paginate gallery |
184f2240 | 107 | pagination = Pagination(page, cursor) |
108 | media_entries = pagination() | |
109 | ||
110 | #if no data is available, return NotFound | |
64c2a400 | 111 | # TODO: Should we really also return 404 for empty galleries? |
184f2240 | 112 | if media_entries == None: |
de12b4e7 | 113 | return render_404(request) |
243c3843 | 114 | |
4b5f5a08 | 115 | return render_to_response( |
116 | request, | |
117 | 'mediagoblin/user_pages/gallery.html', | |
f2c0bf3e | 118 | {'user': url_user, 'tag': tag, |
4b5f5a08 | 119 | 'media_entries': media_entries, |
120 | 'pagination': pagination}) | |
184f2240 | 121 | |
2d7b6bde | 122 | |
6f59a3a3 | 123 | MEDIA_COMMENTS_PER_PAGE = 50 |
434b3221 | 124 | |
6bba33d7 | 125 | @user_not_banned |
01674e10 | 126 | @get_user_media_entry |
9074ee7c | 127 | @uses_pagination |
6f59a3a3 | 128 | def media_home(request, media, page, **kwargs): |
9074ee7c JW |
129 | """ |
130 | 'Homepage' of a MediaEntry() | |
131 | """ | |
7d16a01b E |
132 | comment_id = request.matchdict.get('comment', None) |
133 | if comment_id: | |
2d7b6bde JW |
134 | if request.user: |
135 | mark_comment_notification_seen(comment_id, request.user) | |
136 | ||
af2fcba5 | 137 | pagination = Pagination( |
7c378f2c CAW |
138 | page, media.get_comments( |
139 | mg_globals.app_config['comments_ascending']), | |
140 | MEDIA_COMMENTS_PER_PAGE, | |
7d16a01b | 141 | comment_id) |
af2fcba5 JW |
142 | else: |
143 | pagination = Pagination( | |
7c378f2c CAW |
144 | page, media.get_comments( |
145 | mg_globals.app_config['comments_ascending']), | |
146 | MEDIA_COMMENTS_PER_PAGE) | |
9074ee7c | 147 | |
6f59a3a3 | 148 | comments = pagination() |
9074ee7c | 149 | |
111a609d | 150 | comment_form = user_forms.MediaCommentForm(request.form) |
9074ee7c | 151 | |
58a94757 | 152 | media_template_name = media.media_manager.display_template |
93bdab9d | 153 | |
2edd6b0b CAW |
154 | context = { |
155 | 'media': media, | |
156 | 'comments': comments, | |
157 | 'pagination': pagination, | |
158 | 'comment_form': comment_form, | |
159 | 'app_config': mg_globals.app_config} | |
160 | ||
161 | # Since the media template name gets swapped out for each media | |
162 | # type, normal context hooks don't work if you want to affect all | |
163 | # media displays. This gives a general purpose hook. | |
164 | context = hook_transform( | |
165 | "media_home_context", context) | |
166 | ||
9038c9f9 CAW |
167 | return render_to_response( |
168 | request, | |
93bdab9d | 169 | media_template_name, |
2edd6b0b | 170 | context) |
9074ee7c | 171 | |
95e6da02 | 172 | |
461dd971 | 173 | @get_media_entry_by_id |
dfd66b78 | 174 | @user_has_privilege(u'commenter') |
95e12bf2 | 175 | def media_post_comment(request, media): |
9074ee7c JW |
176 | """ |
177 | recieves POST from a MediaEntry() comment form, saves the comment. | |
178 | """ | |
2d7b6bde JW |
179 | if not request.method == 'POST': |
180 | raise MethodNotAllowed() | |
95e12bf2 | 181 | |
64a456a4 | 182 | comment = request.db.TextComment() |
0f3bf8d4 | 183 | comment.actor = request.user.id |
e49b7e02 | 184 | comment.content = six.text_type(request.form['comment_content']) |
9074ee7c | 185 | |
99338b6a DT |
186 | # Show error message if commenting is disabled. |
187 | if not mg_globals.app_config['allow_comments']: | |
188 | messages.add_message( | |
189 | request, | |
190 | messages.ERROR, | |
191 | _("Sorry, comments are disabled.")) | |
192 | elif not comment.content.strip(): | |
7298ffa1 AW |
193 | messages.add_message( |
194 | request, | |
195 | messages.ERROR, | |
eae7d058 | 196 | _("Oops, your comment was empty.")) |
7298ffa1 | 197 | else: |
0f3bf8d4 | 198 | create_activity("post", comment, comment.actor, target=media) |
bc2c06a1 | 199 | add_comment_subscription(request.user, media) |
7298ffa1 | 200 | comment.save() |
b5d3aec6 | 201 | |
64a456a4 JT |
202 | link = request.db.Comment() |
203 | link.target = media | |
204 | link.comment = comment | |
205 | link.save() | |
206 | ||
7298ffa1 | 207 | messages.add_message( |
5c7b2a63 AB |
208 | request, |
209 | messages.SUCCESS, | |
eae7d058 | 210 | _('Your comment has been posted!')) |
6efcab2d | 211 | trigger_notification(link, media, request) |
252eaf21 | 212 | |
2e6ee596 | 213 | return redirect_obj(request, media) |
00c39256 | 214 | |
95e6da02 | 215 | |
5ab60299 EL |
216 | |
217 | def media_preview_comment(request): | |
3bd62dc4 | 218 | """Runs a comment through markdown so it can be previewed.""" |
202d951c RE |
219 | # If this isn't an ajax request, render_404 |
220 | if not request.is_xhr: | |
221 | return render_404(request) | |
222 | ||
e49b7e02 | 223 | comment = six.text_type(request.form['comment_content']) |
3bd62dc4 | 224 | cleancomment = { "content":cleaned_markdown_conversion(comment)} |
5ab60299 | 225 | |
3bd62dc4 | 226 | return Response(json.dumps(cleancomment)) |
5ab60299 | 227 | |
6bba33d7 | 228 | @user_not_banned |
96a2249b | 229 | @get_media_entry_by_id |
be5be115 AW |
230 | @require_active_login |
231 | def media_collect(request, media): | |
f6bc0336 | 232 | """Add media to collection submission""" |
be5be115 | 233 | |
111a609d | 234 | form = user_forms.MediaCollectForm(request.form) |
f6bc0336 | 235 | # A user's own collections: |
2fb36dac | 236 | form.collection.query = Collection.query.filter_by( |
0f3bf8d4 JT |
237 | actor=request.user.id, |
238 | type=Collection.USER_DEFINED_TYPE | |
239 | ).order_by(Collection.title) | |
f6bc0336 SS |
240 | |
241 | if request.method != 'POST' or not form.validate(): | |
242 | # No POST submission, or invalid form | |
243 | if not form.validate(): | |
5c7b2a63 AB |
244 | messages.add_message( |
245 | request, | |
246 | messages.ERROR, | |
f6bc0336 | 247 | _('Please check your entries and try again.')) |
be5be115 | 248 | |
f6bc0336 SS |
249 | return render_to_response( |
250 | request, | |
251 | 'mediagoblin/user_pages/media_collect.html', | |
252 | {'media': media, | |
253 | 'form': form}) | |
254 | ||
255 | # If we are here, method=POST and the form is valid, submit things. | |
256 | # If the user is adding a new collection, use that: | |
c5d341d7 | 257 | if form.collection_title.data: |
f6bc0336 SS |
258 | # Make sure this user isn't duplicating an existing collection |
259 | existing_collection = Collection.query.filter_by( | |
0f3bf8d4 JT |
260 | actor=request.user.id, |
261 | title=form.collection_title.data, | |
262 | type=Collection.USER_DEFINED_TYPE | |
263 | ).first() | |
f6bc0336 | 264 | if existing_collection: |
5c7b2a63 AB |
265 | messages.add_message( |
266 | request, | |
267 | messages.ERROR, | |
268 | _('You already have a collection called "%s"!') % | |
269 | existing_collection.title) | |
be5be115 | 270 | return redirect(request, "mediagoblin.user_pages.media_home", |
0f3bf8d4 | 271 | user=media.get_actor.username, |
b7a3798e | 272 | media=media.slug_or_id) |
be5be115 | 273 | |
f6bc0336 | 274 | collection = Collection() |
2263a4cb HL |
275 | collection.title = form.collection_title.data |
276 | collection.description = form.collection_description.data | |
0f3bf8d4 JT |
277 | collection.actor = request.user.id |
278 | collection.type = Collection.USER_DEFINED_TYPE | |
f6bc0336 | 279 | collection.generate_slug() |
d216d771 | 280 | collection.get_public_id(request.urlgen) |
0f3bf8d4 | 281 | create_activity("create", collection, collection.actor) |
bc2c06a1 | 282 | collection.save() |
be5be115 | 283 | |
f6bc0336 SS |
284 | # Otherwise, use the collection selected from the drop-down |
285 | else: | |
e9330b95 | 286 | collection = form.collection.data |
0f3bf8d4 | 287 | if collection and collection.actor != request.user.id: |
e9330b95 | 288 | collection = None |
be5be115 | 289 | |
f6bc0336 | 290 | # Make sure the user actually selected a collection |
0f3bf8d4 JT |
291 | item = CollectionItem.query.filter_by(collection=collection.id) |
292 | item = item.join(CollectionItem.object_helper).filter_by( | |
293 | model_type=media.__tablename__, | |
294 | obj_pk=media.id | |
295 | ).first() | |
296 | ||
f6bc0336 SS |
297 | if not collection: |
298 | messages.add_message( | |
5c7b2a63 AB |
299 | request, |
300 | messages.ERROR, | |
f6bc0336 | 301 | _('You have to select or add a collection')) |
ba5ea989 | 302 | return redirect(request, "mediagoblin.user_pages.media_collect", |
0f3bf8d4 | 303 | user=media.get_actor.username, |
17e4679d | 304 | media_id=media.id) |
ba5ea989 | 305 | |
f6bc0336 | 306 | # Check whether media already exists in collection |
0f3bf8d4 | 307 | elif item is not None: |
5c7b2a63 AB |
308 | messages.add_message( |
309 | request, | |
310 | messages.ERROR, | |
311 | _('"%s" already in collection "%s"') % | |
312 | (media.title, collection.title)) | |
f6bc0336 | 313 | else: # Add item to collection |
6bea8a90 | 314 | add_media_to_collection(collection, media, form.note.data) |
6d36f75f | 315 | create_activity("add", media, request.user, target=collection) |
5c7b2a63 AB |
316 | messages.add_message( |
317 | request, | |
318 | messages.SUCCESS, | |
319 | _('"%s" added to collection "%s"') % | |
320 | (media.title, collection.title)) | |
f6bc0336 | 321 | |
2e6ee596 | 322 | return redirect_obj(request, media) |
f6bc0336 SS |
323 | |
324 | ||
325 | #TODO: Why does @user_may_delete_media not implicate @require_active_login? | |
686cbcd9 | 326 | @get_media_entry_by_id |
d0ba136f | 327 | @require_active_login |
686cbcd9 SS |
328 | @user_may_delete_media |
329 | def media_confirm_delete(request, media): | |
330 | ||
111a609d | 331 | form = user_forms.ConfirmDeleteForm(request.form) |
502073f2 JW |
332 | |
333 | if request.method == 'POST' and form.validate(): | |
8daef28d | 334 | if form.confirm.data is True: |
0f3bf8d4 | 335 | username = media.get_actor.username |
bdd22421 | 336 | |
d216d771 JT |
337 | # This probably is already filled but just in case it has slipped |
338 | # through the net somehow, we need to try and make sure the | |
339 | # MediaEntry has a public ID so it gets properly soft-deleted. | |
340 | media.get_public_id(request.urlgen) | |
341 | ||
342 | # Decrement the users uploaded quota. | |
0f3bf8d4 | 343 | media.get_actor.uploaded = media.get_actor.uploaded - \ |
0b95003c | 344 | media.file_size |
0f3bf8d4 | 345 | media.get_actor.save() |
bdd22421 | 346 | |
fdc34b8b | 347 | # Delete MediaEntry and all related files, comments etc. |
502073f2 | 348 | media.delete() |
ea33f636 | 349 | messages.add_message( |
5c7b2a63 AB |
350 | request, |
351 | messages.SUCCESS, | |
352 | _('You deleted the media.')) | |
502073f2 | 353 | |
e7b8059f AL |
354 | location = media.url_to_next(request.urlgen) |
355 | if not location: | |
356 | location=media.url_to_prev(request.urlgen) | |
357 | if not location: | |
bd0b5daa RE |
358 | location=request.urlgen("mediagoblin.user_pages.user_home", |
359 | user=username) | |
360 | return redirect(request, location=location) | |
502073f2 | 361 | else: |
d0ba62e2 | 362 | messages.add_message( |
5c7b2a63 AB |
363 | request, |
364 | messages.ERROR, | |
365 | _("The media was not deleted because you didn't check " | |
366 | "that you were sure.")) | |
2e6ee596 | 367 | return redirect_obj(request, media) |
502073f2 | 368 | |
8394febb | 369 | if ((request.user.has_privilege(u'admin') and |
0f3bf8d4 | 370 | request.user.id != media.actor)): |
7a4c0126 | 371 | messages.add_message( |
5c7b2a63 AB |
372 | request, |
373 | messages.WARNING, | |
7a4c0126 CAW |
374 | _("You are about to delete another user's media. " |
375 | "Proceed with caution.")) | |
376 | ||
502073f2 JW |
377 | return render_to_response( |
378 | request, | |
379 | 'mediagoblin/user_pages/media_confirm_delete.html', | |
380 | {'media': media, | |
381 | 'form': form}) | |
382 | ||
6bba33d7 | 383 | @user_not_banned |
e2ae0f59 | 384 | @active_user_from_url |
be5be115 | 385 | @uses_pagination |
e2ae0f59 | 386 | def user_collection(request, page, url_user=None): |
be5be115 | 387 | """A User-defined Collection""" |
e2ae0f59 | 388 | collection = Collection.query.filter_by( |
0f3bf8d4 | 389 | get_actor=url_user, |
e2ae0f59 | 390 | slug=request.matchdict['collection']).first() |
be5be115 | 391 | |
61e39d90 JW |
392 | if not collection: |
393 | return render_404(request) | |
394 | ||
e2ae0f59 | 395 | cursor = collection.get_collection_items() |
be5be115 AW |
396 | |
397 | pagination = Pagination(page, cursor) | |
398 | collection_items = pagination() | |
399 | ||
e2ae0f59 SS |
400 | # if no data is available, return NotFound |
401 | # TODO: Should an empty collection really also return 404? | |
be5be115 AW |
402 | if collection_items == None: |
403 | return render_404(request) | |
404 | ||
405 | return render_to_response( | |
406 | request, | |
407 | 'mediagoblin/user_pages/collection.html', | |
e2ae0f59 | 408 | {'user': url_user, |
be5be115 AW |
409 | 'collection': collection, |
410 | 'collection_items': collection_items, | |
411 | 'pagination': pagination}) | |
412 | ||
6bba33d7 | 413 | @user_not_banned |
b0cc1ade | 414 | @active_user_from_url |
4f8f0353 | 415 | def collection_list(request, url_user=None): |
b0cc1ade SZ |
416 | """A User-defined Collection""" |
417 | collections = Collection.query.filter_by( | |
0f3bf8d4 | 418 | get_actor=url_user) |
b0cc1ade | 419 | |
b0cc1ade SZ |
420 | return render_to_response( |
421 | request, | |
4f8f0353 | 422 | 'mediagoblin/user_pages/collection_list.html', |
b0cc1ade | 423 | {'user': url_user, |
947f38c0 | 424 | 'collections': collections}) |
b0cc1ade SZ |
425 | |
426 | ||
be5be115 AW |
427 | @get_user_collection_item |
428 | @require_active_login | |
429 | @user_may_alter_collection | |
430 | def collection_item_confirm_remove(request, collection_item): | |
431 | ||
111a609d | 432 | form = user_forms.ConfirmCollectionItemRemoveForm(request.form) |
be5be115 AW |
433 | |
434 | if request.method == 'POST' and form.validate(): | |
0f3bf8d4 | 435 | username = collection_item.in_collection.get_actor.username |
be5be115 AW |
436 | collection = collection_item.in_collection |
437 | ||
438 | if form.confirm.data is True: | |
0f3bf8d4 JT |
439 | obj = collection_item.get_object() |
440 | obj.save() | |
be5be115 AW |
441 | |
442 | collection_item.delete() | |
0f3bf8d4 | 443 | collection.num_items = collection.num_items - 1 |
be5be115 AW |
444 | collection.save() |
445 | ||
446 | messages.add_message( | |
5c7b2a63 AB |
447 | request, |
448 | messages.SUCCESS, | |
449 | _('You deleted the item from the collection.')) | |
be5be115 AW |
450 | else: |
451 | messages.add_message( | |
5c7b2a63 AB |
452 | request, |
453 | messages.ERROR, | |
454 | _("The item was not removed because you didn't check " | |
455 | "that you were sure.")) | |
be5be115 | 456 | |
2e6ee596 | 457 | return redirect_obj(request, collection) |
be5be115 | 458 | |
8394febb | 459 | if ((request.user.has_privilege(u'admin') and |
0f3bf8d4 | 460 | request.user.id != collection_item.in_collection.actor)): |
be5be115 | 461 | messages.add_message( |
5c7b2a63 AB |
462 | request, |
463 | messages.WARNING, | |
be5be115 AW |
464 | _("You are about to delete an item from another user's collection. " |
465 | "Proceed with caution.")) | |
466 | ||
467 | return render_to_response( | |
468 | request, | |
469 | 'mediagoblin/user_pages/collection_item_confirm_remove.html', | |
470 | {'collection_item': collection_item, | |
471 | 'form': form}) | |
472 | ||
473 | ||
474 | @get_user_collection | |
475 | @require_active_login | |
476 | @user_may_alter_collection | |
477 | def collection_confirm_delete(request, collection): | |
478 | ||
111a609d | 479 | form = user_forms.ConfirmDeleteForm(request.form) |
be5be115 AW |
480 | |
481 | if request.method == 'POST' and form.validate(): | |
482 | ||
0f3bf8d4 | 483 | username = collection.get_actor.username |
be5be115 AW |
484 | |
485 | if form.confirm.data is True: | |
486 | collection_title = collection.title | |
487 | ||
d216d771 JT |
488 | # Firstly like with the MediaEntry delete, lets ensure the |
489 | # public_id is populated as this is really important! | |
490 | collection.get_public_id(request.urlgen) | |
491 | ||
be5be115 AW |
492 | # Delete all the associated collection items |
493 | for item in collection.get_collection_items(): | |
0f3bf8d4 JT |
494 | obj = item.get_object() |
495 | obj.save() | |
be5be115 AW |
496 | item.delete() |
497 | ||
498 | collection.delete() | |
5c7b2a63 AB |
499 | messages.add_message( |
500 | request, | |
501 | messages.SUCCESS, | |
502 | _('You deleted the collection "%s"') % | |
503 | collection_title) | |
be5be115 AW |
504 | |
505 | return redirect(request, "mediagoblin.user_pages.user_home", | |
506 | user=username) | |
507 | else: | |
508 | messages.add_message( | |
5c7b2a63 AB |
509 | request, |
510 | messages.ERROR, | |
511 | _("The collection was not deleted because you didn't " | |
512 | "check that you were sure.")) | |
be5be115 | 513 | |
2e6ee596 | 514 | return redirect_obj(request, collection) |
be5be115 | 515 | |
8394febb | 516 | if ((request.user.has_privilege(u'admin') and |
0f3bf8d4 | 517 | request.user.id != collection.actor)): |
be5be115 AW |
518 | messages.add_message( |
519 | request, messages.WARNING, | |
520 | _("You are about to delete another user's collection. " | |
521 | "Proceed with caution.")) | |
522 | ||
523 | return render_to_response( | |
524 | request, | |
525 | 'mediagoblin/user_pages/collection_confirm_delete.html', | |
526 | {'collection': collection, | |
527 | 'form': form}) | |
528 | ||
529 | ||
a5303e47 | 530 | ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 15 |
00c39256 | 531 | |
243c3843 | 532 | |
00c39256 BK |
533 | def atom_feed(request): |
534 | """ | |
535 | generates the atom feed with the newest images | |
536 | """ | |
d88fcb03 | 537 | user = LocalUser.query.filter_by( |
25625107 | 538 | username = request.matchdict['user']).first() |
539 | if not user or not user.has_privilege(u'active'): | |
de12b4e7 | 540 | return render_404(request) |
2a01c5e7 AB |
541 | feed_title = "MediaGoblin Feed for user '%s'" % request.matchdict['user'] |
542 | link = request.urlgen('mediagoblin.user_pages.user_home', | |
543 | qualified=True, user=request.matchdict['user']) | |
544 | cursor = MediaEntry.query.filter_by(actor=user.id, state=u'processed') | |
545 | cursor = cursor.order_by(MediaEntry.created.desc()) | |
546 | cursor = cursor.limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS) | |
00c39256 | 547 | |
00c39256 | 548 | |
1df68a35 MA |
549 | """ |
550 | ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI) | |
551 | """ | |
5b1a7bae | 552 | atomlinks = [{ |
2a01c5e7 AB |
553 | 'href': link, |
554 | 'rel': 'alternate', | |
555 | 'type': 'text/html'}] | |
64712915 | 556 | |
bb025ebd MA |
557 | if mg_globals.app_config["push_urls"]: |
558 | for push_url in mg_globals.app_config["push_urls"]: | |
559 | atomlinks.append({ | |
560 | 'rel': 'hub', | |
561 | 'href': push_url}) | |
5b1a7bae | 562 | |
1df68a35 | 563 | feed = AtomFeed( |
2a01c5e7 AB |
564 | feed_title, |
565 | feed_url=request.url, | |
566 | id='tag:{host},{year}:gallery.user-{user}'.format( | |
567 | host=request.host, | |
568 | year=datetime.datetime.today().strftime('%Y'), | |
569 | user=request.matchdict['user']), | |
570 | links=atomlinks) | |
5b1a7bae | 571 | |
00c39256 | 572 | for entry in cursor: |
2a01c5e7 AB |
573 | # Include a thumbnail image in content. |
574 | file_urls = get_media_file_paths(entry.media_files, request.urlgen) | |
575 | if 'thumb' in file_urls: | |
741c25fd | 576 | content = u'<img src="{thumb}" alt='' /> {desc}'.format( |
2a01c5e7 AB |
577 | thumb=file_urls['thumb'], desc=entry.description_html) |
578 | else: | |
579 | content = entry.description_html | |
580 | ||
0f3bf8d4 JT |
581 | feed.add( |
582 | entry.get('title'), | |
2a01c5e7 | 583 | content, |
64712915 | 584 | id=entry.url_for_self(request.urlgen, qualified=True), |
00c39256 | 585 | content_type='html', |
1df68a35 | 586 | author={ |
0f3bf8d4 | 587 | 'name': entry.get_actor.username, |
1df68a35 MA |
588 | 'uri': request.urlgen( |
589 | 'mediagoblin.user_pages.user_home', | |
2a01c5e7 AB |
590 | qualified=True, |
591 | user=entry.get_actor.username)}, | |
00c39256 | 592 | updated=entry.get('created'), |
1df68a35 MA |
593 | links=[{ |
594 | 'href': entry.url_for_self( | |
595 | request.urlgen, | |
be5be115 AW |
596 | qualified=True), |
597 | 'rel': 'alternate', | |
598 | 'type': 'text/html'}]) | |
599 | ||
600 | return feed.get_response() | |
601 | ||
6d1e55b2 | 602 | |
be5be115 AW |
603 | def collection_atom_feed(request): |
604 | """ | |
605 | generates the atom feed with the newest images from a collection | |
606 | """ | |
d88fcb03 | 607 | user = LocalUser.query.filter_by( |
25625107 | 608 | username = request.matchdict['user']).first() |
609 | if not user or not user.has_privilege(u'active'): | |
be5be115 AW |
610 | return render_404(request) |
611 | ||
af008743 | 612 | collection = Collection.query.filter_by( |
0f3bf8d4 | 613 | actor=user.id, |
af008743 | 614 | slug=request.matchdict['collection']).first() |
61e39d90 JW |
615 | if not collection: |
616 | return render_404(request) | |
be5be115 | 617 | |
af008743 SS |
618 | cursor = CollectionItem.query.filter_by( |
619 | collection=collection.id) \ | |
20be9bb7 | 620 | .order_by(CollectionItem.added.desc()) \ |
be5be115 AW |
621 | .limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS) |
622 | ||
623 | """ | |
624 | ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI) | |
625 | """ | |
626 | atomlinks = [{ | |
fc45b386 | 627 | 'href': collection.url_for_self(request.urlgen, qualified=True), |
be5be115 AW |
628 | 'rel': 'alternate', |
629 | 'type': 'text/html' | |
630 | }] | |
631 | ||
632 | if mg_globals.app_config["push_urls"]: | |
633 | for push_url in mg_globals.app_config["push_urls"]: | |
634 | atomlinks.append({ | |
635 | 'rel': 'hub', | |
636 | 'href': push_url}) | |
637 | ||
638 | feed = AtomFeed( | |
19ad2e0c JW |
639 | "MediaGoblin: Feed for %s's collection %s" % |
640 | (request.matchdict['user'], collection.title), | |
641 | feed_url=request.url, | |
ab9b0b41 | 642 | id=u'tag:{host},{year}:gnu-mediagoblin.{user}.collection.{slug}'\ |
19ad2e0c JW |
643 | .format( |
644 | host=request.host, | |
645 | year=collection.created.strftime('%Y'), | |
646 | user=request.matchdict['user'], | |
647 | slug=collection.slug), | |
648 | links=atomlinks) | |
be5be115 AW |
649 | |
650 | for item in cursor: | |
0f3bf8d4 JT |
651 | obj = item.get_object() |
652 | feed.add( | |
653 | obj.get('title'), | |
be5be115 | 654 | item.note_html, |
0f3bf8d4 | 655 | id=obj.url_for_self(request.urlgen, qualified=True), |
be5be115 AW |
656 | content_type='html', |
657 | author={ | |
47f7ff8d | 658 | 'name': obj.get_actor.username, |
be5be115 AW |
659 | 'uri': request.urlgen( |
660 | 'mediagoblin.user_pages.user_home', | |
47f7ff8d | 661 | qualified=True, user=obj.get_actor.username)}, |
be5be115 AW |
662 | updated=item.get('added'), |
663 | links=[{ | |
0f3bf8d4 | 664 | 'href': obj.url_for_self( |
be5be115 | 665 | request.urlgen, |
1df68a35 MA |
666 | qualified=True), |
667 | 'rel': 'alternate', | |
668 | 'type': 'text/html'}]) | |
00c39256 | 669 | |
9074ee7c | 670 | return feed.get_response() |
01c75c7e | 671 | |
75972f0a BB |
672 | @active_user_from_url |
673 | @uses_pagination | |
01c75c7e | 674 | @require_active_login |
75972f0a | 675 | def processing_panel(request, page, url_user): |
01c75c7e CAW |
676 | """ |
677 | Show to the user what media is still in conversion/processing... | |
678 | and what failed, and why! | |
679 | """ | |
d88fcb03 | 680 | user = LocalUser.query.filter_by(username=request.matchdict['user']).first() |
af008743 | 681 | # TODO: XXX: Should this be a decorator? |
01c75c7e CAW |
682 | # |
683 | # Make sure we have permission to access this user's panel. Only | |
684 | # admins and this user herself should be able to do so. | |
8394febb | 685 | if not (user.id == request.user.id or request.user.has_privilege(u'admin')): |
af008743 | 686 | # No? Simply redirect to this user's homepage. |
01c75c7e CAW |
687 | return redirect( |
688 | request, 'mediagoblin.user_pages.user_home', | |
af008743 | 689 | user=user.username) |
01c75c7e | 690 | # Get media entries which are in-processing |
db6cc0c0 | 691 | entries = (MediaEntry.query.filter_by(actor=user.id) |
9ab3c66c | 692 | .order_by(MediaEntry.created.desc())) |
64712915 | 693 | |
906a00b6 BB |
694 | try: |
695 | state = request.matchdict['state'] | |
696 | # no exception was thrown, filter entries by state | |
697 | entries = entries.filter_by(state=state) | |
698 | except KeyError: | |
699 | # show all entries | |
700 | pass | |
701 | ||
75972f0a BB |
702 | pagination = Pagination(page, entries) |
703 | pagination.per_page = 30 | |
704 | entries_on_a_page = pagination() | |
705 | ||
01c75c7e CAW |
706 | # Render to response |
707 | return render_to_response( | |
708 | request, | |
709 | 'mediagoblin/user_pages/processing_panel.html', | |
710 | {'user': user, | |
75972f0a BB |
711 | 'entries': entries_on_a_page, |
712 | 'pagination': pagination}) | |
30a9fe7c | 713 | |
6483b370 | 714 | @allow_reporting |
30a9fe7c | 715 | @get_user_media_entry |
3fb96fc9 | 716 | @user_has_privilege(u'reporter') |
8e91df87 | 717 | @get_optional_media_comment_by_id |
718 | def file_a_report(request, media, comment): | |
719 | """ | |
64a456a4 | 720 | This view handles the filing of a Report. |
8e91df87 | 721 | """ |
30a9fe7c | 722 | if comment is not None: |
64a456a4 | 723 | if not comment.target().id == media.id: |
8e91df87 | 724 | return render_404(request) |
725 | ||
f26c21cd | 726 | form = user_forms.CommentReportForm(request.form) |
64a456a4 | 727 | context = {'media': comment.target(), |
b1d4973c | 728 | 'comment':comment, |
f26c21cd | 729 | 'form':form} |
30a9fe7c | 730 | else: |
f26c21cd | 731 | form = user_forms.MediaReportForm(request.form) |
f26c21cd | 732 | context = {'media': media, |
733 | 'form':form} | |
8e91df87 | 734 | form.reporter_id.data = request.user.id |
735 | ||
f26c21cd | 736 | |
737 | if request.method == "POST": | |
64a456a4 JT |
738 | report_object = build_report_object( |
739 | form, | |
dfd66b78 | 740 | media_entry=media, |
64a456a4 JT |
741 | comment=comment |
742 | ) | |
f26c21cd | 743 | |
744 | # if the object was built successfully, report_table will not be None | |
dfd66b78 | 745 | if report_object: |
746 | report_object.save() | |
f26c21cd | 747 | return redirect( |
748 | request, | |
749 | 'index') | |
750 | ||
9b8ef022 | 751 | |
30a9fe7c | 752 | return render_to_response( |
9b8ef022 | 753 | request, |
754 | 'mediagoblin/user_pages/report.html', | |
755 | context) | |
4fd52036 JT |
756 | |
757 | @require_active_login | |
758 | def activity_view(request): | |
759 | """ /<username>/activity/<id> - Display activity | |
760 | ||
761 | This should display a HTML presentation of the activity | |
762 | this is NOT an API endpoint. | |
763 | """ | |
764 | # Get the user object. | |
765 | username = request.matchdict["username"] | |
d88fcb03 | 766 | user = LocalUser.query.filter_by(username=username).first() |
4fd52036 JT |
767 | |
768 | activity_id = request.matchdict["id"] | |
769 | ||
770 | if request.user is None: | |
771 | return render_404(request) | |
772 | ||
773 | activity = Activity.query.filter_by( | |
774 | id=activity_id, | |
775 | author=user.id | |
776 | ).first() | |
777 | ||
d216d771 JT |
778 | # There isn't many places to check that the public_id is filled so this |
779 | # will do, it really should be, lets try and fix that if it isn't. | |
780 | activity.get_public_id(request.urlgen) | |
781 | ||
4fd52036 JT |
782 | if activity is None: |
783 | return render_404(request) | |
784 | ||
785 | return render_to_response( | |
786 | request, | |
787 | "mediagoblin/api/activity.html", | |
788 | {"activity": activity} | |
789 | ) |