Add new API controller for uploading images dragged/pasted into a TinyMCE editor
This commit is contained in:
@@ -156,6 +156,55 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
}
|
||||
}
|
||||
|
||||
function uploadImageHandler(blobInfo, success, failure, progress){
|
||||
|
||||
//TODO: Worth refactoring to Angular $http calls
|
||||
//Rather than XHR??
|
||||
|
||||
let xhr, formData;
|
||||
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', Umbraco.Sys.ServerVariables.umbracoUrls.tinyMceApiBaseUrl + 'UploadImage');
|
||||
|
||||
xhr.upload.onprogress = function (e) {
|
||||
progress(e.loaded / e.total * 100);
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
|
||||
};
|
||||
|
||||
xhr.onload = function () {
|
||||
let json;
|
||||
|
||||
if (xhr.status < 200 || xhr.status >= 300) {
|
||||
failure('HTTP Error: ' + xhr.status);
|
||||
return;
|
||||
}
|
||||
|
||||
json = JSON.parse(xhr.responseText);
|
||||
|
||||
if (!json || typeof json.location !== 'string') {
|
||||
failure('Invalid JSON: ' + xhr.responseText);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: The JSON contains location & udi
|
||||
// Need to add UDI as attr
|
||||
|
||||
success(json.location);
|
||||
};
|
||||
|
||||
formData = new FormData();
|
||||
formData.append('file', blobInfo.blob(), blobInfo.blob().name);
|
||||
|
||||
//TODO: Send Media Parent ID from config
|
||||
//TODO: How will each RTE pass in this value to this function?!
|
||||
formData.append('mediaParent', -1);
|
||||
|
||||
xhr.send(formData);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
@@ -237,13 +286,19 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
insert_toolbar: toolbars.insertToolbar,
|
||||
selection_toolbar: toolbars.selectionToolbar,
|
||||
|
||||
body_class: 'umb-rte',
|
||||
body_class: "umb-rte",
|
||||
//see http://archive.tinymce.com/wiki.php/Configuration:cache_suffix
|
||||
cache_suffix: "?umb__rnd=" + Umbraco.Sys.ServerVariables.application.cacheBuster,
|
||||
|
||||
//this is used to style the inline macro bits, sorry hard coding this form now since we don't have a standalone
|
||||
//stylesheet to load in for this with only these styles (the color is @pinkLight)
|
||||
content_style: ".mce-content-body .umb-macro-holder { border: 3px dotted #f5c1bc; padding: 7px; display: block; margin: 3px; } .umb-rte .mce-content-body .umb-macro-holder.loading {background: url(assets/img/loader.gif) right no-repeat; background-size: 18px; background-position-x: 99%;}"
|
||||
|
||||
// This allows images to be pasted in & stored as Base64 until they get uploaded to server
|
||||
paste_data_images: true
|
||||
|
||||
images_upload_handler: uploadImageHandler,
|
||||
automatic_uploads: false,
|
||||
};
|
||||
|
||||
if (tinyMceConfig.customConfig) {
|
||||
@@ -281,7 +336,6 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
angular.extend(config, tinyMceConfig.customConfig);
|
||||
}
|
||||
|
||||
|
||||
return $q.when(config);
|
||||
|
||||
});
|
||||
@@ -428,7 +482,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
id: '__mcenew',
|
||||
'data-udi': img.udi
|
||||
};
|
||||
|
||||
|
||||
editor.selection.setContent(editor.dom.createHTML('img', data));
|
||||
|
||||
$timeout(function () {
|
||||
@@ -447,9 +501,9 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
}
|
||||
}
|
||||
editor.dom.setAttrib(imgElm, 'id', null);
|
||||
|
||||
|
||||
editor.fire('Change');
|
||||
|
||||
|
||||
}, 500);
|
||||
}
|
||||
},
|
||||
@@ -521,7 +575,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
onPostRender: function () {
|
||||
|
||||
let ctrl = this;
|
||||
|
||||
|
||||
/**
|
||||
* Check if the macro is currently selected and toggle the menu button
|
||||
*/
|
||||
@@ -1134,7 +1188,7 @@ function tinyMceService($rootScope, $q, imageHelper, $locale, $http, $timeout, s
|
||||
};
|
||||
editorService.linkPicker(linkPicker);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
//Create the insert media plugin
|
||||
|
||||
@@ -309,7 +309,11 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
"webProfilingBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<WebProfilingController>(
|
||||
controller => controller.GetStatus())
|
||||
}
|
||||
},
|
||||
{
|
||||
"tinyMceApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<TinyMceController>(
|
||||
controller => controller.UploadImage())
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
107
src/Umbraco.Web/Editors/TinyMceController.cs
Normal file
107
src/Umbraco.Web/Editors/TinyMceController.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Constants = Umbraco.Core.Constants;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Core.IO;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Web.Composing;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
|
||||
namespace Umbraco.Web.Editors
|
||||
{
|
||||
[PluginController("UmbracoApi")]
|
||||
public class TinyMceController : UmbracoAuthorizedApiController
|
||||
{
|
||||
private IMediaService _mediaService;
|
||||
private IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;
|
||||
|
||||
public TinyMceController(IMediaService mediaService, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider)
|
||||
{
|
||||
_mediaService = mediaService;
|
||||
_contentTypeBaseServiceProvider = contentTypeBaseServiceProvider;
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public async Task<HttpResponseMessage> UploadImage()
|
||||
{
|
||||
|
||||
if (Request.Content.IsMimeMultipartContent() == false)
|
||||
{
|
||||
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
|
||||
}
|
||||
|
||||
var root = IOHelper.MapPath(SystemDirectories.TempFileUploads);
|
||||
|
||||
// Ensure it exists
|
||||
Directory.CreateDirectory(root);
|
||||
var provider = new MultipartFormDataStreamProvider(root);
|
||||
|
||||
var result = await Request.Content.ReadAsMultipartAsync(provider);
|
||||
|
||||
// Must have a file
|
||||
if (result.FileData.Count == 0)
|
||||
{
|
||||
return Request.CreateResponse(HttpStatusCode.NotFound);
|
||||
}
|
||||
|
||||
// Should only have one file
|
||||
if (result.FileData.Count > 1)
|
||||
{
|
||||
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Only one file can be uploaded at a time");
|
||||
}
|
||||
|
||||
// Check we have mediaParent as posted data
|
||||
if (string.IsNullOrEmpty(result.FormData["mediaParent"]))
|
||||
{
|
||||
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Missing the Media Parent folder to save this image");
|
||||
}
|
||||
|
||||
// Really we should only have one file per request to this endpoint
|
||||
var file = result.FileData[0];
|
||||
|
||||
var parentFolder = Convert.ToInt32(result.FormData["mediaParent"]);
|
||||
|
||||
var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }).TrimEnd();
|
||||
var safeFileName = fileName.ToSafeFileName();
|
||||
var ext = safeFileName.Substring(safeFileName.LastIndexOf('.') + 1).ToLower();
|
||||
|
||||
if (Current.Configs.Settings().Content.IsFileAllowedForUpload(ext) == false || Current.Configs.Settings().Content.ImageFileTypes.Contains(ext) == false)
|
||||
{
|
||||
// Throw some error - to say can't upload this IMG type
|
||||
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "This is not an image filetype extension that is approved");
|
||||
}
|
||||
|
||||
var mediaItemName = fileName.ToFriendlyName();
|
||||
var f = _mediaService.CreateMedia(mediaItemName, parentFolder, Constants.Conventions.MediaTypes.Image, Security.CurrentUser.Id);
|
||||
var fileInfo = new FileInfo(file.LocalFileName);
|
||||
var fs = fileInfo.OpenReadWithRetry();
|
||||
if (fs == null) throw new InvalidOperationException("Could not acquire file stream");
|
||||
using (fs)
|
||||
{
|
||||
f.SetValue(Services.ContentTypeBaseServices, Constants.Conventions.Media.File, fileName, fs);
|
||||
}
|
||||
|
||||
_mediaService.Save(f, Security.CurrentUser.Id);
|
||||
|
||||
// Need to get URL to the media item and its UDI
|
||||
var udi = f.GetUdi();
|
||||
|
||||
|
||||
// TODO: Check this is the BEST way to get the URL
|
||||
// Ensuring if they use some CDN & blob storage?!
|
||||
var mediaTyped = Umbraco.Media(f.Id);
|
||||
var location = mediaTyped.Url;
|
||||
|
||||
return Request.CreateResponse(HttpStatusCode.OK, new { location = location, udi = udi });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,7 @@
|
||||
<Compile Include="Editors\KeepAliveController.cs" />
|
||||
<Compile Include="Editors\MacrosController.cs" />
|
||||
<Compile Include="Editors\RelationTypeController.cs" />
|
||||
<Compile Include="Editors\TinyMceController.cs" />
|
||||
<Compile Include="IUmbracoContextFactory.cs" />
|
||||
<Compile Include="Install\ChangesMonitor.cs" />
|
||||
<Compile Include="Logging\WebProfiler.cs" />
|
||||
|
||||
Reference in New Issue
Block a user