TinyMCE image insert plugin with ASP.NET WebApi2 - CodeProject

:

Introduction

When you are using TinyMCE WYSYWIG editor, you want to add images in the editor. But to achieve this purpose, you have to buy file management module, or develop plugin by yourself.

There're two ways to make it work. First method is to upload a image in server in specific directory, and create a link for this in the editor. Another method is to convert image file to Base64 encoded string. Most of modern browsers support the second way, so I will show you how to convert image file to base64 encoded string with .NET WebApi.

Supported Browsers

  • IE 9 or higher version
  • Almost all Chrome version
  • Almost all Firefox version
  • Opera : Not tested.

Using the code

To convert image binary to base64 encoded string, I will user JQuery AJAX and .NET WebApi2.

So, MVC5 is requried for this project.

Let's start with WebApi Controller. There is a method called ConvertImageToBase64. This method convert the image binary to base64 encoded string requested by JQuery AJAX. Once the converting is finished, the string will be returned to the browser.

//
// EditController.cs in Project/Controllers
//

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Web;
using System.Web.Http;

namespace TinyMCE.Controllers
{
    public class EditorController : ApiController
    {
        [Route("api/image/convert-base64")]
        [HttpPost]
        public HttpResponseMessage ConvertImageToBase64()
        {
            string base64String = "";
            HttpResponseMessage response = new HttpResponseMessage();

            if (HttpContext.Current.Request.Files.AllKeys.Any())
            {
                // Get the uploaded image from the Files collection
                var httpPostedFile = HttpContext.Current.Request.Files["file"];

                if (httpPostedFile != null)
                {
                    // Validate the uploaded image(optional)

                    byte[] fileData = null;
                    using (var binaryReader = new BinaryReader(httpPostedFile.InputStream))
                    {
                        fileData = binaryReader.ReadBytes(httpPostedFile.ContentLength);
                        base64String = System.Convert.ToBase64String(fileData, 0, fileData.Length);
                    }
                }
            }

            response.Content = new StringContent(base64String);
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");

            return response;
        }
    }
}

 

This is TinyMCE popup window defintion file. To support same Look and Feel of current TinyMCE version, I added some style sheet in Contents/Site.css & used Bootstrap.

//
// dialog.html in Project/Scripts/tinymce
//

<!DOCTYPE html>
<html>
<head>
    <title>Image insert</title>

    <link href="/Content/bootstrap.css" rel="stylesheet" />
    <link href="/Content/Site.css" rel="stylesheet" />

    <script src="/Scripts/modernizr-2.6.2.js"></script>
    <script src="/Scripts/jquery-1.10.2.js"></script>

    <script src="/Scripts/bootstrap.js"></script>

    <script>
        $(document).on('change', '.btn-file :file', function () {
            var input = $(this),
            numFiles = input.get(0).files ? input.get(0).files.length : 1,
            label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
            input.trigger('fileselect', [numFiles, label]);
        });

        $(document).ready(function () {
            $('.btn-file :file').on('fileselect', function (event, numFiles, label) {

                var input = $(this).parents('.input-group').find(':text'),
                    log = numFiles > 1 ? numFiles + ' files selected' : label;

                if (input.length) {
                    input.val(log);
                } else {
                    if (log) alert(log);
                }

            });
        });
    </script>
</head>
<body>
    <div style="padding:20px;">
        <table style="width: 100%;">
            <tr>
                <td style="width: 141px; vertical-align: middle;"><label style="font-weight: normal;">Source</label></td>
                <td style="vertical-align: middle;">
                    <div class="input-group">
                        <span class="input-group-btn">
                            <span class="btn btn-default btn-file">
                                Browse&hellip; <input type="file" id="content" name="content">
                            </span>
                        </span>
                        <input type="text" class="form-control" readonly>
                    </div>
                </td>
            </tr>
            <tr>
                <td style="width: 141px; vertical-align: middle; padding-top: 20px;"><label style="font-weight: normal;">Image Description</label></td>
                <td style ="vertical-align: middle; padding-top: 20px;">
                    <input id="desc" name="desc" type="text" class="form-control">
                </td>
            </tr>
        </table>
    </div>
</body>
</html>

 

This is a java script plugin to send image binary to send a server.

Quote:

Once you changed something in this file, you MUST minify this file to plugin.min.js file. If not, the function will not work as expected because TinyMCE only reads minified file.

//
// plugin.js in Project/Scripts/tinymce
//

tinymce.PluginManager.add("base64_image", function (a, b) {
    a.addButton("base64_image", {
        icon: "image",
        tooltip: "Insert image",
        onclick: function () {
            a.windowManager.open({
                title: "Insert image",
                url: b + "/dialog.html",
                width: 500,
                height: 150,
                buttons: [{
                    text: "Ok",
                    classes: 'widget btn primary first abs-layout-item',
                    onclick: function () {
                        var b = a.windowManager.getWindows()[0];

                        if (b.getContentWindow().document.getElementById('content').value == '') {
                            alert('Please select a file');
                            return false;
                        }

                        if (b.getContentWindow().document.getElementById('content').files[0].size > 1000 * 1024) {
                            alert('Max image file size is 1MB');
                            return false;
                        }

                        if (b.getContentWindow().document.getElementById('content').files[0].type != "image/jpeg" && b.getContentWindow().document.getElementById('content').files[0].type != "image/jpg" &&
                            b.getContentWindow().document.getElementById('content').files[0].type != "image/png" && b.getContentWindow().document.getElementById('content').files[0].type != "image/gif") {
                            alert('Only image file format can be uploaded');
                            return false;
                        }

                        var data;

                        data = new FormData();
                        data.append('file', b.getContentWindow().document.getElementById('content').files[0]);

                        $.ajax({
                            url: '/api/image/convert-base64',
                            data: data,
                            processData: false,
                            contentType: false,
                            async: false,
                            type: 'POST',
                        }).done(function (msg) {
                            var imageAlt = b.getContentWindow().document.getElementById('desc').value;
                            var imageSrc = "data:" + b.getContentWindow().document.getElementById('content').files[0].type + ";base64," + msg;

                            var imageTag = '<img src="' + imageSrc + '" alt="' + imageAlt + '" style="max-width: 100%;" />';

                            a.insertContent(imageTag), b.close()

                        }).fail(function (jqXHR, textStatus) {
                            alert("Request failed: " + jqXH.responseText + " --- " +RtextStatus);
                        });
                    }
                }, {
                    text: "Close",
                    onclick: "close"
                }]
            })
        }
    }),

    a.addMenuItem("base64_image", {
        icon: "image",
        text: "Insert image",
        context: "insert",
        prependToContext: !0,
        onclick: function () {
            a.windowManager.open({
                title: "Insert image",
                url: b + "/api/image/convert-base64",
                width: 500,
                height: 150,
                buttons: [{
                    text: "Ok",
                    classes: 'widget btn primary first abs-layout-item',
                    onclick: function () {
                        var b = a.windowManager.getWindows()[0];

                        if (b.getContentWindow().document.getElementById('content').value == '') {
                            alert('Please select a file');
                            return false;
                        }

                        if (b.getContentWindow().document.getElementById('content').files[0].size > 1000 * 1024) {
                            alert('Max image file size is 1MB');
                            return false;
                        }

                        if (b.getContentWindow().document.getElementById('content').files[0].type != "image/jpeg" && b.getContentWindow().document.getElementById('content').files[0].type != "image/jpg" &&
                            b.getContentWindow().document.getElementById('content').files[0].type != "image/png" && b.getContentWindow().document.getElementById('content').files[0].type != "image/gif") {
                            alert('Only image file format can be uploaded');
                            return false;
                        }

                        var data;

                        data = new FormData();
                        data.append('file', b.getContentWindow().document.getElementById('content').files[0]);

                        $.ajax({
                            url: '/api/image/convert-base64',
                            data: data,
                            processData: false,
                            contentType: false,
                            async: false,
                            type: 'POST',
                        }).done(function (msg) {
                            var imageAlt = b.getContentWindow().document.getElementById('desc').value;
                            var imageSrc = "data:" + b.getContentWindow().document.getElementById('content').files[0].type + ";base64," + msg;

                            var imageTag = '<img src="' + imageSrc + '" alt="' + imageAlt + '" style="max-width: 100%;" />';

                            a.insertContent(imageTag), b.close()

                        }).fail(function (jqXHR, textStatus) {
                            alert("Request failed: " + jqXH.responseText + " --- " + RtextStatus);
                        });
                    }
                }, {
                    text: "Close",
                    onclick: "close"
                }]
            })
        }
    })

});

 

Last, just change the Index.cshtml to include TinyMCE editor module.

//
// Index.cshtml in Project/View/Home
//


<div class="form-group" style="padding-top: 80px;">
    @Html.TextArea("Content")
</div>

@section Scripts {
    <script type="text/javascript" src="@Url.Content("~/Scripts/tinymce/tinymce.min.js")"></script>

    <script type="text/javascript">
        tinymce.init({
            selector: "textarea",
            width: '100%',
            height: 600,
            statusbar: false,
            menubar: false,

            setup: function (ed) {
                ed.on('init', function () {
                    this.getDoc().body.style.fontSize = '14px';
                    this.getDoc().body.style.fontFamily = '"Helvetica Neue", Helvetica, Arial, sans-serif';
                });
            },

            paste_data_images: true,

            plugins: [
                "advlist autolink lists link base64_image charmap hr anchor pagebreak",
                "searchreplace wordcount visualblocks visualchars code",
                "insertdatetime media nonbreaking save table contextmenu directionality",
                "emoticons textcolor colorpicker textpattern "
            ],
            toolbar: "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link base64_image media | forecolor backcolor"
        });

        // Prevent bootstrap dialog from blocking focusin
        $(document).on('focusin', function (e) {
            if ($(e.target).closest(".mce-window").length) {
                e.stopImmediatePropagation();
            }
        });
    </script>
}

 

Screen Shot

This screen shot just shows how this plugin & WebApi module works.

https://www.codeproject.com/KB/aspnet/890097/ss1.jpg

https://www.codeproject.com/KB/aspnet/890097/ss2.jpg

History

2015-03-24 : Initial article uploaded