Skip to content

Commit

Permalink
Merge b44b2a9 into fd3d593
Browse files Browse the repository at this point in the history
  • Loading branch information
wuuuduu committed Aug 27, 2019
2 parents fd3d593 + b44b2a9 commit 1ac8b94
Show file tree
Hide file tree
Showing 13 changed files with 221 additions and 55 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Expand Up @@ -6,6 +6,7 @@ python:
- "3.4"
- "3.5"
- "3.6"
- "3.7"
install:
- pip install -q -U pip tox tox-travis python-coveralls
script:
Expand Down
1 change: 1 addition & 0 deletions CHANGES
@@ -1,3 +1,4 @@
0.8.11.4.1 - Security fixes and CustomUserPassesTestMixin in SummernoteUploadAttachment view
0.8.11.4 - Minor changes on SummernoteWidget and update jQuery-file-upload files
0.8.11.3 - Fix style issues (Fullscreen and crispy-forms)
0.8.11.2 - Fix Fullscreen button for SummernoteWidget(iframe)
Expand Down
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -199,6 +199,14 @@ SUMMERNOTE_CONFIG = {
# Set `True` to return attachment paths in absolute URIs.
'attachment_absolute_uri': False,

# test_func in summernote upload view. (Allow upload images only when user passes the test)
# https://docs.djangoproject.com/en/2.2/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin
```
def example_test_func(request):
return request.user.groups.filter(name='group_name').exists()
```
'test_func_upload_view': example_test_func,

# You can add custom css/js for SummernoteWidget.
'css': (
),
Expand Down
2 changes: 1 addition & 1 deletion django_summernote/__init__.py
@@ -1,4 +1,4 @@
version_info = (0, 8, 11, 4)
version_info = (0, 8, 11, 4, 1)

__version__ = version = '.'.join(map(str, version_info))
__project__ = PROJECT = 'django-summernote'
Expand Down
7 changes: 5 additions & 2 deletions django_summernote/admin.py
@@ -1,6 +1,8 @@
from django.contrib import admin
from django.contrib.admin.options import InlineModelAdmin
from django.db import models

from django_summernote.forms import AttachmentAdminForm
from django_summernote.utils import get_attachment_model, using_config
from django_summernote.widgets import SummernoteWidget, SummernoteInplaceWidget

Expand All @@ -23,17 +25,18 @@ def formfield_for_dbfield(self, db_field, *args, **kwargs):


class SummernoteInlineModelAdmin(SummernoteModelAdminMixin, InlineModelAdmin):
pass
form = AttachmentAdminForm


class SummernoteModelAdmin(SummernoteModelAdminMixin, admin.ModelAdmin):
pass
form = AttachmentAdminForm


class AttachmentAdmin(admin.ModelAdmin):
list_display = ['name', 'file', 'uploaded']
search_fields = ['name']
ordering = ('-id',)
form = AttachmentAdminForm

def save_model(self, request, obj, form, change):
obj.name = obj.file.name if (not obj.name) else obj.name
Expand Down
8 changes: 6 additions & 2 deletions django_summernote/apps.py
@@ -1,8 +1,8 @@
from django.apps import AppConfig
from django.conf import settings as django_settings
from django_summernote.utils import (
LANG_TO_LOCALE, uploaded_filepath, get_theme_files
)
LANG_TO_LOCALE, uploaded_filepath, get_theme_files,
example_test_func)


class DjangoSummernoteConfig(AppConfig):
Expand Down Expand Up @@ -36,6 +36,10 @@ def get_default_config(self):
'attachment_model': 'django_summernote.Attachment',
'attachment_absolute_uri': False,

# additional test_func, for example you want to check if user is in specific group:
# https://docs.djangoproject.com/en/2.2/topics/auth/default/#django.contrib.auth.mixins.UserPassesTestMixin
'test_func_upload_view': example_test_func,

# Shortcut name for jQuery
'jquery': '$',

Expand Down
15 changes: 15 additions & 0 deletions django_summernote/forms.py
@@ -0,0 +1,15 @@
from django import forms

from django_summernote.utils import get_attachment_model


