CKEditor + CKFinder image uploads - javascript

I'm using CKEditor 4.5.2 with CKFinder 3.0.0 on PHP 5.6. Generally the integration works fine - when I click the CKEditor image button, I can click the "Browse server" button and CKFinder opens, I can select images, and also do uploads.
What doesn't work is the 'Upload' tab in the image2 dialogue. I always get an error saying "The requested resource type is not valid." when I click the "Send to server" button.
In my CKFinder config I have two resource types defined called Images and Library; these are essentialy the same except that Library is read-only - I only want to allow uploads to the Images type.
There are multiple ways of configuring the integration between CKEditor and CKFinder. I am using a custom JS config file for CKEditor and connecting ckFinder to it using the setupCKEditor method, as per the docs:
CKFinder.setupCKEditor(ckeditor_1, {
'height': '100%',
'jquery': '/js/jquery-1.11.3.min.js',
'skin': 'jquery-mobile',
'swatch': 'a',
'themeCSS': '/themes/mytheme.min.css',
'filebrowserBrowseUrl': '/ckfinder/ckfinder.html',
'filebrowserImageBrowseUrl': '/ckfinder/ckfinder.html?Type=Images',
'filebrowserImageUploadUrl': '/ckfinder/core/connector/php/connector.php?command=QuickUpload&type=Images'
}, "Images");
I've tried passing the same values to CKEditor in my CKEDITOR.replace call, and also set them on the config property in my external config file, yet nothing changes - I still get the same error.
How should I do this? Alternatively, since the CKFinder uploader works fine, how can I disable the upload tab in the image2 dialogue?
Update: here's my current code for creating CKEditor and CKFinder instances:
var ckeditor_1 = CKEDITOR.replace('html', {
"baseHref":"http://mysite.mac.local/",
"toolbarStartupExpanded":true,
"extraPlugins":"symbol",
"customConfig":"/js/ckconfig.js"
});
CKFinder.setupCKEditor( ckeditor_1, {
'height': '100%',
'jquery': '/js/jquery-1.11.3.min.js',
'skin': 'jquery-mobile',
'swatch': 'a',
'themeCSS': '/themes/mytheme.min.css'
}, { type: "Images" } );
And my CKEditor config file:
CKEDITOR.editorConfig = function (config) {
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
config.docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">';
config.toolbarGroups = [
{name: 'document', groups: ['mode', 'document', 'doctools']},
{name: 'clipboard', groups: ['clipboard', 'undo']},
{name: 'editing', groups: ['find', 'selection', 'spellchecker']},
{name: 'links'},
{name: 'insert'},
{name: 'tools'},
{name: 'others'},
'/',
{name: 'basicstyles', groups: ['basicstyles', 'cleanup']},
{name: 'paragraph', groups: ['list', 'indent', 'blocks', 'align']},
{name: 'styles'},
{name: 'colors'},
{name: 'about'},
{name: 'symbol'}
];
config.removePlugins = 'templates,save,specialchar,image,flash,scayt,forms,wsc,print,iframe,pagebreak';
CKEDITOR.plugins.addExternal('symbol', '/ckeditorplugins/symbol/', 'plugin.js');
config.extraPlugins = 'symbol';
config.templates_replaceContent = false;
config.templates_files = [
'/ckeditorplugins/templates/templates.js'
];
config.allowedContent = true;
config.toolbarCanCollapse = true;
config.fullPage = true;
config.skin = 'bootstrapck';
config.height = 400;
config.uiColor = '#f9dda0';
config.autoParagraph = false;
config.enterMode = CKEDITOR.ENTER_BR;
CKEDITOR.on('instanceReady', function (ev) {
ev.editor.dataProcessor.writer.selfClosingEnd = '>';
});
};

