romanvm / django-tinymce4-lite

TinyMCE 4 editor widget for Django
MIT License
126 stars 47 forks source link

issue Textfield shows html in production #47

Open samazaphikel opened 5 years ago

samazaphikel commented 5 years ago

Hi,

in development env, the text fields are formatted as expected.

But on production, the textfileds has the blank html and the menubar is not shown

romanvm commented 5 years ago

Make sure you have correctly set-up static files serving in your prod environment.

samazaphikel commented 5 years ago

static-files setup is the same for dev and prod

romanvm commented 5 years ago

"There are no psychics" (c) "The Mentalist"

Obviously, something is different, but I cannot help you if you do not provide any information. Do links to the static files, especially related to TinyMCE 4, in your rendered HTML pages actually work? Are there any errors in the Django log and/or the browser console?

samazaphikel commented 5 years ago

You're right, I'm sorry. I guess I was a too fast.

I have no errors in my logs. nothing in Django log or the browser console, and no error in sentry.

in my settings

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.humanize',
    'widget_tweaks',
    'social_django',
    'accounts',
    'storages',
    'django.contrib.admin',
    'daterange_filter',
    'tinymce',
]

TINYMCE_DEFAULT_CONFIG = {
    'selector': 'textarea',
    'theme': 'modern',
    'plugins': "table,paste,searchreplace,link ",
    'toolbar1': 'bold italic underline | link | preview | ',
    'contextmenu': 'formats | link ',
    'menubar': False,
    'inline': False,
    'statusbar': True,
    #'height': 600,
    'width': 800,
    'branding': True,
}

STATIC_URL = '/static/'
static_root_default = os.path.join(os.path.dirname(BASE_DIR), 'staticfiles')
STATIC_ROOT = config('STATIC_ROOT', default=static_root_default)

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'myproject', 'static'),
]

In my template i Includet {{ form.media }}

In my template, I import only bootstrap4 css and js

romanvm commented 5 years ago

I can only repeat: make sure you have correctly set-up static files serving in your prod environment. And I'm not talking about Django. Unless you are using Whitenoise (which is a special case), Django does not serve static files in production environments. Check the configuration of your front-end server (Nginx, Apache, etc.) or cloud static storage (e.g. AWS S3) that you are using.

samazaphikel commented 5 years ago

Hi,

I deleted tinymce4-lite complete and added new in my project.

in my .html file I tried something like this

<link rel="stylesheet" href="{% static 'tinymce/tinymce4.css' %}">
<script src="{% static 'tinymce/tinymce_init.js' %}"></script>

but its not working.

what do I have to set if the template files stored at AWS_S3?

merwok commented 5 years ago

Are you running collectstatic?

samazaphikel commented 5 years ago

yes

merwok commented 5 years ago

What’s the output from collectstatic?

What are your STATIC_* settings?

Are you using whitenoise to serve static files from the django app, or a more classic nginx+django combo?

Aside: export cat xargs looks weird. If .env-prod is a dotenv-style file using bash-compatible format, sourcing would be cleaner.

samazaphikel commented 5 years ago

ok, I start from the beginning.

settings.py

INSTALLED_APPS = [
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.humanize',
    'widget_tweaks',
    'social_django',
    'app1',
    'app2',
    'storages',
    'django.contrib.admin',
    'tinymce',
]

TINYMCE_DEFAULT_CONFIG = {
    'selector': 'textarea',
    'theme': 'modern',
    'plugins': "table,paste,searchreplace,link ",
    'toolbar1': 'bold italic underline | link | preview | ',
    'contextmenu': 'formats | link ',
    'menubar': False,
    'inline': False,
    'statusbar': True,
    #'height': 600,
    'width': 800,
    'branding': True,
}

STATIC_URL = '/static/'
static_root_default = os.path.join(os.path.dirname(BASE_DIR), 'staticfiles')
STATIC_ROOT = config('STATIC_ROOT', default=static_root_default)

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'myproject', 'static'),
]

# AWS S3 configuration
AWS_STORAGE_BUCKET_NAME = 'xxx'
AWS_S3_REGION_NAME = 'eu-west-1'  

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID', default=None)
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY', default=None)

