diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs index 656251f3a0..7f90e55e57 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Configuration; namespace Umbraco.Core.Configuration.UmbracoSettings @@ -217,6 +218,19 @@ namespace Umbraco.Core.Configuration.UmbracoSettings } } + [Obsolete("This is here so that if this config element exists we won't get a YSOD, it is not used whatsoever and will be removed in future versions")] + [ConfigurationProperty("DocumentTypeIconList")] + internal InnerTextConfigurationElement DocumentTypeIconList + { + get + { + return new OptionalInnerTextConfigurationElement( + (InnerTextConfigurationElement)this["DocumentTypeIconList"], + //set the default + IconPickerBehaviour.HideFileDuplicates); + } + } + [ConfigurationProperty("disallowedUploadFiles")] internal CommaDelimitedConfigurationElement DisallowedUploadFiles { @@ -380,7 +394,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings { get { return MacroErrors; } } - + IEnumerable IContentSection.DisallowedUploadFiles { get { return DisallowedUploadFiles; } diff --git a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs index 33a8a8584b..d3160a216b 100644 --- a/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs +++ b/src/Umbraco.Core/Configuration/UmbracoSettings/SecurityElement.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings return new OptionalInnerTextConfigurationElement( (InnerTextConfigurationElement)this["keepUserLoggedIn"], //set the default - true); + false); } } diff --git a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs index e9f426c323..2d257a0d03 100644 --- a/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs +++ b/src/Umbraco.Core/PropertyEditors/BackwardsCompatibleData.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; using Umbraco.Core.Models; using umbraco.interfaces; @@ -40,17 +41,29 @@ namespace Umbraco.Core.PropertyEditors Value = Value }; var xd = new XmlDocument(); - var xml = propertyEditor.ValueEditor.ConvertDbToXml(property); - xml.GetXmlNode(xd); + var xNode = propertyEditor.ValueEditor.ConvertDbToXml(property); + + //check if this xml fragment can be converted to an XmlNode + var xContainer = xNode as XContainer; + if (xContainer != null) + { + //imports to the document + xContainer.GetXmlNode(xd); + // return the XML node. + return data.ImportNode(xd.DocumentElement, true); + } - // return the XML node. - return data.ImportNode(xd.DocumentElement, true); + return ReturnCDataElement(data); } //if for some reason the prop editor wasn't found we'll default to returning the string value in a CDATA block. - var sValue = Value != null ? Value.ToString() : String.Empty; - return data.CreateCDataSection(sValue); + return ReturnCDataElement(data); + } + private XmlNode ReturnCDataElement(XmlDocument doc) + { + var sValue = Value != null ? Value.ToString() : string.Empty; + return doc.CreateCDataSection(sValue); } public void MakeNew(int PropertyId) diff --git a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs index e004779b60..0a2dccdead 100644 --- a/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs +++ b/src/Umbraco.Core/PropertyEditors/PropertyValueEditor.cs @@ -289,6 +289,8 @@ namespace Umbraco.Core.PropertyEditors /// /// By default this will just return the value of ConvertDbToString but ensure that if the db value type is nvarchar or text /// it is a CDATA fragment, otherwise it is just a text fragment. + /// + /// This method by default will only return XText or XCData which must be wrapped in an element! /// public virtual XNode ConvertDbToXml(Property property) { diff --git a/src/Umbraco.Core/XmlExtensions.cs b/src/Umbraco.Core/XmlExtensions.cs index 25418a11de..d6a93af035 100644 --- a/src/Umbraco.Core/XmlExtensions.cs +++ b/src/Umbraco.Core/XmlExtensions.cs @@ -207,7 +207,7 @@ namespace Umbraco.Core /// /// /// - public static XmlNode ToXmlElement(this XElement xElement) + public static XmlNode ToXmlElement(this XContainer xElement) { var xmlDocument = new XmlDocument(); using (var xmlReader = xElement.CreateReader()) @@ -270,7 +270,7 @@ namespace Umbraco.Core return xDoc.Root; } - public static XmlNode GetXmlNode(this XElement element) + public static XmlNode GetXmlNode(this XContainer element) { using (XmlReader xmlReader = element.CreateReader()) { @@ -280,7 +280,7 @@ namespace Umbraco.Core } } - public static XmlNode GetXmlNode(this XElement element, XmlDocument xmlDoc) + public static XmlNode GetXmlNode(this XContainer element, XmlDocument xmlDoc) { using (XmlReader xmlReader = element.CreateReader()) { @@ -289,13 +289,5 @@ namespace Umbraco.Core } } - public static XmlNode GetXmlNode(this XNode element, XmlDocument xmlDoc) - { - using (XmlReader xmlReader = element.CreateReader()) - { - xmlDoc.Load(xmlReader); - return xmlDoc.DocumentElement; - } - } } } \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 4b571d51ba..a05a8b8f0f 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -478,6 +478,7 @@ + diff --git a/src/Umbraco.Tests/XmlExtensionsTests.cs b/src/Umbraco.Tests/XmlExtensionsTests.cs new file mode 100644 index 0000000000..8cd934effc --- /dev/null +++ b/src/Umbraco.Tests/XmlExtensionsTests.cs @@ -0,0 +1,33 @@ +using System.Xml; +using System.Xml.Linq; +using NUnit.Framework; +using Umbraco.Core; + +namespace Umbraco.Tests +{ + [TestFixture] + public class XmlExtensionsTests + { + [Test] + public void XCDataToXmlNode() + { + var cdata = new XElement("test", new XCData("hello world")); + var xdoc = new XmlDocument(); + + var xmlNode = cdata.GetXmlNode(xdoc); + + Assert.AreEqual(xmlNode.InnerText, "hello world"); + } + + [Test] + public void XTextToXmlNode() + { + var cdata = new XElement("test", new XText("hello world")); + var xdoc = new XmlDocument(); + + var xmlNode = cdata.GetXmlNode(xdoc); + + Assert.AreEqual(xmlNode.InnerText, "hello world"); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/XmlHelperTests.cs b/src/Umbraco.Tests/XmlHelperTests.cs index ad00a364e6..0b7c8fd76b 100644 --- a/src/Umbraco.Tests/XmlHelperTests.cs +++ b/src/Umbraco.Tests/XmlHelperTests.cs @@ -4,7 +4,6 @@ using System.Diagnostics; using System.Linq; using System.Text; using System.Xml; -using System.Xml.Linq; using System.Xml.XPath; using NUnit.Framework; using Umbraco.Core; diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js index ac43f3488e..a4c31df259 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/umbraco.servervariables.js @@ -23,7 +23,8 @@ Umbraco.Sys.ServerVariables = { umbracoSettings: { "umbracoPath": "/umbraco", "appPluginsPath" : "/App_Plugins", - "imageFileTypes": "jpeg,jpg,gif,bmp,png,tiff,tif" + "imageFileTypes": "jpeg,jpg,gif,bmp,png,tiff,tif", + "keepUserLoggedIn": true }, umbracoPlugins: { trees: [ diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js new file mode 100644 index 0000000000..2a814fb2f1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/resources/currentuser.resource.js @@ -0,0 +1,53 @@ +/** + * @ngdoc service + * @name umbraco.resources.currentUserResource + * @description Used for read/updates for the currently logged in user + * + * + **/ +function currentUserResource($q, $http, umbRequestHelper) { + + //the factory object returned + return { + + /** + * @ngdoc method + * @name umbraco.resources.currentUserResource#changePassword + * @methodOf umbraco.resources.currentUserResource + * + * @description + * Changes the current users password + * + * @returns {Promise} resourcePromise object containing the user array. + * + */ + changePassword: function (changePasswordArgs) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "currentUserApiBaseUrl", + "PostChangePassword"), + changePasswordArgs), + 'Failed to change password'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.currentUserResource#getMembershipProviderConfig + * @methodOf umbraco.resources.currentUserResource + * + * @description + * Gets the configuration of the user membership provider which is used to configure the change password form + */ + getMembershipProviderConfig: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "currentUserApiBaseUrl", + "GetMembershipProviderConfig")), + 'Failed to retreive membership provider config'); + }, + }; +} + +angular.module('umbraco.resources').factory('currentUserResource', currentUserResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/user.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/user.resource.js deleted file mode 100644 index 857ef65160..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/resources/user.resource.js +++ /dev/null @@ -1,113 +0,0 @@ -/** - * @ngdoc service - * @name umbraco.resources.userResource - * @description Retrives user data from the server, cannot be used for authentication, for this, use the user.service - * - * - **/ -function userResource($q, $http, umbRequestHelper) { - - //the factory object returned - return { - - /** - * @ngdoc method - * @name umbraco.resources.userResource#getById - * @methodOf umbraco.resources.userResource - * - * @description - * Gets a user with a given id - * - * ##usage - *
-         * userResource.getById(1234)
-         *    .then(function(ent) {
-         *        var myUser = ent; 
-         *        alert('im here!');
-         *    });
-         * 
- * - * @param {Int} id id of user to return - * @returns {Promise} resourcePromise object containing the user. - * - */ - getById: function (id) { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "userApiBaseUrl", - "GetById", - [{ id: id }])), - 'Failed to retreive user data for id ' + id); - }, - - /** - * @ngdoc method - * @name umbraco.resources.userResource#getAll - * @methodOf umbraco.resources.userResource - * - * @description - * Gets all users available on the system - * - * ##usage - *
-         * contentResource.getAll()
-         *    .then(function(userArray) {
-         *        var myUsers = userArray; 
-         *        alert('they are here!');
-         *    });
-         * 
- * - * @returns {Promise} resourcePromise object containing the user array. - * - */ - getAll: function () { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "userApiBaseUrl", - "GetAll")), - 'Failed to retreive all users'); - }, - - /** - * @ngdoc method - * @name umbraco.resources.userResource#changePassword - * @methodOf umbraco.resources.userResource - * - * @description - * Changes the current users password - * - * @returns {Promise} resourcePromise object containing the user array. - * - */ - changePassword: function (changePasswordArgs) { - return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "userApiBaseUrl", - "PostChangePassword"), - changePasswordArgs), - 'Failed to change password'); - }, - - /** - * @ngdoc method - * @name umbraco.resources.userResource#getMembershipProviderConfig - * @methodOf umbraco.resources.userResource - * - * @description - * Gets the configuration of the user membership provider which is used to configure the change password form - */ - getMembershipProviderConfig: function () { - return umbRequestHelper.resourcePromise( - $http.get( - umbRequestHelper.getApiUrl( - "userApiBaseUrl", - "GetMembershipProviderConfig")), - 'Failed to retreive membership provider config'); - }, - }; -} - -angular.module('umbraco.resources').factory('userResource', userResource); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js index 512ae0caf5..e9b41c9fc5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/search.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/search.service.js @@ -5,7 +5,7 @@ angular.module('umbraco.services') member.menuUrl = umbRequestHelper.getApiUrl("memberTreeBaseUrl", "GetMenu", [{ id: member.id }, { application: 'member' }]); member.editorPath = "member/member/edit/" + (member.key ? member.key : member.id); member.metaData = { treeAlias: "member" }; - member.subTitle = member.additionalData.Email; + member.subTitle = member.metaData.Email; } function configureMediaResult(media) @@ -19,7 +19,7 @@ angular.module('umbraco.services') content.menuUrl = umbRequestHelper.getApiUrl("contentTreeBaseUrl", "GetMenu", [{ id: content.id }, { application: 'content' }]); content.editorPath = "content/content/edit/" + content.id; content.metaData = { treeAlias: "content" }; - content.subTitle = content.additionalData.Url; + content.subTitle = content.metaData.Url; } return { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js index 5b6bf1833c..6c03c4ed11 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tree.service.js @@ -9,7 +9,11 @@ */ function treeService($q, treeResource, iconHelper, notificationsService, $rootScope) { - //TODO: implement this in local storage + //SD: Have looked at putting this in sessionStorage (not localStorage since that means you wouldn't be able to work + // in multiple tabs) - however our tree structure is cyclical, meaning a node has a reference to it's parent and it's children + // which you cannot serialize to sessionStorage. There's really no benefit of session storage except that you could refresh + // a tab and have the trees where they used to be - supposed that is kind of nice but would mean we'd have to store the parent + // as a nodeid reference instead of a variable with a getParent() method. var treeCache = {}; var standardCssClass = 'icon umb-tree-icon sprTree'; @@ -36,13 +40,22 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc _formatNodeDataForUseInUI: function (parentNode, treeNodes, section, level) { //if no level is set, then we make it 1 var childLevel = (level ? level : 1); + //set the section if it's not already set if (!parentNode.section) { parentNode.section = section; } + //create a method outside of the loop to return the parent - otherwise jshint blows up + var funcParent = function() { + return parentNode; + }; for (var i = 0; i < treeNodes.length; i++) { treeNodes[i].level = childLevel; - treeNodes[i].parent = parentNode; + + //create a function to get the parent node, we could assign the parent node but + // then we cannot serialize this entity because we have a cyclical reference. + // Instead we just make a function to return the parentNode. + treeNodes[i].parent = funcParent; //set the section for each tree node - this allows us to reference this easily when accessing tree nodes treeNodes[i].section = section; @@ -108,7 +121,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc return undefined; }, - /** clears the tree cache - with optional cacheKey and optional section */ + /** clears the tree cache - with optional cacheKey, optional section or optional filter */ clearCache: function (args) { //clear all if not specified if (!args) { @@ -122,6 +135,50 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc treeCache = _.omit(treeCache, cacheKey); } } + else if (args.childrenOf) { + //if childrenOf is supplied a cacheKey must be supplied as well + if (!args.cacheKey) { + throw "args.cacheKey is required if args.childrenOf is supplied"; + } + //this will clear out all children for the parentId passed in to this parameter, we'll + // do this by recursing and specifying a filter + var self = this; + this.clearCache({ + cacheKey: args.cacheKey, + filter: function(cc) { + //get the new parent node from the tree cache + var parent = self.getDescendantNode(cc.root, args.childrenOf); + //clear it's children and set to not expanded + parent.children = null; + parent.expanded = false; + //return the cache to be saved + return cc; + } + }); + } + else if (args.filter && angular.isFunction(args.filter)) { + //if a filter is supplied a cacheKey must be supplied as well + if (!args.cacheKey) { + throw "args.cacheKey is required if args.filter is supplied"; + } + + //if a filter is supplied the function needs to return the data to keep + var byKey = treeCache[args.cacheKey]; + if (byKey) { + var result = args.filter(byKey); + + if (result) { + //set the result to the filtered data + treeCache[args.cacheKey] = result; + } + else { + //remove the cache + treeCache = _.omit(treeCache, args.cacheKey); + } + + } + + } else if (args.cacheKey) { //if only the cache key is specified, then clear all cache starting with that key var allKeys1 = _.keys(treeCache); @@ -137,7 +194,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc return k.endsWith("_" + args.section); }); treeCache = _.omit(treeCache, toRemove2); - } + } } }, @@ -194,11 +251,11 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc /** Removes a given tree node from the tree */ removeNode: function(treeNode) { - if (treeNode.parent == null) { + if (treeNode.parent() == null) { throw "Cannot remove a node that doesn't have a parent"; } //remove the current item from it's siblings - treeNode.parent.children.splice(treeNode.parent.children.indexOf(treeNode), 1); + treeNode.parent().children.splice(treeNode.parent().children.indexOf(treeNode), 1); }, /** Removes all child nodes from a given tree node */ @@ -261,7 +318,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc root = current; } else { - current = current.parent; + current = current.parent(); } } return root; @@ -370,7 +427,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc if (!node) { throw "node cannot be null"; } - if (!node.parent) { + if (!node.parent()) { throw "cannot reload a single node without a parent"; } if (!node.section) { @@ -382,7 +439,7 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc //set the node to loading node.loading = true; - this.getChildren({ node: node.parent, section: node.section }).then(function(data) { + this.getChildren({ node: node.parent(), section: node.section }).then(function(data) { //ok, now that we have the children, find the node we're reloading var found = _.find(data, function(item) { @@ -390,14 +447,14 @@ function treeService($q, treeResource, iconHelper, notificationsService, $rootSc }); if (found) { //now we need to find the node in the parent.children collection to replace - var index = _.indexOf(node.parent.children, node); + var index = _.indexOf(node.parent().children, node); //the trick here is to not actually replace the node - this would cause the delete animations //to fire, instead we're just going to replace all the properties of this node. - _.extend(node.parent.children[index], found); + _.extend(node.parent().children[index], found); //set the node to loading - node.parent.children[index].loading = false; + node.parent().children[index].loading = false; //return - deferred.resolve(node.parent.children[index]); + deferred.resolve(node.parent().children[index]); } else { deferred.reject(); 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 213982b2ba..78a19e979c 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 @@ -93,10 +93,28 @@ angular.module('umbraco.services') //we are either timed out or very close to timing out so we need to show the login dialog. //NOTE: the safeApply because our timeout is set to not run digests (performance reasons) - angularHelper.safeApply($rootScope, function() { - userAuthExpired(); - }); - + if (Umbraco.Sys.ServerVariables.umbracoSettings.keepUserLoggedIn !== true) { + angularHelper.safeApply($rootScope, function() { + userAuthExpired(); + }); + } + else { + //we've got less than 30 seconds remaining so let's check the server + + if (lastServerTimeoutSet != null) { + //first we'll set the lastServerTimeoutSet to null - this is so we don't get back in to this loop while we + // wait for a response from the server otherwise we'll be making double/triple/etc... calls while we wait. + lastServerTimeoutSet = null; + //now go get it from the server + authResource.getRemainingTimeoutSeconds().then(function (result) { + setUserTimeoutInternal(result); + }); + } + + //recurse the countdown! + countdownUserTimeout(); + + } } } }, 2000, //every 2 seconds diff --git a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js index 7f1d483223..4877bb9972 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/util.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/util.service.js @@ -1,5 +1,36 @@ /*Contains multiple services for various helper tasks */ +/** + * @ngdoc function + * @name umbraco.services.umbSessionStorage + * @function + * + * @description + * Used to get/set things in browser sessionStorage but always prefixes keys with "umb_" and converts json vals so there is no overlap + * with any sessionStorage created by a developer. + */ +function umbSessionStorage($window) { + + //gets the sessionStorage object if available, otherwise just uses a normal object + // - required for unit tests. + var storage = $window['sessionStorage'] ? $window['sessionStorage'] : {}; + + return { + + get: function (key) { + console.log(storage); + console.log(storage["umb_" + key]); + return angular.fromJson(storage["umb_" + key]); + }, + + set : function(key, value) { + storage["umb_" + key] = angular.toJson(value); + } + + }; +} +angular.module('umbraco.services').factory('umbSessionStorage', umbSessionStorage); + /** * @ngdoc function * @name umbraco.services.legacyJsLoader diff --git a/src/Umbraco.Web.UI.Client/src/less/login.less b/src/Umbraco.Web.UI.Client/src/less/login.less index 4f5140aa16..d46bb41317 100644 --- a/src/Umbraco.Web.UI.Client/src/less/login.less +++ b/src/Umbraco.Web.UI.Client/src/less/login.less @@ -4,7 +4,8 @@ .login-overlay { width: 100%; height: 100%; - background: @blackLight url(../img/application/logo.png) no-repeat 20px 30px fixed !important; + background: @blackLight url(../img/application/logo.png) no-repeat 25px 30px fixed !important; + background-size: 30px 30px !important; color: @white; position: absolute; z-index: 2000; @@ -12,6 +13,7 @@ left: 0px; margin: 0 !Important; padding: 0; + border-radius: 0 } .login-overlay .umb-modalcolumn{ diff --git a/src/Umbraco.Web.UI.Client/src/less/navs.less b/src/Umbraco.Web.UI.Client/src/less/navs.less index f4e5b9c610..c79f4b6420 100644 --- a/src/Umbraco.Web.UI.Client/src/less/navs.less +++ b/src/Umbraco.Web.UI.Client/src/less/navs.less @@ -298,7 +298,7 @@ .nav-pills .open .dropdown-toggle, .nav > li.dropdown.open.active > a:hover, .nav > li.dropdown.open.active > a:focus { - color: @white; + /*color: @white;*/ background-color: @grayLight; border-color: @grayLight; } diff --git a/src/Umbraco.Web.UI.Client/src/less/panel.less b/src/Umbraco.Web.UI.Client/src/less/panel.less index bdc552fbb5..97ab641eb6 100644 --- a/src/Umbraco.Web.UI.Client/src/less/panel.less +++ b/src/Umbraco.Web.UI.Client/src/less/panel.less @@ -42,7 +42,7 @@ position: relative; } -.umb-panel-header .umb-headline { +.umb-panel-header .umb-headline, .umb-panel-header h1 { font-size: 18px; border: none; background: none; @@ -72,14 +72,7 @@ padding: 0px 0px 0px 20px; } -.umb-panel-header h1 { - margin: 0; - font-size: 14px; - font-weight: 400; - color: @gray; - line-height: 1em; - width: 100%; -} + .umb-panel-header p { margin:0px 20px; } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js index 29270f7fcf..ba834e5e9e 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/mediapicker.controller.js @@ -1,7 +1,7 @@ //used for the media picker dialog angular.module("umbraco") .controller("Umbraco.Dialogs.MediaPickerController", - function($scope, mediaResource, umbRequestHelper, entityResource, $log, imageHelper, eventsService) { + function($scope, mediaResource, umbRequestHelper, entityResource, $log, imageHelper, eventsService, treeService) { var dialogOptions = $scope.$parent.dialogOptions; $scope.options = { @@ -21,6 +21,12 @@ angular.module("umbraco") .addFolder($scope.newFolderName, $scope.options.formData.currentFolder) .then(function(data) { + //we've added a new folder so lets clear the tree cache for that specific item + treeService.clearCache({ + cacheKey: "__media", //this is the main media tree cache key + childrenOf: data.parentId //clear the children of the parent + }); + $scope.gotoFolder(data.id); }); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js index 8f440c641d..cad419bbfd 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.controller.js @@ -36,6 +36,7 @@ angular.module("umbraco") $scope.user = user; if ($scope.user) { $scope.remainingAuthSeconds = $scope.user.remainingAuthSeconds; + $scope.canEditProfile = _.indexOf($scope.user.allowedSections, "users") > -1; //set the timer updateTimeout(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html index 488e982719..53ba8b8f69 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/user.html @@ -19,8 +19,8 @@
- -
+ + - - diff --git a/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/PropertyEditors/js/simpleeditor.js b/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/PropertyEditors/js/simpleeditor.js deleted file mode 100644 index 343d6516af..0000000000 --- a/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/PropertyEditors/js/simpleeditor.js +++ /dev/null @@ -1,6 +0,0 @@ -(function () { - angular.module("umbraco").controller("Umbraco.Editors.SimpleEditorController", - function ($rootScope, $scope, notificationsService, dialogService) { - - }); -})(); \ No newline at end of file diff --git a/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/package.manifest b/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/package.manifest deleted file mode 100644 index 1c9b3158f2..0000000000 --- a/src/Umbraco.Web.UI/App_Plugins/SimpleEditor/package.manifest +++ /dev/null @@ -1,14 +0,0 @@ -{ - propertyEditors: [ - { - alias: "MyPackage.SimpleEditor", - name: "My Simple Editor", - editor: { - view: "~/App_Plugins/SimpleEditor/PropertyEditors/Views/simpleeditor.html" - } - } - ], - javascript: [ - '~/App_Plugins/SimpleEditor/PropertyEditors/Js/SimpleEditor.js' - ] -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config index 30526ee2a3..2ccc8a5077 100644 --- a/src/Umbraco.Web.UI/config/umbracoSettings.Release.config +++ b/src/Umbraco.Web.UI/config/umbracoSettings.Release.config @@ -52,7 +52,7 @@ - true + false false diff --git a/src/Umbraco.Web/Editors/BackOfficeController.cs b/src/Umbraco.Web/Editors/BackOfficeController.cs index 40d03ecbb2..1e36107689 100644 --- a/src/Umbraco.Web/Editors/BackOfficeController.cs +++ b/src/Umbraco.Web/Editors/BackOfficeController.cs @@ -102,8 +102,8 @@ namespace Umbraco.Web.Editors controller => controller.PostLogin(null, null)) }, { - "userApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( - controller => controller.GetAll()) + "currentUserApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( + controller => controller.GetMembershipProviderConfig()) }, { "legacyApiBaseUrl", Url.GetUmbracoApiServiceBaseUrl( @@ -172,6 +172,7 @@ namespace Umbraco.Web.Editors "imageFileTypes", string.Join(",", UmbracoConfig.For.UmbracoSettings().Content.ImageFileTypes) }, + {"keepUserLoggedIn", UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn}, } }, { diff --git a/src/Umbraco.Web/Editors/UserController.cs b/src/Umbraco.Web/Editors/CurrentUserController.cs similarity index 79% rename from src/Umbraco.Web/Editors/UserController.cs rename to src/Umbraco.Web/Editors/CurrentUserController.cs index bea40acc81..880ca7ab41 100644 --- a/src/Umbraco.Web/Editors/UserController.cs +++ b/src/Umbraco.Web/Editors/CurrentUserController.cs @@ -1,112 +1,87 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using System.Web.Http; -using System.Web.Security; -using AutoMapper; -using Umbraco.Core.Configuration; -using Umbraco.Web.Models; -using Umbraco.Web.Models.ContentEditing; -using Umbraco.Web.Models.Mapping; -using Umbraco.Web.Mvc; -using Umbraco.Web.UI; -using Umbraco.Web.WebApi; -using umbraco; -using legacyUser = umbraco.BusinessLogic.User; -using System.Net.Http; -using System.Collections.Specialized; -using Constants = Umbraco.Core.Constants; - - -namespace Umbraco.Web.Editors -{ - /// - /// Controller to back the User.Resource service, used for fetching user data when already authenticated. user.service is currently used for handling authentication - /// - [PluginController("UmbracoApi")] - public class UserController : UmbracoAuthorizedJsonController - { - - /// - /// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog - /// - /// - public IDictionary GetMembershipProviderConfig() - { - var provider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; - if (provider == null) - { - throw new InvalidOperationException("No back office membership provider found with the name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); - } - return provider.GetConfiguration(); - } - - /// - /// Returns a user by id - /// - /// - /// - public UserDetail GetById(int id) - { - var user = Services.UserService.GetUserById(id); - if (user == null) - { - throw new HttpResponseException(HttpStatusCode.NotFound); - } - - - return Mapper.Map(user); - } - - /// - /// Changes the users password - /// - /// - /// - /// If the password is being reset it will return the newly reset password, otherwise will return an empty value - /// - public ModelWithNotifications PostChangePassword(ChangingPasswordModel data) - { - var userProvider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; - if (userProvider == null) - { - throw new InvalidOperationException("No membership provider found with the name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); - } - - //TODO: WE need to support this! - requires UI updates, etc... - if (userProvider.RequiresQuestionAndAnswer) - { - throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified"); - } - - var passwordChangeResult = Security.ChangePassword(Security.CurrentUser.Username, data, userProvider); - if (passwordChangeResult.Success) - { - //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword - var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); - result.AddSuccessNotification(ui.Text("user", "password"), ui.Text("user", "passwordChanged")); - return result; - } - - //it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form - // so that is why it is being used here. - ModelState.AddPropertyError( - passwordChangeResult.Result.ChangeError, - string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); - - throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); - } - - /// - /// Returns all active users - /// - /// - public IEnumerable GetAll() - { - return Services.UserService.GetAllUsers().Where(x => x.IsLockedOut == false).Select(Mapper.Map); - } - } -} +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Web.Http; +using System.Web.Security; +using AutoMapper; +using Umbraco.Core.Configuration; +using Umbraco.Web.Models; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Models.Mapping; +using Umbraco.Web.Mvc; +using Umbraco.Web.UI; +using Umbraco.Web.WebApi; +using umbraco; +using legacyUser = umbraco.BusinessLogic.User; +using System.Net.Http; +using System.Collections.Specialized; +using Constants = Umbraco.Core.Constants; + + +namespace Umbraco.Web.Editors +{ + /// + /// Controller to back the User.Resource service, used for fetching user data when already authenticated. user.service is currently used for handling authentication + /// + [PluginController("UmbracoApi")] + public class CurrentUserController : UmbracoAuthorizedJsonController + { + + /// + /// Returns the configuration for the backoffice user membership provider - used to configure the change password dialog + /// + /// + public IDictionary GetMembershipProviderConfig() + { + var provider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; + if (provider == null) + { + throw new InvalidOperationException("No back office membership provider found with the name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); + } + return provider.GetConfiguration(); + } + + /// + /// Changes the users password + /// + /// + /// + /// If the password is being reset it will return the newly reset password, otherwise will return an empty value + /// + public ModelWithNotifications PostChangePassword(ChangingPasswordModel data) + { + var userProvider = Membership.Providers[UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider]; + if (userProvider == null) + { + throw new InvalidOperationException("No membership provider found with the name " + UmbracoConfig.For.UmbracoSettings().Providers.DefaultBackOfficeUserProvider); + } + + //TODO: WE need to support this! - requires UI updates, etc... + if (userProvider.RequiresQuestionAndAnswer) + { + throw new NotSupportedException("Currently the user editor does not support providers that have RequiresQuestionAndAnswer specified"); + } + + var passwordChangeResult = Security.ChangePassword(Security.CurrentUser.Username, data, userProvider); + if (passwordChangeResult.Success) + { + //even if we weren't resetting this, it is the correct value (null), otherwise if we were resetting then it will contain the new pword + var result = new ModelWithNotifications(passwordChangeResult.Result.ResetPassword); + result.AddSuccessNotification(ui.Text("user", "password"), ui.Text("user", "passwordChanged")); + return result; + } + + //it wasn't successful, so add the change error to the model state, we've name the property alias _umb_password on the form + // so that is why it is being used here. + ModelState.AddPropertyError( + passwordChangeResult.Result.ChangeError, + string.Format("{0}password", Constants.PropertyEditors.InternalGenericPropertiesPrefix)); + + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + } + + } +} diff --git a/src/Umbraco.Web/Editors/UpdateCheckController.cs b/src/Umbraco.Web/Editors/UpdateCheckController.cs index 57c20e2574..aa4966b14c 100644 --- a/src/Umbraco.Web/Editors/UpdateCheckController.cs +++ b/src/Umbraco.Web/Editors/UpdateCheckController.cs @@ -20,13 +20,21 @@ namespace Umbraco.Web.Editors var updateCheckCookie = updChkCookie != null ? updChkCookie["UMB_UPDCHK"].Value : ""; if (GlobalSettings.VersionCheckPeriod > 0 && string.IsNullOrEmpty(updateCheckCookie) && Security.CurrentUser.UserType.Alias == "admin") { - var check = new global::umbraco.presentation.org.umbraco.update.CheckForUpgrade(); - var result = check.CheckUpgrade(UmbracoVersion.Current.Major, - UmbracoVersion.Current.Minor, - UmbracoVersion.Current.Build, - UmbracoVersion.CurrentComment); - - return new UpgradeCheckResponse(result.UpgradeType.ToString(), result.Comment, result.UpgradeUrl); + try + { + var check = new global::umbraco.presentation.org.umbraco.update.CheckForUpgrade(); + var result = check.CheckUpgrade(UmbracoVersion.Current.Major, + UmbracoVersion.Current.Minor, + UmbracoVersion.Current.Build, + UmbracoVersion.CurrentComment); + + return new UpgradeCheckResponse(result.UpgradeType.ToString(), result.Comment, result.UpgradeUrl); + } + catch (System.Net.WebException) + { + //this occurs if the server is down or cannot be reached + return null; + } } return null; } @@ -41,6 +49,8 @@ namespace Umbraco.Web.Editors { public override void OnActionExecuted(HttpActionExecutedContext context) { + if (context.Response == null) return; + var objectContent = context.Response.Content as ObjectContent; if (objectContent == null || objectContent.Value == null) return; diff --git a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs index f5f779611f..428bb4ba3d 100644 --- a/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs +++ b/src/Umbraco.Web/Models/ContentEditing/EntityBasic.cs @@ -53,7 +53,7 @@ namespace Umbraco.Web.Models.ContentEditing /// /// A collection of extra data that is available for this specific entity/entity type /// - [DataMember(Name = "additionalData")] + [DataMember(Name = "metaData")] public IDictionary AdditionalData { get; private set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs index b6b17d0336..32b890d9fd 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UserDetail.cs @@ -1,4 +1,5 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Runtime.Serialization; namespace Umbraco.Web.Models.ContentEditing @@ -29,5 +30,11 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "remainingAuthSeconds")] public double SecondsUntilTimeout { get; set; } + + /// + /// A list of sections the user is allowed to view. + /// + [DataMember(Name = "allowedSections")] + public IEnumerable AllowedSections { get; set; } } } \ No newline at end of file diff --git a/src/Umbraco.Web/Models/Trees/SectionRootNode.cs b/src/Umbraco.Web/Models/Trees/SectionRootNode.cs index 4d5b34db5e..3116159f5f 100644 --- a/src/Umbraco.Web/Models/Trees/SectionRootNode.cs +++ b/src/Umbraco.Web/Models/Trees/SectionRootNode.cs @@ -1,4 +1,5 @@ using System.Runtime.Serialization; +using Umbraco.Core; namespace Umbraco.Web.Models.Trees { @@ -28,12 +29,12 @@ namespace Umbraco.Web.Models.Trees return new SectionRootNode(nodeId, getChildNodesUrl, menuUrl) { Children = children, - Title = title + Name = title }; } private SectionRootNode(string nodeId, string getChildNodesUrl, string menuUrl) - : base(nodeId, getChildNodesUrl, menuUrl) + : base(nodeId, null, getChildNodesUrl, menuUrl) { //default to false IsContainer = false; diff --git a/src/Umbraco.Web/Models/Trees/TreeNode.cs b/src/Umbraco.Web/Models/Trees/TreeNode.cs index 03fa955426..c413edea37 100644 --- a/src/Umbraco.Web/Models/Trees/TreeNode.cs +++ b/src/Umbraco.Web/Models/Trees/TreeNode.cs @@ -2,6 +2,7 @@ using Umbraco.Core.IO; using System.Collections.Generic; using Umbraco.Core; +using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models.Trees { @@ -12,45 +13,37 @@ namespace Umbraco.Web.Models.Trees /// TreeNode is sealed to prevent developers from adding additional json data to the response /// [DataContract(Name = "node", Namespace = "")] - public class TreeNode + public class TreeNode : EntityBasic { /// /// Internal constructor, to create a tree node use the CreateTreeNode methods of the TreeApiController. /// /// + /// The parent id for the current node /// /// - internal TreeNode(string nodeId, string getChildNodesUrl, string menuUrl) + internal TreeNode(string nodeId, string parentId, string getChildNodesUrl, string menuUrl) { - //_menuItems = menuItems; - //Style = new NodeStyle(); - NodeId = nodeId; - AdditionalData = new Dictionary(); + Mandate.ParameterNotNullOrEmpty(nodeId, "nodeId"); + + Id = nodeId; + ParentId = parentId; ChildNodesUrl = getChildNodesUrl; MenuUrl = menuUrl; CssClasses = new List(); //default Icon = "icon-folder-close"; } - - /// - /// The unique identifier for the node - /// - [DataMember(Name = "id")] - public string NodeId { get; private set; } + + [DataMember(Name = "parentId", IsRequired = true)] + public new object ParentId { get; set; } /// /// A flag to set whether or not this node has children /// [DataMember(Name = "hasChildren")] public bool HasChildren { get; set; } - - /// - /// The text title of the node that is displayed in the tree - /// - [DataMember(Name = "name")] - public string Title { get; set; } - + /// /// The tree nodetype which refers to the type of node rendered in the tree /// @@ -78,20 +71,6 @@ namespace Umbraco.Web.Models.Trees [DataMember(Name = "menuUrl")] public string MenuUrl { get; set; } - /// - /// A dictionary to support any additional meta data that should be rendered for the node which is - /// useful for custom action commands such as 'create', 'copy', etc... - /// - [DataMember(Name = "metaData")] - public Dictionary AdditionalData { get; private set; } - - /// - /// The icon to use for the node, this can be either a path to an image or a Css class. - /// If a '/' is found in the string then it will be considered a path to an image. - /// - [DataMember(Name = "icon")] - public string Icon { get; set; } - /// /// Returns true if the icon represents a CSS class instead of a file path /// diff --git a/src/Umbraco.Web/Security/WebSecurity.cs b/src/Umbraco.Web/Security/WebSecurity.cs index 657691dbe8..e4312ea994 100644 --- a/src/Umbraco.Web/Security/WebSecurity.cs +++ b/src/Umbraco.Web/Security/WebSecurity.cs @@ -173,6 +173,9 @@ namespace Umbraco.Web.Security _httpContext.UmbracoLogout(); } + /// + /// Renews the user's login ticket + /// public void RenewLoginTimeout() { _httpContext.RenewUmbracoAuthTicket(); @@ -357,18 +360,6 @@ namespace Umbraco.Web.Security return userApps.Any(uApp => uApp.InvariantEquals(app)); } - internal void UpdateLogin() - { - _httpContext.RenewUmbracoAuthTicket(); - } - - internal long GetTimeout() - { - var ticket = _httpContext.GetUmbracoAuthTicket(); - var ticks = ticket.Expiration.Ticks - DateTime.Now.Ticks; - return ticks; - } - /// /// Gets the user id. /// @@ -409,15 +400,8 @@ namespace Umbraco.Web.Security /// public bool ValidateCurrentUser() { - var ticket = _httpContext.GetUmbracoAuthTicket(); - if (ticket != null) - { - if (ticket.Expired == false) - { - return true; - } - } - return false; + var result = ValidateCurrentUser(false); + return result == ValidateRequestAttempt.Success; } /// @@ -440,8 +424,7 @@ namespace Umbraco.Web.Security { if (throwExceptions) throw new ArgumentException("You have no priviledges to the umbraco console. Please contact your administrator"); return ValidateRequestAttempt.FailedNoPrivileges; - } - UpdateLogin(); + } return ValidateRequestAttempt.Success; } if (throwExceptions) throw new ArgumentException("User has timed out!!"); diff --git a/src/Umbraco.Web/Trees/ApplicationTreeController.cs b/src/Umbraco.Web/Trees/ApplicationTreeController.cs index 3e96dee554..b16ee8bd00 100644 --- a/src/Umbraco.Web/Trees/ApplicationTreeController.cs +++ b/src/Umbraco.Web/Trees/ApplicationTreeController.cs @@ -80,7 +80,7 @@ namespace Umbraco.Web.Trees } var multiTree = SectionRootNode.CreateMultiTreeSectionRoot(rootId, collection); - multiTree.Title = ui.Text("sections", application); + multiTree.Name = ui.Text("sections", application); return multiTree; } @@ -133,7 +133,7 @@ namespace Umbraco.Web.Trees rootId, rootNode.Result.ChildNodesUrl, rootNode.Result.MenuUrl, - rootNode.Result.Title, + rootNode.Result.Name, byControllerAttempt.Result); foreach (var d in rootNode.Result.AdditionalData) diff --git a/src/Umbraco.Web/Trees/ContentTreeController.cs b/src/Umbraco.Web/Trees/ContentTreeController.cs index 5bfa20e030..9cfc2bcb14 100644 --- a/src/Umbraco.Web/Trees/ContentTreeController.cs +++ b/src/Umbraco.Web/Trees/ContentTreeController.cs @@ -75,6 +75,7 @@ namespace Umbraco.Web.Trees var node = CreateTreeNode( e.Id.ToInvariantString(), + id, queryStrings, e.Name, e.ContentTypeIcon, diff --git a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs index 352f0c3521..5b9a6181f3 100644 --- a/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/ContentTreeControllerBase.cs @@ -72,6 +72,7 @@ namespace Umbraco.Web.Trees { nodes.Add(CreateTreeNode( RecycleBinId.ToInvariantString(), + id, queryStrings, ui.GetText("general", "recycleBin"), "icon-trash", diff --git a/src/Umbraco.Web/Trees/DataTypeTreeController.cs b/src/Umbraco.Web/Trees/DataTypeTreeController.cs index 76d934c9cf..c41deb350b 100644 --- a/src/Umbraco.Web/Trees/DataTypeTreeController.cs +++ b/src/Umbraco.Web/Trees/DataTypeTreeController.cs @@ -33,6 +33,7 @@ namespace Umbraco.Web.Trees .Select(dt => CreateTreeNode( dt.Id.ToInvariantString(), + id, queryStrings, dt.Name, "icon-autofill", diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 98100c771a..9cce00779f 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -348,11 +348,11 @@ namespace Umbraco.Web.Trees //TODO: Might need to add stuff to additional attributes - var node = new TreeNode(xmlTreeNode.NodeID, childNodesSource, menuSource) + var node = new TreeNode(xmlTreeNode.NodeID, isRoot ? null : parentId, childNodesSource, menuSource) { HasChildren = xmlTreeNode.HasChildren, Icon = xmlTreeNode.Icon, - Title = xmlTreeNode.Text, + Name = xmlTreeNode.Text, NodeType = xmlTreeNode.NodeType }; if (isRoot) diff --git a/src/Umbraco.Web/Trees/MediaTreeController.cs b/src/Umbraco.Web/Trees/MediaTreeController.cs index fd85910c8f..e8283b0f90 100644 --- a/src/Umbraco.Web/Trees/MediaTreeController.cs +++ b/src/Umbraco.Web/Trees/MediaTreeController.cs @@ -55,7 +55,7 @@ namespace Umbraco.Web.Trees foreach (var entity in entities) { var e = (UmbracoEntity)entity; - var node = CreateTreeNode(e.Id.ToInvariantString(), queryStrings, e.Name, e.ContentTypeIcon, e.HasChildren); + var node = CreateTreeNode(e.Id.ToInvariantString(), id, queryStrings, e.Name, e.ContentTypeIcon, e.HasChildren); node.AdditionalData.Add("contentType", e.ContentTypeAlias); nodes.Add(node); diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index f7d536c083..6ea9086cc3 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -1,4 +1,5 @@ -using System.Globalization; +using System; +using System.Globalization; using System.Linq; using System.Net.Http.Formatting; using System.Web.Security; @@ -30,14 +31,14 @@ namespace Umbraco.Web.Trees for (var i = 97; i < 123; i++) { var charString = ((char) i).ToString(CultureInfo.InvariantCulture); - var folder = CreateTreeNode(charString, queryStrings, charString, "icon-folder-close", true); + var folder = CreateTreeNode(charString, id, queryStrings, charString, "icon-folder-close", true); folder.NodeType = "member-folder"; nodes.Add(folder); } //list out 'Others' if the membership provider is umbraco if (Member.InUmbracoMemberMode()) { - var folder = CreateTreeNode("others", queryStrings, "Others", "icon-folder-close", true); + var folder = CreateTreeNode("others", id, queryStrings, "Others", "icon-folder-close", true); folder.NodeType = "member-folder"; nodes.Add(folder); } @@ -52,7 +53,7 @@ namespace Umbraco.Web.Trees //get the members from our member data layer nodes.AddRange( Member.getMemberFromFirstLetter(id.ToCharArray()[0]) - .Select(m => CreateTreeNode(m.UniqueId.ToString("N"), queryStrings, m.Text, "icon-user"))); + .Select(m => CreateTreeNode(m.UniqueId.ToString("N"), id, queryStrings, m.Text, "icon-user"))); } else { @@ -60,7 +61,7 @@ namespace Umbraco.Web.Trees int total; nodes.AddRange( Membership.Provider.FindUsersByName(id + "%", 0, 9999, out total).Cast() - .Select(m => CreateTreeNode(m.ProviderUserKey.ToString(), queryStrings, m.UserName, "icon-user"))); + .Select(m => CreateTreeNode(m.ProviderUserKey.ToString(), id, queryStrings, m.UserName, "icon-user"))); } } else if (id == "others") @@ -68,7 +69,7 @@ namespace Umbraco.Web.Trees //others will only show up when in umbraco membership mode nodes.AddRange( Member.getAllOtherMembers() - .Select(m => CreateTreeNode(m.Id.ToInvariantString(), queryStrings, m.Text, "icon-user"))); + .Select(m => CreateTreeNode(m.Id.ToInvariantString(), id, queryStrings, m.Text, "icon-user"))); } } return nodes; @@ -89,8 +90,16 @@ namespace Umbraco.Web.Trees return menu; } - menu.Items.Add(ui.Text("actions", ActionDelete.Instance.Alias)); - menu.Items.Add(ui.Text("actions", ActionRefresh.Instance.Alias), true); + Guid guid; + if (Guid.TryParse(id, out guid)) + { + menu.Items.Add(ui.Text("actions", ActionDelete.Instance.Alias)); + } + else + { + menu.Items.Add(ui.Text("actions", ActionDelete.Instance.Alias)); + menu.Items.Add(ui.Text("actions", ActionRefresh.Instance.Alias), true); + } return menu; } } diff --git a/src/Umbraco.Web/Trees/TreeControllerBase.cs b/src/Umbraco.Web/Trees/TreeControllerBase.cs index 496418cdfd..e31cb85b0d 100644 --- a/src/Umbraco.Web/Trees/TreeControllerBase.cs +++ b/src/Umbraco.Web/Trees/TreeControllerBase.cs @@ -155,12 +155,13 @@ namespace Umbraco.Web.Trees var node = new TreeNode( rootNodeAsString, + null, //this is a root node, there is no parent Url.GetTreeUrl(GetType(), rootNodeAsString, queryStrings), Url.GetMenuUrl(GetType(), rootNodeAsString, queryStrings)) { HasChildren = true, RoutePath = currApp, - Title = RootNodeDisplayName + Name = RootNodeDisplayName }; return node; @@ -172,14 +173,15 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title }; return node; } @@ -187,15 +189,16 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, Icon = icon }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title, Icon = icon }; return node; } @@ -203,16 +206,17 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes /// /// + /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, string routePath) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, string routePath) { var jsonUrl = Url.GetTreeUrl(GetType(), id, queryStrings); var menuUrl = Url.GetMenuUrl(GetType(), id, queryStrings); - var node = new TreeNode(id, jsonUrl, menuUrl) { Title = title, RoutePath = routePath, Icon = icon }; + var node = new TreeNode(id, parentId, jsonUrl, menuUrl) { Name = title, RoutePath = routePath, Icon = icon }; return node; } @@ -220,14 +224,15 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes and automatically generate the json url /// /// + /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, bool hasChildren) { - var treeNode = CreateTreeNode(id, queryStrings, title, icon); + var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon); treeNode.HasChildren = hasChildren; return treeNode; } @@ -236,15 +241,16 @@ namespace Umbraco.Web.Trees /// Helper method to create tree nodes and automatically generate the json url /// /// + /// /// /// /// /// /// /// - public TreeNode CreateTreeNode(string id, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath) + public TreeNode CreateTreeNode(string id, string parentId, FormDataCollection queryStrings, string title, string icon, bool hasChildren, string routePath) { - var treeNode = CreateTreeNode(id, queryStrings, title, icon); + var treeNode = CreateTreeNode(id, parentId, queryStrings, title, icon); treeNode.HasChildren = hasChildren; treeNode.RoutePath = routePath; return treeNode; diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index a9173caa86..563d415509 100644 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -362,7 +362,7 @@ - + diff --git a/src/Umbraco.Web/UmbracoModule.cs b/src/Umbraco.Web/UmbracoModule.cs index 3ee5be98b4..f1cbc978db 100644 --- a/src/Umbraco.Web/UmbracoModule.cs +++ b/src/Umbraco.Web/UmbracoModule.cs @@ -225,10 +225,17 @@ namespace Umbraco.Web /// /// /// - /// We do not want to renew the ticket when we are checking for the user's remaining timeout. + /// We do not want to renew the ticket when we are checking for the user's remaining timeout unless - + /// UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn == true /// internal static bool ShouldIgnoreTicketRenew(Uri url, HttpContextBase httpContext) { + //this setting will renew the ticket for all requests. + if (UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn) + { + return false; + } + //initialize the ignore ticket urls - we don't need to lock this, it's concurrent and a hashset // we don't want to have to gen the url each request so this will speed things up a teeny bit. if (IgnoreTicketRenewUrls.Any() == false) diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs index 551c859d8b..3ec59bd378 100644 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs +++ b/src/Umbraco.Web/umbraco.presentation/umbraco/webservices/legacyAjaxCalls.asmx.cs @@ -185,6 +185,7 @@ namespace umbraco.presentation.webservices return Application[helper.Request("key")].ToString(); } + [Obsolete("This is no longer used and will be removed in future versions")] [WebMethod] [ScriptMethod] public void RenewUmbracoSession() @@ -195,6 +196,7 @@ namespace umbraco.presentation.webservices } + [Obsolete("This is no longer used and will be removed in future versions")] [WebMethod] [ScriptMethod] public int GetSecondsBeforeUserLogout() diff --git a/src/umbraco.businesslogic/BasePages/BasePage.cs b/src/umbraco.businesslogic/BasePages/BasePage.cs index 73df8927e3..9c60a77fa1 100644 --- a/src/umbraco.businesslogic/BasePages/BasePage.cs +++ b/src/umbraco.businesslogic/BasePages/BasePage.cs @@ -7,6 +7,7 @@ using System.Web.Mvc; using System.Web.Routing; using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Cache; using Umbraco.Core.Logging; @@ -30,9 +31,7 @@ namespace umbraco.BasePages private User _user; private bool _userisValidated = false; private ClientTools _clientTools; - - private static readonly int UmbracoTimeOutInMinutes = GlobalSettings.TimeOutInMinutes; - + /// /// The path to the umbraco root folder /// @@ -135,6 +134,7 @@ namespace umbraco.BasePages ClientTools.RefreshAdmin(Seconds); } + //NOTE: This is basically replicated in WebSecurity because this class exists in a poorly placed assembly. - also why it is obsolete. private void ValidateUser() { var ticket = Context.GetUmbracoAuthTicket(); @@ -160,7 +160,7 @@ namespace umbraco.BasePages } else { - throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); + throw new InvalidOperationException("The user has no umbraco contextid - try logging in"); } } @@ -205,13 +205,10 @@ namespace umbraco.BasePages /// public static bool ValidateCurrentUser() { - var ticket = HttpContext.Current.GetUmbracoAuthTicket(); - if (ticket != null) + var identity = HttpContext.Current.GetCurrentIdentity(true); + if (identity != null) { - if (ticket.Expired == false) - { - return true; - } + return true; } return false; } @@ -220,7 +217,8 @@ namespace umbraco.BasePages public static long GetTimeout(bool bypassCache) { var ticket = HttpContext.Current.GetUmbracoAuthTicket(); - var ticks = ticket.Expiration.Ticks - DateTime.Now.Ticks; + if (ticket.Expired) return 0; + var ticks = ticket.Expiration.Ticks - DateTime.Now.Ticks; return ticks; }