diff --git a/src/Umbraco.Core/Macros/MacroTagParser.cs b/src/Umbraco.Core/Macros/MacroTagParser.cs index 3945454087..9ba529f7cd 100644 --- a/src/Umbraco.Core/Macros/MacroTagParser.cs +++ b/src/Umbraco.Core/Macros/MacroTagParser.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Macros internal class MacroTagParser { private static readonly Regex MacroRteContent = new Regex(@"(
asdfasdf
", result); } + [Test] + public void Format_RTE_Data_For_Editor_Closing_Tag() + { + var content = @"asdfasdf
+asdfsadf
+?UMBRACO_MACRO> +asdfasdf
"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionaryasdfasdf
+asdfsadf
+asdfasdf
", result); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params() + { + var content = @"asdfasdf
+asdfsadf
+ +asdfasdf
"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionaryasdfasdf
+asdfsadf
+asdfasdf
", result); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag() + { + var content = @"asdfasdf
+asdfsadf
+?UMBRACO_MACRO> +asdfasdf
"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionaryasdfasdf
+asdfsadf
+asdfasdf
", result); + } + + [Test] + public void Format_RTE_Data_For_Editor_With_Params_Closing_Tag_And_Content() + { + var content = @"asdfasdf
+asdfsadf
+
?UMBRACO_MACRO>
+asdfasdf
"; + var result = MacroTagParser.FormatRichTextPersistedDataForEditor(content, new Dictionaryasdfasdf
+asdfsadf
+asdfasdf
", result); + } + [Test] public void Format_RTE_Data_For_Persistence() { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js index ca80d4aa04..10101f0a98 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/macro.service.js @@ -10,6 +10,31 @@ function macroService() { return { + /** parses the special macro syntax like and returns an object with the macro alias and it's parameters */ + parseMacroSyntax: function (syntax) { + + var expression = /(<\?UMBRACO_MACRO macroAlias=["'](\w+?)["'].+?)(\/>|>.*?<\/\?UMBRACO_MACRO>)/im; + var match = expression.exec(syntax); + if (!match || match.length < 3) { + return null; + } + var alias = match[2]; + + //this will leave us with just the parameters + var paramsChunk = match[1].trim().replace(new RegExp("UMBRACO_MACRO macroAlias=[\"']" + alias + "[\"']"), "").trim(); + + var paramExpression = new RegExp("(\\w+?)=['\"](.*?)['\"]", "g"); + var paramMatch; + var returnVal = { + alias: alias, + params: [] + }; + while (paramMatch = paramExpression.exec(paramsChunk)) { + returnVal.params.push({ alias: paramMatch[1], value: paramMatch[2] }); + } + return returnVal; + }, + /** * @ngdoc function * @name generateWebFormsSyntax @@ -25,7 +50,7 @@ function macroService() { // - var macroString = '"; + macroString += "/>"; return macroString; }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js index 5866b62fb9..3387b44b91 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tinymce.service.js @@ -6,7 +6,7 @@ * @description * A service containing all logic for all of the Umbraco TinyMCE plugins */ -function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeout, macroResource) { +function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeout, macroResource, macroService) { return { /** @@ -143,6 +143,25 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou }); + /** + * Because the macro gets wrapped in a P tag because of the way 'enter' works, this + * method will return the macro element if not wrapped in a p, or the p if the macro + * element is the only one inside of it even if we are deep inside an element inside the macro + */ + function getRealMacroElem(element) { + var e = $(element).closest(".umb-macro-holder"); + if (e.length > 0) { + if (e.get(0).parentNode.nodeName === "P") { + //now check if we're the only element + if (element.parentNode.childNodes.length === 1) { + return e.get(0).parentNode; + } + } + return e.get(0); + } + return null; + } + /** Adds the button instance */ editor.addButton('umbmacro', { icon: 'custom icon-settings-alt', @@ -152,25 +171,6 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou var ctrl = this; var isOnMacroElement = false; - /** - * Because the macro gets wrapped in a P tag because of the way 'enter' works, this - * method will return the macro element if not wrapped in a p, or the p if the macro - * element is the only one inside of it even if we are deep inside an element inside the macro - */ - function getRealMacroElem(element) { - var e = $(element).closest(".umb-macro-holder"); - if (e.length > 0) { - if (e.get(0).parentNode.nodeName === "P") { - //now check if we're the only element - if (element.parentNode.childNodes.length === 1) { - return e.get(0).parentNode; - } - } - return e.get(0); - } - return null; - } - /** * Add a node change handler, test if we're editing a macro and select the whole thing, then set our isOnMacroElement flag. * If we change the selection inside this method, then we end up in an infinite loop, so we have to remove ourselves @@ -310,8 +310,32 @@ function tinyMceService(dialogService, $log, imageHelper, assetsService, $timeou /** The insert macro button click event handler */ onclick: function () { + var dialogData; + + //when we click we could have a macro already selected and in that case we'll want to edit the current parameters + //so we'll need to extract them and submit them to the dialog. + var macroElement = editor.selection.getNode(); + macroElement = getRealMacroElem(macroElement); + if (macroElement) { + //we have a macro selected so we'll need to parse it's alias and parameters + var contents = $(macroElement).contents(); + var comment = _.find(contents, function(item) { + return item.nodeType === 8; + }); + if (!comment) { + throw "Cannot parse the current macro, the syntax in the editor is invalid"; + } + var syntax = comment.textContent.trim(); + var parsed = macroService.parseMacroSyntax(syntax); + dialogData = { + macroData: parsed + }; + + } + dialogService.macroPicker({ scope: $scope, + dialogData : dialogData, callback: function(data) { //put the macro syntax in comments, we will parse this out on the server side to be used diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js index dfd0a6e556..99e2fca98d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/insertmacro.controller.js @@ -23,6 +23,19 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi } else { $scope.wizardStep = "paramSelect"; $scope.macroParams = data; + + //fill in the data if we are editing this macro + if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.params) { + _.each($scope.dialogData.macroData.params, function(p) { + var prop = _.find($scope.macroParams, function (item) { + return item.alias == p.alias; + }); + if (prop) { + prop.value = p.value; + } + }); + + } } }); } @@ -82,26 +95,35 @@ function InsertMacroController($scope, entityResource, macroResource, umbPropEdi }; + //here we check to see if we've been passed a selected macro and if so we'll set the + //editor to start with parameter editing + if ($scope.dialogData && $scope.dialogData.macroData) { + $scope.wizardStep = "paramSelect"; + } + //get the macro list entityResource.getAll("Macro") - .then(function (data) { + .then(function (data) { $scope.macros = data; - + //check if there's a pre-selected macro and if it exists - if ($scope.dialogData.selectedAlias) { - var found = _.find(data, function(item) { - return item.alias === $scope.dialogData.selectedAlias; + if ($scope.dialogData && $scope.dialogData.macroData && $scope.dialogData.macroData.alias) { + var found = _.find(data, function (item) { + return item.alias === $scope.dialogData.macroData.alias; }); if (found) { //select the macro and go to next screen $scope.selectedMacro = found.id; editParams(); + return; } - } + //we don't have a pre-selected macro so ensure the correct step is set + $scope.wizardStep = "macroSelect"; }); + } angular.module("umbraco").controller("Umbraco.Dialogs.InsertMacroController", InsertMacroController); diff --git a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js index 1348c1e488..4b578c2418 100644 --- a/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js +++ b/src/Umbraco.Web.UI.Client/test/unit/common/services/macro-service.spec.js @@ -7,6 +7,40 @@ describe('macro service tests', function () { macroService = $injector.get('macroService'); })); + describe('generates macro syntax', function() { + + it('can parse syntax for macros', function () { + + var result = macroService.parseMacroSyntax(""); + + expect(result).not.toBeNull(); + expect(result.alias).toBe("Map"); + expect(result.params.length).toBe(2); + expect(result.params[0].alias).toBe("test1"); + expect(result.params[0].value).toBe("asdf"); + expect(result.params[1].alias).toBe("test2"); + expect(result.params[1].value).toBe("hello"); + + + }); + + it('can parse syntax for macros with body', function () { + + var result = macroService.parseMacroSyntax("
?UMBRACO_MACRO>");
+
+ expect(result).not.toBeNull();
+ expect(result.alias).toBe("Map");
+ expect(result.params.length).toBe(2);
+ expect(result.params[0].alias).toBe("test1");
+ expect(result.params[0].value).toBe("asdf");
+ expect(result.params[1].alias).toBe("test2");
+ expect(result.params[1].value).toBe("hello");
+
+
+ });
+
+ });
+
describe('generates macro syntax', function () {
it('can generate syntax for macros', function () {
@@ -21,7 +55,7 @@ describe('macro service tests', function () {
});
expect(syntax).
- toBe("");
+ toBe("");
});