STATICFILES_STORAGE = config(
    'STATICFILES_STORAGE',
    default='django.contrib.staticfiles.storage.StaticFilesStorage'
)

in my html template I have


{% extends 'base.html' %}
{% load static %}
{% block title %}Request {{ album.pk }}{% endblock %}
{% block albums_active %}active{% endblock %}
{% load humanize %}
{% block breadcrumb %}

{% endblock %}
{% block content %}
<link rel="stylesheet" href="{% static 'tinymce/tinymce4.css' %}">
<script src="{% static 'tinymce/tinymce_init.js' %}"></script>

{% csrf_token %}
{{ form.media }}
   ### form content ###
{% endblock %}

Then i copied tinymce/tinymce4.css and tinymce/tinymce_init.js from /home/PycharmProjects/mysite/venv/lib/python3.6/site-packages/tinymce to /home/PycharmProjects/mysite/myproject/static/tinymce

Then I upload and run on production:

output from collectstatic: 0 static files copied to '/home/PycharmProjects/staticfiles', 461 unmodified.

When I run local or development I have the tinymce header toolbar and also html is displayed as a word document, no html brackets

image

But when I open on productiv, I have raw html and no tinymce header toolbar

merwok commented 5 years ago

Then i copied tinymce/tinymce4.css and tinymce/tinymce_init.js from /home/PycharmProjects/mysite/venv/lib/python3.6/site-packages/tinymce to /home/PycharmProjects/mysite/myproject/static/tinymce

Why? That’s the job of collectstatic.

But when I open on productiv, I have raw html and no tinymce header toolbar

Is the HTML generated with a link like <link rel="stylesheet" href="">?

What’s the output of collectstatic on production?

romanvm commented 5 years ago

@samazaphikel You still haven't said how you are serving your static files in production. What happens If you click links to static assets in your pages HTML code?

Also, you don't need to add TinyMCE static assets to your templates explicitly, they are added by {{ form.media }} tag.

romanvm commented 5 years ago

This I didn't notice at first.

<link rel="stylesheet" href="{% static 'tinymce/tinymce4.css' %}">
<script src="{% static 'tinymce/tinymce_init.js' %}"></script>

Then i copied tinymce/tinymce4.css and tinymce/tinymce_init.js from /home/PycharmProjects/mysite/venv/lib/python3.6/site-packages/tinymce to /home/PycharmProjects/mysite/myproject/static/tinymce

Why on earth did you do that? Those files are Django templates rendered by template engine. They are not valid JS and CSS files. The fact that they are placed in /templates subdirectory should have given you a hint.

fsecada01 commented 5 years ago

I am also running into this issue. I'm serving the files using Django-Storages. All static files except those from this module load as expected.

merwok commented 5 years ago

Without relevant parts of your settings it is hard to help!

Do you have tinymce in installed apps? Does the output of collectstatic look ok? Can you run findstatic tinymce/tinymce4.css ?

fsecada01 commented 5 years ago

Do you have tinymce in installed apps?

Yes

Does the output of collectstatic look ok?

Yes

Can you run findstatic tinymce/tinymce4.css ?

It cannot be found. Judging from the file hierarchy, it seems to be notably absent. Screenshot_35

fsecada01 commented 5 years ago

The error seems to be caused by a request error when pulling the static files from AWS. It is perplexing that this is the case, given that all other static files load correctly. From my browser: Screenshot_36

fsecada01 commented 5 years ago

The issue can be resolved by linking to the CDN via the settings.py file. The module cannot import the underlying tinymce4 js file locally (for some reason).

From the documentation:

TINYMCE_JS_URL – a path to TinyMCE JavaScript library. Default: your_static_url/tinymce/js/tinymce/tinymce.min.js. The following example shows how to load the TinyMCE library from a CDN:

The latest V4 file can be accessed here

romanvm commented 5 years ago

Can you run findstatic tinymce/tinymce4.css

Once again, this is a template, not a static asset.

raugbein commented 5 years ago

Hi, I have the same issue.

My steps: in settings.py

INSTALLED_APPS = (
    ...
    'tinymce',  #the last entry in installed apps
)

