TinyMCE in Django Template - django

Using django-tinymce I have successfully embedded TinyMCE in the Admin before. Embedding it in a front-end form does not seem to work for me however.
I have a form which is a modelForm. It doesn't add any extra fields ('comment' and 'statement' are the only fields used and they exist in the model). On the textarea field, 'comment', of that form I would like to use TinyMCE. I have tried the following:
class EntryForm(forms.ModelForm):
comment = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}, mce_attrs=TINYMCE_USER_CONFIG))
class Meta:
model = Entry
fields = ['statement','comment',]
and
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['statement','comment',]
def __init__(self, *args, **kwargs):
super(EntryForm, self).__init__(*args, **kwargs)
self.fields['comment'].widget = TinyMCE(attrs={'cols': 80, 'rows': 15}, mce_attrs=TINYMCE_USER_CONFIG,)
and
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['statement','comment',]
class Media:
js = ('/media/tinymce/jscripts/tiny_mce/tiny_mce.js',
'/sitemedia/js/tinymce_setup.js',)
In the HEAD html tags I have put {{ form.media }} (the form is called form in the views and template). I am also using Grappelli and Filebrowser for the Admin.
Could someone please explain what I'm missing or the process of getting this to work?
Thanks very much!

To answer my own question:the last option works.
The problem was that `/sitemdia/js/tinymce_setup.js' was a Grappelli setup file. This should only be used by the Admin. I removed that bit so I ended up with:
class Media:
js = ('/media/tinymce/jscripts/tiny_mce/tiny_mce.js',
'',)
And in the header I added
<script type="text/javascript">
tinyMCE.init({
mode: "textareas",
theme: "simple"
});
</script>
You can also instead of removing the setup file insert another file that does work (with for example the tinyMCE.init bit of code in it).
That should do the trick :).

After a full day searching and trying I found it much easier to use a frontend version of tinyMCE on a Django App, delivered through TinyMCE's CDN
Content is saved with the HTML tags and can be displayed to the user with html tags.
I've tried to keep the solution as generic as possible, ie, the javascript should be moved and referenced. If the frontend wysiwyg will be used by more people than you, you should change the |safe in the index to a cleaning function to ensure security/prevent unwanted code hacks.
Being a frontend CDN to render TinyMCE doesn't 'require' backend installation, the viewspy and urls.py are included to show us providing the user with a view of what they enter.
Link to CDN
https://www.tinymce.com/download/
Index.html
{% load staticfiles %}
<!-- <link rel="stylesheet" type="text/css" href="{% static 'wys/style.css' %}" /> -->
<!DOCTYPE html>
<html>
<head>
<!-- Load TinyMCE from CDN -->
<script src="//cdn.tinymce.com/4/tinymce.js"></script>
<!-- Set preference of what should be visible on init -->
<script>tinymce.init({
selector: '#foo',
height: 200,
theme: 'modern',
plugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
'searchreplace wordcount visualblocks visualchars code fullscreen',
'insertdatetime media nonbreaking save table contextmenu directionality',
'emoticons template paste textcolor colorpicker textpattern imagetools codesample toc'
],
toolbar1: 'undo redo | insert | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
toolbar2: 'print preview media | forecolor backcolor emoticons | codesample',
image_advtab: true,
templates: [
{ title: 'Test template 1', content: 'Test 1' },
{ title: 'Test template 2', content: 'Test 2' }
],
content_css: [
'//fonts.googleapis.com/css?family=Lato:300,300i,400,400i',
'//www.tinymce.com/css/codepen.min.css'
]
});
</script>
</head>
<body>
<h1>This is the homepage</h1>
<h1>Below Are 2 Form Areas</h1>
<form method="GET">
Question: <input type="text" id="foo" name="search"><br/><br/><br/><br/><br/>
Name: <input type="text" id="bar" name="name"><br/><br/><br/><br/><br/><br/>
<input type="submit" value="Submit" />
</form><br/><br/>
<h3>Display of question</h3>
{% for question in questions %}
<p>{{ question.query|safe}}</p>
<p>{{ question.user_id|safe}}</p>
{% endfor %}
</body>
views.py
from django.shortcuts import render
from .models import Queries
def MainHomePage(request):
questions=None
if request.GET.get('search'):
search = request.GET.get('search')
questions = Queries.objects.filter(query__icontains=search)
name = request.GET.get('name')
query = Queries.objects.create(query=search, user_id=name)
query.save()
return render(request, 'wys/index.html',{
'questions': questions,
})
urls.py in the App
from django.conf.urls import url
from . import views
app_name = 'wys'
urlpatterns = [
url(r'^', views.MainHomePage, name='index'),
# url(r'^', views.MainHomePage, name='index'),
]

Related

How to add information in a page without refreshing the page?

I would like to have a page in my django application that has a button that search a product and after selecting quantities gets added to the page (without reloading) in a list where the total get calculated (something like that), I am a beginner in programing and I have a week reading and investigated how to do it but I don't found anything.
Is because you need other programming language? Or could you indicate me some documentation or some example that I can read. Mostly because for my inexperience I don't know how to identify the relevant information in the documentation or even what to look for.
This can be done using Ajax call,
check this example:
forms.py
class sampleForm(forms.Form):
input = forms.CharField()
views.py
from django.http import JsonResponse
def sampleview(request):
input = request.POST.get('input', None)
#perform your logic here
#let us say your data is in variable result
result = {'product1' : 'python' ,'product2' : 'django' , 'product3' : 'ajax' }
return JsonResponse(result)
urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path('sampleview',views.sampleview,name='sampleview'),
]
your HTML
<form method="POST">
{% csrf_token %}
{{form.as_p}}
<button id="sampleform-submit" type="submit">submit</button>
</form>
<div id="results"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script>
$("#sampleform-submt").click(function(){
e.preventDefault();
var form = $('#id_input').closest("form")
//id_input is id of input tag generated by form and above line selects the form
$.ajax({
url : "{% url 'sampleview' %}",
type: "POST",
data : form.serialize(),
dataType: 'json',
success: function(data){
#data is 'result' you return in sampleview function
$("#results").append('<p> '+ data.product1 +'</p>');
$("#results").append('<p> '+ data.product2 +'</p>');
$("#results").append('<p> '+ data.product3 +'</p>');
}
});
}
</script>
I hope this helps

How to upload images (like from my computer) to my django-tinymce formField?

I have an HTML page (not django admin) showing a WYSIYYG tinymce field:
What i need to do with it is writing some text (it works), upload some images to illustrate the text (it doesn't work) and finally if possible give a class to these uploaded images.
This is for a kind of 'page' generator, every content written in the edidor will show up as new page on my webite.
the form :
class PageForm(forms.Form):
name = forms.CharField(max_length=255)
content = forms.CharField(widget=TinyMCE())
the model:
class Page(models.Model):
name = models.CharField(max_length=255,
null=False,
blank=False,
unique=True,)
content = models.TextField(null=False,
blank=False)
slug = models.CharField(max_length=255)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Page, self).save(*args, **kwargs)
the html page (basic):
<body>
{% if error %}
<p>Une erreur est survenue</p>
{% endif %}
{% if action == "update-page" %}
<form method="post" action="{% url "page_update" page.slug %}">
{% elif action == "create-page" %}
<form method="post" action="{% url 'page_create' %}">
{% endif %}
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Enregistrer" />
</form>
</body>
For the moment when i click on the insert/edit icon it just offers me to give a 'link' and not upload an image.
So how do i have to setup my django and/or setup tinymce
Thank you.
(please consider in your answers that my english and my dev lvl is sometimes too weak to understand some parts of technical documentation)
The solution suggested by this tutorial works, and it uses a file picker JavaScript callback, but it overrides your TINYMCE_DEFAULT_CONFIG if you have one in settings.py.
So, I suggest adding that file_picker_callback in settings.py, like this:
TINYMCE_DEFAULT_CONFIG = {
"entity_encoding": "raw",
"menubar": "file edit view insert format tools table help",
"plugins": 'print preview paste importcss searchreplace autolink autosave save code visualblocks visualchars fullscreen image link media template codesample table charmap hr pagebreak nonbreaking anchor toc insertdatetime advlist lists wordcount imagetools textpattern noneditable help charmap emoticons quickbars',
"toolbar": "fullscreen preview | undo redo | bold italic forecolor backcolor | formatselect | image link | "
"alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist checklist | fontsizeselect "
"emoticons | ",
"custom_undo_redo_levels": 50,
"quickbars_insert_toolbar": False,
"file_picker_callback": """function (cb, value, meta) {
var input = document.createElement("input");
input.setAttribute("type", "file");
if (meta.filetype == "image") {
input.setAttribute("accept", "image/*");
}
if (meta.filetype == "media") {
input.setAttribute("accept", "video/*");
}
input.onchange = function () {
var file = this.files[0];
var reader = new FileReader();
reader.onload = function () {
var id = "blobid" + (new Date()).getTime();
var blobCache = tinymce.activeEditor.editorUpload.blobCache;
var base64 = reader.result.split(",")[1];
var blobInfo = blobCache.create(id, file, base64);
blobCache.add(blobInfo);
cb(blobInfo.blobUri(), { title: file.name });
};
reader.readAsDataURL(file);
};
input.click();
}""",
"content_style": "body { font-family:Roboto,Helvetica,Arial,sans-serif; font-size:14px }",
}
I made this using https://django-filebrowser.readthedocs.io/en/3.5.2/quickstart.html
and https://django-tinymce.readthedocs.io/en/latest/usage.html.
settings.py
wyswyg = [ # what you see is what you get --- enables to add a text editor on website
"tinymce",
"grappelli",
"filebrowser",
]
INSTALLED_APPS = (
[
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
+ wyswyg
)
TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, "tinymce")
urls.py
from filebrowser.sites import site
urlpatterns = [
url(r"^admin/filebrowser/", site.urls),
url(r"^tinymce/", include("tinymce.urls")),
path("admin/", admin.site.urls),
]
form
from tinymce.widgets import TinyMCE
class MailForm(forms.Form):
to = forms.SelectMultiple()
subject = forms.CharField(max_length=50)
content = forms.CharField(widget=TinyMCE(attrs={"cols": 80, "rows": 30}))
You can dynamically create pages with WYSWYG editors like django-tinymce.
First of all install django-tinymce pip install django-tinymce and add tinymce to INSTALLED_APPS in settings.py for your project
and add to urlpatterns
urlpatterns = [
path('admin/', admin.site.urls),
path('tinymce/', include('tinymce.urls')),
...
)
Create a Model just like you did
from tinymce.models import HTMLField
class Page(models.Model):
name = models.CharField(max_length=255,unique=True)
slug = models.CharField(max_length=255,unique=True)
content = models.HTMLField()
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Page, self).save(*args, **kwargs)
note that you didnt need to use null=False,blank=False to any field. It is set by default and you should use HTMLField() instead of TextField.
And your forms.py should be like
from django import forms
from django.forms.widgets import TextInput
from app.models import Page
from tinymce.widgets import TinyMCE
class PageForm(forms.ModelForm):
class Meta:
model = Page
exclude = ['slug']
widgets = {
'name' : TextInput(attrs={'class': 'form-control', 'placeholder': 'Page Title'}),
'content' : TinyMCE(attrs={'cols': 80, 'rows': 30,'class': 'form-control'}),
}
And template should be like
<form method="post" action="
{% if action == "update-page" %}
{% url "page_update" page.slug %}
{% elif action == "create-page" %}
{% url 'page_create' %}
{% endif %}
">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Enregistrer" />
</form>
This is the basic setup of django-tinymce. Additionally, the following options can be defined for tinymce in your Django project’s settings.py file.
DEFAULT = {
'selector': 'textarea',
'theme': 'modern',
'plugins': 'link image preview codesample contextmenu table code',
'toolbar1': 'bold italic underline | alignleft aligncenter alignright alignjustify '
'| bullist numlist | outdent indent | table | link image | codesample | preview code',
'contextmenu': 'formats | link image',
'menubar': False,
'inline': False,
'statusbar': True,
'height': 360,
}
For image handling, I'd suggest you to use the popular Pillow library and a models.ImageField.
This field only saves the URL / path of the image and not the actual image within the database. However, django saves the actual image in your static files assets folder.
The image will be served by your server when you put something like into a template containing the image object as a context variable.
A great tutorial is here: https://coderwall.com/p/bz0sng/simple-django-image-upload-to-model-imagefield
finally i found exactly what I was looking for here:
https://karansthr.gitlab.io/fosstack/how-to-set-up-tinymce-in-django-app/index.html
So if you what to setup a WYSIWYG editor in your django project, be able to upload images/files from the client computer and use it in admin and/or in your personalised forms just follow the steps, if it doesn't work perfectly you may need to check the code he uploaded on github witch is a bit different but functional with the very lasts versions of django (2.1 if you read this in 2054).

djangocms integration filer_image with apphook objects

I want to add filer_image field to my Gift class instance object. It works as apphook in django-cms. The main problem is that after making migrations and open the view where the form is I don't have loaded js.
I already added all tags:
{% load staticfiles i18n cms_tags sekizai_tags menu_tags thumbnail filer_tags filer_image_tags %}
The model is:
class Gift(models.Model):
filer_image = FilerImageField(related_name="book_covers")
The form:
class GiftForm(ModelForm):
class Meta:
model = Gift
fields = '__all__'
widgets = {
'name': forms.TextInput(attrs={'class': 'basic-input full-width'}),
}
The rendered output:
The thumbnail and input view
Please tell me what am I doing wrong with these. It seems to me like some js files are not loaded. After click it opens FileImageFiler gallery, but I also cannot select any image.
Ok, i find the soliton. Basically I added {{ form.media }} after {{ csrf_token } and I have extended the form class with:
class Media:
extend = False
css = {
'all': [
'filer/css/admin_filer.css',
]
}
js = (
'admin/js/core.js',
'admin/js/vendor/jquery/jquery.js',
'admin/js/jquery.init.js',
'admin/js/admin/RelatedObjectLookups.js',
'admin/js/actions.js',
'admin/js/admin/urlify.js',
'admin/js/prepopulate.js',
'filer/js/libs/dropzone.min.js',
'filer/js/addons/dropzone.init.js',
'filer/js/addons/popup_handling.js',
'filer/js/addons/widget.js',
'admin/js/related-widget-wrapper.js',
)
That is all!

Django DateInput() Widget appears in Chrome, but not Firefox or IE

Issue
I have a DateInput() widget that appears fine in Chrome, but does not appear in Firefox or IE. I'm using Django 1.6.5 and the latest Chrome (Version 35.0.1916.153) and FireFox(30.0)
Works Correctly in Chrome (Calendar Selector appears)
Does not work correctly in Firefox or IE (Calendar Selector doesn't appear)
forms.py
class DateInput(forms.DateInput):
input_type = 'date'
class TimeForm(forms.ModelForm):
class Meta:
model = Time
fields = ['date_select']
widgets = {
'date_select': DateInput()
}
html
<form method='POST' action=''>{% csrf_token %}
{{ time_form.as_p }}
{{ program_form.as_p }} {# can ignore this part #}
<input type='submit' class="btn btn-lg btn-danger">
</form>
models.py
class Time(models.Model):
date_select = models.DateField()
def __unicode__(self):
return smart_unicode(self.date_select)
This is my first app since the Polls tutorial so let me know if there's more relevant code that I should post here. Thanks for your time.
EDIT AFTER ANSWERS AND COMMENTS
I wanted to include what I did in response to the great comments and answers. I went with the jQuery UI solution by using the code from http://jqueryui.com/datepicker/ To implement it into my project, I added:
html
<!-- Custom CS for JQuery UI -->
<link rel="stylesheet" href="/static/css/jquery-ui-1.10.4.min.css">
<!-- JQuery UI for things like Calendar Widget -->
<script src="/static/js/jquery-ui-1.10.4.min.js"></script>
<!-- Custom JS -->
<script src="/static/js/custom.js"></script>
custom.js
// For JQuery UI Calendar
$(function() {
$( "#id_date_select" ).datepicker();
});
forms.py
class TimeForm(forms.ModelForm):
class Meta:
model = Time
fields = ['date_select']
date_select = forms.DateField()
Django's default date widget is rendered as <input type="date"> in HTML. Chrome is the only major browser that has built in calendar for date input types. FF and IE read it as default text input.
The solution would be to create custom widget in django, that uses some javascript to generate the datepicker. This should point you to the right direction https://docs.djangoproject.com/en/1.6/ref/forms/widgets/#customizing-widget-instances. You could also use some library like jQueryUI(http://jqueryui.com/datepicker/) so you don't have to code it all by yourself.

How can i hook up WMD editor on my django forms?

How can i hook-up WMD editor on to my django forms?
Here is complete Django widget class:
class WMDEditor(forms.Textarea):
def __init__(self, *args, **kwargs):
attrs = kwargs.setdefault('attrs', {})
if 'cols' not in attrs:
attrs['cols'] = 58
if 'rows' not in attrs:
attrs['rows'] = 8
super(WMDEditor, self).__init__(*args, **kwargs)
def render(self, name, value, attrs=None):
rendered = super(WMDEditor, self).render(name, value, attrs)
return rendered + mark_safe(u'''<script type="text/javascript">
wmd_options = {
output: "Markdown",
buttons: "bold italic | link blockquote code image | ol ul"
};
</script>
<script type="text/javascript" src="%sjs/wmd/wmd.js"></script>''' % settings.MEDIA_URL)
Use it in your form definition like text = forms.CharField(widget=WMDEditor).
From the readme.txt in the current WMD download:
To install the editor, include wmd.js
right before your closing <body>
tag:
<script type="text/javascript" src="wmd/wmd.js"></script>
Example:
<!DOCTYPE html>
<html>
<head>
<title>My Page</title>
</head>
<body>
<textarea></textarea>
<script type="text/javascript" src="wmd/wmd.js"></script>
</body>
</html>
By default, WMD will turn the first
textarea on your page into an editor.
You can modify this behavior with the
wmd-ignore class, described below.
(It's also possible to disable
autostart and instantiate the editor
through JavaScript, as shown in
apiExample.html. But be warned that
the current API will change a lot in
the upcoming open-source release; it
was never actually meant for public
consumption.)
So, add the necessary code to the template you're using to render the form, and make sure that the textarea you want to use WMD on is the first on the page, and you'll be good to go.
I just finished doing this. There are a couple more subtleties to be aware of that aren't covered in the other answers (so far).
First, in order for Django to properly retrieve the value of WMD editor when the form is submitted, it needs to have Django's value for id="" set on it. Normally this would look something like 'id_'
But #1 creates a problem: WMD Editor is hard-coded to look for id="wmd-input" to know what text area it makes use of.
Therefore, what we need is a way to pass the value of the id attribute to WMD. I've done this myself by having the Django template render a global javascript variable, that when executed client side will be used by WMD to properly locate the textarea tag.
If we're re-doing the WMD id tag, then we'll also need to make sure the CSS still works
Lastly, in order for the value to be pre-populated by Django, we need to make sure that value is being rendered in the textarea tag
So here's some code for you.
widgets.py
from django import forms
from django.contrib.admin import widgets as admin_widgets
from django.template import loader, Context
from django.utils.html import conditional_escape
from django.utils.encoding import force_unicode
class WMDEditor(forms.Textarea):
def render(self, name, value, attrs=None):
# Prepare values
if not value:
value = ''
attrs = self.build_attrs(attrs, name=name)
# Render widget to HTML
t = loader.get_template('wmd/widget.html')
c = Context({
'attributes' : self._render_attrs(attrs),
'value' : conditional_escape(force_unicode(value)),
'id' : attrs['id'],
})
return t.render(c)
wmd/widget.html
(name this whatever you need for your app)
<div class="wmd-wrapper">
<div id="wmd-button-bar" class="wmd-panel"></div><br/>
<textarea class="wmd-panel wmd-input" {{ attributes|safe }}>{{ value }}</textarea><br/>
Preview
<div id="wmd-preview" class="wmd-panel"></div><br/>
</div>
<script type="text/javascript">// <![CDATA[
var WMD_TEXTAREA_ID = '{{ id }}'
// ]]> </script>
<script type="text/javascript" src="{{ MEDIA_URL }}js/wmd/wmd.js"></script>
NOTE: You might need to adjust the MEDIA_URL depending on how you're handling that (custom template tag, middleware, whatever). If you're new to Django and don't understand what I just said, just hardcode the value in for now to get it working and learn what that all means later.
Lastly, you'll need to make 1 minor edit to the WMD source (note that I'm using the StackOverflow fork, so this might be slightly different for other versions)
wmd.js
// This is around line 69
// Change this -> this.input = doc.getElementById("wmd-input");
// Into this:
this.input = doc.getElementById(WMD_TEXTAREA_ID);
If you're using the wmd.css and haven't written your own already, you'll also need to make a slight update to that. Because that element is no longer #wmd-input, we'll need to update it to make sure it uses the wmd-input class:
wmd.css
.wmd-input, /* <-- Add this here, around line 33 */
#wmd-input
{
height: 250px;
width: 100%;
background-color: #FFFFFF;
border: 1px solid #4d86c1;
}
Whew! That was a bunch. Hope that helps everyone.