Last active
September 24, 2020 15:54
-
-
Save auroris/e351ca2d1cfe132cfa58208eef0ec0d5 to your computer and use it in GitHub Desktop.
An Umbraco 8 enhancement that converts a base64 encoded image into a proper media image. This file is a basic class that expects to be placed in an App_Code folder and compiled along with your Umbraco project.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using HtmlAgilityPack; | |
using Newtonsoft.Json.Linq; | |
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using Umbraco.Core; | |
using Umbraco.Core.Composing; | |
using Umbraco.Core.Events; | |
using Umbraco.Core.Models; | |
using Umbraco.Core.Models.Entities; | |
using Umbraco.Core.Services; | |
using Umbraco.Core.Services.Implement; | |
namespace Umbraco.App_Code | |
{ | |
[RuntimeLevel(MinLevel = RuntimeLevel.Run)] | |
public class Base64MediaConverterComposer : ComponentComposer<Base64MediaConverterComponent> | |
{ } | |
public class Base64MediaConverterComponent : IComponent | |
{ | |
public void Initialize() | |
{ | |
// Subscribe to Umbraco events | |
ContentService.Saving += ContentService_Saving; | |
} | |
private void ContentService_Saving(IContentService sender, ContentSavingEventArgs e) | |
{ | |
// Iterate through all values being saved | |
foreach (IContent savedEntity in e.SavedEntities) | |
{ | |
foreach (Property prop in savedEntity.Properties) | |
{ | |
foreach (Property.PropertyValue val in prop.Values) | |
{ | |
// If the value is a Grid editor | |
if (prop.PropertyType.PropertyEditorAlias.Equals("Umbraco.Grid")) | |
{ | |
// Load the JSON document | |
JObject doc = JObject.Parse(val.EditedValue as String); | |
// Find the "controls" elements which specifies subdocuments in a grid's row/column location | |
List<JToken> controlGroup = doc.Descendants().Where(x => x is JObject && x["controls"] != null).ToList(); | |
// For each row/column location with at least one control | |
foreach (var controls in controlGroup) | |
{ | |
// For each control in a location | |
foreach (var control in controls["controls"]) | |
{ | |
// If the control is type "rte" then we do stuff; else, ignore | |
// Other control types like "image" or "macro" are irrelevant for this purpose | |
// see config\grid.editors.config.js for control aliases | |
if (control["editor"]["alias"].ToString().Equals("rte")) | |
{ | |
try | |
{ | |
if ((String)control["value"] != null && ((String)control["value"]).IndexOf("data:", StringComparison.OrdinalIgnoreCase) > -1) | |
{ | |
// Replace the sub document in the json | |
control["value"] = ReplaceBase64((String)control["value"], savedEntity); | |
} | |
} | |
catch (Exception ex) | |
{ | |
// Log the error, bubble up message to back office | |
Current.Logger.Error(this.GetType(), "Error converting base64 image: " + ex.ToString(), ex); | |
e.Cancel = true; | |
e.Messages.Add(new EventMessage("Error converting image", "An error occured when attempting to convert an embedded image into a media library image.", EventMessageType.Error)); | |
} | |
} | |
} | |
} | |
// Replace the JSON document | |
prop.SetValue(doc.ToString(), val.Culture, val.Segment); | |
} | |
// If the value is a TinyMCE editor | |
if (prop.PropertyType.PropertyEditorAlias.Equals("Umbraco.TinyMCE")) | |
{ | |
try | |
{ | |
if (val.EditedValue != null && ((String)val.EditedValue).IndexOf("data:", StringComparison.OrdinalIgnoreCase) > -1) | |
{ | |
// Replace the html document | |
prop.SetValue(ReplaceBase64(val.EditedValue as String, savedEntity), val.Culture, val.Segment); | |
} | |
} | |
catch (Exception ex) | |
{ | |
// Log the error, bubble up message to back office | |
Current.Logger.Error(this.GetType(), "Error converting base64 image: " + ex.ToString(), ex); | |
e.Cancel = true; | |
e.Messages.Add(new EventMessage("Error converting image", "An error occured when attempting to convert an embedded image into a media library image.", EventMessageType.Error)); | |
} | |
} | |
} | |
} | |
} | |
} | |
/// <summary> | |
/// Saves a base64 encoded image into the image library and then replaces the img src with the location of the image | |
/// </summary> | |
/// <param name="content">An HTML document fragment</param> | |
/// <param name="entity">The content entity the image belongs to</param> | |
/// <returns>An updated HTML document fragment</returns> | |
String ReplaceBase64(String content, IUmbracoEntity entity) | |
{ | |
if (content == null || entity == null) { return content; } | |
HtmlDocument doc = new HtmlDocument(); | |
doc.LoadHtml(content); | |
// For every image | |
foreach (HtmlNode node in doc.DocumentNode.Descendants("img")) | |
{ | |
// Is it a data url? | |
if (node.Attributes["src"] != null && node.Attributes["src"].Value.IndexOf("data:", StringComparison.OrdinalIgnoreCase) > -1) | |
{ | |
// Convert the data url | |
String[] data = node.Attributes["src"].Value.Split(new[] { ':', ';', ',' }); | |
String fileMime = data[1]; | |
String fileName = Guid.NewGuid().ToString() + "." + fileMime.Split('/')[1]; | |
byte[] fileBytes = Convert.FromBase64String(data[3]); | |
// Umbraco save image | |
IMedia image = Current.Services.MediaService.CreateMedia(fileName, Constants.System.Root, Constants.Conventions.MediaTypes.Image); | |
image.SetValue(Current.Services.ContentTypeBaseServices, Constants.Conventions.Media.File, fileName, new MemoryStream(fileBytes)); | |
Current.Services.MediaService.Save(image); | |
// Tell Umbraco that the document uses the image | |
Current.Services.RelationService.Relate(entity, image, Constants.Conventions.RelationTypes.RelatedMediaAlias); | |
// Update img src | |
node.SetAttributeValue("src", image.GetUrl(Constants.Conventions.Media.File, Current.Logger)); | |
} | |
} | |
// Return the html document fragment | |
return doc.DocumentNode.OuterHtml; | |
} | |
public void Terminate() | |
{ | |
//unsubscribe during shutdown | |
ContentService.Saving -= ContentService_Saving; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment