diff --git a/build/UmbracoVersion.txt b/build/UmbracoVersion.txt index 07f33971ff..0294012380 100644 --- a/build/UmbracoVersion.txt +++ b/build/UmbracoVersion.txt @@ -1,3 +1,3 @@ # Usage: on line 2 put the release version, on line 3 put the version comment (example: beta) 7.6.0 -alpha055 \ No newline at end of file +alpha056 \ No newline at end of file diff --git a/src/SolutionInfo.cs b/src/SolutionInfo.cs index 40455f708c..7fc240f4f0 100644 --- a/src/SolutionInfo.cs +++ b/src/SolutionInfo.cs @@ -12,4 +12,4 @@ using System.Resources; [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyFileVersion("7.6.0")] -[assembly: AssemblyInformationalVersion("7.6.0-alpha055")] \ No newline at end of file +[assembly: AssemblyInformationalVersion("7.6.0-alpha056")] \ No newline at end of file diff --git a/src/Umbraco.Core/Configuration/UmbracoVersion.cs b/src/Umbraco.Core/Configuration/UmbracoVersion.cs index 1637763e6e..a741f65e4e 100644 --- a/src/Umbraco.Core/Configuration/UmbracoVersion.cs +++ b/src/Umbraco.Core/Configuration/UmbracoVersion.cs @@ -24,7 +24,7 @@ namespace Umbraco.Core.Configuration /// Gets the version comment (like beta or RC). /// /// The version comment. - public static string CurrentComment { get { return "alpha055"; } } + public static string CurrentComment { get { return "alpha056"; } } // Get the version of the umbraco.dll by looking at a class in that dll // Had to do it like this due to medium trust issues, see: http://haacked.com/archive/2010/11/04/assembly-location-and-medium-trust.aspx diff --git a/src/Umbraco.Core/Constants-ObjectTypes.cs b/src/Umbraco.Core/Constants-ObjectTypes.cs index 8edc552012..7bba427f12 100644 --- a/src/Umbraco.Core/Constants-ObjectTypes.cs +++ b/src/Umbraco.Core/Constants-ObjectTypes.cs @@ -162,6 +162,36 @@ namespace Umbraco.Core /// Guid for a relation type. /// public static readonly Guid RelationTypeGuid = new Guid(RelationType); + + /// + /// Guid for a Forms Form. + /// + public const string FormsForm = "F5A9F787-6593-46F0-B8FF-BFD9BCA9F6BB"; + + /// + /// Guid for a Forms Form. + /// + public static readonly Guid FormsFormGuid = new Guid(FormsForm); + + /// + /// Guid for a Forms Workflow. + /// + public const string FormsWorkflow = "42D7BF9B-A362-4FEE-B45A-674D5C064B70"; + + /// + /// Guid for a Forms Workflow. + /// + public static readonly Guid FormsWorkflowGuid = new Guid(FormsWorkflow); + + /// + /// Guid for a Forms Record. + /// + public const string FormsRecord = "CFED6CE4-9359-443E-9977-9956FEB1D867"; + + /// + /// Guid for a Forms Record. + /// + public static readonly Guid FormsRecordGuid = new Guid(FormsRecord); } } } \ No newline at end of file diff --git a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs index 162c66e174..5e729a7d05 100644 --- a/src/Umbraco.Core/Models/UmbracoObjectTypes.cs +++ b/src/Umbraco.Core/Models/UmbracoObjectTypes.cs @@ -157,6 +157,27 @@ namespace Umbraco.Core.Models [UmbracoObjectType(Constants.ObjectTypes.RelationType)] [FriendlyName("Relation Type")] [UmbracoUdiType(Constants.UdiEntityType.RelationType)] - RelationType + RelationType, + + /// + /// Forms Form + /// + [UmbracoObjectType(Constants.ObjectTypes.FormsForm)] + [FriendlyName("Form")] + FormsForm, + + /// + /// Forms Workflow + /// + [UmbracoObjectType(Constants.ObjectTypes.FormsWorkflow)] + [FriendlyName("Workflow")] + FormsWorkflow, + + /// + /// Forms Record + /// + [UmbracoObjectType(Constants.ObjectTypes.FormsRecord)] + [FriendlyName("Record")] + FormsRecord } } \ No newline at end of file diff --git a/src/Umbraco.Core/UdiEntityType.cs b/src/Umbraco.Core/UdiEntityType.cs index d89376f3d9..3ab32a6eac 100644 --- a/src/Umbraco.Core/UdiEntityType.cs +++ b/src/Umbraco.Core/UdiEntityType.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core { - + public static partial class Constants { /// @@ -55,6 +55,11 @@ namespace Umbraco.Core [UdiType(UdiType.GuidUdi)] public const string RelationType = "relation-type"; + + // forms + public const string FormsForm = "forms-form"; + public const string FormsWorkflow = "forms-workflow"; + public const string FormsRecord = "forms-record"; // string entity types @@ -108,6 +113,12 @@ namespace Umbraco.Core return Stylesheet; case UmbracoObjectTypes.RelationType: return RelationType; + case UmbracoObjectTypes.FormsForm: + return FormsForm; + case UmbracoObjectTypes.FormsWorkflow: + return FormsWorkflow; + case UmbracoObjectTypes.FormsRecord: + return FormsRecord; } throw new NotSupportedException(string.Format("UmbracoObjectType \"{0}\" does not have a matching EntityType.", umbracoObjectType)); } @@ -144,6 +155,12 @@ namespace Umbraco.Core return UmbracoObjectTypes.Stylesheet; case RelationType: return UmbracoObjectTypes.RelationType; + case FormsForm: + return UmbracoObjectTypes.FormsForm; + case FormsWorkflow: + return UmbracoObjectTypes.FormsWorkflow; + case FormsRecord: + return UmbracoObjectTypes.FormsRecord; } throw new NotSupportedException( string.Format("EntityType \"{0}\" does not have a matching UmbracoObjectType.", entityType)); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js index 4655b6fa3b..b6e35d9d7b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/codefile.resource.js @@ -127,13 +127,13 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { * * *
-         * codefileResource.deleteByPath('partialView', 'Grid%2fEditors%2fBase.cshtml')
+         * codefileResource.deleteByPath('partialViews', 'Grid%2fEditors%2fBase.cshtml')
          *    .then(function() {
          *        alert('its gone!');
          *    });
          * 
