jazzband / django-tinymce

TinyMCE integration for Django
http://django-tinymce.readthedocs.org/
MIT License
1.25k stars 317 forks source link

Howto multiples settings of TINYMCE_DEFAULT_CONFIG #272

Open gusarg81 opened 4 years ago

gusarg81 commented 4 years ago

Hi,

For example, with django_tinymce_lite, I could do this in a form definition:

... widgets = { 'text': TinyMCE(profile=settings.TINYMCE_BASIC_CONFIG) } ....

So, being TINYMCE_BASIC_CONFIG an alternative config in settings.py besides TINYMCE_DEFAULT_CONFIG. How can I acchieve this in here?

Thanks in advance.

gusarg81 commented 3 years ago

Hi, nothing about this?

aitorres commented 3 years ago

+1 If needed, I think I could work on and submit a PR that added this feature!

gusarg81 commented 3 years ago

I think will be needed, just imagine different scenarios where you only need to show a few tools and others the full toolbar (at least many cases in my mind).

Lets says that as an admin of a site, in certain apps you need more tools than an user doing a comment in other public app. Just to mention an example.

gusarg81 commented 3 years ago

Hi,

Any advance on this? Is the only thing that keeps me from upgrading (still using django-tinymce4-lite that has this feature).

Thanks.

claudep commented 3 years ago

I guess a pull request should be a start...

aitorres commented 3 years ago

Yes, @claudep! I still want to work on it, but haven't been able these past days because of my work load. However I think I can work on it this week and upload a Pull Request

claudep commented 3 years ago

I worked a bit on this code these last days, and it seems you can achieve what you want by filling the mce_attrs dictionary when initializing the widget, as described here: https://django-tinymce.readthedocs.io/en/latest/usage.html#using-the-widget

gusarg81 commented 3 years ago

So, lets say I have one of the settings, called TINYMCE_BASIC_CONFIG (in settings.py), then I can pass that settings dict to mce_attrs like this?:

from django.conf import settings
...
content = forms.CharField(widget=TinyMCE(attrs=settings.TINYMCE_BASIC_CONFIG))
...
claudep commented 3 years ago

The attribute should be mce_attrs, not attrs (which is reserved for standard widget attributes).

Your TINYMCE_BASIC_CONFIG will be merged with the default config. Is dict merging a possible issue?

gusarg81 commented 3 years ago

The attribute should be mce_attrs, not attrs (which is reserved for standard widget attributes).

Sorry, yes a bad typo.

Your TINYMCE_BASIC_CONFIG will be merged with the default config. Is dict merging a possible issue?

I think it will be an issue. Should not be merged, but a new one.

claudep commented 3 years ago

Your TINYMCE_BASIC_CONFIG will be merged with the default config. Is dict merging a possible issue?

I think it will be an issue. Should not be merged, but a new one.

Then that would be a new feature. We could for example imagine a special config key like '__replace' be set to True so the config is replaced instead of merged.

gusarg81 commented 3 years ago

In django-tinymce4-lite I use it like, for example in forms:

widgets = {
    'text': TinyMCE(profile=settings.TINYMCE_ALT_CONFIG)
}

Just mention again in case that helps to apply something similar.

claudep commented 3 years ago

If your alternate config has the same keys as the default config, you can do the same currently with:

widgets = {
    'text': TinyMCE(mce_attrs=settings.TINYMCE_ALT_CONFIG)
}

So the subject of this ticket could be to add an option to replace entirely the default config instead of merging its keys with the default.

gusarg81 commented 3 years ago

Hi,

Sorry to insist, but there is any progress about this?

Thanks.

claudep commented 3 years ago

Didn't my comment above help you? What problem do you encounter with the current method of providing a widget mce_attrs parameter?

gusarg81 commented 3 years ago

Didn't my comment above help you? What problem do you encounter with the current method of providing a widget mce_attrs parameter?

Didn't tested yet. But this method will do a keys merge (like you said), which is not ideal.

Anyways, in the meanwhile I will test today.

claudep commented 3 years ago

