How to upload an image with CKEditor in AngularJS? - javascript

Currently I am using angular-ckeditor to add CKEditor 4.
In my template, I display it as:
<div ng-repeat="editor in editors">
<div ckeditor="options" ng-model="editor.content"></div>
</div>
I'm looking for a way to upload images to CKEditor from desktop. As far as I understand, angular-ckeditor and CKEditor libraries are separate, so I can add widgets and plugins easily.
The problem is that I can't seem to find the right plugins/widgets (that do NOT use jQuery), which will help me to upload an image from desktop. I made it work only with image links.
There is not a lot of documentation about it on the official website. They say to use PHP files that will upload and browse images, but don't really explain how, especially with angular-ckeditor. So I have several questions now:
Which plugins do I need for a simple image uploader, so that I can paste images into CKEditor?
How do I set it up with AngularJS?
What does a PHP file uploader(/browser) look like?
What I have tried so far doesn't even change the CKEditor tabs (it should change the image properties dialog by adding an "Upload" tab and some other UI). So clearly I'm missing a solid tutorial somewhere for all of this.
(I could also try to switch to ng-ckeditor, if a solution with this would be simpler)

First, let's take a look at some basics without Angular. For CKEditor version 4, we can initialize an editor with the filebrowserImageUploadUrl configuration option, which enables functionality from the File Browser API:
CKEDITOR.replace('editor', {
filebrowserImageUploadUrl: '/upload.php?type=image'
});
This just loads an editor instance onto a <textarea name="editor">. Because we set the filebrowserImageUploadUrl option, the Upload tab becomes available in the editor's image dialog. The example value, /upload.php?type=image, is the URL to the PHP script on the server that handles the uploaded image files.
When a user uploads an image, CKEditor will send the image to this URL on the server. The handler at this URL should validate the request, resize the image (if needed), and move the uploaded image to a permanent location on the server. Then, the handler sends an HTML response back to CKEditor with the image's public URL.
Of course, we can write the server-side handler in any language. Here's a basic example for PHP that we'll save as upload.php:
<?php
$tempName = $_FILES['upload']['tmp_name'];
$fileName = uniqid() . $_FILES['upload']['name'];
$uploadPath = '/path/to/uploads/' . $fileName;
$imageUrl = 'http://example.com/uploads/' . $fileName;
$success = move_uploaded_file($tempName, $uploadPath);
$html = '<script>window.parent.CKEDITOR.tools.callFunction(%s, "%s", "%s");</script>';
$message = $success ? 'Uploaded successfully.' : 'Upload failed.';
echo sprintf($html, $_GET['CKEditorFuncNum'], $imageUrl, $message);
This script places an uploaded image into the web server's uploads/ directory so the browser can fetch the image. It passes back the CKEditorFuncNum parameter from the request to identify the appropriate callback for the upload. This example provides some basic protection against duplicate filenames, but, for a real-world application, we'd need to add security, validation, and error handling (authentication, CSRF, file type checking, max size, file name sanitization, etc.).
So far, this all works with CKEditor's standard, built-in functionality (no plugins, Angular, or jQuery needed). To enable users to drag-and-drop or paste images into the editor, we can add the Upload Image plugin to our CKEditor build (or use the standard-all distribution from the CKEditor CDN).
We need to declare the plugin when initializing the editor:
CKEDITOR.replace('editor', {
extraPlugins: 'uploadimage',
filebrowserImageUploadUrl: '/upload.php?type=image'
});
...and then extend our upload.php script to return the JSON response expected by the plugin. Add this block before the last three lines of the previous example:
if (isset($_GET['responseType']) && $_GET['responseType'] === 'json') {
echo json_encode([
'uploaded' => $success,
'fileName' => $fileName,
'url' => $imageUrl,
]);
return;
}
The Upload Image plugin sends the responseType=json URL parameter that the server-side script can check for to determine which type of response to send back.
Finally, let's take a look at how to initialize an editor using the angular-ckeditor package described in the question:
<div ng-controller="EditorCtrl as editor">
<textarea ckeditor="editor.options" ng-model="editor.content"></textarea>
</div>
var myApp = angular.module('myApp', ['ckeditor'])
myApp.controller('EditorCtrl', function () {
this.options = {
extraPlugins: 'uploadimage',
filebrowserImageUploadUrl: '/image-upload.php?type=image'
};
});
As we can see, we don't need to do much to "angularize" this. Once we create our template, we declare the same configuration options that we'd pass to the plain CKEDITOR.replace() on a controller that we reference on an element with the ckeditor directive.

Related

Trumbowyg: Django server can detect file upload but not image URL input

