1. Installation dependencies
django-simditor==0.0.15
2. Configuration
INSTALLED_APPS += (
'simditor',
)
SIMDITOR_TOOLBAR = [
'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale',
'color', '|', 'ol', 'ul', 'blockquote', 'table', '|', 'link', 'image',
'hr', '|', 'indent', 'outdent', 'alignment', # '|', 'markdown'
]
SIMDITOR_UPLOAD_PATH = 'uploads/'
SIMDITOR_IMAGE_BACKEND = 'pillow'
# 图片上传的url
SIMDITOR_CONFIGS = {
'toolbar': SIMDITOR_TOOLBAR,
'upload': {
'url': SITE_URL + '/simditor/upload/',
'fileKey': 'upload',
'image_size': 1024 * 1024 * 4 # max image size 4MB
},
'emoji': {
'imagePath': '/static/simditor/images/emoji/'
}
}
3. Generate code for simditor editor form
# -*- coding: utf-8
from __future__ import unicode_literals, absolute_import
from django.conf.urls import include, url
from simditor import utils, image_processing
from .widgets import SimditorWidget
import os
from datetime import datetime
from django.conf import settings
from django.core.files.storage import default_storage
from django.http import JsonResponse
from django.views import generic
from django.views.decorators.csrf import csrf_exempt
# from . import utils, image_processing
class SimditorEditor():
"""Editors should inherit from this. See wiki.editors for examples."""
# The editor id can be used for conditional testing. If you write your
# own editor class, you can use the same editor_id as some editor
editor_id = 'simditor'
def get_admin_widget(self):
return SimditorWidget()
def get_widget(self, instance=None):
return SimditorWidget()
def get_urls(self):
return [url(r'^simditor/', include('simditor.urls'))]
class AdminMedia:
css = {'all': (
'wiki/simditor/styles/simditor.css',
'wiki/simditor/styles/simditor-markdown.css',
)}
js = (
'wiki/simditor/scripts/jquery.min.js',
'wiki/simditor/scripts/module.js',
'wiki/simditor/scripts/uploader.js',
'wiki/simditor/scripts/hotkeys.js',
'wiki/simditor/scripts/simditor.js',
'wiki/simditor/scripts/marked.js',
'wiki/simditor/scripts/to-markdown.js',
'wiki/simditor/scripts/simditor-markdown.js',
)
class Media:
css = {'all': (
'wiki/simditor/styles/simditor.css',
'wiki/simditor/styles/simditor-markdown.css',
)}
js = (
'wiki/simditor/scripts/jquery.min.js',
'wiki/simditor/scripts/module.js',
'wiki/simditor/scripts/uploader.js',
'wiki/simditor/scripts/hotkeys.js',
'wiki/simditor/scripts/simditor.js',
'wiki/simditor/scripts/marked.js',
'wiki/simditor/scripts/to-markdown.js',
'wiki/simditor/scripts/simditor-markdown.js',
)
def get_upload_filename(upload_name):
# Generate date based path to put uploaded file.
date_path = datetime.now().strftime('%Y/%m/%d')
# Complete upload path (upload_path + date_path).
upload_path = os.path.join(settings.SIMDITOR_UPLOAD_PATH, date_path)
if getattr(settings, 'SIMDITOR_UPLOAD_SLUGIFY_FILENAME', True):
upload_name = utils.slugify_filename(upload_name)
return default_storage.get_available_name(os.path.join(upload_path, upload_name))
def upload_handler(request):
files = request.FILES
upload_config = settings.SIMDITOR_CONFIGS.get(
'upload', {'fileKey': 'upload'})
filekey = upload_config.get('fileKey', 'upload')
uploaded_file = files.get(filekey)
if not uploaded_file:
retdata = {'file_path': '', 'success': False,
'msg': '图片上传失败,无法获取到图片对象!'}
return JsonResponse(retdata)
image_size = upload_config.get('image_size')
if image_size and uploaded_file.size > image_size:
retdata = {'file_path': '', 'success': False,
'msg': '上传失败,已超出图片最大限制!'}
return JsonResponse(retdata)
backend = image_processing.get_backend()
if not getattr(settings, 'SIMDITOR_ALLOW_NONIMAGE_FILES', True):
try:
backend.image_verify(uploaded_file)
except utils.NotAnImageException:
retdata = {'file_path': '', 'success': False,
'msg': '图片格式错误!'}
return JsonResponse(retdata)
filename = get_upload_filename(uploaded_file.name)
saved_path = default_storage.save(filename, uploaded_file)
url = utils.get_media_url(saved_path)
is_api = settings.SIMDITOR_CONFIGS.get('is_api', False)
url = request.META.get('HTTP_ORIGIN') + url if is_api else url
retdata = {'file_path': url, 'success': True, 'msg': '上传成功!'}
return JsonResponse(retdata)
class ImageUploadView(generic.View):
"""ImageUploadView."""
http_method_names = ['post']
def post(self, request, **kwargs):
"""Post."""
return upload_handler(request)
UPLOAD = csrf_exempt(ImageUploadView.as_view())
There is a pit here. Since SimditorWidget() is not recognized from the dependency, my solution: copy the file where the content is located from the dependency, and import widgets.py directly from this file
"""simditor widgets."""
from __future__ import absolute_import
from django import forms
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.core.serializers.json import DjangoJSONEncoder
from django.template.loader import render_to_string
from django.utils.encoding import force_text
from django.utils.safestring import mark_safe
from django.utils.html import conditional_escape
from django.utils.functional import Promise
from conf.default import SITE_URL
try:
# Django >=2.1
from django.forms.widgets import get_default_renderer
IS_NEW_WIDGET = True
except ImportError:
IS_NEW_WIDGET = False
try:
# Django >=1.7
from django.forms.utils import flatatt
except ImportError:
# Django <1.7
from django.forms.util import flatatt # pylint disable=E0611, E0401
class LazyEncoder(DjangoJSONEncoder):
"""LazyEncoder."""
# pylint disable=E0202
def default(self, obj):
if isinstance(obj, Promise):
return force_text(obj)
return super(LazyEncoder, self).default(obj)
JSON_ENCODE = LazyEncoder().encode
FULL_TOOLBAR = [
'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale',
'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link',
'image', 'hr', '|', 'indent', 'outdent', 'alignment', 'checklist',
'markdown', 'fullscreen'
]
DEFAULT_TOOLBAR = [
'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale',
'color', '|', 'ol', 'ul', 'blockquote', 'table', '|', 'link', 'image',
'hr', '|', 'indent', 'outdent', 'alignment', # '|', 'markdown'
]
SIMDITOR_UPLOAD_PATH = 'uploads/'
SIMDITOR_IMAGE_BACKEND = 'pillow'
# 我的理解是前端的上传图片请求配置
DEFAULT_CONFIG = {
'toolbar': DEFAULT_TOOLBAR,
'cleanPaste': True,
'tabIndent': True,
'pasteImage': True,
'upload': {
'url': SITE_URL + '/simditor/upload/',
'fileKey': 'upload',
'image_size': 1024 * 1024 * 4 # max image size 4MB
},
}
class SimditorWidget(forms.Textarea):
"""
Widget providing Simditor for Rich Text Editing.abs
Supports direct image uploads and embed.
"""
class Media:
"""Media."""
css_list = [
'simditor/styles/simditor.css'
]
if 'emoji' in settings.SIMDITOR_TOOLBAR:
css_list.append('simditor/styles/simditor-emoji.css')
if 'fullscreen' in settings.SIMDITOR_TOOLBAR:
css_list.append('simditor/styles/simditor-fullscreen.min.css')
if 'checklist' in settings.SIMDITOR_TOOLBAR:
css_list.append('simditor/styles/simditor-checklist.min.css')
if 'markdown' in settings.SIMDITOR_TOOLBAR:
css_list.append('simditor/styles/simditor-markdown.min.css')
css = {'all': tuple(settings.STATIC_URL + url for url in css_list)}
jquery_list = ['simditor/scripts/jquery.min.js',
'simditor/scripts/module.min.js',
'simditor/scripts/hotkeys.min.js',
'simditor/scripts/uploader.min.js',
# 'simditor/scripts/simditor.min.js',
'simditor/scripts/simditor.js',
]
if 'fullscreen' in settings.SIMDITOR_TOOLBAR:
jquery_list.append('simditor/scripts/simditor-fullscreen.min.js')
if 'checklist' in settings.SIMDITOR_TOOLBAR:
jquery_list.append('simditor/scripts/simditor-checklist.min.js')
if 'markdown' in settings.SIMDITOR_TOOLBAR:
jquery_list.append('simditor/scripts/marked.min.js')
jquery_list.append('simditor/scripts/to-markdown.min.js')
jquery_list.append('simditor/scripts/simditor-markdown.min.js')
if 'image' in settings.SIMDITOR_TOOLBAR:
jquery_list.append('simditor/scripts/simditor-dropzone.min.js')
if 'emoji' in settings.SIMDITOR_TOOLBAR:
jquery_list.append('simditor/scripts/simditor-emoji.js')
js = tuple(settings.STATIC_URL + url for url in jquery_list)
try:
js += (settings.STATIC_URL + 'simditor/simditor-init.js',)
except AttributeError:
raise ImproperlyConfigured("django-simditor requires \
SIMDITOR_MEDIA_PREFIX setting. This setting specifies a \
URL prefix to the ckeditor JS and CSS media (not \
uploaded media). Make sure to use a trailing slash: \
SIMDITOR_MEDIA_PREFIX = '/media/simditor/'")
def __init__(self, *args, **kwargs):
super(SimditorWidget, self).__init__(*args, **kwargs)
# Setup config from defaults.
self.config = DEFAULT_CONFIG.copy()
# Try to get valid config from settings.
configs = getattr(settings, 'SIMDITOR_CONFIGS', None)
if configs:
if isinstance(configs, dict):
self.config.update(configs)
else:
raise ImproperlyConfigured(
'SIMDITOR_CONFIGS setting must be a dictionary type.')
def build_attrs(self, base_attrs, extra_attrs=None, **kwargs):
"""
Helper function for building an attribute dictionary.
This is combination of the same method from Django<=1.10 and Django1.11
"""
attrs = dict(base_attrs, **kwargs)
if extra_attrs:
attrs.update(extra_attrs)
return attrs
def render(self, name, value, attrs=None, renderer=None):
if value is None:
value = ''
final_attrs = self.build_attrs(self.attrs, attrs, name=name)
params = ('simditor/widget.html', {
'final_attrs': flatatt(final_attrs),
'value': conditional_escape(force_text(value)),
'id': final_attrs['id'],
'config': JSON_ENCODE(self.config)
})
if renderer is None and IS_NEW_WIDGET:
renderer = get_default_renderer()
data = renderer.render(*params) if IS_NEW_WIDGET else render_to_string(*params)
return mark_safe(data)
4. Form part of the code
forms.py文件
class CreateForm(forms.Form, SpamProtectionMixin):
def __init__(self, request, urlpath_parent, *args, **kwargs):
super(CreateForm, self).__init__(*args, **kwargs)
# 富文本字段
richtext = forms.CharField(
label=_('Contents'),
required=False,
widget=SimditorEditor().get_widget())
article.py文件
class Create(FormView, ArticleMixin):
form_class = forms.CreateForm
template_name = "wiki/create.html"
The last problem is the problem that will occur when uploading pictures: uploading requires url routing, otherwise uploading does not recognize the routing and reporting an error, resulting in upload failure
from django.views.decorators.csrf import csrf_exempt
from .simditor import ImageUploadView # 从simditor.py导入的
urlpatterns += [
url(r'^simditor/upload', csrf_exempt(ImageUploadView.as_view())),
]
In this way, the
effect can be recognized :