* - * @param {type} the type of script (partialView, partialViewMacro, script) + * @param {type} the type of script (partialViews, partialViewMacros, scripts) * @param {virtualpath} the virtual path of the script * @returns {Promise} resourcePromise object. * @@ -145,7 +145,7 @@ function codefileResource($q, $http, umbDataFormatter, umbRequestHelper) { "codeFileApiBaseUrl", "Delete", [{ type: type }, { virtualPath: virtualpath}])), - "Failed to delete item " + id); + "Failed to delete item: " + virtualpath); }, /** diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js index 0649277c54..6c83f69f84 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/membertype.resource.js @@ -19,14 +19,14 @@ function memberTypeResource($q, $http, umbRequestHelper, umbDataFormatter) { _.each(filterContentTypes, function (item) { query += "filterContentTypes=" + item + "&"; }); - // if filterContentTypes array is empty we need a empty variable in the querystring otherwise the service returns a error + // if filterContentTypes array is empty we need a empty variable in the querystring otherwise the service returns a error if (filterContentTypes.length === 0) { query += "filterContentTypes=&"; } _.each(filterPropertyTypes, function (item) { query += "filterPropertyTypes=" + item + "&"; }); - // if filterPropertyTypes array is empty we need a empty variable in the querystring otherwise the service returns a error + // if filterPropertyTypes array is empty we need a empty variable in the querystring otherwise the service returns a error if (filterPropertyTypes.length === 0) { query += "filterPropertyTypes=&"; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js index c759169752..86137888fa 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/user.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/user.service.js @@ -204,7 +204,7 @@ angular.module('umbraco.services') //when it's successful, return the user data setCurrentUser(data); - var result = { user: data, authenticated: true, lastUserId: lastUserId }; + var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "credentials" }; //broadcast a global event eventsService.emit("app.authenticated", result); @@ -232,7 +232,7 @@ angular.module('umbraco.services') authResource.getCurrentUser() .then(function (data) { - var result = { user: data, authenticated: true, lastUserId: lastUserId }; + var result = { user: data, authenticated: true, lastUserId: lastUserId, loginType: "implicit" }; //TODO: This is a mega backwards compatibility hack... These variables SHOULD NOT exist in the server variables // since they are not supposed to be dynamic but I accidentally added them there in 7.1.5 IIRC so some people might diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 74eb872aa2..1504abf7c1 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -8,7 +8,7 @@ * The main application controller * */ -function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale) { +function MainController($scope, $rootScope, $location, $routeParams, $timeout, $http, $log, appState, treeService, notificationsService, userService, navigationService, historyService, updateChecker, assetsService, eventsService, umbRequestHelper, tmhDynamicLocale, localStorageService) { //the null is important because we do an explicit bool check on this in the view //the avatar is by default the umbraco logo @@ -81,6 +81,14 @@ function MainController($scope, $rootScope, $location, $routeParams, $timeout, $ $location.path("/").search(""); historyService.removeAll(); treeService.clearCache(); + + //if the user changed, clearout local storage too - could contain sensitive data + localStorageService.clearAll(); + } + + //if this is a new login (i.e. the user entered credentials), then clear out local storage - could contain sensitive data + if (data.loginType === "credentials") { + localStorageService.clearAll(); } //Load locale file diff --git a/src/Umbraco.Web.UI.Client/src/init.js b/src/Umbraco.Web.UI.Client/src/init.js index 3233974cee..3300c47ab9 100644 --- a/src/Umbraco.Web.UI.Client/src/init.js +++ b/src/Umbraco.Web.UI.Client/src/init.js @@ -13,11 +13,7 @@ app.run(['userService', '$log', '$rootScope', '$location', 'navigationService', /** Listens for authentication and checks if our required assets are loaded, if/once they are we'll broadcast a ready event */ eventsService.on("app.authenticated", function(evt, data) { - - //Removes all stored LocalStorage browser items - that may contain sensitive data - //So if a machine or computer is shared and a new user logs in, we clear out the previous persons localStorage items - localStorageService.clearAll(); - + assetsService._loadInitAssets().then(function() { appState.setGlobalState("isReady", true); diff --git a/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.controller.js index 061ca7eb2c..c0cd0c8598 100644 --- a/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/membertypes/delete.controller.js @@ -1,10 +1,10 @@ /** * @ngdoc controller - * @name Umbraco.Editors.DocumentType.DeleteController + * @name Umbraco.Editors.MemberTypes.DeleteController * @function * * @description - * The controller for deleting content + * The controller for deleting member types */ function MemberTypesDeleteController($scope, memberTypeResource, treeService, navigationService) { diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.controller.js new file mode 100644 index 0000000000..9789467827 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.controller.js @@ -0,0 +1,34 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.PartialViewMacros.DeleteController + * @function + * + * @description + * The controller for deleting partial view macros + */ +function PartialViewMacrosDeleteController($scope, codefileResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name; + + codefileResource.deleteByPath('partialViewMacros', virtualPath) + .then(function() { + $scope.currentNode.loading = false; + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.PartialViewMacros.DeleteController", PartialViewMacrosDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html new file mode 100644 index 0000000000..6b66a48821 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviewmacros/delete.html @@ -0,0 +1,12 @@ +
+
+ +

+ Are you sure you want to delete {{currentNode.name}} ? +

+ + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js new file mode 100644 index 0000000000..d6645df558 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.controller.js @@ -0,0 +1,34 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.PartialViews.DeleteController + * @function + * + * @description + * The controller for deleting partial views + */ +function PartialViewsDeleteController($scope, codefileResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name; + + codefileResource.deleteByPath('partialViews', virtualPath) + .then(function() { + $scope.currentNode.loading = false; + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.PartialViews.DeleteController", PartialViewsDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html new file mode 100644 index 0000000000..0f75e8514e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/partialviews/delete.html @@ -0,0 +1,12 @@ +
+
+ +

+ Are you sure you want to delete {{currentNode.name}} ? +

+ + + + +
+
diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.controller.js new file mode 100644 index 0000000000..f23fa9367b --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.controller.js @@ -0,0 +1,34 @@ +/** + * @ngdoc controller + * @name Umbraco.Editors.Scripts.DeleteController + * @function + * + * @description + * The controller for deleting scripts + */ +function ScriptsDeleteController($scope, codefileResource, treeService, navigationService) { + + $scope.performDelete = function() { + + //mark it for deletion (used in the UI) + $scope.currentNode.loading = true; + + var virtualPath = $scope.currentNode.parentId + $scope.currentNode.name; + + codefileResource.deleteByPath('scripts', virtualPath) + .then(function() { + $scope.currentNode.loading = false; + //get the root node before we remove it + var rootNode = treeService.getTreeRoot($scope.currentNode); + //TODO: Need to sync tree, etc... + treeService.removeNode($scope.currentNode); + navigationService.hideMenu(); + }); + }; + + $scope.cancel = function() { + navigationService.hideDialog(); + }; +} + +angular.module("umbraco").controller("Umbraco.Editors.Scripts.DeleteController", ScriptsDeleteController); diff --git a/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html new file mode 100644 index 0000000000..187db7f443 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/scripts/delete.html @@ -0,0 +1,12 @@ +
+
+ +

+ Are you sure you want to delete {{currentNode.name}} ? +

+ + + + +
+
diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 58494369f5..ed0ac95a2b 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -4,11 +4,9 @@ using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Http.Formatting; using System.Text; using System.Web.Http; using System.Web.Http.ModelBinding; -using System.Web.Http.ModelBinding.Binders; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; @@ -17,24 +15,15 @@ using Umbraco.Core.Models.Membership; using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Publishing; using Umbraco.Core.Services; -using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Models.Mapping; using Umbraco.Web.Mvc; -using Umbraco.Web.Security; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Binders; using Umbraco.Web.WebApi.Filters; -using umbraco; -using Umbraco.Core.Models; -using Umbraco.Core.Dynamics; -using umbraco.BusinessLogic.Actions; using umbraco.cms.businesslogic.web; using umbraco.presentation.preview; -using Umbraco.Core.PropertyEditors; -using Umbraco.Web.UI; using Constants = Umbraco.Core.Constants; -using Notification = Umbraco.Web.Models.ContentEditing.Notification; namespace Umbraco.Web.Editors { diff --git a/src/Umbraco.Web/Editors/MacroController.cs b/src/Umbraco.Web/Editors/MacroController.cs index aed6cec602..b0b2cb4bf2 100644 --- a/src/Umbraco.Web/Editors/MacroController.cs +++ b/src/Umbraco.Web/Editors/MacroController.cs @@ -4,21 +4,26 @@ using System.Net; using System.Net.Http; using System.Text; using System.Web.Http; +using System.Web.SessionState; using AutoMapper; using Umbraco.Web.Models.ContentEditing; using Umbraco.Web.Mvc; using umbraco; +using Umbraco.Core; namespace Umbraco.Web.Editors { /// /// API controller to deal with Macro data /// + /// + /// Note that this implements IRequiresSessionState which will enable HttpContext.Session - generally speaking we don't normally + /// enable this for webapi controllers, however since this controller is used to render macro content and macros can access + /// Session, we don't want it to throw null reference exceptions. + /// [PluginController("UmbracoApi")] - public class MacroController : UmbracoAuthorizedJsonController + public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState { - - /// /// Gets the macro parameters to be filled in for a particular macro /// @@ -124,6 +129,6 @@ namespace Umbraco.Web.Editors "text/html"); return result; } - + } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs index afed4bfb20..6f23ecee0f 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentModelMapper.cs @@ -181,16 +181,10 @@ namespace Umbraco.Web.Models.Mapping { {"items", templateItemConfig} } - }, - new ContentPropertyDisplay - { - Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("content/urls"), - Value = string.Join(",", display.Urls), - View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor } }; + TabsAndPropertiesResolver.MapGenericProperties(content, display, localizedText, properties.ToArray(), genericProperties => { @@ -222,6 +216,15 @@ namespace Umbraco.Web.Models.Mapping //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor docTypeProperty.View = "urllist"; } + + // inject 'Link to document' as the first generic property + genericProperties.Insert(0, new ContentPropertyDisplay + { + Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("content/urls"), + Value = string.Join(",", display.Urls), + View = "urllist" //TODO: Hard coding this because the templatepicker doesn't necessarily need to be a resolvable (real) property editor + }); }); } diff --git a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs index eddb4a582e..453e1567ab 100644 --- a/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs +++ b/src/Umbraco.Web/Models/Mapping/MediaModelMapper.cs @@ -69,8 +69,8 @@ namespace Umbraco.Web.Models.Mapping private static void AfterMap(IMedia media, MediaItemDisplay display, IDataTypeService dataTypeService, ILocalizedTextService localizedText, ILogger logger) { - // Adapted from ContentModelMapper - //map the IsChildOfListView (this is actually if it is a descendant of a list view!) + // Adapted from ContentModelMapper + //map the IsChildOfListView (this is actually if it is a descendant of a list view!) //TODO: Fix this shorthand .Ancestors() lookup, at least have an overload to use the current if (media.HasIdentity) { @@ -95,7 +95,7 @@ namespace Umbraco.Web.Models.Mapping display.IsChildOfListView = ancesctorListView != null; } } - + //map the tree node url if (HttpContext.Current != null) { @@ -103,12 +103,12 @@ namespace Umbraco.Web.Models.Mapping var url = urlHelper.GetUmbracoApiService(controller => controller.GetTreeNode(display.Id.ToString(), null)); display.TreeNodeUrl = url; } - + if (media.ContentType.IsContainer) { TabsAndPropertiesResolver.AddListView(display, "media", dataTypeService, localizedText); } - + var genericProperties = new List { new ContentPropertyDisplay @@ -120,20 +120,6 @@ namespace Umbraco.Web.Models.Mapping } }; - var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger); - - if (links.Any()) - { - var link = new ContentPropertyDisplay - { - Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), - Label = localizedText.Localize("media/urls"), - Value = string.Join(",", links), - View = "urllist" - }; - genericProperties.Add(link); - } - TabsAndPropertiesResolver.MapGenericProperties(media, display, localizedText, genericProperties, properties => { if (HttpContext.Current != null && UmbracoContext.Current != null && UmbracoContext.Current.Security.CurrentUser != null @@ -155,6 +141,20 @@ namespace Umbraco.Web.Models.Mapping }; docTypeProperty.View = "urllist"; } + + // inject 'Link to media' as the first generic property + var links = media.GetUrls(UmbracoConfig.For.UmbracoSettings().Content, logger); + if (links.Any()) + { + var link = new ContentPropertyDisplay + { + Alias = string.Format("{0}urls", Constants.PropertyEditors.InternalGenericPropertiesPrefix), + Label = localizedText.Localize("media/urls"), + Value = string.Join(",", links), + View = "urllist" + }; + properties.Insert(0, link); + } }); } } diff --git a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs index 982286c24b..f1c27ffb96 100644 --- a/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs +++ b/src/Umbraco.Web/Mvc/AreaRegistrationExtensions.cs @@ -3,8 +3,10 @@ using System.Collections.Generic; using System.Web.Http; using System.Web.Mvc; using System.Web.Routing; +using System.Web.SessionState; using Umbraco.Core; using Umbraco.Core.Configuration; +using Umbraco.Web.WebApi; namespace Umbraco.Web.Mvc { @@ -93,6 +95,13 @@ namespace Umbraco.Web.Mvc } //look in this namespace to create the controller controllerPluginRoute.DataTokens.Add("Namespaces", new[] {controllerType.Namespace}); + + //Special case! Check if the controller type implements IRequiresSessionState and if so use our + //custom webapi session handler + if (typeof(IRequiresSessionState).IsAssignableFrom(controllerType)) + { + controllerPluginRoute.RouteHandler = new SessionHttpControllerRouteHandler(); + } } //Don't look anywhere else except this namespace! @@ -100,9 +109,9 @@ namespace Umbraco.Web.Mvc //constraints: only match controllers ending with 'controllerSuffixName' and only match this controller's ID for this route if (controllerSuffixName.IsNullOrWhiteSpace() == false) - { - controllerPluginRoute.Constraints = new RouteValueDictionary( - new Dictionary + { + controllerPluginRoute.Constraints = new RouteValueDictionary( + new Dictionary { {"controller", @"(\w+)" + controllerSuffixName} }); diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index ca602cdbd8..9c43953d9c 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -972,6 +972,7 @@ + diff --git a/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs b/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs new file mode 100644 index 0000000000..d4d462c5f8 --- /dev/null +++ b/src/Umbraco.Web/WebApi/SessionHttpControllerRouteHandler.cs @@ -0,0 +1,31 @@ +using System.Web; +using System.Web.Http.WebHost; +using System.Web.Routing; +using System.Web.SessionState; + +namespace Umbraco.Web.WebApi +{ + /// + /// A custom WebApi route handler that enables session on the HttpContext - use with caution! + /// + /// + /// WebApi controllers (and REST in general) shouldn't have session state enabled since it's stateless, + /// enabling session state puts additional locks on requests so only use this when absolutley needed + /// + internal class SessionHttpControllerRouteHandler : HttpControllerRouteHandler + { + protected override IHttpHandler GetHttpHandler(RequestContext requestContext) + { + return new SessionHttpControllerHandler(requestContext.RouteData); + } + + /// + /// A custom WebApi handler that enables session on the HttpContext + /// + private class SessionHttpControllerHandler : HttpControllerHandler, IRequiresSessionState + { + public SessionHttpControllerHandler(RouteData routeData) : base(routeData) + { } + } + } +} \ No newline at end of file