class UploadForm(forms.Form):
file = forms.ImageField(required=True)


class AttachmentAdminForm(forms.ModelForm):
file = forms.ImageField(required=True)

class Meta:
model = get_attachment_model()
fields = '__all__'
2 changes: 1 addition & 1 deletion django_summernote/test_settings.py
Expand Up @@ -16,7 +16,7 @@
'django.middleware.locale.LocaleMiddleware',
)

if django.VERSION <= (1, 9):
if django.VERSION < (1, 10):
MIDDLEWARE_CLASSES = __MIDDLEWARE__
else:
MIDDLEWARE = __MIDDLEWARE__
Expand Down
158 changes: 120 additions & 38 deletions django_summernote/tests.py
@@ -1,8 +1,14 @@
# -*- coding: utf-8 -*-
try:
from unittest.mock import patch
except ImportError:
from mock import patch

from django.apps import apps
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import User
from django.db import models

try:
# Django >= 2.0
from django.urls import reverse
Expand All @@ -13,6 +19,8 @@
from django_summernote.utils import get_attachment_storage, get_attachment_model
import json
from imp import reload
from io import BytesIO
from PIL import Image


class DjangoSummernoteTest(TestCase):
Expand All @@ -25,6 +33,16 @@ def setUp(self):
self.app_config.update_config()
self.summernote_config = self.app_config.config

@staticmethod
def create_test_image():
# https://wildfish.com/blog/2014/02/27/generating-in-memory-image-for-tests-python/
file = BytesIO()
image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
image.save(file, 'png')
file.name = 'test.png'
file.seek(0)
return file

def test_base(self):
self.assertTrue(True)

Expand Down Expand Up @@ -124,16 +142,15 @@ class SimpleForm(forms.Form):
assert f.cleaned_data.get('foobar')

def test_attachment(self):
import os
url = reverse('django_summernote-upload_attachment')
image = self.create_test_image()

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 200)
self.assertContains(
response, '"name": "%s"' % os.path.basename(__file__))
self.assertContains(response, '"url": ')
self.assertContains(response, '"size": ')
response = self.client.post(url, {'files': image})
self.assertEqual(response.status_code, 200)
self.assertContains(
response, '"name": "%s"' % image.name)
self.assertContains(response, '"url": ')
self.assertContains(response, '"size": ')

def test_attachment_with_custom_storage(self):
self.summernote_config['attachment_storage_class'] = \
Expand All @@ -145,9 +162,10 @@ def test_attachment_with_custom_storage(self):

url = reverse('django_summernote-upload_attachment')

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 200)
image = self.create_test_image()

response = self.client.post(url, {'files': image})
self.assertEqual(response.status_code, 200)

file_field.storage = original_storage

Expand Down Expand Up @@ -184,9 +202,9 @@ def test_attachment_with_bad_storage(self):

url = reverse('django_summernote-upload_attachment')

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertNotEqual(response.status_code, 200)
image = self.create_test_image()
response = self.client.post(url, {'files': image})
self.assertNotEqual(response.status_code, 200)

file_field.storage = original_storage

Expand Down Expand Up @@ -224,16 +242,17 @@ def test_attachment_no_attachment(self):
self.assertNotEqual(response.status_code, 200)

def test_attachment_filesize_exceed(self):
import os

url = reverse('django_summernote-upload_attachment')
size = os.path.getsize(__file__)
image = self.create_test_image()

size = len(image.getvalue()) # how to do it in better way?

old_limit = self.summernote_config['attachment_filesize_limit']
self.summernote_config['attachment_filesize_limit'] = size - 1

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertNotEqual(response.status_code, 200)
response = self.client.post(url, {'files': [image]})
self.assertNotEqual(response.status_code, 200)
self.assertEqual(response.json()['message'], 'File size exceeds the limit allowed and cannot be saved')

self.summernote_config['attachment_filesize_limit'] = old_limit

Expand All @@ -244,41 +263,82 @@ def test_attachment_require_authentication(self):
self.user = User.objects.create_user(
username=self.username, password=self.password)

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 403)
image = self.create_test_image()

response = self.client.post(url, {'files': image})
self.assertEqual(response.status_code, 403)

