Comments (20)
Hi @jlgit, thanks for the kind words. Once inserted the image should be resizeable and dragable, so sounds like there's an error. Any chance you can post any error output in the console along with your image uploaded code?
from contenttools.
I was able to figurate out, there was a mix of browser cache and old css acting out, thank you very much for your response, I really appreciate what you've done with this code!
from contenttools.
@jlgit could you help me?
The image setup is not working for me, no matter what I do, and the documentation for Cloudinary is super buggy
from contenttools.
Hey @Sirlazer2 if you post me your code I'll take a look for you?
from contenttools.
I don't have anything yet, I've tired both local and cloudinary so far, but ended up deleting it multiple times.
But here is the documentation flaws I've found so far
// The "=" is some leftover thing from Coffeescript I suppose
"function = cloudinaryImageUploader(dialog) {"
//Object is not defined
for (name in transform) {
if (object.hasOwnProperty(name)) {
transformArgs.push(name + '_' + object[name]);
}
}
//A disturbing lack of curly brackets around the array.
"transforms.push(c: 'fill', w: element.size()[0], h: element.size()[1]);"
From URL: http://getcontenttools.com/tutorials/image-uploads-with-cloudinary
from contenttools.
Thanks for highlighting those issue - yes some of them certainly look left over from CoffeeScript. If you give me an hour I'll run through the tutorial now and clean those up an post a full working JavaScript example for you?
from contenttools.
That would be absolutely fantastic!
Thank you so much :)
from contenttools.
@Sirlazer2 Make that +15 minutes - almost there just an issue with the resize code in the page now.
from contenttools.
No worries
from contenttools.
Right (eventually) - I think that's the tutorial correctly updated and as promised a full working example. Let me know how you get on.
// Define settings for the uploader
var CLOUDINARY_PRESET_NAME = 'ctpreset';
var CLOUDINARY_RETRIEVE_URL = 'http://res.cloudinary.com/contenttools/image/upload';
var CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/contenttools/image/upload';
// Define the image uploader
function cloudinaryImageUploader(dialog) {
var image, xhr, xhrComplete, xhrProgress;
// Set up the event handlers
dialog.bind('imageUploader.cancelUpload', function () {
// Cancel the current upload
// Stop the upload
if (xhr) {
xhr.upload.removeEventListener('progress', xhrProgress);
xhr.removeEventListener('readystatechange', xhrComplete);
xhr.abort();
}
// Set the dialog to empty
dialog.state('empty');
});
dialog.bind('imageUploader.clear', function () {
// Clear the current image
dialog.clear();
image = null;
});
dialog.bind('imageUploader.fileReady', function (file) {
// Upload a file to Cloudinary
var formData;
// Define functions to handle upload progress and completion
function xhrProgress(ev) {
// Set the progress for the upload
dialog.progress((ev.loaded / ev.total) * 100);
}
function xhrComplete(ev) {
var response;
// Check the request is complete
if (ev.target.readyState != 4) {
return;
}
// Clear the request
xhr = null
xhrProgress = null
xhrComplete = null
// Handle the result of the upload
if (parseInt(ev.target.status) == 200) {
// Unpack the response (from JSON)
response = JSON.parse(ev.target.responseText);
// Store the image details
image = {
angle: 0,
height: parseInt(response.height),
maxWidth: parseInt(response.width),
width: parseInt(response.width)
};
// Apply a draft size to the image for editing
image.filename = parseCloudinaryURL(response.url)[0];
image.url = buildCloudinaryURL(
image.filename,
[{c: 'fit', h: 600, w: 600}]
);
// Populate the dialog
dialog.populate(image.url, [image.width, image.height]);
} else {
// The request failed, notify the user
new ContentTools.FlashUI('no');
}
}
// Set the dialog state to uploading and reset the progress bar to 0
dialog.state('uploading');
dialog.progress(0);
// Build the form data to post to the server
formData = new FormData();
formData.append('file', file);
formData.append('upload_preset', CLOUDINARY_PRESET_NAME);
// Make the request
xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', xhrProgress);
xhr.addEventListener('readystatechange', xhrComplete);
xhr.open('POST', CLOUDINARY_UPLOAD_URL, true);
xhr.send(formData);
});
function rotate(angle) {
// Handle a request by the user to rotate the image
var height, transforms, width;
// Update the angle of the image
image.angle += angle;
// Stay within 0-360 degree range
if (image.angle < 0) {
image.angle += 360;
} else if (image.angle > 270) {
image.angle -= 360;
}
// Rotate the image's dimensions
width = image.width;
height = image.height;
image.width = height;
image.height = width;
image.maxWidth = width;
// Build the transform to rotate the image
transforms = [{c: 'fit', h: 600, w: 600}];
if (image.angle > 0) {
transforms.unshift({a: image.angle});
}
// Build a URL for the transformed image
image.url = buildCloudinaryURL(image.filename, transforms);
// Update the image in the dialog
dialog.populate(image.url, [image.width, image.height]);
}
dialog.bind('imageUploader.rotateCCW', function () { rotate(-90); });
dialog.bind('imageUploader.rotateCW', function () { rotate(90); });
dialog.bind('imageUploader.save', function () {
// Handle a user saving an image
var cropRegion, cropTransform, imageAttrs, ratio, transforms;
// Build a list of transforms
transforms = [];
// Angle
if (image.angle != 0) {
transforms.push({a: image.angle});
}
// Crop
cropRegion = dialog.cropRegion();
if (cropRegion.toString() != [0, 0, 1, 1].toString()) {
cropTransform = {
c: 'crop',
x: parseInt(image.width * cropRegion[1]),
y: parseInt(image.height * cropRegion[0]),
w: parseInt(image.width * (cropRegion[3] - cropRegion[1])),
h: parseInt(image.height * (cropRegion[2] - cropRegion[0]))
};
transforms.push(cropTransform);
// Update the image size based on the crop
image.width = cropTransform.w;
image.height = cropTransform.h;
image.maxWidth = cropTransform.w;
}
// Resize (the image is inserted in the page at a default size)
if (image.width > 400 || image.height > 400) {
transforms.push({c: 'fit', w: 400, h: 400});
// Update the size of the image in-line with the resize
ratio = Math.min(400 / image.width, 400 / image.height);
image.width *= ratio;
image.height *= ratio;
}
// Build a URL for the image we'll insert
image.url = buildCloudinaryURL(image.filename, transforms);
// Build attributes for the image
imageAttrs = {'alt': '', 'data-ce-max-width': image.maxWidth};
// Save/insert the image
dialog.save(image.url, [image.width, image.height]);
});
}
function buildCloudinaryURL(filename, transforms) {
// Build a Cloudinary URL from a filename and the list of transforms
// supplied. Transforms should be specified as objects (e.g {a: 90} becomes
// 'a_90').
var i, name, transform, transformArgs, transformPaths, urlParts;
// Convert the transforms to paths
transformPaths = [];
for (i = 0; i < transforms.length; i++) {
transform = transforms[i];
// Convert each of the object properties to a transform argument
transformArgs = [];
for (name in transform) {
if (transform.hasOwnProperty(name)) {
transformArgs.push(name + '_' + transform[name]);
}
}
transformPaths.push(transformArgs.join(','));
}
// Build the URL
urlParts = [CLOUDINARY_RETRIEVE_URL];
if (transformPaths.length > 0) {
urlParts.push(transformPaths.join('/'));
}
urlParts.push(filename);
return urlParts.join('/');
}
function parseCloudinaryURL(url) {
// Parse a Cloudinary URL and return the filename and list of transforms
var filename, i, j, transform, transformArgs, transforms, urlParts;
// Strip the URL down to just the transforms, version (optional) and
// filename.
url = url.replace(CLOUDINARY_RETRIEVE_URL, '');
// Split the remaining path into parts
urlParts = url.split('/');
// The path starts with a '/' so the first part will be empty and can be
// discarded.
urlParts.shift();
// Extract the filename
filename = urlParts.pop();
// Strip any version number from the URL
if (urlParts.length > 0 && urlParts[urlParts.length - 1].match(/v\d+/)) {
urlParts.pop();
}
// Convert the remaining parts into transforms (e.g `w_90,h_90,c_fit >
// {w: 90, h: 90, c: 'fit'}`).
transforms = [];
for (i = 0; i < urlParts.length; i++) {
transformArgs = urlParts[i].split(',');
transform = {};
for (j = 0; j < transformArgs.length; j++) {
transform[transformArgs[j].split('_')[0]] =
transformArgs[j].split('_')[1];
}
transforms.push(transform);
}
return [filename, transforms];
}
// Capture image resize events and update the Cloudinary URL
ContentEdit.Root.get().bind('taint', function (element) {
var args, filename, newSize, transforms, url;
// Check the element tainted is an image
if (element.constructor.name != 'Image') {
return;
}
// Parse the existing URL
args = parseCloudinaryURL(element.attr('src'));
filename = args[0];
transforms = args[1];
// If no filename is found then exit (not a Cloudinary image)
if (!filename) {
return;
}
// Remove any existing resize transform
if (transforms.length > 0 &&
transforms[transforms.length -1]['c'] == 'fill') {
transforms.pop();
}
// Change the resize transform for the element
transforms.push({c: 'fill', w: element.size()[0], h: element.size()[1]});
url = buildCloudinaryURL(filename, transforms);
if (url != element.attr('src')) {
element.attr('src', url);
}
});
window.onload = function() {
var editor, req;
ContentTools.IMAGE_UPLOADER = cloudinaryImageUploader;
editor = ContentTools.EditorApp.get();
editor.init('.editable', 'data-name');
};
Don't forget to update your cloud name and upload preset in the settings.
from contenttools.
P.S @Sirlazer2 - thank you for highlighting this and I'm sorry about this - those were some really sloppy examples on my behalf.
from contenttools.
@anthonyjb Hah, it happens.
I am still not having any luck though. I have two files, editor.js with auto-saving feature, and image.js with the code you just provided, just copied and pasted.
I am not even getting a progress bar, but no server error either.
from contenttools.
Hello Guys;
I'm sorry I didn't see the request from @Siriazer2 before, it's being a crazy day today, please let me know if you need any help still.
To @anthonyjb, thank you very much for the code, I think this make it easier to handle!
from contenttools.
@Sirlazer2 ok I've updated my code above to include a really simple editor initialization as well - you should be able to drop it in to the editor.js
file for now and remove image.js
for the moment - just to see if that works. It's working for me in the sandbox demo in the repo at least.
from contenttools.
@jlgit thanks 👍
from contenttools.
ok @Sirlazer2, Search for the function imageUploader.fileReady and replace it with this code.
dialog.bind('imageUploader.fileReady', function (file) {
var formData;
uploadProgress = function (ev) {
// Set the progress for the upload
dialog.progress((ev.loaded / ev.total) * 100);
}
uploadComplete = function (ev) {
xhr = null
uploadProgress = null
uploadComplete = null
if (parseInt(ev.target.status) == 200) {
var response = JSON.parse(ev.target.responseText);
image = {
size: response.size,
url: response.url
};
dialog.populate(image.url, image.size);
} else {
// The request failed, notify the user
new ContentTools.FlashUI('no');
}
}
dialog.state('uploading');
dialog.progress(0);
// POST variables to send to the server
formData = new FormData()
formData.append('folder_name', 'uploads');
formData.append('image', file);
xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', uploadProgress);
xhr.addEventListener('load', uploadComplete);
xhr.open('POST', '_UPLOAD_HANDLER_SERVER_SIDE', true);
xhr.send(formData);
});
The server side should handle the image resize and response with a json object with the following info:
{"url":"\/uploads\/9375035le.jpg","size":[500,362]}
Try that and let us know.
from contenttools.
@anthonyjb It's working now.
I suggest uploading a full example somewhere, it's always nicer to look at completed code than something that's patched together through a tutorial. Especially since you somewhere note "This belongs under function x" or "We can put this at the bottom of x, for now".
Also, thank you so much for the supreme and extremely fast support. I am super impressed with you and ContentTools! 👍
from contenttools.
@Sirlazer2 great news glad it's working and thanks for sticking with it - I'll be taking your advice re. the full example.
from contenttools.
@anthonyjb Of course I'm sticking with it, it does have some great potential, ease of use and is super lightweight - I won't let a mere bug stand in the way of that :p
from contenttools.
hello everyone,
i chose to follow cloudinary code to be able to upload images, seems everything worked well but i did not know how to coordinate between my cloud account and my code even if defined settings:
var CLOUDINARY_PRESET_NAME = 'ctpreset';
var CLOUDINARY_RETRIEVE_URL = 'http://res.cloudinary.com/../image/upload';
var CLOUDINARY_UPLOAD_URL = 'https://api.cloudinary.com/v1_1/.../image/upload';
if someone could upload succefully and help would be great. thanks
from contenttools.
Related Issues (20)
- Save issue - saves fine, but won't allow a second "save" HOT 5
- Making CT compatible with CSP style-src set to 'self'
- Can't edit files that were included or read by php HOT 17
- Edit multiple paragraphs at once. HOT 2
- Is there a way to change the default tag output for normal html tags HOT 2
- Missing translation in /src/scripts/ui/dialogs/table.coffee
- Problem update image classes HOT 5
- Build files to use dynamic import in pure javascript HOT 1
- How to use assign ? HOT 2
- Hot to keep style attributes on save.
- Support for Sass >= 1.33 HOT 11
- npm install failed HOT 2
- Feature: Support drag & drop of images
- The image uploader HOT 2
- Inspector bar doesn't show embedded HTML elements when wrapped with paragraph HOT 1
- Line breaks not saving
- Add class to a link
- Lazy loading of images
- Source code edit? Insert <script> tag HOT 3
- The part in italics is not the right one HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from contenttools.