TINYMCE_DEFAULT_CONFIG = {
    'selector': 'textarea',
    'theme': 'modern',
    'plugins': 'link image preview codesample contextmenu table code lists',
    'toolbar1': 'formatselect | bold italic underline | alignleft aligncenter alignright alignjustify '
               '| bullist numlist | outdent indent | table | link image | codesample | preview code',
    'contextmenu': 'formats | link ',
    'menubar': False,
    'inline': False,
    'statusbar': True,
    'height': 400,
    'width': 400,
    'branding': True,
}

in urls.py I added

url(r'^tinymce/', include('tinymce.urls')),

and in my specific html I added (in my case is named testing_form.html)

{{ form.media }}

When I run local, it looks gread, but when I publish to AWS, its not working

I use Django in combination with AWS S3 and NGINX

STATIC_URL = '/static/'
static_root_default = os.path.join(os.path.dirname(BASE_DIR), 'staticfiles')
STATIC_ROOT = config('STATIC_ROOT', default=static_root_default)

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'newproject', 'static'),
]

AWS_STORAGE_BUCKET_NAME = 'deleted for public'
AWS_S3_REGION_NAME = 'eu-west-1'  

AWS_ACCESS_KEY_ID = config('AWS_ACCESS_KEY_ID', default=None)
AWS_SECRET_ACCESS_KEY = config('AWS_SECRET_ACCESS_KEY', default=None)

STATICFILES_STORAGE = config(
    'STATICFILES_STORAGE',
    default='django.contrib.staticfiles.storage.StaticFilesStorage'
)

Run DB migrations: django-admin migrate --noinput Run static gathering: django-admin collectstatic --noinput output from collectstatic: 0 static files copied to '/home/Projects/newproject/staticfiles', 588 unmodified.

I can open https://s3-eu-west-1.amazonaws.com/projectname/tinymce/tinymce_init.js and https://s3-eu-west-1.amazonaws.com/projectname/tinymce/tinymce4.css

from my understanding collectstatic was executed correctly because I can call the url directly

But when I open the console I get ReferenceError: tinymce is not defined

what did I forget?

romanvm commented 5 years ago

I can open https://s3-eu-west-1.amazonaws.com/projectname/tinymce/tinymce_init.js

I guess you have confused something because such URL does not exist in this project so it cannot be opened in any case.

https://s3-eu-west-1.amazonaws.com/projectname/tinymce/tinymce4.css

This is not a static CSS file but a dynamically rendered endpoint.

First of all, click Ctrl+U to see the source of your page with TinyMCE editor. It should have the following line:

<script type="text/javascript" src="/static/tinymce/js/tinymce/tinymce.min.js"></script>

The path may be longer if you are using django-storages with an external cloud storage like S3. Click on this path and see what happens. If you get 404, then you need to configure serving static files in your setup, be it Nginx, or S3 or whatever you are using. And such configuration is outside the scope of this project. There are plenty materials in the Internet.

raugbein commented 5 years ago

Hi

I was a little bit sick, thats why no response.

When I click on the Link on published source for tinymce.min.js, I get this Error

<Error>
<Code>AccessDenied</Code>
<Message>Request has expired</Message>
<Expires>2019-05-14T11:08:55Z</Expires>
<ServerTime>2019-05-20T15:03:01Z</ServerTime>
<RequestId>910DBC899FBB74FD</RequestId>
<HostId>
rJk701fdz4If0fIUNd4LoXlE5b1pDEKptIcwyfQ/FoSBpO7XwR4iOA5q6DcdOHKpBXVSP5IuF4=
</HostId>
</Error>

I tried for all other files, and the other files are fine. Only for this one I get this Error

romanvm commented 5 years ago

I can only repeat: you need to fix serving static files in your configuration. This response has nothing to do with this project or Django in general.

fsecada01 commented 5 years ago

I think I identified the problem. django-tinymce4-lite is rendering the URLs from S3 incorrectly. It is failing to convert the & character to &amp;. I don't know if that's a Unicode issue or not, but I can see what can be done.

fsecada01 commented 5 years ago