c = Client()
c.login(username=self.username, password=self.password)

image.seek(0)
response = c.post(url, {'files': image})
self.assertEqual(response.status_code, 200)

self.summernote_config['attachment_require_authentication'] = False

@patch('django_summernote.views.logger')
def test_attachment_disable_attachment(self, mock_logging):
url = reverse('django_summernote-upload_attachment')
self.summernote_config['disable_attachment'] = True

self.user = User.objects.create_user(
username=self.username, password=self.password)

image = self.create_test_image()

c = Client()
c.login(username=self.username, password=self.password)

response = c.post(url, {'files': image})
self.assertEqual(response.status_code, 403)
self.assertDictEqual(response.json(), {"status": "false", "message": "Attachment module is disabled"})
self.assertTrue(mock_logging.error.called)
self.summernote_config['disable_attachment'] = False

@patch('django_summernote.views.logger')
def test_wrong_attachment(self, mock_logging):
self.user = User.objects.create_user(
username=self.username, password=self.password)

c = Client()
c.login(username=self.username, password=self.password)

url = reverse('django_summernote-upload_attachment')
with open(__file__, 'rb') as fp:
response = c.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 200)

self.summernote_config['attachment_require_authentication'] = False
self.assertEqual(response.status_code, 400)
self.assertDictEqual(
response.json(),
{
"status": "false",
"message": "Upload a valid image. The file you uploaded was either not an image or a corrupted image."
}
)
self.assertTrue(mock_logging.error.called)

def test_attachment_not_require_authentication(self):
image = self.create_test_image()
url = reverse('django_summernote-upload_attachment')
self.summernote_config['attachment_require_authentication'] = False

self.user = User.objects.create_user(
username=self.username, password=self.password)

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 200)
response = self.client.post(url, {'files': image})
self.assertEqual(response.status_code, 200)

@override_settings(USE_THOUSAND_SEPARATOR=True)
def test_attachment_with_thousand_separator_option(self):
import os
url = reverse('django_summernote-upload_attachment')
size = os.path.getsize(__file__)
image = self.create_test_image()
size = len(image.getvalue()) # how to do it in better way?

with open(__file__, 'rb') as fp:
response = self.client.post(url, {'files': [fp]})
self.assertEqual(response.status_code, 200)
res = json.loads(response.content.decode('utf-8'))
self.assertEqual(res['files'][0]['size'], size)
response = self.client.post(url, {'files': [image]})
self.assertEqual(response.status_code, 200)
res = json.loads(response.content.decode('utf-8'))
self.assertEqual(res['files'][0]['size'], size)

def test_lang_specified(self):
old_lang = self.summernote_config['summernote']['lang']
Expand Down Expand Up @@ -395,13 +455,35 @@ def test_attachment_admin_default_name(self):
aa = AttachmentAdmin(Attachment, self.site)
attachment = Attachment()
with open(__file__, 'rb') as fp:
djangoFile = File(fp)
djangoFile.name = os.path.basename(djangoFile.name)
attachment.file = djangoFile
django_file = File(fp)
django_file.name = os.path.basename(django_file.name)
attachment.file = django_file
self.assertFalse(aa.form().is_valid())
self.assertEqual(attachment.name, None)
aa.save_model(None, attachment, None, None)
self.assertEqual(attachment.name, os.path.basename(__file__))

def test_attachment_admin_default_name_valid_file(self):
from django_summernote.admin import AttachmentAdmin
from django_summernote.models import Attachment
from django.core.files import File
import os

aa = AttachmentAdmin(Attachment, self.site)

attachment = Attachment()

image = self.create_test_image()

django_file = File(image)
django_file.name = os.path.basename(image.name)
attachment.file = django_file
form = aa.form(files={'file': django_file})
self.assertTrue(form.is_valid())
self.assertEqual(attachment.name, None)
aa.save_model(request=None, obj=attachment, form=form, change=None)
self.assertEqual(attachment.name, image.name)

def test_config_allow_blank_values(self):
from django_summernote.widgets import SummernoteWidget

Expand Down

0 comments on commit 1ac8b94

Please sign in to comment.