I'm using Trumbowyg, a WYSIWYG JavaScript editor which has a feature of rendering images from URLs pasted in. It also has an upload plugin which enables uploading local images and custom server side handling.
My python/django function upload_image() can successfully detect the uploaded image - however when I use the URL image input, my python function cannot detect it. Trumbowyg simply renders the image without going through my python backend.
Here's my code:
$('#id_content').trumbowyg({
btnsDef: {
// Create a new dropdown
image: {
dropdown: ['insertImage', 'upload'],
ico: 'insertImage'
}
},
// Redefine the button pane
btns: [
['strong', 'em', 'del'],
['link'],
['image'], // Our fresh created dropdown
],
plugins: {
// Add imagur parameters to upload plugin for demo purposes
upload: {
serverPath: '/upload_image/',
fileFieldName: 'content_image',
urlPropertyName: 'url'
}
}
});
def upload_image(request):
print('Success') #only prints when I use the upload input, not the URL input
Why can in detect the uploaded image but not the URL input?
As already pointed, Trumbowyg doesn't send the URL to backend if the user uploads the image using a URL.
But if you really want to host the images on your own server, there's a way you can do that.
When the user submits the form, you'll receive the content of the textarea in your backend. You can then read the content and look for <img src="..."> tag.
At that point, you can check if the src value doesn't start with your S3 bucket hostname, you can download that image using urllib or requests library, save it to your bucket and replace the src value.
Since the submitted data will be in HTML format, check out the excellent Beautiful Soup. It will make parsing HTML easy.

Customizing CKEditor to work with clear URL in framework