It's most likely this code from tinymce/widgets.py, starting at line 200

        else:
            html = '<textarea{0}>{1}</textarea>\n'.format(flatatt(final_attrs), escape(value))
        html += '<script type="text/javascript">{0}</script>'.format(
            jsmin(render_tinymce_init_js(mce_config,
                                         mce_settings.CALLBACKS.copy(),
                                         final_attrs['id'])
                  )

maybe putting the URL through a encode('ascii', 'ignore').decode('unicode_escape') filter would solve the problem.

romanvm commented 5 years ago

@fsecada01

I think I identified the problem. django-tinymce4-lite is rendering the URLs from S3 incorrectly. It is failing to convert the & character to &. I don't know if that's a Unicode issue or not, but I can see what can be done.

Could you explain what URLs you are talking about?

maybe putting the URL through a encode('ascii', 'ignore').decode('unicode_escape') filter would solve the problem.

This operation makes no sense from technical point of view. And that part of the code does not work with any URLs. It renders a textarea HTML field with the corresponding inline JS code for initializing TinyMCE widget attached to this textarea.

fsecada01 commented 5 years ago

@romanvm

Could you explain what URLs you are talking about?

The S3 URL that gets generated when {{form.media}} is inserted into the Django HTML template

This operation makes no sense from technical point of view. And that part of the code does not work with any URLs. It renders a textarea HTML field with the corresponding inline JS code for initializing TinyMCE widget attached to this textarea. That part of the code references the JS file required to run the HTMLField, and so references the URL location for said file when using S3.

I'm sharing with you what is most likely happening, as this is localized to JS files for this module only. No other module or collected static files are having this issue when being pulled from AWS.

romanvm commented 5 years ago

The S3 URL that gets generated when {{form.media}} is inserted into the Django HTML template

OK, but the code fragment above has nothing to do with rendering {{form.media}} tag. It is done by Django itself based on media property of a widget class: https://github.com/romanvm/django-tinymce4-lite/blob/master/tinymce/widgets.py#L211

I'm sharing with you what is most likely happening, as this is localized to JS files for this module only.

Sorry, but I don't see you sharing any information at all. I still don't understand what exactly your problem is. If you think that this application incorrectly renders static URLs from S3, concrete examples of rendered HTML code compared to what is expected would be a good start.

fsecada01 commented 5 years ago

OK, but the code fragment above has nothing to do with rendering {{form.media}} tag. It is done by Django itself based on media property of a widget class: https://github.com/romanvm/django-tinymce4-lite/blob/master/tinymce/widgets.py#L211

Ah, this helps illuminate things a bit. The code snippet I shared related to how the js script address would populate the HTML head tag with the tinymce.min.js file location. Sorry, but I don't see you sharing any information at all. I still don't understand what exactly your problem is. If you think that this application incorrectly renders static URLs from S3, concrete examples of rendered HTML code compared to what is expected would be a good start.

I'll have to roll back the fix I did for my app and share the specific addresses, but suffice to say, special characters show up in the URL addresses when they should be converted to ASCII.

fsecada01 commented 5 years ago

Now that I'm looking at this, I think this is an issue relating to using django-tinymce4-lite with django-storages if you customize the location addresses for s3 files. I know I did that to split public and private files with S3 and staticfiles_storages attempts to get the storage class from the STATICFILES_STORAGE key.

For the greater context of what I mean: https://simpleisbetterthancomplex.com/tutorial/2017/08/01/how-to-setup-amazon-s3-in-a-django-project.html#working-with-static-and-media-assets

fsecada01 commented 5 years ago

So basically, make sure that STATICFILES_STORAGE actually points to the S3 backend.

batyr993 commented 3 years ago

It's most likely this code from tinymce/widgets.py, starting at line 200

        else:
            html = '<textarea{0}>{1}</textarea>\n'.format(flatatt(final_attrs), escape(value))
        html += '<script type="text/javascript">{0}</script>'.format(
            jsmin(render_tinymce_init_js(mce_config,
                                         mce_settings.CALLBACKS.copy(),
                                         final_attrs['id'])
                  )

maybe putting the URL through a encode('ascii', 'ignore').decode('unicode_escape') filter would solve the problem. I am using python 2.7 and django 1.11.17 and putting # -- coding: utf-8 -- on top of widgets.py and having u' infront of '<textarea{0}>{1}\n'.format(flatatt(final_attrs), escape(value)) solved my problem. :)