It looks like you mixed options for CKEditor and CKFinder. The second argument of CKFinder.setupCKEditor() is CKFinder configuration, while all filebrowser* options that you used belong to CKEditor configuration. Please have a look at CKFinder.setupCKEditor() docs. The third argument allows you to define upload URL parameters, so here you can point uploads to Images.
Please try following snippet:
CKFinder.setupCKEditor(ckeditor_1, {
'height': '100%',
'jquery': '/js/jquery-1.11.3.min.js',
'skin': 'jquery-mobile',
'swatch': 'a',
'themeCSS': '/themes/mytheme.min.css'
}, {type: "Images"});
When using CKFinder.setupCKEditor() the upload path for CKEditor will be set automatically, so there should be no need to configure it explicitly.
If the above doesn't work for you, please provide also code that creates ckeditor_1.

Related

saveSnapshot does not work after modifying the DOM

I have a web site with inline CKEditors, on which I modify the HTML DOM with javascript and then try to fire the saveSnapshot event to save the modifications to the server. And here's the tricky part; if I make only one DOM change (remove an element) and call editor.fire("saveSnapshot"); it seems to work fine, but if I remove more than one element from the DOM I very often get the error below.
I have tried many different variations on doing what I need to do but with no success, except fire the event editor.fire("change") instead (which seems to be not recommended for some reason by CK). I have tried to delay with timeouts if things are happening to fast for the CK, using different combinations of lock/unlock snapshot events etc.
So, my questions are:
1) what is the reason using fire("change) is not recommended?
2) why is this behaviour causing CK to err?
Any clue that can take me forward on this issue is greatly appreciated.
Error message from CK:
ckeditor.js:149 Uncaught TypeError: Cannot read property 'type' of null
at a (ckeditor.js:149)
at CKEDITOR.dom.range.createBookmark2 (ckeditor.js:151)
at Array.createBookmarks2 (ckeditor.js:510)
at CKEDITOR.dom.selection.createBookmarks2 (ckeditor.js:463)
at CKEDITOR.plugins.undo.Image (ckeditor.js:1072)
at CKEDITOR.plugins.undo.UndoManager.save (ckeditor.js:1067)
at a.<anonymous> (ckeditor.js:1063)
at a.n (ckeditor.js:10)
at a.CKEDITOR.event.CKEDITOR.event.fire (ckeditor.js:12)
at a.CKEDITOR.editor.CKEDITOR.editor.fire (ckeditor.js:13)
Here's the code where I modify the DOM (remove one or several elements) and fire the saveSnapshot event for each affected editor instance:
var sectionIds = [];
if (textMarkers instanceof Array) {
for (var i = 0; i < textMarkers.length; i++) {
var textMarker = textMarkers[i];
$("#sectionText-" + textMarker.sectionId).find("span.hypo-marker[data-marker-id='" + textMarker.id + "']").remove();
if (sectionIds.indexOf(textMarker.sectionId) === -1) {
sectionIds.push(textMarker.sectionId);
}
}
} else {
$("#sectionText-" + textMarkers.sectionId).find("span.hypo-marker[data-marker-id='" + textMarkers.id + "']").remove();
sectionIds.push(textMarkers.sectionId);
}
// Trigger save event on editor for each section that's been updated
for (var c = 0; c < sectionIds.length; c++) {
var editor = CKEDITOR.instances["sectionText-" + sectionIds[c]];
// editor.fire("saveSnapshot");
editor.fire("change");
}
I use CKEditor standard 4.7 with Chrome Version 60.0.3112.113 (Official Build) (64-bit) with the following CKEditor config:
var options = {
removePlugins: 'magicline,horizontalrule',
skin: 'office2013',
extraPlugins: 'colorbutton,panelbutton,panel,floatpanel,button,scayt,html5audio,uploadwidget,youtube,image2,uploadimage,keystrokes,notification,notificationaggregator,magicline2,undo',
language: 'sv',
title: false,
entities: false,
basicEntities: false,
extraAllowedContent: 'span(*)[*]; img(*)[!src,*]; div(*)[*]; a(*)[*]; p(*)[*]; i(*); table(*)[*]; thead(*)[*]; tbody(*)[*]; tr(*)[*]; td(*)[*];',
// disallowedContent: 'span(rangySelectionBoundary)[id];',
imageUploadUrl: '/api/v2/editor/images',
//filebrowserUploadUrl: '/api/v2/editor/images',
toolbarGroups: [
{ name: 'clipboard', groups: [ 'clipboard', 'undo' ] },
{ name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ] },
{ name: 'links' },
{ name: 'insert' },
'/',
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
{ name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
{ name: 'styles' },
{ name: 'colors' }
],
removeButtons: "Anchor",
magicline_color: "#00a651",
coreStyles_italic: { element: 'i', overrides: 'em' }
};
I can answer your first question:
It is not recommended to catch every change event because it gets fired quite often and it may prove quite heavy however firing should not be a problem.
If you intend to catch it maybe you could add extra data to that event based on which you can identify yours - https://docs.ckeditor.com/#!/api/CKEDITOR.event-method-fire.
The change event also doesn't get fired in source mode so if you use that mode, you need to find some other way of checking for code changes (maybe periodically comparing previous and current textarea value).

