Merge pull request #1147 from normanjaeckel/EditOwnFiles

Enable update and delete view for uploader refering to his own files.
This commit is contained in:
Oskar Hahn 2013-12-04 07:18:59 -08:00
commit 863e5262a6
5 changed files with 134 additions and 42 deletions

View File

@ -10,6 +10,8 @@ Version 1.6 (unreleased)
Participants: Participants:
- Disable widgets by default. - Disable widgets by default.
Files:
- Enabled update and delete view for uploader refering to his own files.
Other: Other:
- Changed widget api. Used new metaclass. - Changed widget api. Used new metaclass.

View File

@ -7,30 +7,34 @@ from openslides.utils.forms import CssClassMixin
from .models import Mediafile from .models import Mediafile
class MediafileNormalUserCreateForm(CssClassMixin, ModelForm): class MediafileFormMixin(object):
""" """
Form to create a media file. This form is only used by normal users, Mixin for mediafile forms. It is used to delete old files.
not by managers.
""" """
class Meta:
model = Mediafile
exclude = ('uploader',)
class MediafileUpdateForm(CssClassMixin, ModelForm):
"""
Form to edit mediafile entries. This form is only for managers to update
the mediafile entry.
"""
class Meta:
model = Mediafile
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" """
Method to save the form. Here the overwrite is to delete old files. Method to save the form. Here the override is to delete old files.
""" """
if not self.instance.pk is None: if not self.instance.pk is None:
old_file = Mediafile.objects.get(pk=self.instance.pk).mediafile old_file = Mediafile.objects.get(pk=self.instance.pk).mediafile
if not old_file == self.instance.mediafile: if not old_file == self.instance.mediafile:
old_file.delete() old_file.delete()
return super(MediafileUpdateForm, self).save(*args, **kwargs) return super(MediafileFormMixin, self).save(*args, **kwargs)
class MediafileNormalUserForm(MediafileFormMixin, CssClassMixin, ModelForm):
"""
This form is only used by normal users, not by managers.
"""
class Meta:
model = Mediafile
fields = ('mediafile', 'title', 'is_presentable')
class MediafileManagerForm(MediafileFormMixin, CssClassMixin, ModelForm):
"""
This form is only used be managers, not by normal users.
"""
class Meta:
model = Mediafile
fields = ('mediafile', 'title', 'uploader', 'is_presentable')

View File

@ -20,7 +20,7 @@
<th>{% trans 'Size' %}</th> <th>{% trans 'Size' %}</th>
<th>{% trans 'Upload time' %}</th> <th>{% trans 'Upload time' %}</th>
<th>{% trans 'Uploaded by' %}</th> <th>{% trans 'Uploaded by' %}</th>
{% if perms.mediafile.can_manage %} {% if perms.mediafile.can_manage or perms.mediafile.can_upload %}
<th class="mini_width">{% trans "Actions" %}</th> <th class="mini_width">{% trans "Actions" %}</th>
{% endif %} {% endif %}
</tr> </tr>
@ -31,18 +31,22 @@
<td>{{ mediafile.get_filesize }}</td> <td>{{ mediafile.get_filesize }}</td>
<td>{{ mediafile.timestamp }}</td> <td>{{ mediafile.timestamp }}</td>
<td><a href="{{ mediafile.uploader|absolute_url }}">{{ mediafile.uploader }}</a></td> <td><a href="{{ mediafile.uploader|absolute_url }}">{{ mediafile.uploader }}</a></td>
{% if perms.mediafile.can_manage %} {% if perms.mediafile.can_manage or perms.mediafile.can_upload %}
<td> <td>
{% if mediafile.with_action_buttons %}
<span style="width: 1px; white-space: nowrap;"> <span style="width: 1px; white-space: nowrap;">
<a href="{{ mediafile|absolute_url:'update' }}" rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini"><i class="icon-pencil"></i></a> <a href="{{ mediafile|absolute_url:'update' }}" rel="tooltip" data-original-title="{% trans 'Edit' %}" class="btn btn-mini"><i class="icon-pencil"></i></a>
<a href="{{ mediafile|absolute_url:'delete' }}" rel="tooltip" data-original-title="{% trans 'Delete' %}" class="btn btn-mini"><i class="icon-remove"></i></a> <a href="{{ mediafile|absolute_url:'delete' }}" rel="tooltip" data-original-title="{% trans 'Delete' %}" class="btn btn-mini"><i class="icon-remove"></i></a>
{% if mediafile.is_presentable %}{% if mediafile.filetype in mediafile.PRESENTABLE_FILE_TYPES %} {% if perms.mediafile.can_manage and mediafile.is_presentable %}{% if mediafile.filetype in mediafile.PRESENTABLE_FILE_TYPES %}
<a href="{{ mediafile|absolute_url:'projector' }}" class="activate_link choose-pdf btn {% if mediafile.is_active_slide %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}"> <a href="{{ mediafile|absolute_url:'projector' }}" class="activate_link choose-pdf btn {% if mediafile.is_active_slide %}btn-primary{% endif %} btn-mini" title="{% trans 'Show' %}">
<i class="icon-facetime-video {% if mediafile.is_active_slide %}icon-white{% endif %}"> <i class="icon-facetime-video {% if mediafile.is_active_slide %}icon-white{% endif %}">
</i> </i>
</a> </a>
{% endif %}{% endif %} {% endif %}{% endif %}
</span> </span>
{% else %}
&nbsp;
{% endif %}
</td> </td>
{% endif %} {% endif %}
</tr> </tr>

