views.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. # GNU MediaGoblin -- federated, autonomous media hosting
  2. # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
  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. import logging
  17. import datetime
  18. import json
  19. import six
  20. from mediagoblin import messages, mg_globals
  21. from mediagoblin.db.models import (MediaEntry, MediaTag, Collection, Comment,
  22. CollectionItem, LocalUser, Activity, \
  23. GenericModelReference)
  24. from mediagoblin.tools.response import render_to_response, render_404, \
  25. redirect, redirect_obj
  26. from mediagoblin.tools.text import cleaned_markdown_conversion
  27. from mediagoblin.tools.translate import pass_to_ugettext as _
  28. from mediagoblin.tools.pagination import Pagination
  29. from mediagoblin.tools.federation import create_activity
  30. from mediagoblin.user_pages import forms as user_forms
  31. from mediagoblin.user_pages.lib import (send_comment_email,
  32. add_media_to_collection, build_report_object)
  33. from mediagoblin.notifications import trigger_notification, \
  34. add_comment_subscription, mark_comment_notification_seen
  35. from mediagoblin.tools.pluginapi import hook_transform
  36. from mediagoblin.decorators import (uses_pagination, get_user_media_entry,
  37. get_media_entry_by_id, user_has_privilege, user_not_banned,
  38. require_active_login, user_may_delete_media, user_may_alter_collection,
  39. get_user_collection, get_user_collection_item, active_user_from_url,
  40. get_optional_media_comment_by_id, allow_reporting)
  41. from werkzeug.contrib.atom import AtomFeed
  42. from werkzeug.exceptions import MethodNotAllowed
  43. from werkzeug.wrappers import Response
  44. _log = logging.getLogger(__name__)
  45. _log.setLevel(logging.DEBUG)
  46. @user_not_banned
  47. @uses_pagination
  48. def user_home(request, page):
  49. """'Homepage' of a LocalUser()"""
  50. user = LocalUser.query.filter_by(username=request.matchdict['user']).first()
  51. if not user:
  52. return render_404(request)
  53. elif not user.has_privilege(u'active'):
  54. return render_to_response(
  55. request,
  56. 'mediagoblin/user_pages/user_nonactive.html',
  57. {'user': user})
  58. cursor = MediaEntry.query.\
  59. filter_by(actor = user.id,
  60. state = u'processed').order_by(MediaEntry.created.desc())
  61. pagination = Pagination(page, cursor)
  62. media_entries = pagination()
  63. #if no data is available, return NotFound
  64. if media_entries == None:
  65. return render_404(request)
  66. user_gallery_url = request.urlgen(
  67. 'mediagoblin.user_pages.user_gallery',
  68. user=user.username)
  69. return render_to_response(
  70. request,
  71. 'mediagoblin/user_pages/user.html',
  72. {'user': user,
  73. 'user_gallery_url': user_gallery_url,
  74. 'media_entries': media_entries,
  75. 'pagination': pagination})
  76. @user_not_banned
  77. @active_user_from_url
  78. @uses_pagination
  79. def user_gallery(request, page, url_user=None):
  80. """'Gallery' of a LocalUser()"""
  81. tag = request.matchdict.get('tag', None)
  82. cursor = MediaEntry.query.filter_by(
  83. actor=url_user.id,
  84. state=u'processed').order_by(MediaEntry.created.desc())
  85. # Filter potentially by tag too:
  86. if tag:
  87. cursor = cursor.filter(
  88. MediaEntry.tags_helper.any(
  89. MediaTag.slug == request.matchdict['tag']))
  90. # Paginate gallery
  91. pagination = Pagination(page, cursor)
  92. media_entries = pagination()
  93. #if no data is available, return NotFound
  94. # TODO: Should we really also return 404 for empty galleries?
  95. if media_entries == None:
  96. return render_404(request)
  97. return render_to_response(
  98. request,
  99. 'mediagoblin/user_pages/gallery.html',
  100. {'user': url_user, 'tag': tag,
  101. 'media_entries': media_entries,
  102. 'pagination': pagination})
  103. MEDIA_COMMENTS_PER_PAGE = 50
  104. @user_not_banned
  105. @get_user_media_entry
  106. @uses_pagination
  107. def media_home(request, media, page, **kwargs):
  108. """
  109. 'Homepage' of a MediaEntry()
  110. """
  111. comment_id = request.matchdict.get('comment', None)
  112. if comment_id:
  113. if request.user:
  114. mark_comment_notification_seen(comment_id, request.user)
  115. pagination = Pagination(
  116. page, media.get_comments(
  117. mg_globals.app_config['comments_ascending']),
  118. MEDIA_COMMENTS_PER_PAGE,
  119. comment_id)
  120. else:
  121. pagination = Pagination(
  122. page, media.get_comments(
  123. mg_globals.app_config['comments_ascending']),
  124. MEDIA_COMMENTS_PER_PAGE)
  125. comments = pagination()
  126. comment_form = user_forms.MediaCommentForm(request.form)
  127. media_template_name = media.media_manager.display_template
  128. context = {
  129. 'media': media,
  130. 'comments': comments,
  131. 'pagination': pagination,
  132. 'comment_form': comment_form,
  133. 'app_config': mg_globals.app_config}
  134. # Since the media template name gets swapped out for each media
  135. # type, normal context hooks don't work if you want to affect all
  136. # media displays. This gives a general purpose hook.
  137. context = hook_transform(
  138. "media_home_context", context)
  139. return render_to_response(
  140. request,
  141. media_template_name,
  142. context)
  143. @get_media_entry_by_id
  144. @user_has_privilege(u'commenter')
  145. def media_post_comment(request, media):
  146. """
  147. recieves POST from a MediaEntry() comment form, saves the comment.
  148. """
  149. if not request.method == 'POST':
  150. raise MethodNotAllowed()
  151. comment = request.db.TextComment()
  152. comment.actor = request.user.id
  153. comment.content = six.text_type(request.form['comment_content'])
  154. # Show error message if commenting is disabled.
  155. if not mg_globals.app_config['allow_comments']:
  156. messages.add_message(
  157. request,
  158. messages.ERROR,
  159. _("Sorry, comments are disabled."))
  160. elif not comment.content.strip():
  161. messages.add_message(
  162. request,
  163. messages.ERROR,
  164. _("Oops, your comment was empty."))
  165. else:
  166. create_activity("post", comment, comment.actor, target=media)
  167. add_comment_subscription(request.user, media)
  168. comment.save()
  169. link = request.db.Comment()
  170. link.target = media
  171. link.comment = comment
  172. link.save()
  173. messages.add_message(
  174. request, messages.SUCCESS,
  175. _('Your comment has been posted!'))
  176. trigger_notification(comment, media, request)
  177. return redirect_obj(request, media)
  178. def media_preview_comment(request):
  179. """Runs a comment through markdown so it can be previewed."""
  180. # If this isn't an ajax request, render_404
  181. if not request.is_xhr:
  182. return render_404(request)
  183. comment = six.text_type(request.form['comment_content'])
  184. cleancomment = { "content":cleaned_markdown_conversion(comment)}
  185. return Response(json.dumps(cleancomment))
  186. @user_not_banned
  187. @get_media_entry_by_id
  188. @require_active_login
  189. def media_collect(request, media):
  190. """Add media to collection submission"""
  191. form = user_forms.MediaCollectForm(request.form)
  192. # A user's own collections:
  193. form.collection.query = Collection.query.filter_by(
  194. actor=request.user.id,
  195. type=Collection.USER_DEFINED_TYPE
  196. ).order_by(Collection.title)
  197. if request.method != 'POST' or not form.validate():
  198. # No POST submission, or invalid form
  199. if not form.validate():
  200. messages.add_message(request, messages.ERROR,
  201. _('Please check your entries and try again.'))
  202. return render_to_response(
  203. request,
  204. 'mediagoblin/user_pages/media_collect.html',
  205. {'media': media,
  206. 'form': form})
  207. # If we are here, method=POST and the form is valid, submit things.
  208. # If the user is adding a new collection, use that:
  209. if form.collection_title.data:
  210. # Make sure this user isn't duplicating an existing collection
  211. existing_collection = Collection.query.filter_by(
  212. actor=request.user.id,
  213. title=form.collection_title.data,
  214. type=Collection.USER_DEFINED_TYPE
  215. ).first()
  216. if existing_collection:
  217. messages.add_message(request, messages.ERROR,
  218. _('You already have a collection called "%s"!')
  219. % existing_collection.title)
  220. return redirect(request, "mediagoblin.user_pages.media_home",
  221. user=media.get_actor.username,
  222. media=media.slug_or_id)
  223. collection = Collection()
  224. collection.title = form.collection_title.data
  225. collection.description = form.collection_description.data
  226. collection.actor = request.user.id
  227. collection.type = Collection.USER_DEFINED_TYPE
  228. collection.generate_slug()
  229. collection.get_public_id(request.urlgen)
  230. create_activity("create", collection, collection.actor)
  231. collection.save()
  232. # Otherwise, use the collection selected from the drop-down
  233. else:
  234. collection = form.collection.data
  235. if collection and collection.actor != request.user.id:
  236. collection = None
  237. # Make sure the user actually selected a collection
  238. item = CollectionItem.query.filter_by(collection=collection.id)
  239. item = item.join(CollectionItem.object_helper).filter_by(
  240. model_type=media.__tablename__,
  241. obj_pk=media.id
  242. ).first()
  243. if not collection:
  244. messages.add_message(
  245. request, messages.ERROR,
  246. _('You have to select or add a collection'))
  247. return redirect(request, "mediagoblin.user_pages.media_collect",
  248. user=media.get_actor.username,
  249. media_id=media.id)
  250. # Check whether media already exists in collection
  251. elif item is not None:
  252. messages.add_message(request, messages.ERROR,
  253. _('"%s" already in collection "%s"')
  254. % (media.title, collection.title))
  255. else: # Add item to collection
  256. add_media_to_collection(collection, media, form.note.data)
  257. create_activity("add", media, request.user, target=collection)
  258. messages.add_message(request, messages.SUCCESS,
  259. _('"%s" added to collection "%s"')
  260. % (media.title, collection.title))
  261. return redirect_obj(request, media)
  262. #TODO: Why does @user_may_delete_media not implicate @require_active_login?
  263. @get_media_entry_by_id
  264. @require_active_login
  265. @user_may_delete_media
  266. def media_confirm_delete(request, media):
  267. form = user_forms.ConfirmDeleteForm(request.form)
  268. if request.method == 'POST' and form.validate():
  269. if form.confirm.data is True:
  270. username = media.get_actor.username
  271. # This probably is already filled but just in case it has slipped
  272. # through the net somehow, we need to try and make sure the
  273. # MediaEntry has a public ID so it gets properly soft-deleted.
  274. media.get_public_id(request.urlgen)
  275. # Decrement the users uploaded quota.
  276. media.get_actor.uploaded = media.get_actor.uploaded - \
  277. media.file_size
  278. media.get_actor.save()
  279. # Delete MediaEntry and all related files, comments etc.
  280. media.delete()
  281. messages.add_message(
  282. request, messages.SUCCESS, _('You deleted the media.'))
  283. location = media.url_to_next(request.urlgen)
  284. if not location:
  285. location=media.url_to_prev(request.urlgen)
  286. if not location:
  287. location=request.urlgen("mediagoblin.user_pages.user_home",
  288. user=username)
  289. return redirect(request, location=location)
  290. else:
  291. messages.add_message(
  292. request, messages.ERROR,
  293. _("The media was not deleted because you didn't check that you were sure."))
  294. return redirect_obj(request, media)
  295. if ((request.user.has_privilege(u'admin') and
  296. request.user.id != media.actor)):
  297. messages.add_message(
  298. request, messages.WARNING,
  299. _("You are about to delete another user's media. "
  300. "Proceed with caution."))
  301. return render_to_response(
  302. request,
  303. 'mediagoblin/user_pages/media_confirm_delete.html',
  304. {'media': media,
  305. 'form': form})
  306. @user_not_banned
  307. @active_user_from_url
  308. @uses_pagination
  309. def user_collection(request, page, url_user=None):
  310. """A User-defined Collection"""
  311. collection = Collection.query.filter_by(
  312. get_actor=url_user,
  313. slug=request.matchdict['collection']).first()
  314. if not collection:
  315. return render_404(request)
  316. cursor = collection.get_collection_items()
  317. pagination = Pagination(page, cursor)
  318. collection_items = pagination()
  319. # if no data is available, return NotFound
  320. # TODO: Should an empty collection really also return 404?
  321. if collection_items == None:
  322. return render_404(request)
  323. return render_to_response(
  324. request,
  325. 'mediagoblin/user_pages/collection.html',
  326. {'user': url_user,
  327. 'collection': collection,
  328. 'collection_items': collection_items,
  329. 'pagination': pagination})
  330. @user_not_banned
  331. @active_user_from_url
  332. def collection_list(request, url_user=None):
  333. """A User-defined Collection"""
  334. collections = Collection.query.filter_by(
  335. get_actor=url_user)
  336. return render_to_response(
  337. request,
  338. 'mediagoblin/user_pages/collection_list.html',
  339. {'user': url_user,
  340. 'collections': collections})
  341. @get_user_collection_item
  342. @require_active_login
  343. @user_may_alter_collection
  344. def collection_item_confirm_remove(request, collection_item):
  345. form = user_forms.ConfirmCollectionItemRemoveForm(request.form)
  346. if request.method == 'POST' and form.validate():
  347. username = collection_item.in_collection.get_actor.username
  348. collection = collection_item.in_collection
  349. if form.confirm.data is True:
  350. obj = collection_item.get_object()
  351. obj.save()
  352. collection_item.delete()
  353. collection.num_items = collection.num_items - 1
  354. collection.save()
  355. messages.add_message(
  356. request, messages.SUCCESS, _('You deleted the item from the collection.'))
  357. else:
  358. messages.add_message(
  359. request, messages.ERROR,
  360. _("The item was not removed because you didn't check that you were sure."))
  361. return redirect_obj(request, collection)
  362. if ((request.user.has_privilege(u'admin') and
  363. request.user.id != collection_item.in_collection.actor)):
  364. messages.add_message(
  365. request, messages.WARNING,
  366. _("You are about to delete an item from another user's collection. "
  367. "Proceed with caution."))
  368. return render_to_response(
  369. request,
  370. 'mediagoblin/user_pages/collection_item_confirm_remove.html',
  371. {'collection_item': collection_item,
  372. 'form': form})
  373. @get_user_collection
  374. @require_active_login
  375. @user_may_alter_collection
  376. def collection_confirm_delete(request, collection):
  377. form = user_forms.ConfirmDeleteForm(request.form)
  378. if request.method == 'POST' and form.validate():
  379. username = collection.get_actor.username
  380. if form.confirm.data is True:
  381. collection_title = collection.title
  382. # Firstly like with the MediaEntry delete, lets ensure the
  383. # public_id is populated as this is really important!
  384. collection.get_public_id(request.urlgen)
  385. # Delete all the associated collection items
  386. for item in collection.get_collection_items():
  387. obj = item.get_object()
  388. obj.save()
  389. item.delete()
  390. collection.delete()
  391. messages.add_message(request, messages.SUCCESS,
  392. _('You deleted the collection "%s"') % collection_title)
  393. return redirect(request, "mediagoblin.user_pages.user_home",
  394. user=username)
  395. else:
  396. messages.add_message(
  397. request, messages.ERROR,
  398. _("The collection was not deleted because you didn't check that you were sure."))
  399. return redirect_obj(request, collection)
  400. if ((request.user.has_privilege(u'admin') and
  401. request.user.id != collection.actor)):
  402. messages.add_message(
  403. request, messages.WARNING,
  404. _("You are about to delete another user's collection. "
  405. "Proceed with caution."))
  406. return render_to_response(
  407. request,
  408. 'mediagoblin/user_pages/collection_confirm_delete.html',
  409. {'collection': collection,
  410. 'form': form})
  411. ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 15
  412. def atom_feed(request):
  413. """
  414. generates the atom feed with the newest images
  415. """
  416. user = LocalUser.query.filter_by(
  417. username = request.matchdict['user']).first()
  418. if not user or not user.has_privilege(u'active'):
  419. return render_404(request)
  420. cursor = MediaEntry.query.filter_by(
  421. actor = user.id,
  422. state = u'processed').\
  423. order_by(MediaEntry.created.desc()).\
  424. limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS)
  425. """
  426. ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI)
  427. """
  428. atomlinks = [{
  429. 'href': request.urlgen(
  430. 'mediagoblin.user_pages.user_home',
  431. qualified=True, user=request.matchdict['user']),
  432. 'rel': 'alternate',
  433. 'type': 'text/html'
  434. }]
  435. if mg_globals.app_config["push_urls"]:
  436. for push_url in mg_globals.app_config["push_urls"]:
  437. atomlinks.append({
  438. 'rel': 'hub',
  439. 'href': push_url})
  440. feed = AtomFeed(
  441. "MediaGoblin: Feed for user '%s'" % request.matchdict['user'],
  442. feed_url=request.url,
  443. id='tag:{host},{year}:gallery.user-{user}'.format(
  444. host=request.host,
  445. year=datetime.datetime.today().strftime('%Y'),
  446. user=request.matchdict['user']),
  447. links=atomlinks)
  448. for entry in cursor:
  449. feed.add(
  450. entry.get('title'),
  451. entry.description_html,
  452. id=entry.url_for_self(request.urlgen, qualified=True),
  453. content_type='html',
  454. author={
  455. 'name': entry.get_actor.username,
  456. 'uri': request.urlgen(
  457. 'mediagoblin.user_pages.user_home',
  458. qualified=True, user=entry.get_actor.username)},
  459. updated=entry.get('created'),
  460. links=[{
  461. 'href': entry.url_for_self(
  462. request.urlgen,
  463. qualified=True),
  464. 'rel': 'alternate',
  465. 'type': 'text/html'}])
  466. return feed.get_response()
  467. def collection_atom_feed(request):
  468. """
  469. generates the atom feed with the newest images from a collection
  470. """
  471. user = LocalUser.query.filter_by(
  472. username = request.matchdict['user']).first()
  473. if not user or not user.has_privilege(u'active'):
  474. return render_404(request)
  475. collection = Collection.query.filter_by(
  476. actor=user.id,
  477. slug=request.matchdict['collection']).first()
  478. if not collection:
  479. return render_404(request)
  480. cursor = CollectionItem.query.filter_by(
  481. collection=collection.id) \
  482. .order_by(CollectionItem.added.desc()) \
  483. .limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS)
  484. """
  485. ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI)
  486. """
  487. atomlinks = [{
  488. 'href': collection.url_for_self(request.urlgen, qualified=True),
  489. 'rel': 'alternate',
  490. 'type': 'text/html'
  491. }]
  492. if mg_globals.app_config["push_urls"]:
  493. for push_url in mg_globals.app_config["push_urls"]:
  494. atomlinks.append({
  495. 'rel': 'hub',
  496. 'href': push_url})
  497. feed = AtomFeed(
  498. "MediaGoblin: Feed for %s's collection %s" %
  499. (request.matchdict['user'], collection.title),
  500. feed_url=request.url,
  501. id=u'tag:{host},{year}:gnu-mediagoblin.{user}.collection.{slug}'\
  502. .format(
  503. host=request.host,
  504. year=collection.created.strftime('%Y'),
  505. user=request.matchdict['user'],
  506. slug=collection.slug),
  507. links=atomlinks)
  508. for item in cursor:
  509. obj = item.get_object()
  510. feed.add(
  511. obj.get('title'),
  512. item.note_html,
  513. id=obj.url_for_self(request.urlgen, qualified=True),
  514. content_type='html',
  515. author={
  516. 'name': obj.get_actor.username,
  517. 'uri': request.urlgen(
  518. 'mediagoblin.user_pages.user_home',
  519. qualified=True, user=obj.get_actor.username)},
  520. updated=item.get('added'),
  521. links=[{
  522. 'href': obj.url_for_self(
  523. request.urlgen,
  524. qualified=True),
  525. 'rel': 'alternate',
  526. 'type': 'text/html'}])
  527. return feed.get_response()
  528. @active_user_from_url
  529. @uses_pagination
  530. @require_active_login
  531. def processing_panel(request, page, url_user):
  532. """
  533. Show to the user what media is still in conversion/processing...
  534. and what failed, and why!
  535. """
  536. user = LocalUser.query.filter_by(username=request.matchdict['user']).first()
  537. # TODO: XXX: Should this be a decorator?
  538. #
  539. # Make sure we have permission to access this user's panel. Only
  540. # admins and this user herself should be able to do so.
  541. if not (user.id == request.user.id or request.user.has_privilege(u'admin')):
  542. # No? Simply redirect to this user's homepage.
  543. return redirect(
  544. request, 'mediagoblin.user_pages.user_home',
  545. user=user.username)
  546. # Get media entries which are in-processing
  547. entries = (MediaEntry.query.filter_by(actor=user.id)
  548. .order_by(MediaEntry.created.desc()))
  549. try:
  550. state = request.matchdict['state']
  551. # no exception was thrown, filter entries by state
  552. entries = entries.filter_by(state=state)
  553. except KeyError:
  554. # show all entries
  555. pass
  556. pagination = Pagination(page, entries)
  557. pagination.per_page = 30
  558. entries_on_a_page = pagination()
  559. # Render to response
  560. return render_to_response(
  561. request,
  562. 'mediagoblin/user_pages/processing_panel.html',
  563. {'user': user,
  564. 'entries': entries_on_a_page,
  565. 'pagination': pagination})
  566. @allow_reporting
  567. @get_user_media_entry
  568. @user_has_privilege(u'reporter')
  569. @get_optional_media_comment_by_id
  570. def file_a_report(request, media, comment):
  571. """
  572. This view handles the filing of a Report.
  573. """
  574. if comment is not None:
  575. if not comment.target().id == media.id:
  576. return render_404(request)
  577. form = user_forms.CommentReportForm(request.form)
  578. context = {'media': comment.target(),
  579. 'comment':comment.comment(),
  580. 'form':form}
  581. else:
  582. form = user_forms.MediaReportForm(request.form)
  583. context = {'media': media,
  584. 'form':form}
  585. form.reporter_id.data = request.user.id
  586. if request.method == "POST":
  587. report_object = build_report_object(
  588. form,
  589. media_entry=media,
  590. comment=comment
  591. )
  592. # if the object was built successfully, report_table will not be None
  593. if report_object:
  594. report_object.save()
  595. return redirect(
  596. request,
  597. 'index')
  598. return render_to_response(
  599. request,
  600. 'mediagoblin/user_pages/report.html',
  601. context)
  602. @require_active_login
  603. def activity_view(request):
  604. """ /<username>/activity/<id> - Display activity
  605. This should display a HTML presentation of the activity
  606. this is NOT an API endpoint.
  607. """
  608. # Get the user object.
  609. username = request.matchdict["username"]
  610. user = LocalUser.query.filter_by(username=username).first()
  611. activity_id = request.matchdict["id"]
  612. if request.user is None:
  613. return render_404(request)
  614. activity = Activity.query.filter_by(
  615. id=activity_id,
  616. author=user.id
  617. ).first()
  618. # There isn't many places to check that the public_id is filled so this
  619. # will do, it really should be, lets try and fix that if it isn't.
  620. activity.get_public_id(request.urlgen)
  621. if activity is None:
  622. return render_404(request)
  623. return render_to_response(
  624. request,
  625. "mediagoblin/api/activity.html",
  626. {"activity": activity}
  627. )