Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Example] Need simpler or better example for mongoengine and Flask Admin #8

Closed
ufo911 opened this Issue May 28, 2018 · 65 comments

Comments

Projects
None yet
2 participants
@ufo911
Copy link

ufo911 commented May 28, 2018

I am using Flask-Admin and have already integrated ckeditor and works with only texts but I need to upload images with text too and I need more examples if it is possible

Without WTF

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented May 29, 2018

Here are some basic examples. However, image upload behavior changed since CKEditor 4.5, I will release a new version to support it today.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented May 29, 2018

I need it to use with flask admin, upload image or better is to browse directory too and if you can help me that would be great

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented May 29, 2018

I just updated the newer example for image upload and release 0.4.0.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented May 29, 2018

@greyli
where are you telling ckeditor to override image field ? I need it to use with flask-admin and maybe it has to be without WTF..

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented May 29, 2018

I don't understand your question. What's the image field?

When you set CKEDITOR_FILE_UPOLOADER with proper value, you can just drag and drop the image file into the editor or upload with image widget.

These resources may be helpful:

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented May 30, 2018

@greyli
Are you planning adding directory browser ? I think it is CKfinder

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented May 30, 2018

I did think about it, but currently, I don't have time to investigate and implement it. Happy to accept PR addressing this.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 2, 2018

Can I use it without FlaskForm class ?

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jun 3, 2018

Sure, check out this example.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 3, 2018

In Flask Admin Standard way is to override StringField but for your extension I can not understand for images how to get worked it..

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jun 3, 2018

  1. Override StringField with flask_ckeditor.CKEditorField
  2. Implement a view function which handles upload files
  3. Set CKEDITOR_FILE_UPLOADER to the upload handler view's endpoint
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 3, 2018

So.. tried several things and not working, may Flask-Admin needs different details to upload images within ckeditor

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jun 3, 2018

Provide the error traceback and a minimal application will be helpful.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 3, 2018

class CKTextAreaWidget(widgets.TextArea):
    def __call__(self, field, **kwargs):
        # add WYSIWYG class to existing classes
        existing_classes = kwargs.pop('class', '') or kwargs.pop('class_', '')
        kwargs['class'] = u'%s %s' % (existing_classes, "ckeditor")
        return super(CKTextAreaWidget, self).__call__(field, **kwargs)


class CKTextAreaField(TextAreaField):
    widget = CKTextAreaWidget()
class OffersView(ModelView):
    column_list = ["title_ka", "title_en", "title_ru", "is_main", "is_enabled", "position"]
    form_overrides = dict(desc_ka=CKTextAreaField,desc_en=CKTextAreaField,desc_ru=CKTextAreaField)
    create_template = 'edit.html'
    edit_template = 'edit.html'
    def is_accessible(self):
        if flask_login.current_user.is_authenticated:
            user = CustomUser.objects.get(email=flask_login.current_user.id)
            return user.is_superuser

    def inaccessible_callback(self, name, **kwargs):
        # redirect to login page if user doesn't have access
        return redirect(url_for('login', next=request.url))
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 3, 2018

{% extends 'admin/model/edit.html' %}

{% block tail %}
    {{ super() }}
    <script src="//cdn.ckeditor.com/4.9.2/full/ckeditor.js"></script>
    <script type="text/javascript">
		$('.control-group a').click( function(e) {e.preventDefault(); 
		for(var instanceName in CKEDITOR.instances)
    			CKEDITOR.remove(CKEDITOR.instances[instanceName]);        
				CKEDITOR.replaceAll('ckeditor');
		 return false; } );

    </script>
{% endblock %}
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 3, 2018

form override does not work for your extension, you may test it for flask-admin

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jun 4, 2018

I never used Flask-Admin before, so I can't help you too much. IMO, you should replace your code like this:

from flask_ckeditor import CKEditorField

class OffersView(ModelView):
    column_list = ["title_ka", "title_en", "title_ru", "is_main", "is_enabled", "position"]
    form_overrides = dict(desc_ka=CKEditorField, desc_en=CKEditorField, desc_ru=CKEditorField)
    create_template = 'edit.html'
    edit_template = 'edit.html'
    def is_accessible(self):
        if flask_login.current_user.is_authenticated:
            user = CustomUser.objects.get(email=flask_login.current_user.id)
            return user.is_superuser

    def inaccessible_callback(self, name, **kwargs):
        # redirect to login page if user doesn't have access
        return redirect(url_for('login', next=request.url))

You should provide the error traceback or I can't know what's the actual problem. Besides, Expected behavior and Actual behavior also helpful.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jun 4, 2018

I will see further in future thanks.

https://stackoverflow.com/questions/50675748/can-not-catch-ckeditor-id-and-reload-instance-on-it

this is other issue if you can help me ?

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

