Removed unnecessary reference to may_edit_collection in edit views.py
[mediagoblin.git] / mediagoblin / edit / views.py
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
17 from webob import exc
18 from cgi import FieldStorage
19 from datetime import datetime
20
21 from werkzeug.utils import secure_filename
22
23 from mediagoblin import messages
24 from mediagoblin import mg_globals
25
26 from mediagoblin.auth import lib as auth_lib
27 from mediagoblin.edit import forms
28 from mediagoblin.edit.lib import may_edit_media
29 from mediagoblin.decorators import require_active_login, get_user_media_entry, \
30 user_may_alter_collection, get_user_collection
31 from mediagoblin.tools.response import render_to_response, redirect
32 from mediagoblin.tools.translate import pass_to_ugettext as _
33 from mediagoblin.tools.text import (
34 convert_to_tag_list_of_dicts, media_tags_as_string)
35 from mediagoblin.db.util import check_media_slug_used, check_collection_slug_used
36
37 import mimetypes
38
39
40 @get_user_media_entry
41 @require_active_login
42 def edit_media(request, media):
43 if not may_edit_media(request, media):
44 return exc.HTTPForbidden()
45
46 defaults = dict(
47 title=media.title,
48 slug=media.slug,
49 description=media.description,
50 tags=media_tags_as_string(media.tags),
51 license=media.license)
52
53 form = forms.EditForm(
54 request.POST,
55 **defaults)
56
57 if request.method == 'POST' and form.validate():
58 # Make sure there isn't already a MediaEntry with such a slug
59 # and userid.
60 slug_used = check_media_slug_used(request.db, media.uploader,
61 request.POST['slug'], media.id)
62
63 if slug_used:
64 form.slug.errors.append(
65 _(u'An entry with that slug already exists for this user.'))
66 else:
67 media.title = unicode(request.POST['title'])
68 media.description = unicode(request.POST.get('description'))
69 media.tags = convert_to_tag_list_of_dicts(
70 request.POST.get('tags'))
71
72 media.license = unicode(request.POST.get('license', '')) or None
73
74 media.slug = unicode(request.POST['slug'])
75
76 media.save()
77
78 return exc.HTTPFound(
79 location=media.url_for_self(request.urlgen))
80
81 if request.user.is_admin \
82 and media.uploader != request.user._id \
83 and request.method != 'POST':
84 messages.add_message(
85 request, messages.WARNING,
86 _("You are editing another user's media. Proceed with caution."))
87
88 return render_to_response(
89 request,
90 'mediagoblin/edit/edit.html',
91 {'media': media,
92 'form': form})
93
94
95 # Mimetypes that browsers parse scripts in.
96 # Content-sniffing isn't taken into consideration.
97 UNSAFE_MIMETYPES = [
98 'text/html',
99 'text/svg+xml']
100
101
102 @get_user_media_entry
103 @require_active_login
104 def edit_attachments(request, media):
105 if mg_globals.app_config['allow_attachments']:
106 form = forms.EditAttachmentsForm()
107
108 # Add any attachements
109 if ('attachment_file' in request.POST
110 and isinstance(request.POST['attachment_file'], FieldStorage)
111 and request.POST['attachment_file'].file):
112
113 # Security measure to prevent attachments from being served as
114 # text/html, which will be parsed by web clients and pose an XSS
115 # threat.
116 #
117 # TODO
118 # This method isn't flawless as some browsers may perform
119 # content-sniffing.
120 # This method isn't flawless as we do the mimetype lookup on the
121 # machine parsing the upload form, and not necessarily the machine
122 # serving the attachments.
123 if mimetypes.guess_type(
124 request.POST['attachment_file'].filename)[0] in \
125 UNSAFE_MIMETYPES:
126 public_filename = secure_filename('{0}.notsafe'.format(
127 request.POST['attachment_file'].filename))
128 else:
129 public_filename = secure_filename(
130 request.POST['attachment_file'].filename)
131
132 attachment_public_filepath \
133 = mg_globals.public_store.get_unique_filepath(
134 ['media_entries', unicode(media._id), 'attachment',
135 public_filename])
136
137 attachment_public_file = mg_globals.public_store.get_file(
138 attachment_public_filepath, 'wb')
139
140 try:
141 attachment_public_file.write(
142 request.POST['attachment_file'].file.read())
143 finally:
144 request.POST['attachment_file'].file.close()
145
146 media.attachment_files.append(dict(
147 name=request.POST['attachment_name'] \
148 or request.POST['attachment_file'].filename,
149 filepath=attachment_public_filepath,
150 created=datetime.utcnow(),
151 ))
152
153 media.save()
154
155 messages.add_message(
156 request, messages.SUCCESS,
157 "You added the attachment %s!" \
158 % (request.POST['attachment_name']
159 or request.POST['attachment_file'].filename))
160
161 return exc.HTTPFound(
162 location=media.url_for_self(request.urlgen))
163 return render_to_response(
164 request,
165 'mediagoblin/edit/attachments.html',
166 {'media': media,
167 'form': form})
168 else:
169 return exc.HTTPForbidden()
170
171
172 @require_active_login
173 def edit_profile(request):
174 # admins may edit any user profile given a username in the querystring
175 edit_username = request.GET.get('username')
176 if request.user.is_admin and request.user.username != edit_username:
177 user = request.db.User.find_one({'username': edit_username})
178 # No need to warn again if admin just submitted an edited profile
179 if request.method != 'POST':
180 messages.add_message(
181 request, messages.WARNING,
182 _("You are editing a user's profile. Proceed with caution."))
183 else:
184 user = request.user
185
186 form = forms.EditProfileForm(request.POST,
187 url=user.get('url'),
188 bio=user.get('bio'))
189
190 if request.method == 'POST' and form.validate():
191 user.url = unicode(request.POST['url'])
192 user.bio = unicode(request.POST['bio'])
193
194 user.save()
195
196 messages.add_message(request,
197 messages.SUCCESS,
198 _("Profile changes saved"))
199 return redirect(request,
200 'mediagoblin.user_pages.user_home',
201 user=user.username)
202
203 return render_to_response(
204 request,
205 'mediagoblin/edit/edit_profile.html',
206 {'user': user,
207 'form': form})
208
209
210 @require_active_login
211 def edit_account(request):
212 user = request.user
213 form = forms.EditAccountForm(request.POST,
214 wants_comment_notification=user.get('wants_comment_notification'))
215
216 if request.method == 'POST':
217 form_validated = form.validate()
218
219 #if the user has not filled in the new or old password fields
220 if not form.new_password.data and not form.old_password.data:
221 if form.wants_comment_notification.validate(form):
222 user.wants_comment_notification = \
223 form.wants_comment_notification.data
224 user.save()
225 messages.add_message(request,
226 messages.SUCCESS,
227 _("Account settings saved"))
228 return redirect(request,
229 'mediagoblin.user_pages.user_home',
230 user=user.username)
231
232 #so the user has filled in one or both of the password fields
233 else:
234 if form_validated:
235 password_matches = auth_lib.bcrypt_check_password(
236 form.old_password.data,
237 user.pw_hash)
238 if password_matches:
239 #the entire form validates and the password matches
240 user.pw_hash = auth_lib.bcrypt_gen_password_hash(
241 form.new_password.data)
242 user.wants_comment_notification = \
243 form.wants_comment_notification.data
244 user.save()
245 messages.add_message(request,
246 messages.SUCCESS,
247 _("Account settings saved"))
248 return redirect(request,
249 'mediagoblin.user_pages.user_home',
250 user=user.username)
251 else:
252 form.old_password.errors.append(_('Wrong password'))
253
254 return render_to_response(
255 request,
256 'mediagoblin/edit/edit_account.html',
257 {'user': user,
258 'form': form})
259
260
261 @require_active_login
262 @user_may_alter_collection
263 @get_user_collection
264 def edit_collection(request, collection):
265 defaults = dict(
266 title=collection.title,
267 slug=collection.slug,
268 description=collection.description)
269
270 form = forms.EditCollectionForm(
271 request.POST,
272 **defaults)
273
274 if request.method == 'POST' and form.validate():
275 # Make sure there isn't already a Collection with such a slug
276 # and userid.
277 slug_used = check_collection_slug_used(request.db, collection.creator,
278 request.POST['slug'], collection.id)
279
280 # Make sure there isn't already a Collection with this title
281 existing_collection = request.db.Collection.find_one({
282 'creator': request.user._id,
283 'title':request.POST['title']})
284
285 if existing_collection and existing_collection.id != collection.id:
286 messages.add_message(
287 request, messages.ERROR, _('You already have a collection called "%s"!' % request.POST['title']))
288 elif slug_used:
289 form.slug.errors.append(
290 _(u'A collection with that slug already exists for this user.'))
291 else:
292 collection.title = unicode(request.POST['title'])
293 collection.description = unicode(request.POST.get('description'))
294 collection.slug = unicode(request.POST['slug'])
295
296 collection.save()
297
298 return redirect(request, "mediagoblin.user_pages.user_collection",
299 user=collection.get_creator.username,
300 collection=collection.slug)
301
302 if request.user.is_admin \
303 and collection.creator != request.user._id \
304 and request.method != 'POST':
305 messages.add_message(
306 request, messages.WARNING,
307 _("You are editing another user's collection. Proceed with caution."))
308
309 return render_to_response(
310 request,
311 'mediagoblin/edit/edit_collection.html',
312 {'collection': collection,
313 'form': form})