As the base config doesn't have so much keys, I'm not sure it will be a problem in real cases.

gusarg81 commented 3 years ago

Hi,

Finally I was available to test this and does not work. For example in my form:

widgets = {
    'description': TinyMCE(mce_attrs=settings.TINYMCE_BASIC_CONFIG)
}

And in the frontend, it trows the error:

Uncaught TypeError: mce_conf[fn_name].includes is not a function

Now, without mce_attrs it does works.

This is my custom TINYMCE_BASIC_CONFIG:

TINYMCE_BASIC_CONFIG = {
    'cleanup_on_startup': True,
    'selector': 'textarea',
    'plugins': '''
        link image preview lists spellchecker hr textcolor colorpicker
        ''',
    'toolbar1': 'undo redo | formatselect | bold italic underline fontsizeselect | '
        'forecolor backcolor | alignleft aligncenter alignright alignjustify | '
        'bullist numlist | blockquote | outdent indent | table hr | link image | '
        'preview | spellchecker',
    'file_browser_callback': False,
    'menubar': False,
    'inline': False,
    'statusbar': True,
    'branding': False,
    'width': '100%',
    'height': '250px',
    'spellchecker_languages': 'Español AR=es_AR,Inglés US=en_US',
    'content_css': '/static/css/base/tinymce.css',
    'convert_urls': True,
    'relative_urls': False,
    'remove_script_host': False,
}

Is the same as the default, but with less items in the toolbar. It does works with django-tinymce4-lite.

gusarg81 commented 3 years ago

Ok, seems the problem is file_browser_callback, removing it does work (which I saw it changed to file_picker_callback in current TinyMCE).

Now, how can I disable file_picker_callback? Which means, disabling image upload button.

Thanks.

EDIT: using 'file_picker_callback': 'null', did the trick. I don't know if is the right way.

some1ataplace commented 1 year ago

To allow multiple TINYMCE_DEFAULT_CONFIG settings in django-tinymce and also provide an option to replace the default config entirely instead of merging its keys with the default, you can modify the TinyMCE widget's render method. Here's an example:

from django import forms
from tinymce.widgets import TinyMCE

class MyTinyMCE(TinyMCE):
    def render(self, name, value, attrs=None, renderer=None):
        mce_attrs = attrs.get('mce_attrs', {})
        if 'configs' in mce_attrs:
            configs = mce_attrs.pop('configs')
            replace = mce_attrs.pop('replace', False)
            if replace:
                self.attrs['data-mce-config'] = configs
            else:
                for key, value in configs.items():
                    self.attrs['data-mce-' + key] = value
        return super().render(name, value, attrs=attrs, renderer=renderer)

TINYMCE_CONFIGS = {
    'default': {
        'height': 400,
        'plugins': 'advlist autolink lists link image charmap print preview anchor',
        'toolbar': 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
        'file_browser_callback': 'filebrowser',
    },
    'simple': {
        'height': 200,
        'plugins': 'textcolor',
        'toolbar': 'undo redo | bold italic | forecolor backcolor',
        'file_browser_callback': 'filebrowser',
    },
}

class MyForm(forms.Form):
    content = forms.CharField(widget=MyTinyMCE(mce_attrs={'configs': TINYMCE_CONFIGS['simple'], 'replace': True}))

To allow multiple TINYMCE_DEFAULT_CONFIG settings in django-tinymce, you can define a dictionary of configurations and pass it to the TinyMCE widget using the attrs argument. Here's an example:


from django import forms
from tinymce.widgets import TinyMCE

TINYMCE_CONFIGS = {
    'default': {
        'height': 400,
        'plugins': 'advlist autolink lists link image charmap print preview anchor',
        'toolbar': 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
        'file_browser_callback': 'filebrowser',
    },
    'simple': {
        'height': 200,
        'plugins': 'textcolor',
        'toolbar': 'undo redo | bold italic | forecolor backcolor',
        'file_browser_callback': 'filebrowser',
    },
}

