Commit | Line | Data |
---|---|---|
01c75c7e | 1 | # 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 | ||
1c63ad5d | 17 | from webob import exc |
52359e91 | 18 | |
3a8c3a38 | 19 | from mediagoblin import messages, mg_globals |
9074ee7c | 20 | from mediagoblin.db.util import DESCENDING, ObjectId |
152a3bfa AW |
21 | from mediagoblin.tools.text import cleaned_markdown_conversion |
22 | from mediagoblin.tools.response import render_to_response, render_404, redirect | |
ae3bc7fa | 23 | from mediagoblin.tools.translate import pass_to_ugettext as _ |
152a3bfa AW |
24 | from mediagoblin.tools.pagination import Pagination |
25 | from mediagoblin.tools.files import delete_media_files | |
9074ee7c | 26 | from mediagoblin.user_pages import forms as user_forms |
f6249408 | 27 | |
50854db0 | 28 | from mediagoblin.decorators import (uses_pagination, get_user_media_entry, |
502073f2 | 29 | require_active_login, user_may_delete_media) |
9a16e16f | 30 | |
00c39256 | 31 | from werkzeug.contrib.atom import AtomFeed |
1301a8ad | 32 | |
93bdab9d JW |
33 | from mediagoblin.media_types import get_media_manager |
34 | ||
9074ee7c | 35 | |
3eb6fc4f | 36 | @uses_pagination |
1301a8ad | 37 | def user_home(request, page): |
9a16e16f | 38 | """'Homepage' of a User()""" |
7acdbfd3 | 39 | user = request.db.User.find_one({ |
990d3b69 | 40 | 'username': request.matchdict['user']}) |
7acdbfd3 | 41 | if not user: |
de12b4e7 | 42 | return render_404(request) |
7a3d00ec | 43 | elif user.status != u'active': |
990d3b69 CAW |
44 | return render_to_response( |
45 | request, | |
46 | 'mediagoblin/user_pages/user.html', | |
47 | {'user': user}) | |
9a16e16f | 48 | |
434b3221 | 49 | cursor = request.db.MediaEntry.find( |
eabe6b67 | 50 | {'uploader': user._id, |
434b3221 | 51 | 'state': 'processed'}).sort('created', DESCENDING) |
9a16e16f | 52 | |
1301a8ad | 53 | pagination = Pagination(page, cursor) |
ca3ca51c | 54 | media_entries = pagination() |
44e3e917 | 55 | |
ae85ed0f BK |
56 | #if no data is available, return NotFound |
57 | if media_entries == None: | |
de12b4e7 | 58 | return render_404(request) |
243c3843 | 59 | |
5949be9a CAW |
60 | user_gallery_url = request.urlgen( |
61 | 'mediagoblin.user_pages.user_gallery', | |
5a4e3ff1 | 62 | user=user.username) |
5949be9a | 63 | |
9038c9f9 CAW |
64 | return render_to_response( |
65 | request, | |
c9c24934 E |
66 | 'mediagoblin/user_pages/user.html', |
67 | {'user': user, | |
5949be9a | 68 | 'user_gallery_url': user_gallery_url, |
c9c24934 E |
69 | 'media_entries': media_entries, |
70 | 'pagination': pagination}) | |
f6249408 | 71 | |
243c3843 | 72 | |
184f2240 | 73 | @uses_pagination |
74 | def user_gallery(request, page): | |
75 | """'Gallery' of a User()""" | |
76 | user = request.db.User.find_one({ | |
77 | 'username': request.matchdict['user'], | |
78 | 'status': 'active'}) | |
79 | if not user: | |
de12b4e7 | 80 | return render_404(request) |
184f2240 | 81 | |
82 | cursor = request.db.MediaEntry.find( | |
eabe6b67 | 83 | {'uploader': user._id, |
184f2240 | 84 | 'state': 'processed'}).sort('created', DESCENDING) |
85 | ||
86 | pagination = Pagination(page, cursor) | |
87 | media_entries = pagination() | |
88 | ||
89 | #if no data is available, return NotFound | |
90 | if media_entries == None: | |
de12b4e7 | 91 | return render_404(request) |
243c3843 | 92 | |
4b5f5a08 | 93 | return render_to_response( |
94 | request, | |
95 | 'mediagoblin/user_pages/gallery.html', | |
96 | {'user': user, | |
97 | 'media_entries': media_entries, | |
98 | 'pagination': pagination}) | |
184f2240 | 99 | |
6f59a3a3 | 100 | MEDIA_COMMENTS_PER_PAGE = 50 |
434b3221 | 101 | |
243c3843 | 102 | |
01674e10 | 103 | @get_user_media_entry |
9074ee7c | 104 | @uses_pagination |
6f59a3a3 | 105 | def media_home(request, media, page, **kwargs): |
9074ee7c JW |
106 | """ |
107 | 'Homepage' of a MediaEntry() | |
108 | """ | |
af2fcba5 JW |
109 | if ObjectId(request.matchdict.get('comment')): |
110 | pagination = Pagination( | |
7c378f2c CAW |
111 | page, media.get_comments( |
112 | mg_globals.app_config['comments_ascending']), | |
113 | MEDIA_COMMENTS_PER_PAGE, | |
af2fcba5 JW |
114 | ObjectId(request.matchdict.get('comment'))) |
115 | else: | |
116 | pagination = Pagination( | |
7c378f2c CAW |
117 | page, media.get_comments( |
118 | mg_globals.app_config['comments_ascending']), | |
119 | MEDIA_COMMENTS_PER_PAGE) | |
9074ee7c | 120 | |
6f59a3a3 | 121 | comments = pagination() |
9074ee7c | 122 | |
6f59a3a3 | 123 | comment_form = user_forms.MediaCommentForm(request.POST) |
9074ee7c | 124 | |
f4ee8399 | 125 | media_template_name = get_media_manager(media.media_type)['display_template'] |
93bdab9d | 126 | |
9038c9f9 CAW |
127 | return render_to_response( |
128 | request, | |
93bdab9d | 129 | media_template_name, |
9074ee7c JW |
130 | {'media': media, |
131 | 'comments': comments, | |
132 | 'pagination': pagination, | |
3a8c3a38 JW |
133 | 'comment_form': comment_form, |
134 | 'app_config': mg_globals.app_config}) | |
9074ee7c | 135 | |
95e6da02 | 136 | |
95e12bf2 | 137 | @get_user_media_entry |
9074ee7c | 138 | @require_active_login |
95e12bf2 | 139 | def media_post_comment(request, media): |
9074ee7c JW |
140 | """ |
141 | recieves POST from a MediaEntry() comment form, saves the comment. | |
142 | """ | |
95e12bf2 CAW |
143 | assert request.method == 'POST' |
144 | ||
9074ee7c | 145 | comment = request.db.MediaComment() |
eabe6b67 E |
146 | comment['media_entry'] = media._id |
147 | comment['author'] = request.user._id | |
08750772 | 148 | comment['content'] = unicode(request.POST['comment_content']) |
95e6da02 | 149 | comment['content_html'] = cleaned_markdown_conversion(comment['content']) |
9074ee7c | 150 | |
7298ffa1 AW |
151 | if not comment['content'].strip(): |
152 | messages.add_message( | |
153 | request, | |
154 | messages.ERROR, | |
eae7d058 | 155 | _("Oops, your comment was empty.")) |
7298ffa1 AW |
156 | else: |
157 | comment.save() | |
b5d3aec6 | 158 | |
7298ffa1 AW |
159 | messages.add_message( |
160 | request, messages.SUCCESS, | |
eae7d058 | 161 | _('Your comment has been posted!')) |
52359e91 | 162 | |
95e12bf2 CAW |
163 | return exc.HTTPFound( |
164 | location=media.url_for_self(request.urlgen)) | |
00c39256 | 165 | |
95e6da02 | 166 | |
502073f2 JW |
167 | @get_user_media_entry |
168 | @require_active_login | |
169 | @user_may_delete_media | |
170 | def media_confirm_delete(request, media): | |
171 | ||
172 | form = user_forms.ConfirmDeleteForm(request.POST) | |
173 | ||
174 | if request.method == 'POST' and form.validate(): | |
8daef28d | 175 | if form.confirm.data is True: |
05751758 | 176 | username = media.get_uploader.username |
502073f2 JW |
177 | |
178 | # Delete all files on the public storage | |
179 | delete_media_files(media) | |
180 | ||
181 | media.delete() | |
ea33f636 E |
182 | messages.add_message( |
183 | request, messages.SUCCESS, _('You deleted the media.')) | |
502073f2 JW |
184 | |
185 | return redirect(request, "mediagoblin.user_pages.user_home", | |
186 | user=username) | |
187 | else: | |
d0ba62e2 PUS |
188 | messages.add_message( |
189 | request, messages.ERROR, | |
56bfd91a | 190 | _("The media was not deleted because you didn't check that you were sure.")) |
8d7b549b E |
191 | return exc.HTTPFound( |
192 | location=media.url_for_self(request.urlgen)) | |
502073f2 | 193 | |
bec591d8 | 194 | if ((request.user.is_admin and |
4deda94a | 195 | request.user._id != media.uploader)): |
7a4c0126 CAW |
196 | messages.add_message( |
197 | request, messages.WARNING, | |
198 | _("You are about to delete another user's media. " | |
199 | "Proceed with caution.")) | |
200 | ||
502073f2 JW |
201 | return render_to_response( |
202 | request, | |
203 | 'mediagoblin/user_pages/media_confirm_delete.html', | |
204 | {'media': media, | |
205 | 'form': form}) | |
206 | ||
207 | ||
a5303e47 | 208 | ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 15 |
00c39256 | 209 | |
243c3843 | 210 | |
00c39256 BK |
211 | def atom_feed(request): |
212 | """ | |
213 | generates the atom feed with the newest images | |
214 | """ | |
215 | ||
216 | user = request.db.User.find_one({ | |
217 | 'username': request.matchdict['user'], | |
218 | 'status': 'active'}) | |
219 | if not user: | |
de12b4e7 | 220 | return render_404(request) |
00c39256 BK |
221 | |
222 | cursor = request.db.MediaEntry.find({ | |
eabe6b67 | 223 | 'uploader': user._id, |
00c39256 BK |
224 | 'state': 'processed'}) \ |
225 | .sort('created', DESCENDING) \ | |
226 | .limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS) | |
227 | ||
1df68a35 MA |
228 | """ |
229 | ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI) | |
230 | """ | |
5b1a7bae MA |
231 | atomlinks = [{ |
232 | 'href': request.urlgen( | |
233 | 'mediagoblin.user_pages.user_home', | |
234 | qualified=True,user=request.matchdict['user']), | |
235 | 'rel': 'alternate', | |
236 | 'type': 'text/html' | |
237 | }]; | |
bb025ebd MA |
238 | if mg_globals.app_config["push_urls"]: |
239 | for push_url in mg_globals.app_config["push_urls"]: | |
240 | atomlinks.append({ | |
241 | 'rel': 'hub', | |
242 | 'href': push_url}) | |
5b1a7bae | 243 | |
1df68a35 MA |
244 | feed = AtomFeed( |
245 | "MediaGoblin: Feed for user '%s'" % request.matchdict['user'], | |
00c39256 | 246 | feed_url=request.url, |
1df68a35 | 247 | id='tag:'+request.host+',2011:gallery.user-'+request.matchdict['user'], |
5b1a7bae MA |
248 | links=atomlinks) |
249 | ||
243c3843 | 250 | |
00c39256 BK |
251 | for entry in cursor: |
252 | feed.add(entry.get('title'), | |
44e2da2f | 253 | entry.get('description_html'), |
1df68a35 | 254 | id=entry.url_for_self(request.urlgen,qualified=True), |
00c39256 | 255 | content_type='html', |
1df68a35 MA |
256 | author={ |
257 | 'name': entry.get_uploader.username, | |
258 | 'uri': request.urlgen( | |
259 | 'mediagoblin.user_pages.user_home', | |
260 | qualified=True, user=entry.get_uploader.username)}, | |
00c39256 | 261 | updated=entry.get('created'), |
1df68a35 MA |
262 | links=[{ |
263 | 'href': entry.url_for_self( | |
264 | request.urlgen, | |
265 | qualified=True), | |
266 | 'rel': 'alternate', | |
267 | 'type': 'text/html'}]) | |
00c39256 | 268 | |
9074ee7c | 269 | return feed.get_response() |
01c75c7e CAW |
270 | |
271 | ||
272 | @require_active_login | |
273 | def processing_panel(request): | |
274 | """ | |
275 | Show to the user what media is still in conversion/processing... | |
276 | and what failed, and why! | |
277 | """ | |
278 | # Get the user | |
279 | user = request.db.User.find_one( | |
280 | {'username': request.matchdict['user'], | |
281 | 'status': 'active'}) | |
282 | ||
283 | # Make sure the user exists and is active | |
284 | if not user: | |
de12b4e7 | 285 | return render_404(request) |
7a3d00ec | 286 | elif user.status != u'active': |
01c75c7e CAW |
287 | return render_to_response( |
288 | request, | |
289 | 'mediagoblin/user_pages/user.html', | |
290 | {'user': user}) | |
291 | ||
292 | # XXX: Should this be a decorator? | |
293 | # | |
294 | # Make sure we have permission to access this user's panel. Only | |
295 | # admins and this user herself should be able to do so. | |
eabe6b67 | 296 | if not (user._id == request.user._id |
01c75c7e CAW |
297 | or request.user.is_admin): |
298 | # No? Let's simply redirect to this user's homepage then. | |
299 | return redirect( | |
300 | request, 'mediagoblin.user_pages.user_home', | |
301 | user=request.matchdict['user']) | |
302 | ||
303 | # Get media entries which are in-processing | |
304 | processing_entries = request.db.MediaEntry.find( | |
eabe6b67 | 305 | {'uploader': user._id, |
01c75c7e CAW |
306 | 'state': 'processing'}).sort('created', DESCENDING) |
307 | ||
308 | # Get media entries which have failed to process | |
309 | failed_entries = request.db.MediaEntry.find( | |
eabe6b67 | 310 | {'uploader': user._id, |
01c75c7e CAW |
311 | 'state': 'failed'}).sort('created', DESCENDING) |
312 | ||
313 | # Render to response | |
314 | return render_to_response( | |
315 | request, | |
316 | 'mediagoblin/user_pages/processing_panel.html', | |
317 | {'user': user, | |
318 | 'processing_entries': processing_entries, | |
319 | 'failed_entries': failed_entries}) |