So,
I have done some tests with flask admin and I got upload option but when I am attempting to upload image it has 404 error (there is extra parameters in form action='/upload?some_params_of_ckeditor may this is the issue ? )

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

As I have seen it needs csrf token or I am doing something wrong with flask admin

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

SO I avoid csrf but now I am getting {"filename":"","uploaded":1,"url":"/files/GeoAdsCover_1.png"} this response when uploading image in ckeditor, so it is not parsing name

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

4.9.2 it is working on this version, I will test fully for flask admin and bring you full example :)

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

from flask_ckeditor import *
csrf = CSRFProtect(app)
csrf.init_app(app)

ckeditor = CKEditor(app)

app.config['CKEDITOR_SERVE_LOCAL'] = False
app.config['CKEDITOR_HEIGHT'] = 400
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload'
app.config['UPLOADED_PATH'] = os.path.join(basedir, 'uploads')

@app.route('/files/<filename>')
@csrf.exempt
def uploaded_files(filename):
    path = app.config['UPLOADED_PATH']
    return send_from_directory(path, filename)

import uuid

@app.route('/upload', methods=['POST'])
@csrf.exempt
def upload():
    f = request.files.get('upload')
    extension = f.filename.split('.')[1].lower()
    if extension not in ['jpg', 'gif', 'png', 'jpeg']:
        return upload_fail(message='Image only!')
    unique_filename = str(uuid.uuid4())
    f.filename = unique_filename + '.' + extension
    f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
    url = url_for('uploaded_files', filename=f.filename)
    return upload_success(url=url)

f.filename = unique_filename + '.' + extension

in flask admin edit.html

    CKEDITOR.plugins.addExternal( 'filebrowser', '/static/ckeditor/filebrowser/', 'plugin.js' );
    CKEDITOR.config.extraPlugins = 'filebrowser';
    
    
    CKEDITOR.config.filebrowserBrowseUrl  = '/upload';
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 22, 2018

If I will use {{ ckeditor.load() }} it will load CKEDITOR but it will not add plugin to ckeditor window and that is a issue left

app.config['CKEDITOR_FILE_UPLOADER'] = 'upload'

does not have any impact

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 23, 2018

How do you create the textarea? Did you call {{ ckeditor.config() }} in the template?

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 23, 2018

@greyli Yes I have Called But did not work, I will test it again with some details and tell you

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 23, 2018

Currently, I do not know too much about Flask-Admin, I will try to learn how to integrate Flask-Admin with Flask-CKEditor when I have free time.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 23, 2018

@greyli integration is not problem, I have to add additional JS to get work image uploads

CKEDITOR.plugins.addExternal( 'filebrowser', '/static/ckeditor/filebrowser/', 'plugin.js' );
CKEDITOR.config.extraPlugins = 'filebrowser';


CKEDITOR.config.filebrowserBrowseUrl  = '/upload';
@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 23, 2018

When calling {{ ckeditor.config() }}, Flask-CKEditor initialize a CKEditor for a <textarea> named ckeditor. You may need to find out what is the name used when Flask-Admin create the <textarea>, and pass it to config() (e.g. {{ ckeditor.config(name='the_name_attr') }}).

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 23, 2018

@greyli that is not a problem. ckeditor is working, only image upload window is not working without adding extra plugin, I will test it again.

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 24, 2018

ckeditor.config() is the way to tell CKEditor what is the upload URL. The name should be the name value of CKEditor <textarea>, you need to render the page, click F12 and find the textarea element, then you will get the correct name value.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 24, 2018

it is not required for me to tell which is the textarea ckeditor already loads without telling that. i need to tell config that to use url for upload.

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 24, 2018

You set app.config['CKEDITOR_FILE_UPLOADER'] = 'upload', then Flask-CKEditor will turn it into url, and when you call ckeditor.config(), it will generate JavaScript code to set the url (i.e. filebrowserUploadUrl: "/upload").

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 24, 2018

So I only was loading ckeditor without

ckeditor.config()

and do I have to call ckeditor.config() too ??

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 24, 2018

If you want to write config code yourself, for example:

CKEDITOR.plugins.addExternal( 'filebrowser', '/static/ckeditor/filebrowser/', 'plugin.js' );
CKEDITOR.config.extraPlugins = 'filebrowser';
CKEDITOR.config.filebrowserBrowseUrl  = '/upload';

then you can skip cdeditor.config()

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 24, 2018

only calling cdeditor.config() is not working

app.config['CKEDITOR_FILE_UPLOADER'] = 'upload' does not have any impact that is problem

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 24, 2018

https://github.com/zrq495/flask-ckfinder
Can you also fork this ?

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 25, 2018

As I said, you need to find out the name of CKEditor <textarea> and pass it as cdeditor.config(name='the_name').

I'll try to integrate a file browser when I have free time.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

@greyli So

cdeditor.config(name='the_name') does not needed, ckeditor is loading without it, but
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload' this does not work when pressing image icon in ckeditor, it is not loading upload tab

to load upload tag it needs

