Skip to content

Commit

Permalink
Merge pull request #99 from EightMedia/master
Browse files Browse the repository at this point in the history
Fix django-tinymce on admin inlnes for Django >= 1.6
  • Loading branch information
aljosa committed Sep 15, 2014
2 parents c2c315e + b6a90e5 commit f80966c
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 50 deletions.
11 changes: 10 additions & 1 deletion testtinymce/settings.py
Expand Up @@ -28,6 +28,15 @@
STATIC_ROOT = join(ROOT_PATH, "static")
STATIC_URL = "/static/"

MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

SECRET_KEY = 'w4o4x^&b4h4zne9&3b1m-_p-=+&n_i_sdf@oz=gd+6h6v1$sd9'

ROOT_URLCONF = 'testtinymce.urls'
Expand All @@ -42,7 +51,7 @@
'django.contrib.admin',
'django.contrib.flatpages',
'tinymce',
'testapp',
'testtinymce.testapp',
)

TINYMCE_SPELLCHECKER = True
Expand Down
20 changes: 19 additions & 1 deletion testtinymce/testapp/admin.py
Expand Up @@ -3,9 +3,23 @@
from django.contrib.flatpages.admin import FlatPageAdmin
from django.contrib.flatpages.models import FlatPage

from testtinymce.testapp.models import TestPage
from testtinymce.testapp.models import TestPage, TestInline
from tinymce.widgets import TinyMCE


class TinyMCETestInlineAdmin(admin.StackedInline):
model = TestInline
extra = 1

def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in ('content1', 'content2'):
return db_field.formfield(widget=TinyMCE(
attrs={'cols': 80, 'rows': 30},
mce_attrs={'external_link_list_url': reverse('tinymce.views.flatpages_link_list')},
))
return super(TinyMCETestInlineAdmin, self).formfield_for_dbfield(db_field, **kwargs)


class TinyMCEFlatPageAdmin(FlatPageAdmin):
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'content':
Expand All @@ -15,7 +29,10 @@ def formfield_for_dbfield(self, db_field, **kwargs):
))
return super(TinyMCEFlatPageAdmin, self).formfield_for_dbfield(db_field, **kwargs)


class TinyMCETestPageAdmin(admin.ModelAdmin):
inlines = [TinyMCETestInlineAdmin]

def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name in ('content1', 'content2'):
return db_field.formfield(widget=TinyMCE(
Expand All @@ -24,6 +41,7 @@ def formfield_for_dbfield(self, db_field, **kwargs):
))
return super(TinyMCETestPageAdmin, self).formfield_for_dbfield(db_field, **kwargs)


admin.site.unregister(FlatPage)
admin.site.register(FlatPage, TinyMCEFlatPageAdmin)
admin.site.register(TestPage, TinyMCETestPageAdmin)
6 changes: 6 additions & 0 deletions testtinymce/testapp/models.py
Expand Up @@ -3,3 +3,9 @@
class TestPage(models.Model):
content1 = models.TextField()
content2 = models.TextField()