I wrote a small framework with URI structure of lang/domain/controller/method/id an now that I want to use CKFinder integrated with CKEditor I cannot upload and browse server because of address structure? What should I do? To browse the server it uses:
http://localhost/public/admin/style1/plugins/ckfinder/ckfinder.html?CKEditor=abstraction&CKEditorFuncNum=1&langCode=fa
Now I have changed upload and browse address by these:
CKEDITOR.replace('article',
{
filebrowserBrowseUrl : '/browser/browse.php',
filebrowserUploadUrl : '/uploader/upload.php'
});
it shows the images but when I choose them it does not bring it to page and it uploads the file to my image folder but cannot add them or even preview them in body or upload dialogbox. How can I use ckeditor with clear URL?
CKFinder with custom paths
If you are doing URL rewrite and you expect CKFinder to return URLs with custom paths you can do the following:
You can configure how CKFinder sends URLs to CKEditor in CKFinder's config.php file in the backends section:
$config['backends'][] = array(
'name' => 'default',
'adapter' => 'local',
'baseUrl' => 'http://base/url/ckfinder/will/give/to/ckeditor',
'root' => '/path/to/files/on/disk',
'chmodFiles' => 0777,
'chmodFolders' => 0755,
'filesystemEncoding' => 'UTF-8'
);
The file path is appended to all URLs and this behavior can't be changed by configuration.
E.g. for /path/to/files/on/disk/images/picture.png the returned URL is http://base/url/ckfinder/will/give/to/ckeditor/images/picture.png
Alternatively you may add 'useProxyCommand' => true to the backend configuration.
This will change all returned URLs to the form of http://localhost/core/connector/php/connector.php?command=Proxy&lang=en&type=Files&currentFolder=%2F&hash=9fd5e9f22b8dea6a&fileName=picture.png, where http://localhost/core/connector/php/connector.php is the URL that was used to make the request to get the file's URL.
Custom integration with a file manager
If you are implementing your own integration with a file manager check your response from /uploader/upload.php. CKEditor expects something like:
<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction(1, 'http://file/url', 'message');</script>`
You can find more information in the CKEditor's documentation

Get filebrowserUploadUrl to update url

I am using the plugin image2, and would like the ability to upload images to our server.
I have added config.filebrowserUploadUrl="/CkeditorImageUpload"
to my config.js, and the "Image properties" dialog does have a upload image tab which can upload images to my server. So far so good but the problem is that when an image is uploaded, the url field is not updated in the image properties dialog so the user can't use the uploaded image.
Should my /CkeditorImageUpload return something special to cause the dialog to update?
Yes, CKEditor expects the upload script to return a <script> tag that calls an anonymous function.
Everything is described in the documentation http://docs.ckeditor.com/#!/guide/dev_file_browser_api - see Example 3 with a PHP example of script that sends a response after a file upload:
<?php
// Required: anonymous function reference number as explained above.
$funcNum = $_GET['CKEditorFuncNum'] ;
// Optional: instance name (might be used to load a specific configuration file or anything else).
$CKEditor = $_GET['CKEditor'] ;
// Optional: might be used to provide localized messages.
$langCode = $_GET['langCode'] ;
// Check the $_FILES array and save the file. Assign the correct path to a variable ($url).
$url = '/path/to/uploaded/file.ext';
// Usually you will only assign something here if the file could not be uploaded.
$message = ;
echo "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction($funcNum, '$url', '$message');</script>";
?>

Remove XML Node With jQuery

I wonder whether someone may be able yo help me please.
I've put together this page which allows users to view a gallery of their uploaded images.
Upon initial upload, the physical images are saved in the following file structure:
UploadedFiles/userid/locationid/image and the details of the image i.e. description etc are saved in an xml file called files.xml which is in the same directory as the physical images.
I'm now working on allowing the user to be able to delete these images.
By way of a deletion icon under each image, I've, admitedly with some help, put together the following which successfully deletes the physical image.
'Deletion Icon Onclick Event'
<script type="text/javascript">
Galleria.ready(function() {
this.$('thumblink').click();
$(".galleria-image").append(
"<span class='btn-delete ui-icon ui-icon-trash'></span>");
$(".btn-delete").live("click", function(){
var img = $(this).closest(".galleria-image").find("img");
// send the AJAX request
$.ajax({
url : 'delete.php',
type : 'post',
data : { image : img.attr('src') },
success : function(){
alert('Deleting image... ');
img.parent().fadeOut('slow');
}
});
return false;
});
});
</script>
Original 'delete.php'
<?php
if (!empty($_POST)) {
$image = $_POST['image'];
if (file_exists($image)) {
unlink($image);
}
}
?>
Updated 'delete.php'
<?php
if (!empty($_POST)) {
$image = $_POST['image'];
if (file_exists($image)) {
unlink($image);
}
}
$doc = new DOMDocument;
$doc->load('files.xml');
$thedocument = $doc->documentElement;
$list = $thedocument->getElementsByTagName('files');
$nodeToRemove = null;
foreach ($list as $domElement){
$attrValue = $domElement->getAttribute('file_name');
if ($attrValue == 'image') {
$nodeToRemove = $domElement;
}
}
if ($nodeToRemove != null)
$thedocument->removeChild($nodeToRemove);
echo $doc->saveXML();
?>
The problem I'm having is deleting the xml node form the xml file. I've provided an extract of the XML file below.
<?xml version="1.0" encoding="utf-8" ?>
- <files>
<file name="stag.jpg" source="stag.jpg" size="21341" originalname="stag.jpg" description="No description provided" userid="1" locationid="1" />
</files>
I've done quite a bit of research about how to go about this and found that jQuery had it's own command i.e. jQuery.remove which I thought would be able to delete the node. Following the brief tutorial I added the following to the end of my 'Onclick Event' script:
var doc = $(files.xml);
doc.find('file_name:src').remove();
Unfortunately, although I don't receive a specific error, the node isn't being deleted from the file. I'm a complete beginner when it comes to XML so perhaps I'm looking at this too simplistically.
I just wondered wheteher someone could perhaps have a look at this please and let me know where I'm going wrong.
Many thanks and regards
This is because JavaScript(JQuery) loads the XML DOM in memory and then when you delete a node,
it gets deleted from the in-memory xml doc(the object).
It wont be removed from the physical XML file.
JS runs in a sandbox Browser environment and cannot alter local files on the system.
and if you are trying to load xml from a remote server then its a very bad idea.
the XML file from remote server is downloaded as temp file and then when you load XML again an in-memory DOM is created and the node is deleted from it.
So in case you want the actual file to be changed,
you will need to use AJAX and send some HTTP request to your server to do the same to the physical file.
UPDATE:
Check this tutorial
http://www.phpeveryday.com/articles/PHP-XML-Removing-Node-P415.html
and try to load the xml file in your delete.php and remove the corresponding node from it and then save this xml back to the original file which will be overwritten.

HTML5: drag out a JS generated file

I have a feeling security concerns may not allow this but is it possible to generate a file with JavaScript and allow the user to drag it to the desktop (or file system)?
The following code drags out a file from a server
files[0].addEventListener("dragstart",function(evt){
evt.dataTransfer.setData("DownloadURL", "application/octet-stream:Eadui2.ttf:http://thecssninja.come/demo/gmail_dragout/Eadui.ttf");
},false);
And with the below code I can generate a file and have it download but I can't set the file name or let the user select the location.
var uriContent = "data:application/octet-stream," + encodeURIComponent(JSON.stringify(map));
location.href = uriContent;
Ideally I'd like a magical combination of both.
following code is currently working in Chrome only:
// generate downloadable URL, file name here will not affect stored file
var url = URL.createObjectURL(new File([JSON.stringify(map)], 'file_name.txt'));
// note that any draggable element may be used instead of files[0]
// since JSON.stringify returns a string, we use 'text' type in setData
files[0].addEventListener("dragstart", function(evt) {
evt.dataTransfer.setData("DownloadURL", "text:file_name.txt:" + url);
}, false);
now, dragging our files[0] element from the browser to desktop or file system, will store there a text file called, file_name.txt.
Feel free to choose another file name :)
This is only possible for Chrome, and even in Chrome you can't set the location. If using only Chrome is okay then you will have the following options:
Stick with Drag n' Drop like from the CSS Ninja's tutorial, then you should try Ben's answer. encodeURIComponent is one way, but if you have the file generated using BlobBuilder then you can use window.webkitURL.createObjectURL() to get the file's URL. You can also try using FileWriter() with requestFileSystem(TEMPORARY, ...).
Chrome supports download attribute for anchor tags so you can have regular link for the user to click (dragging also works):
Download
For cross browser support I suggest Downloadify.
You could try sending it to the server, saving the file, checking the return value and firing the download file function, followed by a server file that deletes the file from the server.
Something like this (with jQuery)
$.ajax({
url: 'saveFile.php',
method: 'post',
data: {
Filedata: data// file data variable
},
success: function(d) {
// save file function, where d is the filename
}
})
PHP:
$filename = ;//generate filename
file_put_contents($filename, $_POST['Filedata']);
echo $filename;
Obviously there is more to it but that should be the basics

Categories