CKEDITOR.plugins.addExternal( 'filebrowser', '/static/ckeditor/filebrowser/', 'plugin.js' );
CKEDITOR.config.extraPlugins = 'filebrowser';
CKEDITOR.config.filebrowserBrowseUrl = '/upload';

and how to tell ckeditor upload url without extra JS above ?

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

If you want to tell CKEditor the upload URL without extra JS, set this in Python:

app.config['CKEDITOR_FILE_UPLOADER'] = 'upload' 

And call it in Jinja:

ckeditor.config(name='the_name') 
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

So that I was talking about that this:
app.config['CKEDITOR_FILE_UPLOADER'] = 'upload'
is not working without extra JS
that is bug

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

Do you notice the "And" in my last comment? As I said, ckeditor.config(name='the_name') will generate the JS code, so you have to call it to make the configuration work.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

Nope, I have tried that too and other combinations too.. but not working without extra JS

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

You have to pass the correct name value.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

all my textareas has ckeditor class

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

Could you please provide the complete code of your template file?

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

csrf = CSRFProtect(app)
csrf.init_app(app)

ckeditor = CKEditor(app)

app.config['CKEDITOR_SERVE_LOCAL'] = False
app.config['CKEDITOR_HEIGHT'] = 400
app.config['CKEDITOR_PKG_TYPE'] = 'full'
app.config['CKEDITOR_EXTRA_PLUGINS'] = ['filebrowser']
app.config['CKEDITOR_FILE_UPLOADER'] = '/upload'
app.config['UPLOADED_PATH'] = os.path.join(basedir, 'uploads')

@app.route('/files/<filename>')
@csrf.exempt
def uploaded_files(filename):
    path = app.config['UPLOADED_PATH']
    return send_from_directory(path, filename)

import uuid

@app.route('/upload', methods=['POST'])
@csrf.exempt
def upload_():
    f = request.files.get('upload')
    extension = f.filename.split('.')[1].lower()
    if extension not in ['jpg', 'gif', 'png', 'jpeg']:
        return upload_fail(message='Image only!')
    unique_filename = str(uuid.uuid4())
    f.filename = unique_filename + '.' + extension
    f.save(os.path.join(app.config['UPLOADED_PATH'], f.filename))
    url = url_for('uploaded_files', filename=f.filename)
    return upload_success(url=url)
{% block tail %}
    {{ super() }}
    {{ ckeditor.load() }}
    {{ ckeditor.config(name='ckeditor') }}
    <script>
    CKEDITOR.config.filebrowserUploadUrl  = '/st/upload';
    CKEDITOR.config.filebrowserBrowseUrl = '/static/filemanager/index.html';
    </script>
{% endblock %}
@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

Where are the codes that create <textarea> element for CKEditor?

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 26, 2018

@greyli
even only {{ ckeditor.load() }} creates the editor for textarea.

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 26, 2018

ckeditor.load() will output <script src="https://cdn.ckeditor.com/ckeditor.min.js"></script> in template, it will not create CKEditor <textarea>.

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 27, 2018

class TestAdminView(ModelView):
    form_overrides = dict(text=CKEditorField)
    can_view_details = True
    create_template = 'edit.html'
    edit_template = 'edit.html'

In flask admin I am overriding textarea field
without this
{{ ckeditor.config(name='ckeditor') }} has error
err

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 27, 2018

How do you know the CKEditor <textarea>'s name is ckeditor?

IMO, the name seems to be text, so you need to call:

{{ ckeditor.config(name='text') }}
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 27, 2018

@greyli OK! you are right but it was field name and I was always using ckeditor with class name (other flask documentations and tutorials are creating ckeditor for textarea with class name). so now it is working but can you also implement it with class name ?
In addition is there any other plugin to for responsive images ?

So in conclusion

In flask admin everything is almost same

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 27, 2018

CKEditor only accepts name or id, it's not what I can implement. Maybe you can create an issue in ckeditor-dev.

As for responsive images, just use CSS.

Since the problem was solved, I just close this issue for now.

@greyli greyli closed this Jul 27, 2018

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 29, 2018

So Without class I can not always tell to config what is the name of field because my field names will be different.

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 29, 2018

What do you mean? Do you have multiple CKEditor texterea fields?

@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 29, 2018

Yes, I have many models and with textareas and their names are different always, for example I may have list of textareas with names title_ka title_en title_ru so telling config these names is not convenient way

@greyli

This comment has been minimized.

Copy link
Owner

greyli commented Jul 29, 2018

Call ckeditor.config() for every fields, for example:

{{ ckeditor.config(name='title_ka') }}
{{ ckeditor.config(name='title_ru') }}
{{ ckeditor.config(name='title_en') }}
@ufo911

This comment has been minimized.

Copy link
Author

ufo911 commented Jul 29, 2018

Yes that is right but it is not generic I may ask ckeditor dev to create ckeditor with class

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.