class TestInline(models.Model):
page = models.ForeignKey(TestPage)
content1 = models.TextField()
content2 = models.TextField()
Binary file not shown.
38 changes: 38 additions & 0 deletions tinymce/static/django_tinymce/init_tinymce.js
@@ -0,0 +1,38 @@
(function ($) {
function initTinyMCE($e) {
if ($e.parents('.empty-form').length == 0) { // Don't do empty inlines
var mce_conf = $.parseJSON($e.attr('data-mce-conf'));
var id = $e.attr('id');
if ('elements' in mce_conf && mce_conf['mode'] == 'exact') {
mce_conf['elements'] = id;
}
if ($e.attr('data-mce-gz-conf')) {
tinyMCE_GZ.init($.parseJSON($e.attr('data-mce-gz-conf')));
}
if (!tinyMCE.editors[id]) {
tinyMCE.init(mce_conf);
}
}
}

$(function () {
// initialize the TinyMCE editors on load
$('.tinymce').each(function () {
initTinyMCE($(this));
});

// initialize the TinyMCE editor after adding an inline
// XXX: We don't use jQuery's click event as it won't work in Django 1.4
document.body.addEventListener("click", function(ev) {
if(!ev.target.parentNode || ev.target.parentNode.className.indexOf("add-row") === -1) {
return;
}
var $addRow = $(ev.target.parentNode);
setTimeout(function() { // We have to wait until the inline is added
$('textarea.tinymce', $addRow.parent()).each(function () {
initTinyMCE($(this));
});
}, 0);
}, true);
});
}(django.jQuery));
73 changes: 25 additions & 48 deletions tinymce/widgets.py
Expand Up @@ -47,52 +47,50 @@ class TinyMCE(forms.Textarea):
the current Django language, the others from the 'content_language'
parameter.
"""

def __init__(self, content_language=None, attrs=None, mce_attrs=None):
super(TinyMCE, self).__init__(attrs)
if mce_attrs is None:
mce_attrs = {}
mce_attrs = mce_attrs or {}
self.mce_attrs = mce_attrs
if not 'mode' in self.mce_attrs:
self.mce_attrs['mode'] = 'exact'
self.mce_attrs['strict_loading_mode'] = 1
if content_language is None:
content_language = mce_attrs.get('language', None)
self.content_language = content_language

def render(self, name, value, attrs=None):
if value is None: value = ''
value = smart_unicode(value)
final_attrs = self.build_attrs(attrs)
final_attrs['name'] = name
assert 'id' in final_attrs, "TinyMCE widget attributes must contain 'id'"

def get_mce_config(self, attrs):
mce_config = tinymce.settings.DEFAULT_CONFIG.copy()
mce_config.update(get_language_config(self.content_language))
if tinymce.settings.USE_FILEBROWSER:
mce_config['file_browser_callback'] = "djangoFileBrowser"
mce_config.update(self.mce_attrs)
if not 'mode' in mce_config:
mce_config['mode'] = 'exact'
if mce_config['mode'] == 'exact':
mce_config['elements'] = final_attrs['id']
mce_config['strict_loading_mode'] = 1

mce_config['elements'] = attrs['id']
return mce_config

def get_mce_json(self, mce_config):
# Fix for js functions
js_functions = {}
for k in ('paste_preprocess','paste_postprocess'):
for k in ('paste_preprocess', 'paste_postprocess'):
if k in mce_config:
js_functions[k] = mce_config[k]
del mce_config[k]
mce_json = json.dumps(mce_config)

pos = final_attrs['id'].find('__prefix__')
if pos != -1:
mce_json = mce_json.replace('"%s"' % final_attrs['id'], 'elements')

for k in js_functions:
index = mce_json.rfind('}')
mce_json = mce_json[:index]+', '+k+':'+js_functions[k].strip()+mce_json[index:]
return mce_json

html = ['<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))]
def render(self, name, value, attrs=None):
if value is None:
value = ''
value = smart_unicode(value)
final_attrs = self.build_attrs(attrs)
final_attrs['name'] = name
final_attrs['class'] = 'tinymce'
assert 'id' in final_attrs, "TinyMCE widget attributes must contain 'id'"
mce_config = self.get_mce_config(final_attrs)
mce_json = self.get_mce_json(mce_config)
if tinymce.settings.USE_COMPRESSOR:
compressor_config = {
'plugins': mce_config.get('plugins', ''),
Expand All @@ -101,31 +99,9 @@ def render(self, name, value, attrs=None):
'diskcache': True,
'debug': False,
}
compressor_json = json.dumps(compressor_config)
html.append('<script type="text/javascript">tinyMCE_GZ.init(%s)</script>' % compressor_json)

if pos != -1:
html.append('''<script type="text/javascript">
setTimeout(function () {
var id = '%s';
if (typeof(window._tinymce_inited) == 'undefined') {
window._tinymce_inited = [];
}
if (typeof(window._tinymce_inited[id]) == 'undefined') {
window._tinymce_inited[id] = true;
} else {
var elements = id.replace(/__prefix__/, parseInt(document.getElementById('%sTOTAL_FORMS').value) - 1);
if (document.getElementById(elements)) {
tinymce.init(%s);
}
}
}, 0);
</script>''' % (final_attrs['id'], final_attrs['id'][0:pos], mce_json))
else:
html.append('<script type="text/javascript">tinyMCE.init(%s)</script>' % mce_json)

final_attrs['data-mce-gz-conf'] = json.dumps(compressor_config)
final_attrs['data-mce-conf'] = mce_json
html = ['<textarea%s>%s</textarea>' % (flatatt(final_attrs), escape(value))]
return mark_safe('\n'.join(html))

def _media(self):
Expand All @@ -135,6 +111,7 @@ def _media(self):
js = [tinymce.settings.JS_URL]
if tinymce.settings.USE_FILEBROWSER:
js.append(reverse('tinymce-filebrowser'))
js.append('django_tinymce/init_tinymce.js')
return forms.Media(js=js)
media = property(_media)

Expand Down

0 comments on commit f80966c

Please sign in to comment.