Second File Upload fails in ExtJS 6, works in ExtJS 4

Very low-level file upload:
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
items: [{
xtype: 'form',
items: [{
xtype: 'filefield',
fieldLabel: 'Select file',
listeners: {
change: {
fn: me.onFilefieldChange,
scope: me
}
}
}]
},{
...
onFilefieldChange: function(filefield, value, eOpts) {
var form = filefield.up('form').getForm();
form.submit({
url: APIURI+'FileUpload',
headers: {'Accept':'application/json','Content-Type':'application/json'},
waitMsg: 'Uploading',
success: function(fp, o) {
var filedata = Ext.decode(o.response.responseText).data;
var rec = Ext.create("MyApp.model.FileModel",filedata);
Ext.getStore("FileStore").add(rec);
},
failure: function(fp, o) {
Ext.alert("ERROR", "File save failed"));
}
});
The first file upload works like a charm; the file is packed into the multipart/mime and submitted correctly.
The second file upload from the very same file upload field fails, because the file is not packed into the mime.
If I close the window and open it again, the file field is working again - for a single upload, that is.
The difference in DOM of the file input field tells us why the browser behaves like this. The following three attributes are magically missing from the <input type="file" field after the first file upload:
data-ref=​"fileInputEl"
name=​"filefield-1333-button"
data-componentid=​"filefield-1333-button"
Now, this does not happen if I use the file upload field in the sencha docs. It doesn't happen with my code in ExtJS 4.2.2. But it does happen with ExtJS 6.0.1.
You wouldn't know why, would you?
It is a bug in Sencha ExtJS 6 Framework. Everything works as expected if you provide a name property to the file upload field.
xtype: 'filefield',
fieldLabel: 'Select file',
name: 'RequiredSenchaBugWorkaround',
...
Adding a name doesn't always work. It still has issues in Ext 6.2. In addition to that you should do
FileInputField1.reset();
This makes sure that the field is reset and lets you upload the same file again.

File download - ExtJs 4.2.3

I'm using ExtJs 4.2.3.
In my web application I need to download files.
In order to do that I'm using a 'FileDownloader' component defined as in post
fileDownloader
The code is:
Ext.define('myApp.FileDownload', {
extend: 'Ext.Component',
alias: 'widget.FileDownloader',
autoEl: {
tag: 'iframe',
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
},
load: function(config) {
var e = this.getEl();
e.dom.src = config.url +
(config.params ? '?' + Ext.Object.toQueryString(config.params) : '');
console.log('in FileDownloader - src: ' + e.dom.src);
e.dom.onLoad = function() {
if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
Ext.Msg.show({
title: 'Attachment missing',
msg: 'File cannot be found on the server !',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
};
};
}
});
I call it by code:
downloader.load({
url: src
});
where src is complete path to file.
If I download Word, Excel, PDF files it's working well, file reference is visualized in download bar of browser, but with other data types (ex. .txt, .jpg) it doesn't do nothing.
I think it's no a question about Extjs or js, it's the server's reponse. See this download the text file instead of opening in the browser

What does the filebrowser field actually do in CKEditor?

The upload tab of image2 plugin looks like this -
{
id: 'Upload',
hidden: false,
filebrowser: 'uploadButton',
label: lang.uploadTab,
elements: [
{
type: 'file',
id: 'upload',
label: lang.btnUpload,
style: 'height:40px',
onChange: function(evt){
alert('file uploaded');
}
},
{
type: 'fileButton',
id: 'uploadButton',
filebrowser: 'info:src',
label: lang.btnUpload,
'for': [ 'Upload', 'upload' ]
}
]
}
Here in the 'tab' details there is filebrowser field which is equal to 'uploadButton' and filebrowser field is also in UI element object where it is equal to 'info:src'.
I am not implementing Browse Server functionality, only upload functionality. I have implemented it but I want to understand how filebrowser plugin and filebrowser fields are facilitating it?
Can anyone here explain here a little bit in detail as CKEditor documentation does not tell much?
The ck_config.js file has settings which determine the URL of the file browser for various purposes:
config.filebrowserBrowseUrl = '';
config.filebrowserImageBrowseUrl = '';
config.filebrowserFlashBrowseUrl = '';
config.filebrowserUploadUrl = '';
config.filebrowserImageUploadUrl = '';
config.filebrowserFlashUploadUrl = '';
Basically this will be a webpage which appears in a roughly 600px-wide popup.
One of the GET parameters of this URL (automatically added) will be CKEditorFuncNum which determines the callback function for sending the results back to CK.
On selection of a file/path, you would typically do:
window.opener.CKEDITOR.tools.callFunction(ck_callback,pathrel2page);
where ck_callback is the CKEditorFuncNum value and pathrel2page is the selected file.
HTH.

ExtJS 4 - How to download a file using Ajax?

I have a form with various textfields and two buttons - Export to Excel and Export to CSV.
The user can provide the values to different fields in the form and click one of the buttons.
Expected behaviour is that an Ajax request should be fired carrying the values of fields as parameters and the chosen file (Excel/CSV as per the button click) should get downloaded (I am not submitting the form as there needs to be done some processing at the values before submit).
I have been using the following code in success function of Ajax request for the download:
result = Ext.decode(response.responseText);
try {
Ext.destroy(Ext.get('testIframe'));
}
catch(e) {}
Ext.core.DomHelper.append(document.body, {
tag: 'iframe',
id:'testIframe',
css: 'display:none;visibility:hidden;height:0px;',
src: result.filename,
frameBorder: 0,
width: 0,
height: 0
});
The above code has been working fine in the case when the file is created physically at the server. But in my current project, the file is not created at the server, rather the contents are just streamed to the browser with proper headers.
Thus, is there a way to download a file using Ajax when the file is not present at the server physically? Just to add that I have a long list of parameters which I need to send to the server and hence can not add them all to the src of iframe.
Could anyone guide at this?
Thanks for any help in advance.
You may use component like this:
Ext.define('CMS.view.FileDownload', {
extend: 'Ext.Component',
alias: 'widget.FileDownloader',
autoEl: {
tag: 'iframe',
cls: 'x-hidden',
src: Ext.SSL_SECURE_URL
},
stateful: false,
load: function(config){
var e = this.getEl();
e.dom.src = config.url +
(config.params ? '?' + Ext.urlEncode(config.params) : '');
e.dom.onload = function() {
if(e.dom.contentDocument.body.childNodes[0].wholeText == '404') {
Ext.Msg.show({
title: 'Attachment missing',
msg: 'The document you are after can not be found on the server.',
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
})
}
}
}
});
Put it somewhere in viewport, for example:
{
region: 'south',
html: 'South',
items: [
{
xtype: 'FileDownloader',
id: 'FileDownloader'
}
]
}
Do not forget to require it in your viewport class:
requires: [
'CMS.view.FileDownload'
]
Action handler may look like this:
var downloader = Ext.getCmp('FileDownloader')
downloader.load({
url: '/attachments/' + record.get('id') + '/' + record.get('file')
});
It's very important to have Content-Disposition header in response, otherwise nothing is downloaded.
Regards go to http://www.quispiam.com/blog/post.php?s=2011-06-09-download-a-file-via-an-event-for-extjs4
This thing works for me.

Categories

Resources