View File

@ -11,7 +11,7 @@ from openslides.utils.tornado_webserver import ProjectorSocketHandler
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView, from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
UpdateView) UpdateView)
from .forms import MediafileNormalUserCreateForm, MediafileUpdateForm from .forms import MediafileManagerForm, MediafileNormalUserForm
from .models import Mediafile from .models import Mediafile
@ -26,27 +26,34 @@ class MediafileListView(ListView):
request.user.has_perm('mediafile.can_upload') or request.user.has_perm('mediafile.can_upload') or
request.user.has_perm('mediafile.can_manage')) request.user.has_perm('mediafile.can_manage'))
def get_context_data(self, *args, **kwargs):
context = super(MediafileListView, self).get_context_data(*args, **kwargs)
for mediafile in context['mediafile_list']:
if self.request.user.has_perm('mediafile.can_manage'):
mediafile.with_action_buttons = True
elif self.request.user.has_perm('mediafile.can_upload') and self.request.user == mediafile.uploader:
mediafile.with_action_buttons = True
else:
mediafile.with_action_buttons = False
return context
class MediafileCreateView(CreateView):
class MediafileViewMixin(object):
""" """
View to upload a new file. A manager can also set the uploader, else Mixin for create and update views for mediafiles.
the request user is set as uploader.
A manager can set the uploader manually, else the request user is set as uploader.
""" """
model = Mediafile model = Mediafile
permission_required = 'mediafile.can_upload'
success_url_name = 'mediafile_list' success_url_name = 'mediafile_list'
url_name_args = [] url_name_args = []
def get_form(self, form_class): def get_form(self, form_class):
form_kwargs = self.get_form_kwargs() form_kwargs = self.get_form_kwargs()
if self.request.method == 'GET':
form_kwargs['initial'].update({'uploader': self.request.user.person_id})
if not self.request.user.has_perm('mediafile.can_manage'): if not self.request.user.has_perm('mediafile.can_manage'):
# Returns our own ModelForm from .forms return MediafileNormalUserForm(**form_kwargs)
return MediafileNormalUserCreateForm(**form_kwargs)
else: else:
# Returns a ModelForm created by Django. return MediafileManagerForm(**form_kwargs)
return form_class(**form_kwargs)
def manipulate_object(self, *args, **kwargs): def manipulate_object(self, *args, **kwargs):
""" """
@ -56,18 +63,29 @@ class MediafileCreateView(CreateView):
""" """
if not self.request.user.has_perm('mediafile.can_manage'): if not self.request.user.has_perm('mediafile.can_manage'):
self.object.uploader = self.request.user self.object.uploader = self.request.user
return super(MediafileCreateView, self).manipulate_object(*args, **kwargs) return super(MediafileViewMixin, self).manipulate_object(*args, **kwargs)
class MediafileUpdateView(UpdateView): class MediafileCreateView(MediafileViewMixin, CreateView):
"""
View to upload a new file.
"""
permission_required = 'mediafile.can_upload'
def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(MediafileCreateView, self).get_form_kwargs(*args, **kwargs)
if self.request.method == 'GET':
form_kwargs['initial'].update({'uploader': self.request.user.person_id})
return form_kwargs
class MediafileUpdateView(MediafileViewMixin, UpdateView):
""" """
View to edit the entry of an uploaded file. View to edit the entry of an uploaded file.
""" """
model = Mediafile def has_permission(self, request, *args, **kwargs):
permission_required = 'mediafile.can_manage' return (request.user.has_perm('mediafile.can_manage') or
form_class = MediafileUpdateForm (request.user.has_perm('mediafile.can_upload') and self.get_object().uploader == self.request.user))
success_url_name = 'mediafile_list'
url_name_args = []
def get_form_kwargs(self, *args, **kwargs): def get_form_kwargs(self, *args, **kwargs):
form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs) form_kwargs = super(MediafileUpdateView, self).get_form_kwargs(*args, **kwargs)
@ -80,9 +98,12 @@ class MediafileDeleteView(DeleteView):
View to delete the entry of an uploaded file and the file itself. View to delete the entry of an uploaded file and the file itself.
""" """
model = Mediafile model = Mediafile
permission_required = 'mediafile.can_manage'
success_url_name = 'mediafile_list' success_url_name = 'mediafile_list'
def has_permission(self, request, *args, **kwargs):
return (request.user.has_perm('mediafile.can_manage') or
(request.user.has_perm('mediafile.can_upload') and self.get_object().uploader == self.request.user))
def on_clicked_yes(self, *args, **kwargs): def on_clicked_yes(self, *args, **kwargs):
"""Deletes the file in the filesystem, if user clicks "Yes".""" """Deletes the file in the filesystem, if user clicks "Yes"."""
self.object.mediafile.delete() self.object.mediafile.delete()

