From a3d77b7e7216e45e5148a91a72a73f7376ed1ba5 Mon Sep 17 00:00:00 2001 From: perploug Date: Thu, 3 Oct 2013 20:57:36 +0200 Subject: [PATCH] Adds RTE and stylesheet services --- .../common/resources/stylesheet.resource.js | 43 ++++++ .../views/prevalueeditors/rte.controller.js | 0 .../src/views/prevalueeditors/rte.html | 0 .../src/views/propertyeditors/rte/rte.html | 2 + .../rte/rte.prevalues.controller.js | 48 +++++++ .../propertyeditors/rte/rte.prevalues.html | 35 +++++ .../config/tinyMceConfig.config | 41 +++--- .../Editors/BackOfficeController.cs | 4 + .../Editors/StylesheetController.cs | 40 ++++++ .../ContentEditing/RichTextEditorCommand.cs | 37 ++++++ .../RichTextEditorConfiguration.cs | 28 ++++ .../ContentEditing/RichTextEditorPlugin.cs | 19 +++ .../Models/ContentEditing/StyleSheet.cs | 21 +++ .../Models/ContentEditing/StylesheetRule.cs | 22 +++ .../RichTextPreValueController.cs | 125 ++++++++++++++++++ .../PropertyEditors/RichTextPreValueEditor.cs | 23 ++++ .../PropertyEditors/RichTextPropertyEditor.cs | 6 + src/Umbraco.Web/Umbraco.Web.csproj | 9 ++ 18 files changed, 486 insertions(+), 17 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/common/resources/stylesheet.resource.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.controller.js delete mode 100644 src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.html create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js create mode 100644 src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html create mode 100644 src/Umbraco.Web/Editors/StylesheetController.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs create mode 100644 src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs create mode 100644 src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs create mode 100644 src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/stylesheet.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/stylesheet.resource.js new file mode 100644 index 0000000000..8716fdf29a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/stylesheet.resource.js @@ -0,0 +1,43 @@ +/** + * @ngdoc service + * @name umbraco.resources.stylesheetResource + * @description service to retrieve available stylesheets + * + * + **/ +function stylesheetResource($q, $http, umbRequestHelper) { + + //the factory object returned + return { + + /** + * @ngdoc method + * @name umbraco.resources.stylesheetResource#getAll + * @methodOf umbraco.resources.stylesheetResource + * + * @description + * Gets all registered stylesheets + * + * ##usage + *
+         * stylesheetResource.getAll()
+         *    .then(function(stylesheets) {
+         *        alert('its here!');
+         *    });
+         * 
+ * + * @returns {Promise} resourcePromise object containing the stylesheets. + * + */ + getAll: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "stylesheetApiBaseUrl", + "GetAll")), + 'Failed to retreive stylesheets '); + } + }; +} + +angular.module('umbraco.resources').factory('stylesheetResource', stylesheetResource); diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.controller.js b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.controller.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.html b/src/Umbraco.Web.UI.Client/src/views/prevalueeditors/rte.html deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html index 51a07d5335..1848358833 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html @@ -1,3 +1,5 @@ 
+ + {{model | json}}
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js new file mode 100644 index 0000000000..84c502c03a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.controller.js @@ -0,0 +1,48 @@ +angular.module("umbraco").controller("Umbraco.PrevalueEditors.RteController", + function ($scope, $timeout, tinyMceService, stylesheetResource) { + var cfg = {}; + cfg.toolbar = ["code", "bold", "italic", "umbracocss","alignleft", "aligncenter", "alignright", "bullist","numlist", "outdent", "indent", "link", "image", ",umbmediapicker", "umbembeddialog", "umbmacro"]; + cfg.stylesheets = []; + cfg.dimensions = {height: 400, width: 600}; + + if($scope.model.value){ + if(angular.isString($scope.model.value)){ + $scope.model.value = angular.fromJson($scope.model.value); + } + angular.extend($scope.model.value, cfg); + }else{ + $scope.model.value = cfg; + } + + tinyMceService.configuration().then(function(config){ + $scope.tinyMceConfig = config; + }); + + stylesheetResource.getAll().then(function(stylesheets){ + $scope.stylesheets = stylesheets; + }); + + $scope.selected = function(alias, lookup){ + return lookup.indexOf(alias) >= 0; + }; + + $scope.selectCommand = function(command){ + var index = $scope.model.value.toolbar.indexOf(command.frontEndCommand); + + if(command.selected && index === -1){ + $scope.model.value.toolbar.push(command.frontEndCommand); + }else if(index >= 0){ + $scope.model.value.toolbar.splice(index, 1); + } + }; + + $scope.selectStylesheet = function(css){ + var index = $scope.model.value.stylesheets.indexOf(css.path); + + if(css.selected && index === -1){ + $scope.model.value.stylesheets.push(css.path); + }else if(index >= 0){ + $scope.model.value.stylesheets.splice(index, 1); + } + }; + }); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html new file mode 100644 index 0000000000..407dc92578 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.prevalues.html @@ -0,0 +1,35 @@ +
+ + +
+ + + + + {{cmd.frontEndCommand}} +
+
+ + +
+ + {{css.name}} +
+
+ + + x Pixels + + + {{model.value | json}} + + {{tinyMceConfig | json}} +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/tinyMceConfig.config b/src/Umbraco.Web.UI/config/tinyMceConfig.config index 0a3982b343..6ff945d6d4 100644 --- a/src/Umbraco.Web.UI/config/tinyMceConfig.config +++ b/src/Umbraco.Web.UI/config/tinyMceConfig.config @@ -81,25 +81,25 @@ JustifyLeft images/editor/justifyleft.gif - justifyleft + justifyleft 31 JustifyCenter images/editor/justifycenter.gif - justifycenter + justifycenter 32 JustifyRight images/editor/justifyright.gif - justifyright + justifyright 33 JustifyFull images/editor/justifyfull.gif - justifyfull + justifyfull 34 @@ -147,29 +147,33 @@ 53 - - mceImage - images/editor/image.gif - image - 61 - + + mceImage + images/editor/image.gif + image + 61 + umbracomacro images/editor/insMacro.gif - umbracomacro + umbracomacro 62 + + + mceInsertTable images/editor/table.gif table 63 + umbracoembed images/editor/media.gif - umbracoembed + umbracoembed 66 @@ -207,15 +211,18 @@ + code paste - inlinepopups - noneditable - table + inlinepopups + noneditable + table + umbracomacro - + advlink umbracocss - umbracoembed + umbracoembed + umbracoimg spellchecker diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 641d809ca7..69f79af521 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -9,6 +9,7 @@ using Umbraco.Core; using Umbraco.Web.Mvc; using Umbraco.Web.Trees; using Umbraco.Web.UI.JavaScript; +using Umbraco.Web.PropertyEditors; namespace Umbraco.Web.Editors { @@ -74,6 +75,9 @@ namespace Umbraco.Web.Editors {"dashboardApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetDashboard")}, {"logApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetEntityLog")}, {"memberApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetByLogin")}, + {"rteApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetConfiguration")}, + {"stylesheetApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetAll")}, + {"publishedContentApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl("GetUrl")} } }, { diff --git a/src/Umbraco.Web/Editors/StylesheetController.cs b/src/Umbraco.Web/Editors/StylesheetController.cs new file mode 100644 index 0000000000..842af1cd96 --- /dev/null +++ b/src/Umbraco.Web/Editors/StylesheetController.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using umbraco.cms.businesslogic.web; +using Umbraco.Core.IO; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; + +namespace Umbraco.Web.Editors +{ + /// + /// The API controller used for retrieving available stylesheets + /// + [PluginController("UmbracoApi")] + public class StylesheetController : UmbracoAuthorizedJsonController + { + public IEnumerable GetAll() + { + return StyleSheet.GetAll() + .Select(x => + new Stylesheet() { + Name = x.Text, + Id = x.Id, + Path = SystemDirectories.Css + "/" + x.Text + ".css" + }); + } + + public IEnumerable GetRules(int id) + { + var css = new StyleSheet(id); + if (css == null) + throw new HttpResponseException(System.Net.HttpStatusCode.NotFound); + + return css.Properties.Select(x => new StylesheetRule() { Id = x.Id, Name = x.Text, Selector = x.Alias }); + } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs new file mode 100644 index 0000000000..646046ae56 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorCommand.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "richtexteditorcommand", Namespace = "")] + public class RichTextEditorCommand + { + [DataMember(Name = "icon")] + public string Icon { get; set; } + + [DataMember(Name = "command")] + public string Command { get; set; } + + [DataMember(Name = "alias")] + public string Alias { get; set; } + + [DataMember(Name = "userInterface")] + public string UserInterface { get; set; } + + [DataMember(Name = "frontEndCommand")] + public string FrontendCommand { get; set; } + + [DataMember(Name = "value")] + public string Value { get; set; } + + [DataMember(Name = "priority")] + public int Priority { get; set; } + + [DataMember(Name = "isStylePicker")] + public bool IsStylePicker { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs new file mode 100644 index 0000000000..380780b6d8 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorConfiguration.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "richtexteditorconfiguration", Namespace = "")] + public class RichTextEditorConfiguration + { + [DataMember(Name = "plugins")] + public IEnumerable Plugins { get; set; } + + [DataMember(Name = "commands")] + public IEnumerable Commands { get; set; } + + [DataMember(Name = "validElements")] + public string ValidElements { get; set; } + + [DataMember(Name = "inValidElements")] + public string InvalidElements { get; set; } + + [DataMember(Name = "customConfig")] + public IDictionary CustomConfig { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs new file mode 100644 index 0000000000..aec92a1eff --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/RichTextEditorPlugin.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "richtexteditorplugin", Namespace = "")] + public class RichTextEditorPlugin + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "useOnFrontend")] + public bool UseOnFrontend { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs b/src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs new file mode 100644 index 0000000000..49141d80d5 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/StyleSheet.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "stylesheet", Namespace = "")] + public class Stylesheet + { + [DataMember(Name="name")] + public string Name { get; set; } + + [DataMember(Name = "path")] + public string Path { get; set; } + + [DataMember(Name = "id")] + public int Id { get; set; } + } +} diff --git a/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs new file mode 100644 index 0000000000..9ef402dc89 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/StylesheetRule.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using System.Threading.Tasks; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "stylesheetRule", Namespace = "")] + public class StylesheetRule + { + [DataMember(Name = "name")] + public string Name { get; set; } + + [DataMember(Name = "selector")] + public string Selector { get; set; } + + [DataMember(Name = "id")] + public int Id { get; set; } + } +} diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs b/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs new file mode 100644 index 0000000000..33f8b61cb6 --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/RichTextPreValueController.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using Umbraco.Core.IO; +using Umbraco.Web.Editors; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; + +namespace Umbraco.Web.PropertyEditors +{ + /// + /// ApiController to provide RTE configuration with available plugins and commands from the RTE config + /// + [PluginController("UmbracoApi")] + public class RichTextPreValueController : UmbracoAuthorizedJsonController + { + private static bool _init = false; + private static Dictionary _commands = new Dictionary(); + private static Dictionary _plugins = new Dictionary(); + private static Dictionary _configOptions = new Dictionary(); + + private static string _invalidElements = ""; + private static string _validElements = ""; + + + public RichTextEditorConfiguration GetConfiguration() + { + if (!_init) + init(); + + + RichTextEditorConfiguration config = new RichTextEditorConfiguration(); + config.Plugins = _plugins.Values; + config.Commands = _commands.Values; + config.ValidElements = _validElements; + config.InvalidElements = _invalidElements; + config.CustomConfig = _configOptions; + + return config; + } + + + + private static void init() + { + // Load config + XmlDocument xd = new XmlDocument(); + xd.Load(IOHelper.MapPath(SystemFiles.TinyMceConfig)); + + foreach (XmlNode n in xd.DocumentElement.SelectNodes("//command")) + { + var alias = n.SelectSingleNode("./umbracoAlias").FirstChild.Value.ToLower(); + + bool isStyle = false; + if (n.Attributes.GetNamedItem("isStyle") != null) + isStyle = bool.Parse(n.Attributes.GetNamedItem("isStyle").Value); + + if(!_commands.ContainsKey(alias)) + _commands.Add( + alias, + new RichTextEditorCommand() + { + IsStylePicker = isStyle, + Icon = n.SelectSingleNode("./icon").FirstChild.Value, + Command = n.SelectSingleNode("./tinyMceCommand").FirstChild.Value, + Alias = n.SelectSingleNode("./umbracoAlias").FirstChild.Value.ToLower(), + UserInterface = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("userInterface").Value, + FrontendCommand = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("frontendCommand").Value, + Value = n.SelectSingleNode("./tinyMceCommand").Attributes.GetNamedItem("value").Value, + Priority = int.Parse(n.SelectSingleNode("./priority").FirstChild.Value) + } + ); + } + + + foreach (XmlNode n in xd.DocumentElement.SelectNodes("//plugin")) + { + if (!_plugins.ContainsKey(n.FirstChild.Value)) + { + bool useOnFrontend = false; + if (n.Attributes.GetNamedItem("loadOnFrontend") != null) + useOnFrontend = bool.Parse(n.Attributes.GetNamedItem("loadOnFrontend").Value); + + _plugins.Add( + n.FirstChild.Value.ToLower(), + new RichTextEditorPlugin(){ + Name = n.FirstChild.Value, + UseOnFrontend = useOnFrontend}); + } + } + + + foreach (XmlNode n in xd.DocumentElement.SelectNodes("//config")) + { + if (!_configOptions.ContainsKey(n.Attributes["key"].FirstChild.Value)) + { + var value = ""; + if (n.FirstChild != null) + value = n.FirstChild.Value; + + _configOptions.Add( + n.Attributes["key"].FirstChild.Value.ToLower(), + value); + } + } + + if (xd.DocumentElement.SelectSingleNode("./invalidElements") != null) + _invalidElements = xd.DocumentElement.SelectSingleNode("./invalidElements").FirstChild.Value; + if (xd.DocumentElement.SelectSingleNode("./validElements") != null) + { + string _val = xd.DocumentElement.SelectSingleNode("./validElements").FirstChild.Value.Replace("\r", ""); + foreach (string s in _val.Split("\n".ToCharArray())) + _validElements += "'" + s + "' + \n"; + _validElements = _validElements.Substring(0, _validElements.Length - 4); + } + + _init = true; + } + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs new file mode 100644 index 0000000000..b8a5b497db --- /dev/null +++ b/src/Umbraco.Web/PropertyEditors/RichTextPreValueEditor.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.PropertyEditors; + +namespace Umbraco.Web.PropertyEditors +{ + //need to figure out how to use this... + internal class RichTextPreValueEditor : PreValueEditor + { + public RichTextPreValueEditor() + { + //use a custom editor too + Fields.Add(new PreValueField() { + View = "views/propertyeditors/rte/rte.prevalues.html", + HideLabel= true, Key="editor"}); + } + + + } +} diff --git a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs index 77155bb470..5619743c8d 100644 --- a/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs +++ b/src/Umbraco.Web/PropertyEditors/RichTextPropertyEditor.cs @@ -17,6 +17,12 @@ namespace Umbraco.Web.PropertyEditors return new RichTextPropertyValueEditor(base.CreateValueEditor()); } + protected override PreValueEditor CreatePreValueEditor() + { + return new RichTextPreValueEditor(); + } + + /// /// A custom value editor to ensure that macro syntax is parsed when being persisted and formatted correctly for display in the editor /// diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index c11fa14d2c..8e79d6831b 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -312,11 +312,18 @@ + + + + + + + @@ -360,6 +367,8 @@ + +