diff --git a/.gitignore b/.gitignore
index e2c5822dd1..027bb5d1fe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -104,4 +104,5 @@ src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
-src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
\ No newline at end of file
+src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
+src/Umbraco.Web.UI/umbraco/plugins/*
diff --git a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js
index 4e080d02e6..f8a8f72a2e 100644
--- a/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js
+++ b/src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js
@@ -204,7 +204,7 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
//get our angular navigation service
var injector = getRootInjector();
- var dialogService = injector.get("dialogService");
+ var dialogService = injector.get("dialogService");
var self = this;
@@ -219,12 +219,14 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
//add the callback to the jquery data for the modal so we can call it on close to support the legacy way dialogs worked.
dialog.element.data("modalCb", onCloseCallback);
//add the close triggers
- for (var i = 0; i < closeTriggers.length; i++) {
- var e = dialog.find(closeTriggers[i]);
- if (e.length > 0) {
- e.click(function() {
- self.closeModalWindow();
- });
+ if (angular.isArray(closeTriggers)) {
+ for (var i = 0; i < closeTriggers.length; i++) {
+ var e = dialog.find(closeTriggers[i]);
+ if (e.length > 0) {
+ e.click(function () {
+ self.closeModalWindow();
+ });
+ }
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js
index c1970bdf92..7a45879d70 100644
--- a/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js
+++ b/src/Umbraco.Web.UI.Client/src/common/directives/umbcontextmenu.directive.js
@@ -15,25 +15,31 @@ angular.module("umbraco.directives")
//we'll try to get the jsAction from the injector
var menuAction = action.metaData["jsAction"].split('.');
if (menuAction.length !== 2) {
- throw "The jsAction assigned to a menu action must have two parts delimited by a '.' ";
- }
- var service = $injector.get(menuAction[0]);
- if (!service) {
- throw "The angular service " + menuAction[0] + " could not be found";
- }
+ //if it is not two parts long then this most likely means that it's a legacy action
+ var js = action.metaData["jsAction"];
+ //there's not really a different way to acheive this except for eval
+ eval(js);
- var method = service[menuAction[1]];
-
- if (!method) {
- throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found";
}
+ else {
+ var service = $injector.get(menuAction[0]);
+ if (!service) {
+ throw "The angular service " + menuAction[0] + " could not be found";
+ }
- method.apply(this, [{
- treeNode: currentNode,
- action: action,
- section: currentSection
- }]);
+ var method = service[menuAction[1]];
+
+ if (!method) {
+ throw "The method " + menuAction[1] + " on the angular service " + menuAction[0] + " could not be found";
+ }
+
+ method.apply(this, [{
+ treeNode: currentNode,
+ action: action,
+ section: currentSection
+ }]);
+ }
}
else {
//by default we launch the dialog
diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs
index 1cae76c3c9..98909ec558 100644
--- a/src/Umbraco.Web/Editors/BackOfficeController.cs
+++ b/src/Umbraco.Web/Editors/BackOfficeController.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
+using System.Text.RegularExpressions;
using System.Web.Mvc;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
@@ -197,10 +198,75 @@ namespace Umbraco.Web.Editors
var javascript = new StringBuilder();
javascript.AppendLine(LegacyTreeJavascript.GetLegacyTreeJavascript());
javascript.AppendLine(LegacyTreeJavascript.GetLegacyIActionJavascript());
+ //add all of the menu blocks
+ foreach (var file in GetLegacyActionJs(LegacyJsActionType.JsBlock))
+ {
+ javascript.AppendLine(file);
+ }
return JavaScript(javascript.ToString());
}
+ ///
+ /// Renders out all JavaScript blocks that have bee declared in IActions
+ ///
+ private IEnumerable GetLegacyActionJs(LegacyJsActionType type)
+ {
+ var blockList = new List();
+ var urlList = new List();
+ foreach (var jsFile in global::umbraco.BusinessLogic.Actions.Action.GetJavaScriptFileReferences())
+ {
+ //validate that this is a url, if it is not, we'll assume that it is a text block and render it as a text
+ //block instead.
+ var isValid = true;
+
+ if (Uri.IsWellFormedUriString(jsFile, UriKind.RelativeOrAbsolute))
+ {
+ //ok it validates, but so does alert('hello'); ! so we need to do more checks
+
+ //here are the valid chars in a url without escaping
+ if (Regex.IsMatch(jsFile, @"[^a-zA-Z0-9-._~:/?#\[\]@!$&'\(\)*\+,%;=]"))
+ isValid = false;
+
+ //we'll have to be smarter and just check for certain js patterns now too!
+ var jsPatterns = new string[] {@"\+\s*\=", @"\);", @"function\s*\(", @"!=", @"=="};
+ if (jsPatterns.Any(p => Regex.IsMatch(jsFile, p)))
+ {
+ isValid = false;
+ }
+ if (isValid)
+ {
+ //it is a valid URL add to Url list
+ urlList.Add(jsFile);
+ }
+ }
+ else
+ {
+ isValid = false;
+ }
+
+ if (isValid == false)
+ {
+ //it isn't a valid URL, must be a js block
+ blockList.Add(jsFile);
+ }
+ }
+
+ switch (type)
+ {
+ case LegacyJsActionType.JsBlock:
+ return blockList;
+ case LegacyJsActionType.JsUrl:
+ return urlList;
+ }
+
+ return blockList;
+ }
+ private enum LegacyJsActionType
+ {
+ JsBlock,
+ JsUrl
+ }
}
}
diff --git a/src/Umbraco.Web/Models/Trees/MenuItemExtensions.cs b/src/Umbraco.Web/Models/Trees/MenuItemExtensions.cs
index fc911ef8c3..d7784cfb47 100644
--- a/src/Umbraco.Web/Models/Trees/MenuItemExtensions.cs
+++ b/src/Umbraco.Web/Models/Trees/MenuItemExtensions.cs
@@ -28,6 +28,21 @@ namespace Umbraco.Web.Models.Trees
///
internal const string ActionViewKey = "actionView";
+ ///
+ /// Used to specify the js method to execute for the menu item
+ ///
+ internal const string JsActionKey = "jsAction";
+
+ ///
+ /// Adds the required meta data to the menu item so that angular knows to attempt to call the Js method.
+ ///
+ ///
+ ///
+ public static void LaunchLegacyJs(this MenuItem menuItem, string jsToExecute)
+ {
+ menuItem.SetJsAction(jsToExecute);
+ }
+
///
/// Sets the menu item to display a dialog based on an angular view path
///
@@ -52,6 +67,11 @@ namespace Umbraco.Web.Models.Trees
menuItem.SetActionUrl(url);
}
+ private static void SetJsAction(this MenuItem menuItem, string jsToExecute)
+ {
+ menuItem.AdditionalData[JsActionKey] = jsToExecute;
+ }
+
///
/// Puts a dialog title into the meta data to be displayed on the dialog of the menu item (if there is one)
/// instead of the menu name
diff --git a/src/Umbraco.Web/Models/Trees/MenuItemList.cs b/src/Umbraco.Web/Models/Trees/MenuItemList.cs
index 490e964eb8..0deedc34b1 100644
--- a/src/Umbraco.Web/Models/Trees/MenuItemList.cs
+++ b/src/Umbraco.Web/Models/Trees/MenuItemList.cs
@@ -169,11 +169,11 @@ namespace Umbraco.Web.Models.Trees
if (attribute.MethodName.IsNullOrWhiteSpace())
{
//if no method name is supplied we will assume that the menu action is the type name of the current menu class
- menuItem.AdditionalData.Add("jsAction", string.Format("{0}.{1}", attribute.ServiceName, this.GetType().Name));
+ menuItem.AdditionalData.Add(MenuItemExtensions.JsActionKey, string.Format("{0}.{1}", attribute.ServiceName, this.GetType().Name));
}
else
{
- menuItem.AdditionalData.Add("jsAction", string.Format("{0}.{1}", attribute.ServiceName, attribute.MethodName));
+ menuItem.AdditionalData.Add(MenuItemExtensions.JsActionKey, string.Format("{0}.{1}", attribute.ServiceName, attribute.MethodName));
}
}
}
diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
index 5886dbcbd6..af55925055 100644
--- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
+++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs
@@ -157,9 +157,10 @@ namespace Umbraco.Web.Trees
.Try(GetUrlAndTitleFromLegacyAction(currentAction, xmlTreeNode.NodeID, xmlTreeNode.NodeType, xmlTreeNode.Text, currentSection),
action => menuItem.LaunchDialogUrl(action.Url, action.DialogTitle))
.OnFailure(() => GetLegacyConfirmView(currentAction, currentSection),
- view => menuItem.LaunchDialogView(
- view,
- ui.GetText("defaultdialogs", "confirmdelete") + " '" + xmlTreeNode.Text + "' ?"));
+ view => menuItem.LaunchDialogView(
+ view,
+ ui.GetText("defaultdialogs", "confirmdelete") + " '" + xmlTreeNode.Text + "' ?"))
+ .OnFailure(() => Attempt.Succeed(true), b => menuItem.LaunchLegacyJs(menuItem.Action.JsFunctionName));
numAdded++;
}