2014/04/06

Resizing images with SimpleUploads

One of the recent additions to the SimpleUploads plugin is the ability to process the images on the client before they are uploaded to the server.

At the moment I've added two options to allow automatic conversion of bmp images as well as rejecting images that are bigger than the defined dimensions, but it's easy to customize your page so that you can for example resize the images on the client and that way it will be much faster.

Of course this isn't a magical solution: the resizing is done using a canvas, so if any animated image will lose the animation, and I'm not sure about transparency in gif and pngs.

Anyway, I've created a Sizing demo showing the different options:

1) Don't restrict the uploads in anyway

2) Use only a server-side script. If you want to limit the size of images you should always use this kind of setup as a last option so that even if the image isn't resized on the client it's correctly processed and validated on the server.

3) Check the size and reject them

4) Reescale the image on the client, that way it's uploaded much faster.

I might add this option directly in the plugin, but for the momento I think that it's good to have the option to use your own code and that way you can tweak it to better fit your requirements.

The code itself is simple, this is it:

function scaleImage(ev)
{
 var data = ev.data,
  editor = ev.editor;

 var img = data.image;

 // maximum size allowed for images
 var maxX = 400;
 var maxY = 300;
 var imgX = img.width;
 var imgY = img.height;

 if (imgX == 0 || imgY == 0)
 {
  alert("Ops, the image doesn't seem to be valid");
  ev.cancel();
  return;
 }

 // if it's smaller, get out
 if (imgX <= maxX && imgY <= maxY)
  return;

 var ratio = imgX / imgY;
 if ((maxX / maxY) > ratio)
  maxX = Math.round(maxY * (ratio));
 else
  maxY = Math.round(maxX / (ratio));

 var canvas = document.createElement("canvas");
 canvas.width = maxX;
 canvas.height = maxY;

 var ctx = canvas.getContext('2d');
 ctx.drawImage(img, 0, 0, maxX, maxY);

 if ( /\.jpe?g$/.test(data.name))
 {
  // You could adjust here the quality of jpg
  evData.file = canvas.toDataURL("image/jpeg", 0.9);
 }
 else
  data.file = canvas.toDataURL("image/png");
}

2014/02/15

How to launch the ImageMaps dialog with your own code

If you have some code outside of CKEditor that inserts an image into its content and then you want to automatically launch the ImageMaps dialog on that image, you can use simply this two lines of code:

First, select the image using the CKEditor API:

editor.getSelection().selectElement(editor.document.getById("myImageID"));

"editor" is a reference to the editor instance that contains the image, and "myImageID" is the id attribute assigned to that image.

It first gets a reference to the element with editor.document.getById and then tells the selection system to select that element.

Then launch the dialog:

editor.openDialog("ImageMaps");
It's just that simple, use the name registered for that dialog and tell editor that you want to open it :-)
 
I hope that this can be helpful for someone.

2014/02/08

How to insert a new "Browse page" button in the ImageMaps plugin of CKEditor

In this post I'll provide a simple code that inserts a new browse button in the ImageMaps dialog that allows the user to browse existing pages in your CMS besides the normal browse button used to browse for files.

The code can easily be reused for other dialogs like the link or image, you just need to check the name of the elements and then test that it works as expected.

The main idea is to use the "dialogDefinition" event to modify the definition of the dialog on the fly and then create a new button that we will insert besides the existing one. You can check other uses in the samples folder of your CKEditor, there's an "api_dialog" file that shows some of these ideas.

The code is as simple as this:

CKEDITOR.on( "dialogDefinition", function( ev ) {
    // Take the dialog name and its definition from the event data.
    var dialogName = ev.data.name;
    var dialogDefinition = ev.data.definition;

   //Customize "Image Map" dialog 
    if ( dialogName == "ImageMaps" ) {

  var infoTab = dialogDefinition.getContents('info');
  // get the existing "browse" button, adjust its position
  var browseFile = infoTab.get('browse');
  browseFile.style = 'display:inline-block;margin-top:15px; ';

  // Create a new "Browse page" button, linking to our custom page browser
  var browsePage = {
   type : 'button',
   id : 'browsePage',
   hidden : true,
   style: 'display:inline-block;margin-top:15px; ',
   filebrowser :
   {
    action : 'Browse',
    target: 'info:url',
    url: '../intLinks2.aspx'
   },
   label : 'Link to CMS Page or Form'
  };
  // Create a container for the two buttons and replace the existing browse button with this one
  var hBox = { type :'hbox', widths: ['120px', '120px'], children : [browsePage] };
  infoTab.add( hBox, 'browse' );
  infoTab.remove( 'browse' );
  infoTab.add( browseFile, 'browsePage' );

  // Force a better width for the href field
  var txtHref = infoTab.get('href');
  txtHref.style = 'width:200px';
    }
});

I hope that someone finds this useful :-)

2014/02/02

Upgrading to a SSD

Whenever I get a new computer I don't go for the top of the line, I usually prefer to just get one with some nice specs and then one or two years later upgrade some of its components.

This year I realized that I have kept this one for too long :-) and that I should have bought a SSD a little while ago. So I looked up some reviews and one of the main ideas in "Tom's Hardware" talking about which one to pick is:

No matter which one, any SSD is a huge improvement over traditional disk, so I decided that I wouldn't spend too much time researching marks and models, instead I calculated how much space I would need for C: if I move the movies/music/pictures to the existing disk after formatting it as "Data", and it seems that 128Gb is all that I need.

Then I ordered the one that came out as a good option in the reviews that I had found, together with an adapter to use the 2.5" in a 3.5" slot. Fortunately I opened up the tower and as I noticed that there was no empty power connector I ordered also one, I had one SATA cable around so I didn't need that :-)