View File

@ -35,7 +35,7 @@ class MediafileTest(TestCase):
# Setup a mediafile object # Setup a mediafile object
self.tmp_dir = settings.MEDIA_ROOT self.tmp_dir = settings.MEDIA_ROOT
tmpfile_no, mediafile_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir) tmpfile_no, mediafile_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
self.object = Mediafile.objects.create(title='Title File 1', mediafile=mediafile_path, uploader=self.vip_user) self.object = Mediafile.objects.create(title='Title File 1', mediafile=mediafile_path, uploader=self.normal_user)
os.close(tmpfile_no) os.close(tmpfile_no)
def tearDown(self): def tearDown(self):
@ -132,7 +132,7 @@ class MediafileTest(TestCase):
clients = self.login_clients() clients = self.login_clients()
response = clients['client_manager'].get('/mediafile/1/edit/') response = clients['client_manager'].get('/mediafile/1/edit/')
self.assertContains(response, '---------', status_code=200) self.assertContains(response, '---------', status_code=200)
self.assertContains(response, '<option value="user:2" selected="selected">mediafile_test_vip_user</option>', status_code=200) self.assertContains(response, '<option value="user:3" selected="selected">mediafile_test_normal_user</option>', status_code=200)
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html') self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
response = clients['client_vip_user'].get('/mediafile/1/edit/') response = clients['client_vip_user'].get('/mediafile/1/edit/')
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
@ -142,6 +142,15 @@ class MediafileTest(TestCase):
response = bad_client.get('/mediafile/1/edit/') response = bad_client.get('/mediafile/1/edit/')
self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/edit/', status_code=302, target_status_code=200) self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/edit/', status_code=302, target_status_code=200)
def test_edit_mediafile_get_request_own_file(self):
clients = self.login_clients()
self.object.uploader = self.vip_user
self.object.save()
response = clients['client_vip_user'].get('/mediafile/1/edit/')
self.assertNotContains(response, '---------', status_code=200)
self.assertNotContains(response, '<option value="user:2" selected="selected">mediafile_test_vip_user</option>', status_code=200)
self.assertTemplateUsed(response, 'mediafile/mediafile_form.html')
def test_edit_mediafile_post_request(self): def test_edit_mediafile_post_request(self):
# Test only one user # Test only one user
tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir) tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
@ -161,6 +170,31 @@ class MediafileTest(TestCase):
object_2.mediafile.delete() object_2.mediafile.delete()
self.assertFalse(os.path.exists(path_2)) self.assertFalse(os.path.exists(path_2))
def test_edit_mediafile_post_request_own_file(self):
tmpfile_no, mediafile_2_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_2 = Mediafile.objects.create(title='Title File 2b', mediafile=mediafile_2_path, uploader=self.vip_user)
client = self.login_clients()['client_vip_user']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello vip user')
response_1 = client.post('/mediafile/2/edit/',
{'title': 'new_test_file_title_2b',
'mediafile': new_file_1})
self.assertEqual(response_1.status_code, 302)
object_2 = Mediafile.objects.get(pk=2)
self.assertEqual(object_2.mediafile.url, '/media/file/new_test_file.txt')
self.assertEqual(object_2.uploader, self.vip_user)
path_2 = object_2.mediafile.path
object_2.mediafile.delete()
self.assertFalse(os.path.exists(path_2))
def test_edit_mediafile_post_request_another_file(self):
client = self.login_clients()['client_vip_user']
new_file_1 = SimpleUploadedFile(name='new_test_file.txt', content='test content hello vip user')
response = client.post('/mediafile/1/edit/',
{'title': 'new_test_file_title_2c',
'mediafile': new_file_1})
self.assertEqual(response.status_code, 403)
def test_delete_mediafile_get_request(self): def test_delete_mediafile_get_request(self):
clients = self.login_clients() clients = self.login_clients()
response = clients['client_manager'].get('/mediafile/1/del/') response = clients['client_manager'].get('/mediafile/1/del/')
@ -173,6 +207,12 @@ class MediafileTest(TestCase):
response = bad_client.get('/mediafile/2/del/') response = bad_client.get('/mediafile/2/del/')
self.assertRedirects(response, expected_url='/login/?next=/mediafile/2/del/', status_code=302, target_status_code=200) self.assertRedirects(response, expected_url='/login/?next=/mediafile/2/del/', status_code=302, target_status_code=200)
def test_delete_mediafile_get_request_own_file(self):
self.object.uploader = self.vip_user
self.object.save()
response = self.login_clients()['client_vip_user'].get('/mediafile/1/del/')
self.assertRedirects(response, expected_url='/mediafile/1/edit/', status_code=302, target_status_code=200)
def test_delete_mediafile_post_request(self): def test_delete_mediafile_post_request(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir) tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no) os.close(tmpfile_no)
@ -182,6 +222,27 @@ class MediafileTest(TestCase):
self.assertRedirects(response_1, expected_url='/mediafile/', status_code=302, target_status_code=200) self.assertRedirects(response_1, expected_url='/mediafile/', status_code=302, target_status_code=200)
self.assertFalse(os.path.exists(object_3.mediafile.path)) self.assertFalse(os.path.exists(object_3.mediafile.path))
def test_delete_mediafile_post_request_own_file(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_3 = Mediafile.objects.create(title='Title File 3b', mediafile=mediafile_3_path, uploader=self.vip_user)
client_1 = self.login_clients()['client_vip_user']
response_1 = client_1.post('/mediafile/2/del/', {'yes': 'foo'})
self.assertRedirects(response_1, expected_url='/mediafile/', status_code=302, target_status_code=200)
self.assertFalse(os.path.exists(object_3.mediafile.path))
def test_delete_mediafile_post_request_another_file(self):
tmpfile_no, mediafile_3_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no)
object_3 = Mediafile.objects.create(title='Title File 3c', mediafile=mediafile_3_path, uploader=self.normal_user)
client_1 = self.login_clients()['client_vip_user']
response = client_1.post('/mediafile/2/del/', {'yes': 'foo'})
self.assertEqual(response.status_code, 403)
path_3 = object_3.mediafile.path
self.assertTrue(os.path.exists(path_3))
object_3.mediafile.delete()
self.assertFalse(os.path.exists(path_3))
def test_filesize(self): def test_filesize(self):
tmpfile_no, mediafile_4_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir) tmpfile_no, mediafile_4_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=self.tmp_dir)
os.close(tmpfile_no) os.close(tmpfile_no)