:-3px;left:0}.simditor .simditor-toolbar .toolbar-menu ul{min-width:160px;list-style:none;margin:0;padding:10px 1px}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item{display:block;font-size:16px;line-height:2em;padding:0 10px;text-decoration:none;color:#666}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item:hover{background:#f6f6f6}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item.menu-item-h1{font-size:24px;color:#333}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item.menu-item-h2{font-size:22px;color:#333}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item.menu-item-h3{font-size:20px;color:#333}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item.menu-item-h4{font-size:18px;color:#333}.simditor .simditor-toolbar .toolbar-menu ul>li .menu-item.menu-item-h5{font-size:16px;color:#333}.simditor .simditor-toolbar .toolbar-menu ul>li .separator{display:block;border-top:1px solid #ccc;height:0;line-height:0;font-size:0;margin:6px 0}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color{width:96px}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list{height:40px;margin:10px 6px 6px 10px;padding:0;min-width:0}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li{float:left;margin:0 4px 4px 0}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color{display:block;width:16px;height:16px;background:#dfdfdf;border-radius:2px}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color:hover{opacity:.8}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color.font-color-default{background:#333}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-1{background:#e33737}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-2{background:#e28b41}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-3{background:#c8a732}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-4{background:#209361}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-5{background:#418caf}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-6{background:#aa8773}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-color .color-list li .font-color-7{background:#999}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-create-table{background:#fff;padding:1px}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-create-table table{border:0;border-collapse:collapse;border-spacing:0;table-layout:fixed}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-create-table table td{padding:0;cursor:pointer}
+.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-create-table table td:before{width:16px;height:16px;border:1px solid #fff;background:#f3f3f3;display:block;content:""}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-create-table table td.selected:before{background:#cfcfcf}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-edit-table{display:none}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-table .menu-edit-table ul li{white-space:nowrap}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-image .menu-item-upload-image{position:relative;overflow:hidden}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-image .menu-item-upload-image input[type=file]{position:absolute;right:0;top:0;opacity:0;font-size:100px;cursor:pointer}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-alignment{width:100%}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-alignment ul{min-width:100%}.simditor .simditor-toolbar .toolbar-menu.toolbar-menu-alignment .menu-item{text-align:center}.simditor .simditor-popover{display:none;padding:5px 8px 0;background:#fff;box-shadow:0 1px 4px rgba(0,0,0,0.4);border-radius:2px;position:absolute;z-index:2}.simditor .simditor-popover .settings-field{margin:0 0 5px 0;font-size:12px;height:25px;line-height:25px}.simditor .simditor-popover .settings-field label{display:inline-block;margin:0 5px 0 0}.simditor .simditor-popover .settings-field input[type=text]{display:inline-block;width:200px;box-sizing:border-box;font-size:12px}.simditor .simditor-popover .settings-field input[type=text].image-size{width:83px}.simditor .simditor-popover .settings-field .times{display:inline-block;width:26px;font-size:12px;text-align:center}.simditor .simditor-popover.link-popover .btn-unlink,.simditor .simditor-popover.image-popover .btn-upload,.simditor .simditor-popover.image-popover .btn-restore{display:inline-block;margin:0 0 0 5px;color:#333;font-size:14px;outline:0}.simditor .simditor-popover.link-popover .btn-unlink span,.simditor .simditor-popover.image-popover .btn-upload span,.simditor .simditor-popover.image-popover .btn-restore span{opacity:.6}.simditor .simditor-popover.link-popover .btn-unlink:hover span,.simditor .simditor-popover.image-popover .btn-upload:hover span,.simditor .simditor-popover.image-popover .btn-restore:hover span{opacity:1}.simditor .simditor-popover.image-popover .btn-upload{position:relative;display:inline-block;overflow:hidden;vertical-align:middle}.simditor .simditor-popover.image-popover .btn-upload input[type=file]{position:absolute;right:0;top:0;opacity:0;height:100%;width:28px}.simditor.simditor-mobile .simditor-wrapper.toolbar-floating .simditor-toolbar{position:absolute;top:0;z-index:10;box-shadow:0 0 6px rgba(0,0,0,0.1)}.simditor .simditor-body,.editor-style{font-size:16px;font-family:arial,sans-serif;line-height:1.6;color:#333;outline:0;word-wrap:break-word}.simditor .simditor-body>:first-child,.editor-style>:first-child{margin-top:0!important}.simditor .simditor-body a,.editor-style a{color:#4298ba;text-decoration:none;word-break:break-all}.simditor .simditor-body a:visited,.editor-style a:visited{color:#4298ba}.simditor .simditor-body a:hover,.editor-style a:hover{color:#0f769f}.simditor .simditor-body a:active,.editor-style a:active{color:#9e792e}.simditor .simditor-body a:hover,.simditor .simditor-body a:active,.editor-style a:hover,.editor-style a:active{outline:0}.simditor .simditor-body h1,.simditor .simditor-body h2,.simditor .simditor-body h3,.simditor .simditor-body h4,.simditor .simditor-body h5,.simditor .simditor-body h6,.editor-style h1,.editor-style h2,.editor-style h3,.editor-style h4,.editor-style h5,.editor-style h6{font-weight:normal;margin:40px 0 20px;color:#000}.simditor .simditor-body h1,.editor-style h1{font-size:24px}.simditor .simditor-body h2,.editor-style h2{font-size:22px}.simditor .simditor-body h3,.editor-style h3{font-size:20px}.simditor .simditor-body h4,.editor-style h4{font-size:18px}.simditor .simditor-body h5,.editor-style h5{font-size:16px}.simditor .simditor-body h6,.editor-style h6{font-size:16px}.simditor .simditor-body p,.simditor .simditor-body div,.editor-style p,.editor-style div{word-wrap:break-word;margin:0 0 15px 0;color:#333;word-wrap:break-word}.simditor .simditor-body b,.simditor .simditor-body strong,.editor-style b,.editor-style strong{font-weight:bold}.simditor .simditor-body i,.simditor .simditor-body em,.editor-style i,.editor-style em{font-style:italic}.simditor .simditor-body u,.editor-style u{text-decoration:underline}.simditor .simditor-body strike,.simditor .simditor-body del,.editor-style strike,.editor-style del{text-decoration:line-through}.simditor .simditor-body ul,.simditor .simditor-body ol,.editor-style ul,.editor-style ol{list-style:disc outside none;margin:15px 0;padding:0 0 0 40px;line-height:1.6}.simditor .simditor-body ul ul,.simditor .simditor-body ul ol,.simditor .simditor-body ol ul,.simditor .simditor-body ol ol,.editor-style ul ul,.editor-style ul ol,.editor-style ol ul,.editor-style ol ol{padding-left:30px}
+.simditor .simditor-body ul ul,.simditor .simditor-body ol ul,.editor-style ul ul,.editor-style ol ul{list-style:circle outside none}.simditor .simditor-body ul ul ul,.simditor .simditor-body ol ul ul,.editor-style ul ul ul,.editor-style ol ul ul{list-style:square outside none}.simditor .simditor-body ol,.editor-style ol{list-style:decimal}.simditor .simditor-body blockquote,.editor-style blockquote{border-left:6px solid #ddd;padding:5px 0 5px 10px;margin:15px 0 15px 15px}.simditor .simditor-body blockquote>:first-child,.editor-style blockquote>:first-child{margin-top:0}.simditor .simditor-body code,.editor-style code{display:inline-block;padding:0 4px;margin:0 5px;background:#eee;border-radius:3px;font-size:13px;font-family:'monaco','Consolas',"Liberation Mono",Courier,monospace}.simditor .simditor-body pre,.editor-style pre{padding:10px 5px 10px 10px;margin:15px 0;display:block;line-height:18px;background:#f0f0f0;border-radius:3px;font-size:13px;font-family:'monaco','Consolas',"Liberation Mono",Courier,monospace;white-space:pre;word-wrap:normal;overflow-x:auto}.simditor .simditor-body pre code,.editor-style pre code{display:block;padding:0;margin:0;background:0;border-radius:0}.simditor .simditor-body hr,.editor-style hr{display:block;height:0;border:0;border-top:1px solid #ccc;margin:15px 0;padding:0}.simditor .simditor-body table,.editor-style table{width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;margin:15px 0}.simditor .simditor-body table thead,.editor-style table thead{background-color:#f9f9f9}.simditor .simditor-body table td,.simditor .simditor-body table th,.editor-style table td,.editor-style table th{min-width:40px;height:30px;border:1px solid #ccc;vertical-align:top;padding:2px 4px;text-align:left;box-sizing:border-box}.simditor .simditor-body table td.active,.simditor .simditor-body table th.active,.editor-style table td.active,.editor-style table th.active{background-color:#ffe}.simditor .simditor-body img,.editor-style img{margin:0 5px;vertical-align:middle}
@@ -0,0 +1,4 @@ |
||
| 1 |
+@charset "UTF-8"; |
|
| 2 |
+ |
|
| 3 |
+@import 'fonticon'; |
|
| 4 |
+@import 'editor'; |
@@ -0,0 +1,3 @@ |
||
| 1 |
+<div class="django-simditor-widget" data-field-id="{{id}}" style="display: inline-block;">
|
|
| 2 |
+ <textarea{{ final_attrs|safe }} data-processed="0" data-config='{{config|safe}}' data-id="{{id}}" data-type="simditortype">{{ value }}</textarea>
|
|
| 3 |
+</div> |
@@ -0,0 +1,39 @@ |
||
| 1 |
+"""simditor urls.""" |
|
| 2 |
+from __future__ import absolute_import |
|
| 3 |
+ |
|
| 4 |
+import django |
|
| 5 |
+ |
|
| 6 |
+from django.conf import settings |
|
| 7 |
+from django.conf.urls import url, static |
|
| 8 |
+from django.contrib.admin.views.decorators import staff_member_required |
|
| 9 |
+ |
|
| 10 |
+from . import views |
|
| 11 |
+ |
|
| 12 |
+if django.VERSION >= (2, 0): |
|
| 13 |
+ # pylint disable=C0103 |
|
| 14 |
+ from django.urls import path |
|
| 15 |
+ urlpatterns = [ |
|
| 16 |
+ path('upload/', staff_member_required(views.UPLOAD),
|
|
| 17 |
+ name='simditor_upload'), |
|
| 18 |
+ ] |
|
| 19 |
+elif django.VERSION >= (1, 8): |
|
| 20 |
+ # pylint disable=C0103 |
|
| 21 |
+ urlpatterns = [ |
|
| 22 |
+ url(r'^upload/', staff_member_required(views.UPLOAD), |
|
| 23 |
+ name='simditor_upload'), |
|
| 24 |
+ ] |
|
| 25 |
+else: |
|
| 26 |
+ from django.conf.urls import patterns # pylint disable=C0411 |
|
| 27 |
+ |
|
| 28 |
+ # pylint disable=C0103 |
|
| 29 |
+ urlpatterns = patterns( |
|
| 30 |
+ '', |
|
| 31 |
+ url(r'^upload/', staff_member_required(views.UPLOAD), |
|
| 32 |
+ name='simditor_upload'), |
|
| 33 |
+ ) |
|
| 34 |
+ |
|
| 35 |
+if settings.DEBUG: |
|
| 36 |
+ urlpatterns += static.static(settings.MEDIA_URL, |
|
| 37 |
+ document_root=settings.MEDIA_ROOT) |
|
| 38 |
+ urlpatterns += static.static(settings.STATIC_URL, |
|
| 39 |
+ document_root=settings.STATIC_ROOT) |
@@ -0,0 +1,46 @@ |
||
| 1 |
+"""simditor utils.""" |
|
| 2 |
+from __future__ import absolute_import |
|
| 3 |
+ |
|
| 4 |
+import os.path |
|
| 5 |
+import random |
|
| 6 |
+ |
|
| 7 |
+import string |
|
| 8 |
+ |
|
| 9 |
+from django.core.files.storage import default_storage |
|
| 10 |
+from django.template.defaultfilters import slugify |
|
| 11 |
+ |
|
| 12 |
+ |
|
| 13 |
+class NotAnImageException(Exception): |
|
| 14 |
+ pass |
|
| 15 |
+ |
|
| 16 |
+ |
|
| 17 |
+def get_random_string(): |
|
| 18 |
+ """Get random string.""" |
|
| 19 |
+ return ''.join(random.sample(string.ascii_lowercase * 6, 6)) |
|
| 20 |
+ |
|
| 21 |
+ |
|
| 22 |
+def get_slugified_name(filename): |
|
| 23 |
+ """get_slugified_name.""" |
|
| 24 |
+ slugified = slugify(filename) |
|
| 25 |
+ return slugified or get_random_string() |
|
| 26 |
+ |
|
| 27 |
+ |
|
| 28 |
+def slugify_filename(filename): |
|
| 29 |
+ """ Slugify filename """ |
|
| 30 |
+ name, ext = os.path.splitext(filename) |
|
| 31 |
+ slugified = get_slugified_name(name) |
|
| 32 |
+ return slugified + ext |
|
| 33 |
+ |
|
| 34 |
+ |
|
| 35 |
+def get_media_url(path): |
|
| 36 |
+ """ |
|
| 37 |
+ Determine system file's media URL. |
|
| 38 |
+ """ |
|
| 39 |
+ return default_storage.url(path) |
|
| 40 |
+ |
|
| 41 |
+ |
|
| 42 |
+def is_valid_image_extension(file_path): |
|
| 43 |
+ """is_valid_image_extension.""" |
|
| 44 |
+ valid_extensions = ['.jpeg', '.jpg', '.gif', '.png'] |
|
| 45 |
+ _, extension = os.path.splitext(file_path) |
|
| 46 |
+ return extension.lower() in valid_extensions |
@@ -0,0 +1,84 @@ |
||
| 1 |
+"""simditor views.""" |
|
| 2 |
+from __future__ import absolute_import |
|
| 3 |
+ |
|
| 4 |
+import os |
|
| 5 |
+from datetime import datetime |
|
| 6 |
+ |
|
| 7 |
+from django.conf import settings |
|
| 8 |
+from django.core.files.storage import default_storage |
|
| 9 |
+ |
|
| 10 |
+from django.http import JsonResponse |
|
| 11 |
+ |
|
| 12 |
+from django.views import generic |
|
| 13 |
+from django.views.decorators.csrf import csrf_exempt |
|
| 14 |
+ |
|
| 15 |
+from . import utils, image_processing |
|
| 16 |
+ |
|
| 17 |
+ |
|
| 18 |
+def get_upload_filename(upload_name): |
|
| 19 |
+ # Generate date based path to put uploaded file. |
|
| 20 |
+ date_path = datetime.now().strftime('%Y/%m/%d')
|
|
| 21 |
+ |
|
| 22 |
+ # Complete upload path (upload_path + date_path). |
|
| 23 |
+ upload_path = os.path.join(settings.SIMDITOR_UPLOAD_PATH, date_path) |
|
| 24 |
+ |
|
| 25 |
+ if getattr(settings, 'SIMDITOR_UPLOAD_SLUGIFY_FILENAME', True): |
|
| 26 |
+ upload_name = utils.slugify_filename(upload_name) |
|
| 27 |
+ |
|
| 28 |
+ return default_storage.get_available_name(os.path.join(upload_path, upload_name)) |
|
| 29 |
+ |
|
| 30 |
+ |
|
| 31 |
+def upload_handler(request): |
|
| 32 |
+ files = request.FILES |
|
| 33 |
+ |
|
| 34 |
+ upload_config = settings.SIMDITOR_CONFIGS.get( |
|
| 35 |
+ 'upload', {'fileKey': 'upload'})
|
|
| 36 |
+ filekey = upload_config.get('fileKey', 'upload')
|
|
| 37 |
+ |
|
| 38 |
+ uploaded_file = files.get(filekey) |
|
| 39 |
+ |
|
| 40 |
+ if not uploaded_file: |
|
| 41 |
+ retdata = {'file_path': '', 'success': False,
|
|
| 42 |
+ 'msg': '图片上传失败,无法获取到图片对象!'} |
|
| 43 |
+ return JsonResponse(retdata) |
|
| 44 |
+ |
|
| 45 |
+ image_size = upload_config.get('image_size')
|
|
| 46 |
+ if image_size and uploaded_file.size > image_size: |
|
| 47 |
+ retdata = {'file_path': '', 'success': False,
|
|
| 48 |
+ 'msg': '上传失败,已超出图片最大限制!'} |
|
| 49 |
+ return JsonResponse(retdata) |
|
| 50 |
+ |
|
| 51 |
+ backend = image_processing.get_backend() |
|
| 52 |
+ |
|
| 53 |
+ if not getattr(settings, 'SIMDITOR_ALLOW_NONIMAGE_FILES', True): |
|
| 54 |
+ try: |
|
| 55 |
+ backend.image_verify(uploaded_file) |
|
| 56 |
+ except utils.NotAnImageException: |
|
| 57 |
+ retdata = {'file_path': '', 'success': False,
|
|
| 58 |
+ 'msg': '图片格式错误!'} |
|
| 59 |
+ return JsonResponse(retdata) |
|
| 60 |
+ |
|
| 61 |
+ filename = get_upload_filename(uploaded_file.name) |
|
| 62 |
+ saved_path = default_storage.save(filename, uploaded_file) |
|
| 63 |
+ |
|
| 64 |
+ url = utils.get_media_url(saved_path) |
|
| 65 |
+ |
|
| 66 |
+ is_api = settings.SIMDITOR_CONFIGS.get('is_api', False)
|
|
| 67 |
+ url = request.META.get('HTTP_ORIGIN') + url if is_api else url
|
|
| 68 |
+ |
|
| 69 |
+ retdata = {'file_path': url, 'success': True, 'msg': '上传成功!'}
|
|
| 70 |
+ |
|
| 71 |
+ return JsonResponse(retdata) |
|
| 72 |
+ |
|
| 73 |
+ |
|
| 74 |
+class ImageUploadView(generic.View): |
|
| 75 |
+ """ImageUploadView.""" |
|
| 76 |
+ |
|
| 77 |
+ http_method_names = ['post'] |
|
| 78 |
+ |
|
| 79 |
+ def post(self, request, **kwargs): |
|
| 80 |
+ """Post.""" |
|
| 81 |
+ return upload_handler(request) |
|
| 82 |
+ |
|
| 83 |
+ |
|
| 84 |
+UPLOAD = csrf_exempt(ImageUploadView.as_view()) |
@@ -0,0 +1,170 @@ |
||
| 1 |
+"""simditor widgets.""" |
|
| 2 |
+from __future__ import absolute_import |
|
| 3 |
+ |
|
| 4 |
+from django import forms |
|
| 5 |
+from django.conf import settings |
|
| 6 |
+from django.core.exceptions import ImproperlyConfigured |
|
| 7 |
+from django.core.serializers.json import DjangoJSONEncoder |
|
| 8 |
+ |
|
| 9 |
+from django.template.loader import render_to_string |
|
| 10 |
+from django.utils.encoding import force_text |
|
| 11 |
+from django.utils.safestring import mark_safe |
|
| 12 |
+from django.utils.html import conditional_escape |
|
| 13 |
+from django.utils.functional import Promise |
|
| 14 |
+ |
|
| 15 |
+try: |
|
| 16 |
+ # Django >=2.1 |
|
| 17 |
+ from django.forms.widgets import get_default_renderer |
|
| 18 |
+ IS_NEW_WIDGET = True |
|
| 19 |
+except ImportError: |
|
| 20 |
+ IS_NEW_WIDGET = False |
|
| 21 |
+ |
|
| 22 |
+try: |
|
| 23 |
+ # Django >=1.7 |
|
| 24 |
+ from django.forms.utils import flatatt |
|
| 25 |
+except ImportError: |
|
| 26 |
+ # Django <1.7 |
|
| 27 |
+ from django.forms.util import flatatt # pylint disable=E0611, E0401 |
|
| 28 |
+ |
|
| 29 |
+ |
|
| 30 |
+class LazyEncoder(DjangoJSONEncoder): |
|
| 31 |
+ """LazyEncoder.""" |
|
| 32 |
+ |
|
| 33 |
+ # pylint disable=E0202 |
|
| 34 |
+ def default(self, obj): |
|
| 35 |
+ if isinstance(obj, Promise): |
|
| 36 |
+ return force_text(obj) |
|
| 37 |
+ return super(LazyEncoder, self).default(obj) |
|
| 38 |
+ |
|
| 39 |
+ |
|
| 40 |
+JSON_ENCODE = LazyEncoder().encode |
|
| 41 |
+ |
|
| 42 |
+ |
|
| 43 |
+FULL_TOOLBAR = [ |
|
| 44 |
+ 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', |
|
| 45 |
+ 'color', '|', 'ol', 'ul', 'blockquote', 'code', 'table', '|', 'link', |
|
| 46 |
+ 'image', 'hr', '|', 'indent', 'outdent', 'alignment', 'checklist', |
|
| 47 |
+ 'markdown', 'fullscreen' |
|
| 48 |
+] |
|
| 49 |
+ |
|
| 50 |
+DEFAULT_TOOLBAR = [ |
|
| 51 |
+ 'title', 'bold', 'italic', 'underline', 'strikethrough', 'fontScale', |
|
| 52 |
+ 'color', '|', 'ol', 'ul', 'blockquote', 'table', '|', 'link', |
|
| 53 |
+ 'image', 'hr', '|', 'indent', 'outdent', 'alignment' |
|
| 54 |
+] |
|
| 55 |
+ |
|
| 56 |
+DEFAULT_CONFIG = {
|
|
| 57 |
+ 'toolbar': DEFAULT_TOOLBAR, |
|
| 58 |
+ 'cleanPaste': True, |
|
| 59 |
+ 'tabIndent': True, |
|
| 60 |
+ 'pasteImage': True, |
|
| 61 |
+ 'upload': {
|
|
| 62 |
+ 'url': '/', |
|
| 63 |
+ 'fileKey': 'file' |
|
| 64 |
+ } |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+ |
|
| 68 |
+class SimditorWidget(forms.Textarea): |
|
| 69 |
+ """ |
|
| 70 |
+ Widget providing Simditor for Rich Text Editing.abs |
|
| 71 |
+ Supports direct image uploads and embed. |
|
| 72 |
+ """ |
|
| 73 |
+ class Media: |
|
| 74 |
+ """Media.""" |
|
| 75 |
+ |
|
| 76 |
+ css_list = [ |
|
| 77 |
+ 'simditor/styles/simditor.min.css' |
|
| 78 |
+ ] |
|
| 79 |
+ |
|
| 80 |
+ if 'emoji' in settings.SIMDITOR_TOOLBAR: |
|
| 81 |
+ css_list.append('simditor/styles/simditor-emoji.css')
|
|
| 82 |
+ |
|
| 83 |
+ if 'fullscreen' in settings.SIMDITOR_TOOLBAR: |
|
| 84 |
+ css_list.append('simditor/styles/simditor-fullscreen.min.css')
|
|
| 85 |
+ |
|
| 86 |
+ if 'checklist' in settings.SIMDITOR_TOOLBAR: |
|
| 87 |
+ css_list.append('simditor/styles/simditor-checklist.min.css')
|
|
| 88 |
+ |
|
| 89 |
+ if 'markdown' in settings.SIMDITOR_TOOLBAR: |
|
| 90 |
+ css_list.append('simditor/styles/simditor-markdown.min.css')
|
|
| 91 |
+ |
|
| 92 |
+ css = {'all': tuple(settings.STATIC_URL + url for url in css_list)}
|
|
| 93 |
+ |
|
| 94 |
+ jquery_list = ['simditor/scripts/jquery.min.js', |
|
| 95 |
+ 'simditor/scripts/module.min.js', |
|
| 96 |
+ 'simditor/scripts/hotkeys.min.js', |
|
| 97 |
+ 'simditor/scripts/uploader.min.js', |
|
| 98 |
+ 'simditor/scripts/simditor.min.js'] |
|
| 99 |
+ |
|
| 100 |
+ if 'fullscreen' in settings.SIMDITOR_TOOLBAR: |
|
| 101 |
+ jquery_list.append('simditor/scripts/simditor-fullscreen.min.js')
|
|
| 102 |
+ |
|
| 103 |
+ if 'checklist' in settings.SIMDITOR_TOOLBAR: |
|
| 104 |
+ jquery_list.append('simditor/scripts/simditor-checklist.min.js')
|
|
| 105 |
+ |
|
| 106 |
+ if 'markdown' in settings.SIMDITOR_TOOLBAR: |
|
| 107 |
+ jquery_list.append('simditor/scripts/marked.min.js')
|
|
| 108 |
+ jquery_list.append('simditor/scripts/to-markdown.min.js')
|
|
| 109 |
+ jquery_list.append('simditor/scripts/simditor-markdown.min.js')
|
|
| 110 |
+ |
|
| 111 |
+ if 'image' in settings.SIMDITOR_TOOLBAR: |
|
| 112 |
+ jquery_list.append('simditor/scripts/simditor-dropzone.min.js')
|
|
| 113 |
+ |
|
| 114 |
+ if 'emoji' in settings.SIMDITOR_TOOLBAR: |
|
| 115 |
+ jquery_list.append('simditor/scripts/simditor-emoji.js')
|
|
| 116 |
+ |
|
| 117 |
+ js = tuple(settings.STATIC_URL + url for url in jquery_list) |
|
| 118 |
+ |
|
| 119 |
+ try: |
|
| 120 |
+ |
|
| 121 |
+ js += (settings.STATIC_URL + 'simditor/simditor-init.js',) |
|
| 122 |
+ except AttributeError: |
|
| 123 |
+ raise ImproperlyConfigured("django-simditor requires \
|
|
| 124 |
+ SIMDITOR_MEDIA_PREFIX setting. This setting specifies a \ |
|
| 125 |
+ URL prefix to the ckeditor JS and CSS media (not \ |
|
| 126 |
+ uploaded media). Make sure to use a trailing slash: \ |
|
| 127 |
+ SIMDITOR_MEDIA_PREFIX = '/media/simditor/'") |
|
| 128 |
+ |
|
| 129 |
+ def __init__(self, *args, **kwargs): |
|
| 130 |
+ super(SimditorWidget, self).__init__(*args, **kwargs) |
|
| 131 |
+ # Setup config from defaults. |
|
| 132 |
+ self.config = DEFAULT_CONFIG.copy() |
|
| 133 |
+ |
|
| 134 |
+ # Try to get valid config from settings. |
|
| 135 |
+ configs = getattr(settings, 'SIMDITOR_CONFIGS', None) |
|
| 136 |
+ if configs: |
|
| 137 |
+ if isinstance(configs, dict): |
|
| 138 |
+ self.config.update(configs) |
|
| 139 |
+ else: |
|
| 140 |
+ raise ImproperlyConfigured( |
|
| 141 |
+ 'SIMDITOR_CONFIGS setting must be a dictionary type.') |
|
| 142 |
+ |
|
| 143 |
+ def build_attrs(self, base_attrs, extra_attrs=None, **kwargs): |
|
| 144 |
+ """ |
|
| 145 |
+ Helper function for building an attribute dictionary. |
|
| 146 |
+ This is combination of the same method from Django<=1.10 and Django1.11 |
|
| 147 |
+ """ |
|
| 148 |
+ attrs = dict(base_attrs, **kwargs) |
|
| 149 |
+ if extra_attrs: |
|
| 150 |
+ attrs.update(extra_attrs) |
|
| 151 |
+ return attrs |
|
| 152 |
+ |
|
| 153 |
+ def render(self, name, value, attrs=None, renderer=None): |
|
| 154 |
+ if value is None: |
|
| 155 |
+ value = '' |
|
| 156 |
+ final_attrs = self.build_attrs(self.attrs, attrs, name=name) |
|
| 157 |
+ |
|
| 158 |
+ params = ('simditor/widget.html', {
|
|
| 159 |
+ 'final_attrs': flatatt(final_attrs), |
|
| 160 |
+ 'value': conditional_escape(force_text(value)), |
|
| 161 |
+ 'id': final_attrs['id'], |
|
| 162 |
+ 'config': JSON_ENCODE(self.config) |
|
| 163 |
+ }) |
|
| 164 |
+ |
|
| 165 |
+ if renderer is None and IS_NEW_WIDGET: |
|
| 166 |
+ renderer = get_default_renderer() |
|
| 167 |
+ |
|
| 168 |
+ data = renderer.render(*params) if IS_NEW_WIDGET else render_to_string(*params) |
|
| 169 |
+ |
|
| 170 |
+ return mark_safe(data) |