When setting up the new disk everything was quite easy, except the 2.5" to 3.5" adapter, the fact is that I didn't have a free 3.5" slot so I had to just let the drive laying over an empty space, I guess that this isn't a huge problem as that little disk doesn't have a motor like the old ones.

The performance change is great, the windows evaluation for the disk states that it gets a 7.4 despite the fact that so far I haven't been able to enable the AHCPI that it's supposed to provide even greater performance.

Windows starts up faster, programs load faster, Visual Studio works much faster, but I still keep typing as slow as always :-D

If you still have an old disk go ahead and get a SSD, it's certainly worth it, but you can skip the 2.5 to 3.5 adapter.

2014/01/30

Updated SingleSite License price

Today I've changed the price of the Single Site licenses for the three CKEditor plugins from 15 to 10 euros.

The new price is already active, and I think that I've changed all the references to it.

Besides that, we all know that when you miss an offer by a few days it leaves a bad taste in our mouth, so I've opted to go ahead and I've refunded also those 5 euros to all the people that have bought a license in the last month == since 1st of January.

Let's call it a birthday gift :-D

I hope to finish some testing this weekend and release updated versions (mainly bugfixes and some little features) for all the plugins.

2014/01/28

New Enterprise license for the CKEditor plugins

So far I've been distributing my commercial CKEditor plugins (SimpleUploads, GoogleMaps and ImageMaps) under two license options:

  • Single Site license
  • OEM license

Their scope was simple: Single site is the correct one when you want to use a plugin in just one domain, OEM license is aimed at companies that create websites and instead of paying a license for each domain, you can get this license to use it in every domain with a single payment.

There are other kind of companies: those that sell their product, and the OEM license is not the right one for them because they don't create any site, so I'm introducing a new license to fit that situation.

The new Enterprise license allows to integrate the plugin with their version of CKEditor and sell it integrated into their product. There's no limit on the number of users and licenses that you sell of your product.

The price for this license is 120 €, and I'll be adding the payment links for all the plugins as soon as I have time.

 

2013/12/11

Validation of files with SimpleUploads

As a user is frustrating to upload a file and then after waiting a long time for it to finish get a message stating that your file isn't valid. That's why the SimpleUploads plugin for CKEditor allows to perform most of the validations before the upload starts and that way you'll have happier users.

In this post I will explain how to configure the most common options and you can test them in a demo page for validation of uploads.

Allowed extensions

This is the most basic check, a whitelist of extensions that the users can upload to the server

config.simpleuploads_acceptedExtensions = "jpg|png|zip";

By default the value is empty so no check is performed. You can add the the list of extensions separated by the pipe "|" and that will be used to check the filename. If the extension isn't allowed the nonAcceptedExtension message will be shown to the user (you can customize it in the lang file).

All the editors in the demo only allow to upload the files with a list matching the settings on the server, except the first inline demo. In that demo the list is set to an empty string so any file is uploaded and then you get the error message from the server (in this case an un-friendly "202 upload failed")

Denied extensions

This is the opposite of allowed, if the extension matches this list the file will be rejected. I only added this configuration option for parity, but everybody knows that a blacklist approach is not good for security. The demo page doesn't include this option.

Image extensions

This setting has two purposes.

The first one is that when you drop a file the plugin must decide if it should create an <img> or an <a> with the upload. The user can opt to press the Shift key while dropping the file to generate a link even if he adds an image file, but as a general rule you might want to allow only png and jpg files for example, or include .bmp and .tiff and then convert those files to other format at the server.

The second purpose is to restrict the accepted files when the user presses the "Add image" button as well as the uploads in the image dialog. If the selected file doesn't match this filter it will be rejected and the nonImageExtension message will be shown to the user (this feature will be included in version 4.1.1, the current one is 4.1.0).

config.simpleuploads_imageExtensions = 'jpe?g|gif|png|bmp';

The bottom editor in the middle is configured to only allow png files as images. You can check that if you drop a jpg is inserted as a link, and the toolbar buttons and image dialogs will reject anything that isn't a png.

File size

Obviously uploading a 100Mb file is usually a bad idea, but the users don't realize that and that's why you can easily set the maximum size that you allow them to use with simpleuploads_maxFileSize. The default value is empty so no check is performed, but you can set here the value that you want to limit (as an integer with the bytes, so 10Mb => 10*1024*1024

The inline editor at the top middle uses a limit of 100Kb, if the selected file is bigger, the fileTooBig message is shown to the user.

config.simpleuploads_maxFileSize = 102400;

As a side note, file size is a common problem and even if you don't bother to adjust this setting, the server might return a "413 Request Entity Too Large" status error, in that case the plugin handles that error and shows again the fileTooBig message to the user instead of just stating that the upload failed.

Custom validation

Maybe you think that you still want to do some other check like forcing the user to select a file with a custom pattern and a minimum file size, or whatever, ok, you can do that also.

The last editor at the bottom right only allows to upload files that start with "wall" by listening to the  "simpleuploads.startUpload" event.

The code in this example is quite simple

editor.on('simpleuploads.startUpload', function (ev) {
 // File name
 var name = ev.data.name;
 if (name.substr(0,4).toLowerCase() != "wall")
 {
  alert( "You must add here only wallpapers" );
  ev.cancel();
 }
});

We set the listener on the editor instance that we want and use the ev.data.name property to check the file name that the user has selected, if it doesn't pass our test we cancel the event and show a message to the user.

In fact most of the previous validations (except for identifying images) are performed using this event, the plugin adds a listener like that for each editor and uses the configuration values to verify if the file is valid or it should be rejected using the file name as well as the file object to check its size.

Server messages

Even after all these checks, there might be some other issues that can't be handled at the client side, in that case remember that in the response that you send from the server you can set an empty Url and the third parameter is a message that will be shown to the user.