1 # GNU MediaGoblin -- federated, autonomous media hosting
2 # Copyright (C) 2011, 2012 MediaGoblin contributors. See AUTHORS.
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.
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.
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/>.
21 from mediagoblin
import messages
, mg_globals
22 from mediagoblin
.db
.util
import DESCENDING
, ObjectId
23 from mediagoblin
.tools
.response
import render_to_response
, render_404
, redirect
24 from mediagoblin
.tools
.translate
import pass_to_ugettext
as _
25 from mediagoblin
.tools
.pagination
import Pagination
26 from mediagoblin
.tools
.files
import delete_media_files
27 from mediagoblin
.user_pages
import forms
as user_forms
28 from mediagoblin
.user_pages
.lib
import send_comment_email
30 from mediagoblin
.decorators
import (uses_pagination
, get_user_media_entry
,
31 require_active_login
, user_may_delete_media
)
33 from werkzeug
.contrib
.atom
import AtomFeed
35 from mediagoblin
.media_types
import get_media_manager
38 _log
= logging
.getLogger(__name__
)
39 _log
.setLevel(logging
.DEBUG
)
43 def user_home(request
, page
):
44 """'Homepage' of a User()"""
45 user
= request
.db
.User
.find_one({
46 'username': request
.matchdict
['user']})
48 return render_404(request
)
49 elif user
.status
!= u
'active':
50 return render_to_response(
52 'mediagoblin/user_pages/user.html',
55 cursor
= request
.db
.MediaEntry
.find(
56 {'uploader': user
._id
,
57 'state': u
'processed'}).sort('created', DESCENDING
)
59 pagination
= Pagination(page
, cursor
)
60 media_entries
= pagination()
62 #if no data is available, return NotFound
63 if media_entries
== None:
64 return render_404(request
)
66 user_gallery_url
= request
.urlgen(
67 'mediagoblin.user_pages.user_gallery',
70 return render_to_response(
72 'mediagoblin/user_pages/user.html',
74 'user_gallery_url': user_gallery_url
,
75 'media_entries': media_entries
,
76 'pagination': pagination
})
80 def user_gallery(request
, page
):
81 """'Gallery' of a User()"""
82 user
= request
.db
.User
.find_one({
83 'username': request
.matchdict
['user'],
86 return render_404(request
)
88 cursor
= request
.db
.MediaEntry
.find(
89 {'uploader': user
._id
,
90 'state': u
'processed'}).sort('created', DESCENDING
)
92 pagination
= Pagination(page
, cursor
)
93 media_entries
= pagination()
95 #if no data is available, return NotFound
96 if media_entries
== None:
97 return render_404(request
)
99 return render_to_response(
101 'mediagoblin/user_pages/gallery.html',
103 'media_entries': media_entries
,
104 'pagination': pagination
})
106 MEDIA_COMMENTS_PER_PAGE
= 50
109 @get_user_media_entry
111 def media_home(request
, media
, page
, **kwargs
):
113 'Homepage' of a MediaEntry()
115 if ObjectId(request
.matchdict
.get('comment')):
116 pagination
= Pagination(
117 page
, media
.get_comments(
118 mg_globals
.app_config
['comments_ascending']),
119 MEDIA_COMMENTS_PER_PAGE
,
120 ObjectId(request
.matchdict
.get('comment')))
122 pagination
= Pagination(
123 page
, media
.get_comments(
124 mg_globals
.app_config
['comments_ascending']),
125 MEDIA_COMMENTS_PER_PAGE
)
127 comments
= pagination()
129 comment_form
= user_forms
.MediaCommentForm(request
.POST
)
131 media_template_name
= get_media_manager(media
.media_type
)['display_template']
133 return render_to_response(
137 'comments': comments
,
138 'pagination': pagination
,
139 'comment_form': comment_form
,
140 'app_config': mg_globals
.app_config
})
143 @get_user_media_entry
144 @require_active_login
145 def media_post_comment(request
, media
):
147 recieves POST from a MediaEntry() comment form, saves the comment.
149 assert request
.method
== 'POST'
151 comment
= request
.db
.MediaComment()
152 comment
.media_entry
= media
.id
153 comment
.author
= request
.user
.id
154 comment
.content
= unicode(request
.POST
['comment_content'])
156 if not comment
.content
.strip():
157 messages
.add_message(
160 _("Oops, your comment was empty."))
164 messages
.add_message(
165 request
, messages
.SUCCESS
,
166 _('Your comment has been posted!'))
168 media_uploader
= media
.get_uploader
169 #don't send email if you comment on your own post
170 if (comment
.author
!= media_uploader
and
171 media_uploader
.wants_comment_notification
):
172 send_comment_email(media_uploader
, comment
, media
, request
)
174 return exc
.HTTPFound(
175 location
=media
.url_for_self(request
.urlgen
))
178 @get_user_media_entry
179 @require_active_login
180 @user_may_delete_media
181 def media_confirm_delete(request
, media
):
183 form
= user_forms
.ConfirmDeleteForm(request
.POST
)
185 if request
.method
== 'POST' and form
.validate():
186 if form
.confirm
.data
is True:
187 username
= media
.get_uploader
.username
189 # Delete all the associated comments
190 for comment
in media
.get_comments():
193 # Delete all files on the public storage
195 delete_media_files(media
)
196 except OSError, error
:
197 _log
.error('No such files from the user "{1}"'
198 ' to delete: {0}'.format(str(error
), username
))
199 messages
.add_message(request
, messages
.ERROR
,
200 _('Some of the files with this entry seem'
201 ' to be missing. Deleting anyway.'))
204 messages
.add_message(
205 request
, messages
.SUCCESS
, _('You deleted the media.'))
207 return redirect(request
, "mediagoblin.user_pages.user_home",
210 messages
.add_message(
211 request
, messages
.ERROR
,
212 _("The media was not deleted because you didn't check that you were sure."))
213 return exc
.HTTPFound(
214 location
=media
.url_for_self(request
.urlgen
))
216 if ((request
.user
.is_admin
and
217 request
.user
._id
!= media
.uploader
)):
218 messages
.add_message(
219 request
, messages
.WARNING
,
220 _("You are about to delete another user's media. "
221 "Proceed with caution."))
223 return render_to_response(
225 'mediagoblin/user_pages/media_confirm_delete.html',
230 ATOM_DEFAULT_NR_OF_UPDATED_ITEMS
= 15
233 def atom_feed(request
):
235 generates the atom feed with the newest images
238 user
= request
.db
.User
.find_one({
239 'username': request
.matchdict
['user'],
240 'status': u
'active'})
242 return render_404(request
)
244 cursor
= request
.db
.MediaEntry
.find({
245 'uploader': user
._id
,
246 'state': u
'processed'}) \
247 .sort('created', DESCENDING
) \
248 .limit(ATOM_DEFAULT_NR_OF_UPDATED_ITEMS
)
251 ATOM feed id is a tag URI (see http://en.wikipedia.org/wiki/Tag_URI)
254 'href': request
.urlgen(
255 'mediagoblin.user_pages.user_home',
256 qualified
=True, user
=request
.matchdict
['user']),
261 if mg_globals
.app_config
["push_urls"]:
262 for push_url
in mg_globals
.app_config
["push_urls"]:
268 "MediaGoblin: Feed for user '%s'" % request
.matchdict
['user'],
269 feed_url
=request
.url
,
270 id='tag:{host},{year}:gallery.user-{user}'.format(
272 year
=datetime
.datetime
.today().strftime('%Y'),
273 user
=request
.matchdict
['user']),
277 feed
.add(entry
.get('title'),
278 entry
.description_html
,
279 id=entry
.url_for_self(request
.urlgen
, qualified
=True),
282 'name': entry
.get_uploader
.username
,
283 'uri': request
.urlgen(
284 'mediagoblin.user_pages.user_home',
285 qualified
=True, user
=entry
.get_uploader
.username
)},
286 updated
=entry
.get('created'),
288 'href': entry
.url_for_self(
292 'type': 'text/html'}])
294 return feed
.get_response()
297 @require_active_login
298 def processing_panel(request
):
300 Show to the user what media is still in conversion/processing...
301 and what failed, and why!
304 user
= request
.db
.User
.find_one(
305 {'username': request
.matchdict
['user'],
306 'status': u
'active'})
308 # Make sure the user exists and is active
310 return render_404(request
)
311 elif user
.status
!= u
'active':
312 return render_to_response(
314 'mediagoblin/user_pages/user.html',
317 # XXX: Should this be a decorator?
319 # Make sure we have permission to access this user's panel. Only
320 # admins and this user herself should be able to do so.
321 if not (user
._id
== request
.user
._id
322 or request
.user
.is_admin
):
323 # No? Let's simply redirect to this user's homepage then.
325 request
, 'mediagoblin.user_pages.user_home',
326 user
=request
.matchdict
['user'])
328 # Get media entries which are in-processing
329 processing_entries
= request
.db
.MediaEntry
.find(
330 {'uploader': user
._id
,
331 'state': u
'processing'}).sort('created', DESCENDING
)
333 # Get media entries which have failed to process
334 failed_entries
= request
.db
.MediaEntry
.find(
335 {'uploader': user
._id
,
336 'state': u
'failed'}).sort('created', DESCENDING
)
338 processed_entries
= request
.db
.MediaEntry
.find(
339 {'uploader': user
._id
,
340 'state': u
'processed'}).sort('created', DESCENDING
).limit(10)
343 return render_to_response(
345 'mediagoblin/user_pages/processing_panel.html',
347 'processing_entries': processing_entries
,
348 'failed_entries': failed_entries
,
349 'processed_entries': processed_entries
})