def test_list_attachments_in_details(self): self.createAttachment(self.object) self.user.user_permissions.add( Permission.objects.get(codename='read_dummymodel')) self.user.user_permissions.add( Permission.objects.get(codename='read_attachment')) self.client.login(username='******', password='******') response = self.client.get( '/dummymodel/{pk}/'.format(pk=self.object.pk)) html = response.content self.assertTemplateUsed(response, template_name='paperclip/attachment_list.html') self.assertEqual( 1, len(get_attachment_model().objects.attachments_for_object( self.object))) self.assertNotIn("Submit attachment", html) for attachment in get_attachment_model( ).objects.attachments_for_object(self.object): self.assertIn(attachment.legend, html) self.assertIn(attachment.title, html) self.assertIn(attachment.attachment_file.url, html) self.assertIn('paperclip/fileicons/odt.png', html)
def setUpTestData(cls): cls.user = User.objects.create_user("foo_user", password="******", last_name="foo lastname", first_name="foo firstname") object = TestObject.objects.create(name="foo object") cls.filetype = get_filetype_model().objects.create(type="foo filetype") get_attachment_model().objects.create(content_object=object, filetype=cls.filetype, attachment_file="foo_file.txt", creator=cls.user, author="foo author", title="foo title", legend="foo legend", starred=True) cls.pk = object.pk
def serve_attachment(request, path): """ Serve media/ for authorized users only, since it can contain sensitive information (uploaded documents) """ original_path = re.sub(r'\.\d+x\d+_q\d+(_crop)?\.(jpg|png|jpeg)$', '', path, count=1, flags=re.IGNORECASE) attachment = get_object_or_404(get_attachment_model(), attachment_file=original_path) obj = attachment.content_object if not issubclass(obj._meta.model, mapentity_models.MapEntityMixin): raise Http404 if not obj.is_public(): if not request.user.is_authenticated: raise PermissionDenied if not request.user.has_perm(get_attachment_permission('read_attachment')): raise PermissionDenied if not request.user.has_perm('{}.read_{}'.format(obj._meta.app_label, obj._meta.model_name)): raise PermissionDenied content_type, encoding = mimetypes.guess_type(path) if settings.DEBUG: response = static.serve(request, path, settings.MEDIA_ROOT) else: response = HttpResponse() response[app_settings['SENDFILE_HTTP_HEADER']] = os.path.join(settings.MEDIA_URL_SECURE, path) response["Content-Type"] = content_type or 'application/octet-stream' if encoding: response["Content-Encoding"] = encoding if app_settings['SERVE_MEDIA_AS_ATTACHMENT']: response['Content-Disposition'] = "attachment; filename={0}".format( os.path.basename(path)) return response
def test_filename_is_used_if_no_title(self): data = self.attachmentPostData() data['title'] = '' self.client.post(add_url_for_obj(self.object), data=data) att = get_attachment_model().objects.attachments_for_object( self.object).get() self.assertTrue('face' in att.attachment_file.name)
def test_upload_creates_attachment(self): data = self.attachmentPostData() self.client.post(add_url_for_obj(self.object), data=data) att = get_attachment_model().objects.attachments_for_object(self.object).get() self.assertEqual(att.title, data['title']) self.assertEqual(att.legend, data['legend']) self.assertEqual(att.filetype.pk, data['filetype'])
def test_add_view(self): perm = Permission.objects.get(codename='add_attachment') self.user.user_permissions.add(perm) self.client.login(username="******", password="******") response = self.client.post('/paperclip/add-for/test_app/testobject/{pk}/'.format(pk=self.pk), {'embed': False, 'filetype': self.filetype.pk, 'next': '/foo-url/'}) self.assertRedirects(response, "/foo-url/", fetch_redirect_response=False) self.assertQuerysetEqual(get_attachment_model().objects.all(), ('<Attachment: foo_user attached >', '<Attachment: foo_user attached foo_file.txt>'))
def test_list_attachments_in_details(self): self.createAttachment(self.object) self.user.user_permissions.add(Permission.objects.get(codename='read_dummymodel')) self.user.user_permissions.add(Permission.objects.get(codename='read_attachment')) self.client.login(username='******', password='******') response = self.client.get('/dummymodel/{pk}/'.format(pk=self.object.pk)) html = response.content self.assertTemplateUsed(response, template_name='paperclip/attachment_list.html') self.assertEqual(1, len(get_attachment_model().objects.attachments_for_object(self.object))) self.assertNotIn(b"Submit attachment", html) for attachment in get_attachment_model().objects.attachments_for_object(self.object): self.assertIn(attachment.legend.encode(), html) self.assertIn(attachment.title.encode(), html) self.assertIn(attachment.attachment_file.url.encode(), html) self.assertIn(b'paperclip/fileicons/odt.png', html)
def createAttachment(self, obj): uploaded = SimpleUploadedFile('file.odt', b'*' * 128, content_type='application/vnd.oasis.opendocument.text') kwargs = { 'content_type': ContentType.objects.get_for_model(obj), 'object_id': obj.pk, 'filetype': get_filetype_model().objects.create(), 'creator': self.user, 'title': "Attachment title", 'legend': "Attachment legend", 'attachment_file': uploaded } return get_attachment_model().objects.create(**kwargs)
class Meta: model = settings.get_attachment_model() if settings.PAPERCLIP_ENABLE_VIDEO and not settings.PAPERCLIP_ENABLE_LINK: fields = ('embed', 'attachment_file', 'attachment_video', 'filetype', 'author', 'title', 'legend') elif settings.PAPERCLIP_ENABLE_VIDEO and settings.PAPERCLIP_ENABLE_LINK: fields = ('embed', 'attachment_file', 'attachment_video', 'attachment_link', 'filetype', 'author', 'title', 'legend') elif settings.PAPERCLIP_ENABLE_LINK and not settings.PAPERCLIP_ENABLE_VIDEO: fields = ('embed', 'attachment_file', 'attachment_link', 'filetype', 'author', 'title', 'legend') else: fields = ('attachment_file', 'filetype', 'author', 'title', 'legend')
def handle(self, *args, **options): interactive = options['interactive'] verbosity = int(options.get('verbosity', 1)) files = get_attachment_model().objects.order_by('attachment_file') files = files.values_list('attachment_file', flat=True) to_keep = [ os.path.join(settings.MEDIA_ROOT, path) for f in files for path in splits(f) ] to_keep = list(set(to_keep)) to_keep.sort(path_cmp) to_delete = [] for root, dirs, files in os.walk(PAPERCLIP_ROOT): for basename in chain(files, [d + '/' for d in dirs]): f = os.path.join(root, basename) if f not in to_keep: to_delete.append(f) to_delete.sort(path_cmp) if not to_delete and verbosity >= 1: self.stdout.write("No obsolete attached file to " "remove from disk.\n") if to_delete and interactive: self.stdout.write("You have requested to remove obsolete attached " "files from disk.\n\n") for f in to_delete: self.stdout.write(" {}".format(f)) confirm = input(""" This will permanently delete these files above! Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """) if confirm != 'yes': raise CommandError("Removing obsolete attached files " "cancelled.") for path in to_delete: if path.endswith('/'): os.rmdir(path) else: os.remove(path) if to_delete and verbosity >= 1: values = { 'n': len(to_delete), 's': "s" if len(to_delete) > 1 else "", } self.stdout.write("{n} obsolete attached file{s} removed from " "disk.\n".format(**values))
def render(self, context): obj = self.resolve(self.obj, context) var_name = self.resolve(self.var_name, context) if self.file_type: file_type = self.resolve(self.file_type, context) else: file_type = None if file_type: method = 'attachments_for_object_only_type' args = [obj, file_type] else: method = 'attachments_for_object' args = [obj] context[var_name] = getattr(settings.get_attachment_model().objects, method)(*args) return ''
def test_add_view(self): perm = Permission.objects.get(codename='add_attachment') self.user.user_permissions.add(perm) self.client.login(username="******", password="******") response = self.client.post( '/paperclip/add-for/test_app/testobject/{pk}/'.format(pk=self.pk), { 'embed': False, 'filetype': self.filetype.pk, 'next': '/foo-url/' }) self.assertRedirects(response, "/foo-url/", fetch_redirect_response=False) self.assertQuerysetEqual( get_attachment_model().objects.all(), ('<Attachment: foo_user attached >', '<Attachment: foo_user attached foo_file.txt>'))
def handle(self, *args, **options): interactive = options['interactive'] verbosity = int(options.get('verbosity', 1)) files = get_attachment_model().objects.order_by('attachment_file') files = files.values_list('attachment_file', flat=True) to_keep = [os.path.join(settings.MEDIA_ROOT, path) for f in files for path in splits(f)] to_keep = list(set(to_keep)) to_keep.sort(path_cmp) to_delete = [] for root, dirs, files in os.walk(PAPERCLIP_ROOT): for basename in chain(files, [d + '/' for d in dirs]): f = os.path.join(root, basename) if f not in to_keep: to_delete.append(f) to_delete.sort(path_cmp) if not to_delete and verbosity >= 1: self.stdout.write("No obsolete attached file to " "remove from disk.\n") if to_delete and interactive: self.stdout.write("You have requested to remove obsolete attached " "files from disk.\n\n") for f in to_delete: self .stdout.write(" {}".format(f)) confirm = input(""" This will permanently delete these files above! Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """) if confirm != 'yes': raise CommandError("Removing obsolete attached files " "cancelled.") for path in to_delete: if path.endswith('/'): os.rmdir(path) else: os.remove(path) if to_delete and verbosity >= 1: values = { 'n': len(to_delete), 's': "s" if len(to_delete) > 1 else "", } self.stdout.write("{n} obsolete attached file{s} removed from " "disk.\n".format(**values))
def update_attachment(request, attachment_pk, attachment_form=AttachmentForm, extra_context=None): attachment = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) obj = attachment.content_object if request.method == 'POST': form = attachment_form(request, request.POST, request.FILES, instance=attachment, object=obj) else: form = attachment_form(request, instance=attachment, object=obj) return _handle_attachment_form(request, obj, form, _('Update attachment %s'), _('Your attachment was updated.'), extra_context)
def star_attachment(request, attachment_pk): g = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) g.starred = request.GET.get('unstar') is None g.save() if g.starred: change_message = _('Star attachment %s') else: change_message = _('Unstar attachment %s') if settings.PAPERCLIP_ACTION_HISTORY_ENABLED: LogEntry.objects.log_action( user_id=request.user.pk, content_type_id=g.content_type.id, object_id=g.object_id, object_repr=force_text(g.content_object), action_flag=CHANGE, change_message=change_message % g.title, ) reply = {'status': 'ok', 'starred': g.starred} return JsonResponse(reply)
def update_attachment(request, attachment_pk, attachment_form=AttachmentForm, extra_context=None): attachment = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) obj = attachment.content_object if request.method == 'POST': form = attachment_form( request, request.POST, request.FILES, instance=attachment, object=obj) else: form = attachment_form( request, instance=attachment, object=obj) return _handle_attachment_form(request, obj, form, _('Update attachment %s'), _('Your attachment was updated.'), extra_context)
def get_attachments(request, app_label, model_name, pk): try: ct = ContentType.objects.get_by_natural_key(app_label, model_name) except ContentType.DoesNotExist: raise Http404 attachments = settings.get_attachment_model().objects.filter( content_type=ct, object_id=pk) reply = [{ 'id': attachment.id, 'title': attachment.title, 'legend': attachment.legend, 'url': attachment.attachment_file.url, 'type': attachment.filetype.type, 'author': attachment.author, 'filename': attachment.filename, 'mimetype': attachment.mimetype, 'is_image': attachment.is_image, 'starred': attachment.starred, } for attachment in attachments] return JsonResponse(reply)
def delete_attachment(request, attachment_pk): g = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) can_delete = (request.user.has_perm('paperclip.delete_attachment_others') or request.user == g.creator) if can_delete: g.delete() if settings.PAPERCLIP_ACTION_HISTORY_ENABLED: LogEntry.objects.log_action( user_id=request.user.pk, content_type_id=g.content_type.id, object_id=g.object_id, object_repr=force_text(g.content_object), action_flag=CHANGE, change_message=_('Remove attachment %s') % g.title, ) messages.success(request, _('Your attachment was deleted.')) else: error_msg = _('You are not allowed to delete this attachment.') messages.error(request, error_msg) next_url = request.GET.get('next', '/') return HttpResponseRedirect(next_url)
def star_attachment(request, attachment_pk): g = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) g.starred = request.GET.get('unstar') is None g.save() if g.starred: change_message = _('Star attachment %s') else: change_message = _('Unstar attachment %s') if settings.PAPERCLIP_ACTION_HISTORY_ENABLED: LogEntry.objects.log_action( user_id=request.user.pk, content_type_id=g.content_type.id, object_id=g.object_id, object_repr=force_text(g.content_object), action_flag=CHANGE, change_message=change_message % g.title, ) reply = { 'status': 'ok', 'starred': g.starred } return JsonResponse(reply)
def delete_attachment(request, attachment_pk): g = get_object_or_404(settings.get_attachment_model(), pk=attachment_pk) can_delete = ( request.user.has_perm(settings.get_attachment_permission('delete_attachment_others')) or request.user == g.creator) if can_delete: g.delete() if settings.PAPERCLIP_ACTION_HISTORY_ENABLED: LogEntry.objects.log_action( user_id=request.user.pk, content_type_id=g.content_type.id, object_id=g.object_id, object_repr=force_text(g.content_object), action_flag=CHANGE, change_message=_('Remove attachment %s') % g.title, ) messages.success(request, _('Your attachment was deleted.')) else: error_msg = _('You are not allowed to delete this attachment.') messages.error(request, error_msg) next_url = request.GET.get('next', '/') return HttpResponseRedirect(next_url)
def get_attachments(request, app_label, model_name, pk): try: ct = ContentType.objects.get_by_natural_key(app_label, model_name) except ContentType.DoesNotExist: raise Http404 attachments = settings.get_attachment_model().objects.filter(content_type=ct, object_id=pk) reply = [ { 'id': attachment.id, 'title': attachment.title, 'legend': attachment.legend, 'url': attachment.attachment_file.url, 'type': attachment.filetype.type, 'author': attachment.author, 'filename': attachment.filename, 'mimetype': attachment.mimetype, 'is_image': attachment.is_image, 'starred': attachment.starred, } for attachment in attachments ] return JsonResponse(reply)
def test_title_gives_name_to_file(self): data = self.attachmentPostData() self.client.post(add_url_for_obj(self.object), data=data) att = get_attachment_model().objects.attachments_for_object(self.object).get() self.assertTrue('a-title' in att.attachment_file.name)
def create_mapentity_model_permissions(model): """ Create all the necessary permissions for the specified model. And give all the required permission to the ``internal_user``, used for screenshotting and document conversion. :notes: Could have been implemented a metaclass on `MapEntityMixin`. We chose this approach to avoid problems with inheritance of permissions on abstract models. See: * https://code.djangoproject.com/ticket/10686 * http://stackoverflow.com/a/727956/141895 """ if not issubclass(model, mapentity_models.MapEntityMixin): return db = DEFAULT_DB_ALIAS internal_user = get_internal_user() perms_manager = Permission.objects.using(db) permissions = set() for view_kind in mapentity_models.ENTITY_KINDS: perm = model.get_entity_kind_permission(view_kind) codename = auth.get_permission_codename(perm, model._meta) name = "Can %s %s" % (perm, model._meta.verbose_name_raw) permissions.add((codename, _(name))) ctype = ContentType.objects.db_manager(db).get_for_model(model) for (codename, name) in permissions: p, created = perms_manager.get_or_create(codename=codename, content_type=ctype) if created: p.name = name[:50] p.save() logger.info("Permission '%s' created." % codename) for view_kind in (mapentity_models.ENTITY_LIST, mapentity_models.ENTITY_DOCUMENT): perm = model.get_entity_kind_permission(view_kind) codename = auth.get_permission_codename(perm, model._meta) internal_user_permission = internal_user.user_permissions.filter(codename=codename, content_type=ctype) if not internal_user_permission.exists(): permission = perms_manager.get(codename=codename, content_type=ctype) internal_user.user_permissions.add(permission) logger.info("Added permission %s to internal user %s" % (codename, internal_user)) attachmenttype = ContentType.objects.db_manager(db).get_for_model(get_attachment_model()) read_perm = dict(codename='read_attachment', content_type=attachmenttype) if not internal_user.user_permissions.filter(**read_perm).exists(): permission = perms_manager.get(**read_perm) internal_user.user_permissions.add(permission) logger.info("Added permission %s to internal user %s" % (permission.codename, internal_user))
def test_filename_is_used_if_no_title(self): data = self.attachmentPostData() data['title'] = '' self.client.post(add_url_for_obj(self.object), data=data) att = get_attachment_model().objects.attachments_for_object(self.object).get() self.assertTrue('face' in att.attachment_file.name)
from django.contrib import admin from paperclip import settings from paperclip.admin import AttachmentInlines from .models import TestObject admin.site.register(settings.get_filetype_model()) admin.site.register(settings.get_attachment_model()) @admin.register(TestObject) class TestObjectAdmin(admin.ModelAdmin): inlines = [AttachmentInlines]
def test_title_gives_name_to_file(self): data = self.attachmentPostData() self.client.post(add_url_for_obj(self.object), data=data) att = get_attachment_model().objects.attachments_for_object( self.object).get() self.assertTrue('a-title' in att.attachment_file.name)
class AttachmentInlines(GenericStackedInline): model = settings.get_attachment_model() extra = 1