class MyForm(forms.Form):
    content = forms.CharField(widget=TinyMCE(attrs={'configs': TINYMCE_CONFIGS}))

In the above example, we define two configurations (default and simple) in a dictionary called TINYMCE_CONFIGS. We then pass this dictionary to the TinyMCE widget using the attrs argument in our form definition. The user can then select which configuration to use by setting the config attribute on the textarea element.


Unfortunately, django-tinymce does not allow for multiple TINYMCE_DEFAULT_CONFIG settings out of the box. However, you can achieve something similar by creating different tinymce widgets with different configurations, and then using these widgets in your forms.

Here's an example of how you can create two different tinymce widgets:

from django.forms.widgets import Textarea
from tinymce.widgets import TinyMCE

class CustomTinyMCE(TinyMCE):
    def init(self, args, **kwargs):
        custom_settings = kwargs.pop('custom_settings', {})
        super().init(args, kwargs)
        self.attrs.update({'data-custom-settings': json.dumps(custom_settings)})

class DefaultTinyMCE(CustomTinyMCE):
    def init(self, *args, kwargs):
        custom_settings = {
            'theme': 'modern',
            'plugins': 'link,paste,code',
        }
        super().init(args, custom_settings=custom_settings, **kwargs)

class AdvancedTinyMCE(CustomTinyMCE):
    def init(self,args, **kwargs):
        custom_settings = {
            'theme': 'advanced',
            'plugins': 'link,image,media,anchor,code,table,visualblocks,visualchars,fullscreen,layer',
            'toolbar': 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media | code | table | fullscreen | layer',
            'relative_urls': False,
            'remove_script_host': True,
            'convert_urls': True,
        }
        super().init(args, custom_settings=custom_settings, kwargs)

In this example, DefaultTinyMCE and AdvancedTinyMCE inherit from a custom CustomTinyMCE widget that allows passing custom settings to the TinyMCE widget. DefaultTinyMCE uses a simple configuration with just a few plugins and features, while AdvancedTinyMCE has a more complex configuration with many plugins and features.

You can then use these widgets in your forms like so:

from django import forms

class MyForm(forms.Form):
    content = forms.CharField(widget=DefaultTinyMCE())
    advanced_content = forms.CharField(widget=AdvancedTinyMCE())


from tinymce.widgets import TinyMCE
from django.conf import settings

# Create the default configuration dictionary
my_default_config = {
    'height': 500,
    'plugins': 'advlist autolink lists link image charmap print preview anchor',
    'toolbar': 'undo redo | styleselect | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | link image'
}

# Create a second configuration dictionary
my_other_config = {
    'height': 400,
    'plugins': 'advlist autolink lists link charmap',
    'toolbar': 'undo redo | styleselect   | bold italic | alignleft aligncenter alignright | bullist numlist outdent indent | link'
}

# Custom widget to use the default config
class MyTinyMCEWidget(TinyMCE):
    def init(self, attrs=None, mce_attrs=None):
        final_attrs = {'class': 'my_tinymce_class'}
        if attrs is not None:
            final_attrs.update(attrs)
        if mce_attrs is None:
            mce_attrs = {}
        final_attrs['data-mce-config'] = json.dumps(mce_attrs)
        final_attrs['data-mce-settings'] = json.dumps(my_default_config)
        super().init(attrs=final_attrs)

# Custom widget to use the other config
class MyOtherTinyMCEWidget(TinyMCE):
    def init(self, attrs=None, mce_attrs=None):
        final_attrs = {'class': 'my_tinymce_class'}
        if attrs is not None:
            final_attrs.update(attrs)
        if mce_attrs is None:
            mce_attrs = {}
        final_attrs['data-mce-config'] = json.dumps(mce_attrs)
        final_attrs['data-mce-settings'] = json.dumps(my_other_config)

from django import forms

class MyForm(forms.Form):
    my_field = forms.CharField(widget=MyTinyMCEWidget())
    my_other_field = forms.CharField(widget=MyOtherTinyMCEWidget())

In this example, my_field will use the my_default_config settings, while my_other_field will use the my_other_config settings.