diff --git a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs index 12db4daf40..b0dc979ebf 100644 --- a/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs +++ b/src/Umbraco.Core/Services/PublicAccessServiceExtensions.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Web.Security; +using Umbraco.Core.Models; namespace Umbraco.Core.Services { @@ -41,7 +42,7 @@ namespace Umbraco.Core.Services return hasChange; } - public static bool HasAccess(this IPublicAccessService publicAccessService, int documentId, IContentService contentService, IEnumerable currentMemberRoles) + public static bool HasAccess(this IPublicAccessService publicAccessService, int documentId, IContentService contentService, string username, IEnumerable currentMemberRoles) { var content = contentService.GetById(documentId); if (content == null) return true; @@ -49,8 +50,7 @@ namespace Umbraco.Core.Services var entry = publicAccessService.GetEntryForContent(content); if (entry == null) return true; - return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType - && currentMemberRoles.Contains(x.RuleValue)); + return HasAccess(entry, username, currentMemberRoles); } public static bool HasAccess(this IPublicAccessService publicAccessService, string path, MembershipUser member, RoleProvider roleProvider) @@ -77,8 +77,15 @@ namespace Umbraco.Core.Services var roles = rolesCallback(username); - return entry.Rules.Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType - && roles.Contains(x.RuleValue)); + return HasAccess(entry, username, roles); + } + + private static bool HasAccess(PublicAccessEntry entry, string username, IEnumerable roles) + { + return entry.Rules.Any(x => + (x.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType && username.Equals(x.RuleValue, StringComparison.OrdinalIgnoreCase)) + || (x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType && roles.Contains(x.RuleValue)) + ); } } } diff --git a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs similarity index 86% rename from src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs rename to src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs index 0616b4098f..50b27da89f 100644 --- a/src/Umbraco.Tests/Cache/CacheRefresherComponentTests.cs +++ b/src/Umbraco.Tests/Cache/DistributedCacheBinderTests.cs @@ -1,22 +1,34 @@ using System; -using System.Collections.Generic; using System.Linq; +using System.Threading; +using Moq; using NUnit.Framework; +using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Events; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; -using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; using Umbraco.Web.Cache; +using Umbraco.Web.PublishedCache; +using Umbraco.Web.Routing; namespace Umbraco.Tests.Cache { [TestFixture] [UmbracoTest(WithApplication = true)] - public class CacheRefresherEventHandlerTests : UmbracoTestBase + public class DistributedCacheBinderTests : UmbracoTestBase { + protected override void Compose(Composition composition) + { + base.Compose(composition); + // refreshers.HandleEvents wants a UmbracoContext + // which wants these + composition.RegisterUnique(_ => Mock.Of()); + composition.WithCollectionBuilder(); + } + [Test] public void Can_Find_All_Event_Handlers() { @@ -114,7 +126,7 @@ namespace Umbraco.Tests.Cache var ok = true; foreach (var definition in definitions) { - var found = CacheRefresherComponent.FindHandler(definition); + var found = DistributedCacheBinder.FindHandler(definition); if (found == null) { Console.WriteLine("Couldn't find method for " + definition.EventName + " on " + definition.Sender.GetType()); @@ -123,5 +135,30 @@ namespace Umbraco.Tests.Cache } Assert.IsTrue(ok, "see log for details"); } + + [Test] + public void CanHandleEvent() + { + // refreshers.HandleEvents wants a UmbracoContext + // which wants an HttpContext, which we build using a SimpleWorkerRequest + // which requires these to be non-null + var domain = Thread.GetDomain(); + if (domain.GetData(".appPath") == null) + domain.SetData(".appPath", ""); + if (domain.GetData(".appVPath") == null) + domain.SetData(".appVPath", ""); + + // create some event definitions + var definitions = new IEventDefinition[] + { + // works because that event definition maps to an empty handler + new EventDefinition>(null, Current.Services.ContentTypeService, new SaveEventArgs(Enumerable.Empty()), "Saved"), + + }; + + // just assert it does not throw + var refreshers = new DistributedCacheBinder(null, null); + refreshers.HandleEvents(definitions); + } } } diff --git a/src/Umbraco.Tests/Integration/ContentEventsTests.cs b/src/Umbraco.Tests/Integration/ContentEventsTests.cs index bb0556a0a8..7b22d282f0 100644 --- a/src/Umbraco.Tests/Integration/ContentEventsTests.cs +++ b/src/Umbraco.Tests/Integration/ContentEventsTests.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Moq; using NUnit.Framework; using Umbraco.Core; using Umbraco.Core.Cache; @@ -31,8 +32,8 @@ namespace Umbraco.Tests.Integration { base.SetUp(); - _h1 = new CacheRefresherComponent(true); - _h1.Initialize(new DistributedCache()); + _h1 = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _h1.BindEvents(true); _events = new List(); @@ -73,7 +74,7 @@ namespace Umbraco.Tests.Integration { base.TearDown(); - _h1?.Unbind(); + _h1?.UnbindEvents(); // clear ALL events @@ -83,7 +84,7 @@ namespace Umbraco.Tests.Integration ContentCacheRefresher.CacheUpdated -= ContentCacheUpdated; } - private CacheRefresherComponent _h1; + private DistributedCacheBinder _h1; private IList _events; private int _msgCount; private IContentType _contentType; diff --git a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs index 9508a5ae4a..6e3b8a2094 100644 --- a/src/Umbraco.Tests/Runtimes/StandaloneTests.cs +++ b/src/Umbraco.Tests/Runtimes/StandaloneTests.cs @@ -97,12 +97,13 @@ namespace Umbraco.Tests.Runtimes composition.Register(_ => Mock.Of(), Lifetime.Singleton); composition.RegisterUnique(f => new DistributedCache()); composition.WithCollectionBuilder().Append(); + composition.RegisterUnique(); // create and register the factory Current.Factory = factory = composition.CreateFactory(); // initialize some components individually - components.Get().Initialize(factory.GetInstance()); + components.Get().Initialize(factory.GetInstance()); // do stuff Console.WriteLine(runtimeState.Level); diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index 6e173128e3..b273ee9526 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -10,6 +10,7 @@ using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence.Repositories; @@ -34,7 +35,7 @@ namespace Umbraco.Tests.Scoping [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] public class ScopedNuCacheTests : TestWithDatabaseBase { - private CacheRefresherComponent _cacheRefresher; + private DistributedCacheBinder _distributedCacheBinder; protected override void Compose() { @@ -54,8 +55,8 @@ namespace Umbraco.Tests.Scoping { base.TearDown(); - _cacheRefresher?.Unbind(); - _cacheRefresher = null; + _distributedCacheBinder?.UnbindEvents(); + _distributedCacheBinder = null; _onPublishedAssertAction = null; ContentService.Published -= OnPublishedAssert; @@ -127,8 +128,8 @@ namespace Umbraco.Tests.Scoping var umbracoContext = GetUmbracoContextNu("http://example.com/", setSingleton: true); // wire cache refresher - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); // create document type, document var contentType = new ContentType(-1) { Alias = "CustomDocument", Name = "Custom Document" }; diff --git a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs index 0f990427ac..3a0cb00ea9 100644 --- a/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedRepositoryTests.cs @@ -12,6 +12,7 @@ using Umbraco.Tests.Testing; using Umbraco.Web.Cache; using Moq; using Umbraco.Core.Events; +using Umbraco.Core.Logging; using Umbraco.Core.Sync; namespace Umbraco.Tests.Scoping @@ -20,7 +21,7 @@ namespace Umbraco.Tests.Scoping [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, WithApplication = true)] public class ScopedRepositoryTests : TestWithDatabaseBase { - private CacheRefresherComponent _cacheRefresher; + private DistributedCacheBinder _distributedCacheBinder; protected override void Compose() { @@ -49,8 +50,8 @@ namespace Umbraco.Tests.Scoping [TearDown] public void Teardown() { - _cacheRefresher?.Unbind(); - _cacheRefresher = null; + _distributedCacheBinder?.UnbindEvents(); + _distributedCacheBinder = null; } [TestCase(true)] @@ -73,8 +74,8 @@ namespace Umbraco.Tests.Scoping // get user again - else we'd modify the one that's in the cache user = service.GetUserById(user.Id); - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) @@ -154,8 +155,8 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(lang.Id, globalCached.Id); Assert.AreEqual("fr-FR", globalCached.IsoCode); - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) @@ -246,8 +247,8 @@ namespace Umbraco.Tests.Scoping Assert.AreEqual(item.Id, globalCached.Id); Assert.AreEqual("item-key", globalCached.ItemKey); - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); Assert.IsNull(scopeProvider.AmbientScope); using (var scope = scopeProvider.CreateScope(repositoryCacheMode: RepositoryCacheMode.Scoped)) diff --git a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs index 912dd317d7..99a5c57058 100644 --- a/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedXmlTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using Umbraco.Core.Cache; using Umbraco.Core.Composing; using Umbraco.Core.Events; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; @@ -22,7 +23,7 @@ namespace Umbraco.Tests.Scoping [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] public class ScopedXmlTests : TestWithDatabaseBase { - private CacheRefresherComponent _cacheRefresher; + private DistributedCacheBinder _distributedCacheBinder; protected override void Compose() { @@ -41,8 +42,8 @@ namespace Umbraco.Tests.Scoping [TearDown] public void Teardown() { - _cacheRefresher?.Unbind(); - _cacheRefresher = null; + _distributedCacheBinder?.UnbindEvents(); + _distributedCacheBinder = null; _onPublishedAssertAction = null; ContentService.Published -= OnPublishedAssert; @@ -89,8 +90,8 @@ namespace Umbraco.Tests.Scoping var item = new Content("name", -1, contentType); // wire cache refresher - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); // check xml in context = "before" var xml = XmlInContext; @@ -208,8 +209,8 @@ namespace Umbraco.Tests.Scoping Current.Services.ContentTypeService.Save(contentType); // wire cache refresher - _cacheRefresher = new CacheRefresherComponent(true); - _cacheRefresher.Initialize(new DistributedCache()); + _distributedCacheBinder = new DistributedCacheBinder(new DistributedCache(), Mock.Of()); + _distributedCacheBinder.BindEvents(true); // check xml in context = "before" var xml = XmlInContext; diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index ea8f1fd13c..661ccb6ab8 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -111,7 +111,7 @@ - + diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbcontextdialog/umbcontextdialog.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbcontextdialog/umbcontextdialog.directive.js index 926c59144d..28e3a9f53c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbcontextdialog/umbcontextdialog.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tree/umbcontextdialog/umbcontextdialog.directive.js @@ -6,11 +6,11 @@ function link($scope) { $scope.outSideClick = function() { - navigationService.hideNavigation(); + navigationService.hideDialog(); } keyboardService.bind("esc", function() { - navigationService.hideNavigation(); + navigationService.hideDialog(); }); //ensure to unregister from all events! diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js index 06ef8748a0..b807a4dc31 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/content.resource.js @@ -940,8 +940,116 @@ function contentResource($q, $http, umbDataFormatter, umbRequestHelper) { ), "Failed to roll back content item with id " + contentId ); - } + }, + /** + * @ngdoc method + * @name umbraco.resources.contentResource#getPublicAccess + * @methodOf umbraco.resources.contentResource + * + * @description + * Returns the public access protection for a content item + * + * ##usage + *
+          * contentResource.getPublicAccess(contentId)
+          *    .then(function(publicAccess) {
+          *        // do your thing
+          *    });
+          * 
+ * + * @param {Int} contentId The content Id + * @returns {Promise} resourcePromise object containing the public access protection + * + */ + getPublicAccess: function (contentId) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl("contentApiBaseUrl", "GetPublicAccess", { + contentId: contentId + }) + ), + "Failed to get public access for content item with id " + contentId + ); + }, + + /** + * @ngdoc method + * @name umbraco.resources.contentResource#updatePublicAccess + * @methodOf umbraco.resources.contentResource + * + * @description + * Sets or updates the public access protection for a content item + * + * ##usage + *
+          * contentResource.updatePublicAccess(contentId, userName, password, roles, loginPageId, errorPageId)
+          *    .then(function() {
+          *        // do your thing
+          *    });
+          * 
+ * + * @param {Int} contentId The content Id + * @param {Array} groups The names of the groups that should have access (if using group based protection) + * @param {Array} usernames The usernames of the members that should have access (if using member based protection) + * @param {Int} loginPageId The Id of the login page + * @param {Int} errorPageId The Id of the error page + * @returns {Promise} resourcePromise object containing the public access protection + * + */ + updatePublicAccess: function (contentId, groups, usernames, loginPageId, errorPageId) { + var publicAccess = { + contentId: contentId, + loginPageId: loginPageId, + errorPageId: errorPageId + }; + if (angular.isArray(groups) && groups.length) { + publicAccess.groups = groups; + } + else if (angular.isArray(usernames) && usernames.length) { + publicAccess.usernames = usernames; + } + else { + throw "must supply either userName/password or roles"; + } + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl("contentApiBaseUrl", "PostPublicAccess", publicAccess) + ), + "Failed to update public access for content item with id " + contentId + ); + }, + + /** + * @ngdoc method + * @name umbraco.resources.contentResource#removePublicAccess + * @methodOf umbraco.resources.contentResource + * + * @description + * Removes the public access protection for a content item + * + * ##usage + *
+          * contentResource.removePublicAccess(contentId)
+          *    .then(function() {
+          *        // do your thing
+          *    });
+          * 
+ * + * @param {Int} contentId The content Id + * @returns {Promise} resourcePromise object that's resolved once the public access has been removed + * + */ + removePublicAccess: function (contentId) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl("contentApiBaseUrl", "RemovePublicAccess", { + contentId: contentId + }) + ), + "Failed to remove public access for content item with id " + contentId + ); + } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js index de53f7ecea..86d5510ca4 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/appstate.service.js @@ -70,6 +70,8 @@ function appState(eventsService) { currentNode: null, //Whether the menu's dialog is being shown or not showMenuDialog: null, + //Whether the menu's dialog can be hidden or not + allowHideMenuDialog: true, // The dialogs template dialogTemplateUrl: null, //Whether the context menu is being shown or not @@ -369,4 +371,4 @@ angular.module('umbraco.services').factory("editorState", function() { }); return state; -}); \ No newline at end of file +}); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js index ecc92263bc..025dbdeee7 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js @@ -53,6 +53,7 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve appState.setGlobalState("showNavigation", true); appState.setMenuState("showMenu", false); appState.setMenuState("showMenuDialog", true); + appState.setMenuState("allowHideMenuDialog", true); break; case 'search': appState.setGlobalState("navMode", "search"); @@ -66,6 +67,7 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve appState.setGlobalState("navMode", "default"); appState.setMenuState("showMenu", false); appState.setMenuState("showMenuDialog", false); + appState.setMenuState("allowHideMenuDialog", true); appState.setSectionState("showSearchResults", false); appState.setGlobalState("stickyNavigation", false); appState.setGlobalState("showTray", false); @@ -570,6 +572,22 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve }, + /** + * @ngdoc method + * @name umbraco.services.navigationService#allowHideDialog + * @methodOf umbraco.services.navigationService + * + * @param {boolean} allow false if the navigation service should disregard instructions to hide the current dialog, true otherwise + * @description + * instructs the navigation service whether it's allowed to hide the current dialog + */ + allowHideDialog: function (allow) { + if (appState.getGlobalState("navMode") !== "dialog") { + return; + } + appState.setMenuState("allowHideMenuDialog", allow); + }, + /** * @ngdoc method * @name umbraco.services.navigationService#hideDialog @@ -579,7 +597,9 @@ function navigationService($routeParams, $location, $q, $timeout, $injector, eve * hides the currently open dialog */ hideDialog: function (showMenu) { - + if (appState.getMenuState("allowHideMenuDialog") === false) { + return; + } if (showMenu) { this.showMenu({ skipDefault: true, node: appState.getMenuState("currentNode") }); } else { diff --git a/src/Umbraco.Web.UI.Client/src/less/hacks.less b/src/Umbraco.Web.UI.Client/src/less/hacks.less index cd32c64782..0bbb89a250 100644 --- a/src/Umbraco.Web.UI.Client/src/less/hacks.less +++ b/src/Umbraco.Web.UI.Client/src/less/hacks.less @@ -106,7 +106,6 @@ iframe, .content-column-body { display: flex; flex-wrap: nowrap; flex-direction: row; - justify-content: center; align-items: flex-start; margin-top: 15px; diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js new file mode 100644 index 0000000000..8d80f308ab --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js @@ -0,0 +1,255 @@ +(function () { + "use strict"; + + function ContentProtectController($scope, $q, contentResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) { + + var vm = this; + var id = $scope.currentNode.id; + + vm.loading = false; + vm.buttonState = "init"; + + vm.isValid = isValid; + vm.next = next; + vm.save = save; + vm.close = close; + vm.toggle = toggle; + vm.pickLoginPage = pickLoginPage; + vm.pickErrorPage = pickErrorPage; + vm.pickGroup = pickGroup; + vm.removeGroup = removeGroup; + vm.pickMember = pickMember; + vm.removeMember = removeMember; + vm.removeProtection = removeProtection; + vm.removeProtectionConfirm = removeProtectionConfirm; + + vm.type = null; + vm.step = null; + + function onInit() { + vm.loading = true; + + // get the current public access protection + contentResource.getPublicAccess(id).then(function (publicAccess) { + vm.loading = false; + + // init the current settings for public access (if any) + vm.loginPage = publicAccess.loginPage; + vm.errorPage = publicAccess.errorPage; + vm.groups = publicAccess.groups || []; + vm.members = publicAccess.members || []; + vm.canRemove = true; + + if (vm.members.length) { + vm.type = "member"; + next(); + } + else if (vm.groups.length) { + vm.type = "group"; + next(); + } + else { + vm.canRemove = false; + } + }); + } + + function next() { + if (vm.type === "group") { + vm.loading = true; + // get all existing member groups for lookup upon selection + // NOTE: if/when member groups support infinite editing, we can't rely on using a cached lookup list of valid groups anymore + memberGroupResource.getGroups().then(function (groups) { + vm.step = vm.type; + vm.allGroups = groups; + vm.hasGroups = groups.length > 0; + vm.loading = false; + }); + } + else { + vm.step = vm.type; + } + } + + function isValid() { + if (!vm.type) { + return false; + } + if (!vm.protectForm.$valid) { + return false; + } + if (!vm.loginPage || !vm.errorPage) { + return false; + } + if (vm.type === "group") { + return vm.groups && vm.groups.length > 0; + } + if (vm.type === "member") { + return vm.members && vm.members.length > 0; + } + return true; + } + + function save() { + vm.buttonState = "busy"; + var groups = _.map(vm.groups, function (group) { return group.name; }); + var usernames = _.map(vm.members, function (member) { return member.username; }); + contentResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then( + function () { + localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) { + vm.success = { + message: value + }; + }); + navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true }); + }, function (error) { + vm.error = error; + vm.buttonState = "error"; + } + ); + } + + function close() { + // ensure that we haven't set a locked state on the dialog before closing it + navigationService.allowHideDialog(true); + navigationService.hideDialog(); + } + + function toggle(group) { + group.selected = !group.selected; + } + + function pickGroup() { + navigationService.allowHideDialog(false); + editorService.memberGroupPicker({ + multiPicker: true, + submit: function(model) { + var selectedGroupIds = model.selectedMemberGroups + ? model.selectedMemberGroups + : [model.selectedMemberGroup]; + _.each(selectedGroupIds, + function (groupId) { + // find the group in the lookup list and add it if it isn't already + var group = _.find(vm.allGroups, function(g) { return g.id === parseInt(groupId); }); + if (group && !_.find(vm.groups, function (g) { return g.id === group.id })) { + vm.groups.push(group); + } + }); + editorService.close(); + navigationService.allowHideDialog(true); + }, + close: function() { + editorService.close(); + navigationService.allowHideDialog(true); + } + }); + } + + function removeGroup(group) { + vm.groups = _.reject(vm.groups, function(g) { return g.id === group.id }); + } + + function pickMember() { + navigationService.allowHideDialog(false); + // TODO: once editorService has a memberPicker method, use that instead + editorService.treePicker({ + multiPicker: true, + entityType: "Member", + section: "member", + treeAlias: "member", + filter: function (i) { + return i.metaData.isContainer; + }, + filterCssClass: "not-allowed", + submit: function (model) { + if (model.selection && model.selection.length) { + var promises = []; + // get the selected member usernames + _.each(model.selection, + function (member) { + // TODO: + // as-is we need to fetch all the picked members one at a time to get their usernames. + // when editorService has a memberPicker method, see if this can't be avoided - otherwise + // add a memberResource.getByKeys() method to do all this in one request + promises.push( + memberResource.getByKey(member.key).then(function(newMember) { + if (!_.find(vm.members, function (currentMember) { return currentMember.username === newMember.username })) { + vm.members.push(newMember); + } + }) + ); + }); + editorService.close(); + navigationService.allowHideDialog(true); + // wait for all the member lookups to complete + vm.loading = true; + $q.all(promises).then(function() { + vm.loading = false; + }); + } + }, + close: function () { + editorService.close(); + navigationService.allowHideDialog(true); + } + }); + } + + function removeMember(member) { + vm.members = _.without(vm.members, member); + } + + function pickLoginPage() { + pickPage(vm.loginPage); + } + + function pickErrorPage() { + pickPage(vm.errorPage); + } + + function pickPage(page) { + navigationService.allowHideDialog(false); + editorService.contentPicker({ + submit: function (model) { + if (page === vm.loginPage) { + vm.loginPage = model.selection[0]; + } + else { + vm.errorPage = model.selection[0]; + } + editorService.close(); + navigationService.allowHideDialog(true); + }, + close: function () { + editorService.close(); + navigationService.allowHideDialog(true); + } + }); + } + + function removeProtection() { + vm.removing = true; + } + + function removeProtectionConfirm() { + vm.buttonState = "busy"; + contentResource.removePublicAccess(id).then( + function () { + localizationService.localize("publicAccess_paIsRemoved", [$scope.currentNode.name]).then(function(value) { + vm.success = { + message: value + }; + }); + navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true }); + }, function (error) { + vm.error = error; + vm.buttonState = "error"; + } + ); + } + + onInit(); + } + + angular.module("umbraco").controller("Umbraco.Editors.Content.ProtectController", ContentProtectController); +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/protect.html b/src/Umbraco.Web.UI.Client/src/views/content/protect.html new file mode 100644 index 0000000000..ae4a15e8c1 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/content/protect.html @@ -0,0 +1,186 @@ +
+
+
+ + +
+
+
{{vm.error.errorMsg}}
+
{{vm.error.data.message}}
+
+
+ + + + +
+

+ Choose how to restrict access to this page +

+ + +
+ + + +
+ +
+ + + +
+
+
+ +
+
+

Select the members that should have access to this page

+ + + + + Add + + +
+ +
+

You need to create a member group before you can use group based authentication

+
+ +
+

Select the groups that should have access to this page

+ + + + + Add + + +
+ +
+

Select the pages that contain login form and error messages

+ +
+
+ + + Add + + + +
+
+
+
+ + + Add + + + +
+
+
+
+
+ +
+

Are you sure you want to remove the protection from this page?

+
+ + + +
+ + +
+ + +
+
+ + +
+
diff --git a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj index ea5cbafcb0..267898e897 100644 --- a/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj +++ b/src/Umbraco.Web.UI/Umbraco.Web.UI.csproj @@ -326,7 +326,6 @@ - Designer diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 84028091ce..8d8ac8e506 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -871,22 +871,23 @@ Mange hilsner fra Umbraco robotten Indsæt, men fjern formattering som ikke bør være på en webside (Anbefales) - Rollebaseret beskyttelse - Hvis du ønsker at kontrollere adgang til siden ved hjælp af rollebaseret godkendelse via Umbracos medlemsgrupper. - Du skal oprette en medlemsgruppe før du kan bruge rollebaseret godkendelse + Gruppebaseret beskyttelse + Hvis du ønsker at give adgang til alle medlemmer af specifikke medlemsgrupper + Du skal oprette en medlemsgruppe før du kan bruge gruppebaseret beskyttelse Fejlside Brugt når folk er logget ind, men ingen adgang - Vælg hvordan siden skal beskyttes - %0% er nu beskyttet - Beskyttelse fjernet fra %0% + %0% skal beskyttes]]> + %0% er nu beskyttet]]> + %0%]]> Log ind-side Vælg siden der indeholder log ind-formularen - Fjern beskyttelse + Fjern beskyttelse... + %0%?]]> Vælg siderne der indeholder log ind-formularer og fejlmeddelelser - Vælg de roller der har adgang til denne side - Indstil login og kodeord for denne side - Enkel brugerbeskyttelse - Hvis du blot ønsker at opsætte simpel beskyttelse ved hjælp af et enkelt login og kodeord + %0%]]> + %0%]]> + Adgang til enkelte medlemmer + Hvis du ønsker at give adgang til enkelte medlemmer Udgivelsen kunne ikke udgives da publiceringsdato er sat diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 08d286d41d..cc4e694b3c 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1123,22 +1123,23 @@ To manage your website, simply open the Umbraco back office and start adding con Paste, but remove formatting (Recommended) - Role based protection - If you wish to control access to the page using role-based authentication, using Umbraco's member groups. - You need to create a membergroup before you can use role-based authentication + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication Error Page Used when people are logged on, but do not have access - Choose how to restrict access to this page - %0% is now protected - Protection removed from %0% + %0%]]> + %0% is now protected]]> + %0%]]> Login Page Choose the page that contains the login form - Remove Protection + Remove protection... + %0%?]]> Select the pages that contain login form and error messages - Pick the roles who have access to this page - Set the login and password for this page - Single user protection - If you just want to setup simple protection using a single login and password + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members Paste, but remove formatting (Recommended) - Role based protection - If you wish to control access to the page using role-based authentication, using Umbraco's member groups. - You need to create a membergroup before you can use role-based authentication + Group based protection + If you want to grant access to all members of specific member groups + You need to create a member group before you can use group based authentication Error Page Used when people are logged on, but do not have access - Choose how to restrict access to this page - %0% is now protected - Protection removed from %0% + %0%]]> + %0% is now protected]]> + %0%]]> Login Page Choose the page that contains the login form - Remove Protection + Remove protection... + %0%?]]> Select the pages that contain login form and error messages - Pick the roles who have access to this page - Set the login and password for this page - Single user protection - If you just want to setup simple protection using a single login and password + %0%]]> + %0%]]> + Specific members protection + If you wish to grant access to specific members Insufficient user permissions to publish all descendant documents diff --git a/src/Umbraco.Web.UI/Umbraco/dialogs/protectPage.aspx b/src/Umbraco.Web.UI/Umbraco/dialogs/protectPage.aspx deleted file mode 100644 index 5bcf97310f..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/dialogs/protectPage.aspx +++ /dev/null @@ -1,201 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../masterpages/umbracoDialog.Master" AutoEventWireup="True" Inherits="umbraco.presentation.umbraco.dialogs.protectPage" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - - - - -
- -
- -
- - - -
- - - -
- - - -
- - -
- - - -
- -
-
- - -
- - -
- - -
- - - - -
- -
- - - -
- - -

Member name already exists, click Change to use a different name or Update to continue

-
-
- - - -

<%= Services.TextService.Localize("publicAccess/paSelectRoles")%>

-
- - - -
- - - - - - <%=Services.TextService.Localize("paLoginPageHelp")%> - -
- -
- - -
- - - - - <%=Services.TextService.Localize("paErrorPageHelp")%> - -
- -
- -
- -
-
- -
- - -
- - - - - - - diff --git a/src/Umbraco.Web/Actions/ActionProtect.cs b/src/Umbraco.Web/Actions/ActionProtect.cs index 0e5f9f8433..cd8b883ca8 100644 --- a/src/Umbraco.Web/Actions/ActionProtect.cs +++ b/src/Umbraco.Web/Actions/ActionProtect.cs @@ -10,7 +10,9 @@ namespace Umbraco.Web.Actions /// public class ActionProtect : IAction { - public char Letter => 'P'; + public const char ActionLetter = 'P'; + + public char Letter => ActionLetter; public string Alias => "protect"; public string Category => Constants.Conventions.PermissionCategories.AdministrationCategory; public string Icon => "lock"; diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinder.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder.cs new file mode 100644 index 0000000000..f7171a840a --- /dev/null +++ b/src/Umbraco.Web/Cache/DistributedCacheBinder.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Umbraco.Core; +using Umbraco.Core.Events; +using Umbraco.Core.Logging; + +namespace Umbraco.Web.Cache +{ + /// + /// Default implementation. + /// + public partial class DistributedCacheBinder : IDistributedCacheBinder + { + private static readonly ConcurrentDictionary FoundHandlers = new ConcurrentDictionary(); + private readonly DistributedCache _distributedCache; + private readonly ILogger _logger; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public DistributedCacheBinder(DistributedCache distributedCache, ILogger logger) + { + _distributedCache = distributedCache; + _logger = logger; + } + + // internal for tests + internal static MethodInfo FindHandler(IEventDefinition eventDefinition) + { + var name = eventDefinition.Sender.GetType().Name + "_" + eventDefinition.EventName; + + return FoundHandlers.GetOrAdd(name, n => CandidateHandlers.Value.FirstOrDefault(x => x.Name == n)); + } + + private static readonly Lazy CandidateHandlers = new Lazy(() => + { + var underscore = new[] { '_' }; + + return typeof(DistributedCacheBinder) + .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) + .Select(x => + { + if (x.Name.Contains("_") == false) return null; + + var parts = x.Name.Split(underscore, StringSplitOptions.RemoveEmptyEntries).Length; + if (parts != 2) return null; + + var parameters = x.GetParameters(); + if (parameters.Length != 2) return null; + if (typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType) == false) return null; + return x; + }) + .WhereNotNull() + .ToArray(); + }); + + /// + public void HandleEvents(IEnumerable events) + { + // ensure we run with an UmbracoContext, because this may run in a background task, + // yet developers may be using the 'current' UmbracoContext in the event handlers + using (UmbracoContext.EnsureContext()) + { + foreach (var e in events) + { + var handler = FindHandler(e); + if (handler == null) + { + // fixme - should this be fatal (ie, an exception)? + var name = e.Sender.GetType().Name + "_" + e.EventName; + _logger.Warn("Dropping event {EventName} because no corresponding handler was found.", name); + continue; + } + + handler.Invoke(this, new[] { e.Sender, e.Args }); + } + } + } + } +} diff --git a/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs new file mode 100644 index 0000000000..b679ce1bfd --- /dev/null +++ b/src/Umbraco.Web/Cache/DistributedCacheBinderComponent.cs @@ -0,0 +1,24 @@ +using Umbraco.Core; +using Umbraco.Core.Components; +using Umbraco.Core.Composing; + +namespace Umbraco.Web.Cache +{ + /// + /// Installs listeners on service events in order to refresh our caches. + /// + [RuntimeLevel(MinLevel = RuntimeLevel.Run)] + [RequiredByComponent(typeof(IUmbracoCoreComponent))] // runs before every other IUmbracoCoreComponent! + public class DistributedCacheBinderComponent : UmbracoComponentBase, IUmbracoCoreComponent + { + public override void Compose(Composition composition) + { + composition.RegisterUnique(); + } + + public void Initialize(IDistributedCacheBinder distributedCacheBinder) + { + distributedCacheBinder.BindEvents(); + } + } +} diff --git a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs similarity index 86% rename from src/Umbraco.Web/Cache/CacheRefresherComponent.cs rename to src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs index 95b037c468..81b133b9ef 100644 --- a/src/Umbraco.Web/Cache/CacheRefresherComponent.cs +++ b/src/Umbraco.Web/Cache/DistributedCacheBinder_Handlers.cs @@ -1,55 +1,50 @@ using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.IO; -using Umbraco.Core; +using System.Linq; using Umbraco.Core.Events; +using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Models.Membership; using Umbraco.Core.Services; -using System.Linq; -using System.Reflection; -using System.Web; -using System.Web.Hosting; -using Umbraco.Core.Components; -using Umbraco.Core.Configuration; -using Umbraco.Core.Logging; -using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Services.Changes; using Umbraco.Core.Services.Implement; -using Umbraco.Web.Composing; -using Umbraco.Web.Security; using Umbraco.Web.Services; -using ApplicationTree = Umbraco.Core.Models.ApplicationTree; namespace Umbraco.Web.Cache { /// - /// Installs listeners on service events in order to refresh our caches. + /// Default implementation. /// - [RuntimeLevel(MinLevel = RuntimeLevel.Run)] - [RequiredByComponent(typeof(IUmbracoCoreComponent))] // runs before every other IUmbracoCoreComponent! - public class CacheRefresherComponent : UmbracoComponentBase, IUmbracoCoreComponent + public partial class DistributedCacheBinder { - private static readonly ConcurrentDictionary FoundHandlers = new ConcurrentDictionary(); - private DistributedCache _distributedCache; private List _unbinders; - public CacheRefresherComponent() - { } + private void Bind(Action binder, Action unbinder) + { + // bind now + binder(); - // for tests - public CacheRefresherComponent(bool supportUnbinding) + // and register unbinder for later, if needed + _unbinders?.Add(unbinder); + } + + /// + public void UnbindEvents() + { + if (_unbinders == null) + throw new NotSupportedException(); + foreach (var unbinder in _unbinders) + unbinder(); + _unbinders = null; + } + + /// + public void BindEvents(bool supportUnbinding = false) { if (supportUnbinding) _unbinders = new List(); - } - public void Initialize(DistributedCache distributedCache) - { - Current.Logger.Info("Initializing Umbraco internal event handlers for cache refreshing."); - - _distributedCache = distributedCache; + _logger.Info("Initializing Umbraco internal event handlers for cache refreshing."); // bind to application tree events Bind(() => ApplicationTreeService.Deleted += ApplicationTreeService_Deleted, @@ -169,74 +164,6 @@ namespace Umbraco.Web.Cache () => RelationService.DeletedRelationType -= RelationService_DeletedRelationType); } - #region Events binding and handling - - private void Bind(Action binder, Action unbinder) - { - // bind now - binder(); - - // and register unbinder for later, if needed - _unbinders?.Add(unbinder); - } - - // for tests - internal void Unbind() - { - if (_unbinders == null) - throw new NotSupportedException(); - foreach (var unbinder in _unbinders) - unbinder(); - _unbinders = null; - } - - internal static MethodInfo FindHandler(IEventDefinition eventDefinition) - { - var name = eventDefinition.Sender.GetType().Name + "_" + eventDefinition.EventName; - - return FoundHandlers.GetOrAdd(name, n => CandidateHandlers.Value.FirstOrDefault(x => x.Name == n)); - } - - private static readonly Lazy CandidateHandlers = new Lazy(() => - { - var underscore = new[] { '_' }; - - return typeof(CacheRefresherComponent) - .GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Select(x => - { - if (x.Name.Contains("_") == false) return null; - - var parts = x.Name.Split(underscore, StringSplitOptions.RemoveEmptyEntries).Length; - if (parts != 2) return null; - - var parameters = x.GetParameters(); - if (parameters.Length != 2) return null; - if (typeof(EventArgs).IsAssignableFrom(parameters[1].ParameterType) == false) return null; - return x; - }) - .WhereNotNull() - .ToArray(); - }); - - internal static void HandleEvents(IEnumerable events) - { - // ensure we run with an UmbracoContext, because this may run in a background task, - // yet developers may be using the 'current' UmbracoContext in the event handlers - using (UmbracoContext.EnsureContext()) - { - foreach (var e in events) - { - var handler = FindHandler(e); - if (handler == null) continue; - - handler.Invoke(null, new[] { e.Sender, e.Args }); - } - } - } - - #endregion - #region PublicAccessService private void PublicAccessService_Saved(IPublicAccessService sender, SaveEventArgs e) @@ -263,7 +190,8 @@ namespace Umbraco.Web.Cache /// case then we need to clear all user permissions cache. /// private void ContentService_Copied(IContentService sender, CopyEventArgs e) - { } + { + } /// /// Handles cache refreshing for when content is saved (not published) @@ -275,7 +203,8 @@ namespace Umbraco.Web.Cache /// stay up-to-date for unpublished content. /// private void ContentService_Saved(IContentService sender, SaveEventArgs e) - { } + { + } private void ContentService_Changed(IContentService sender, TreeChange.EventArgs args) { @@ -323,12 +252,12 @@ namespace Umbraco.Web.Cache #region Application event handlers - private void SectionService_New(Section sender, EventArgs e) + private void SectionService_New(ISectionService sender, EventArgs e) { _distributedCache.RefreshAllApplicationCache(); } - private void SectionService_Deleted(Section sender, EventArgs e) + private void SectionService_Deleted(ISectionService sender, EventArgs e) { _distributedCache.RefreshAllApplicationCache(); } @@ -438,7 +367,8 @@ namespace Umbraco.Web.Cache #region UserService - static void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs e) + // fixme STATIC?? + private void UserService_UserGroupPermissionsAssigned(IUserService sender, SaveEventArgs e) { //TODO: Not sure if we need this yet depends if we start caching permissions //var groupIds = e.SavedEntities.Select(x => x.UserGroupId).Distinct(); @@ -567,6 +497,7 @@ namespace Umbraco.Web.Cache _distributedCache.RemoveMemberGroupCache(m.Id); } } + #endregion #region RelationType diff --git a/src/Umbraco.Web/Cache/IDistributedCacheBinder.cs b/src/Umbraco.Web/Cache/IDistributedCacheBinder.cs new file mode 100644 index 0000000000..e4237fea94 --- /dev/null +++ b/src/Umbraco.Web/Cache/IDistributedCacheBinder.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using Umbraco.Core.Events; +using Umbraco.Core.Services.Implement; + +namespace Umbraco.Web.Cache +{ + /// + /// Binds events to the distributed cache. + /// + /// + /// Use to bind actual events, eg , to + /// the distributed cache, so that the proper refresh operations are executed when these events trigger. + /// Use to handle events that have not actually triggered, but have + /// been queued, so that the proper refresh operations are also executed. + /// + public interface IDistributedCacheBinder + { + /// + /// Handles events from definitions. + /// + void HandleEvents(IEnumerable events); + + /// + /// Binds actual events to the distributed cache. + /// + /// A value indicating whether to support unbinding the events. + void BindEvents(bool enableUnbinding = false); + + /// + /// Unbinds bounded events. + /// + void UnbindEvents(); + } +} diff --git a/src/Umbraco.Web/Editors/ContentController.cs b/src/Umbraco.Web/Editors/ContentController.cs index 2ec92c946e..55862d14b8 100644 --- a/src/Umbraco.Web/Editors/ContentController.cs +++ b/src/Umbraco.Web/Editors/ContentController.cs @@ -8,6 +8,7 @@ using System.Text; using System.Web.Http; using System.Web.Http.Controllers; using System.Web.Http.ModelBinding; +using System.Web.Security; using AutoMapper; using Umbraco.Core; using Umbraco.Core.Logging; @@ -2155,5 +2156,159 @@ namespace Umbraco.Web.Editors return Request.CreateValidationErrorResponse(notificationModel); } + + [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [HttpGet] + public HttpResponseMessage GetPublicAccess(int contentId) + { + var content = Services.ContentService.GetById(contentId); + if (content == null) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + } + + var entry = Services.PublicAccessService.GetEntryForContent(content); + if (entry == null) + { + return Request.CreateResponse(HttpStatusCode.OK); + } + + var loginPageEntity = Services.EntityService.Get(entry.LoginNodeId, UmbracoObjectTypes.Document); + var errorPageEntity = Services.EntityService.Get(entry.NoAccessNodeId, UmbracoObjectTypes.Document); + + // unwrap the current public access setup for the client + // - this API method is the single point of entry for both "modes" of public access (single user and role based) + var usernames = entry.Rules + .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType) + .Select(rule => rule.RuleValue).ToArray(); + + MemberDisplay[] members; + switch (Services.MemberService.GetMembershipScenario()) + { + case MembershipScenario.NativeUmbraco: + members = usernames + .Select(username => Services.MemberService.GetByUsername(username)) + .Where(member => member != null) + .Select(Mapper.Map) + .ToArray(); + break; + // TODO: test support custom membership providers + case MembershipScenario.CustomProviderWithUmbracoLink: + case MembershipScenario.StandaloneCustomProvider: + default: + var provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); + members = usernames + .Select(username => provider.GetUser(username, false)) + .Where(membershipUser => membershipUser != null) + .Select(Mapper.Map) + .ToArray(); + break; + } + + var allGroups = Services.MemberGroupService.GetAll().ToArray(); + var groups = entry.Rules + .Where(rule => rule.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType) + .Select(rule => allGroups.FirstOrDefault(g => g.Name == rule.RuleValue)) + .Where(memberGroup => memberGroup != null) + .Select(Mapper.Map) + .ToArray(); + + return Request.CreateResponse(HttpStatusCode.OK, new PublicAccess + { + Members = members, + Groups = groups, + LoginPage = loginPageEntity != null ? Mapper.Map(loginPageEntity) : null, + ErrorPage = errorPageEntity != null ? Mapper.Map(errorPageEntity) : null + }); + } + + // set up public access using role based access + [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [HttpPost] + public HttpResponseMessage PostPublicAccess(int contentId, [FromUri]string[] groups, [FromUri]string[] usernames, int loginPageId, int errorPageId) + { + if ((groups == null || groups.Any() == false) && (usernames == null || usernames.Any() == false)) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); + } + + var content = Services.ContentService.GetById(contentId); + var loginPage = Services.ContentService.GetById(loginPageId); + var errorPage = Services.ContentService.GetById(errorPageId); + if (content == null || loginPage == null || errorPage == null) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest)); + } + + var isGroupBased = groups != null && groups.Any(); + var candidateRuleValues = isGroupBased + ? groups + : usernames; + var newRuleType = isGroupBased + ? Constants.Conventions.PublicAccess.MemberRoleRuleType + : Constants.Conventions.PublicAccess.MemberUsernameRuleType; + + var entry = Services.PublicAccessService.GetEntryForContent(content); + + if (entry == null) + { + entry = new PublicAccessEntry(content, loginPage, errorPage, new List()); + + foreach (var ruleValue in candidateRuleValues) + { + entry.AddRule(ruleValue, newRuleType); + } + } + else + { + entry.LoginNodeId = loginPage.Id; + entry.NoAccessNodeId = errorPage.Id; + + var currentRules = entry.Rules.ToArray(); + var obsoleteRules = currentRules.Where(rule => + rule.RuleType != newRuleType + || candidateRuleValues.Contains(rule.RuleValue) == false + ); + var newRuleValues = candidateRuleValues.Where(group => + currentRules.Any(rule => + rule.RuleType == newRuleType + && rule.RuleValue == group + ) == false + ); + foreach (var rule in obsoleteRules) + { + entry.RemoveRule(rule); + } + foreach (var ruleValue in newRuleValues) + { + entry.AddRule(ruleValue, newRuleType); + } + } + + return Services.PublicAccessService.Save(entry).Success + ? Request.CreateResponse(HttpStatusCode.OK) + : Request.CreateResponse(HttpStatusCode.InternalServerError); + } + + [EnsureUserPermissionForContent("contentId", ActionProtect.ActionLetter)] + [HttpPost] + public HttpResponseMessage RemovePublicAccess(int contentId) + { + var content = Services.ContentService.GetById(contentId); + if (content == null) + { + throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); + } + + var entry = Services.PublicAccessService.GetEntryForContent(content); + if (entry == null) + { + return Request.CreateResponse(HttpStatusCode.OK); + } + + return Services.PublicAccessService.Delete(entry).Success + ? Request.CreateResponse(HttpStatusCode.OK) + : Request.CreateResponse(HttpStatusCode.InternalServerError); + } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs new file mode 100644 index 0000000000..dcf2dcae92 --- /dev/null +++ b/src/Umbraco.Web/Models/ContentEditing/PublicAccess.cs @@ -0,0 +1,20 @@ +using System.Runtime.Serialization; + +namespace Umbraco.Web.Models.ContentEditing +{ + [DataContract(Name = "publicAccess", Namespace = "")] + public class PublicAccess + { + [DataMember(Name = "groups")] + public MemberGroupDisplay[] Groups { get; set; } + + [DataMember(Name = "loginPage")] + public EntityBasic LoginPage { get; set; } + + [DataMember(Name = "errorPage")] + public EntityBasic ErrorPage { get; set; } + + [DataMember(Name = "members")] + public MemberDisplay[] Members { get; set; } + } +} diff --git a/src/Umbraco.Web/Routing/PublishedRouter.cs b/src/Umbraco.Web/Routing/PublishedRouter.cs index 263b97dd0d..1ff33e42bc 100644 --- a/src/Umbraco.Web/Routing/PublishedRouter.cs +++ b/src/Umbraco.Web/Routing/PublishedRouter.cs @@ -637,7 +637,7 @@ namespace Umbraco.Web.Routing if (loginPageId != request.PublishedContent.Id) request.PublishedContent = request.UmbracoContext.PublishedSnapshot.Content.GetById(loginPageId); } - else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) + else if (_services.PublicAccessService.HasAccess(request.PublishedContent.Id, _services.ContentService, membershipHelper.CurrentUserName, GetRolesForLogin(membershipHelper.CurrentUserName)) == false) { _logger.Debug("EnsurePublishedContentAccess: Current member has not access, redirect to error page"); var errorPageId = publicAccessAttempt.Result.NoAccessNodeId; diff --git a/src/Umbraco.Web/Services/SectionService.cs b/src/Umbraco.Web/Services/SectionService.cs index ff8279a411..5d013d7e79 100644 --- a/src/Umbraco.Web/Services/SectionService.cs +++ b/src/Umbraco.Web/Services/SectionService.cs @@ -200,7 +200,7 @@ namespace Umbraco.Web.Services }, true); //raise event - OnNew(new Section(name, alias, sortOrder), new EventArgs()); + OnNew(this /*new Section(name, alias, sortOrder)*/, new EventArgs()); } } @@ -235,7 +235,7 @@ namespace Umbraco.Web.Services }, true); //raise event - OnDeleted(section, new EventArgs()); + OnDeleted(this, new EventArgs()); } } @@ -262,8 +262,8 @@ namespace Umbraco.Web.Services return tmp; } - internal static event TypedEventHandler Deleted; - private static void OnDeleted(Section app, EventArgs args) + internal static event TypedEventHandler Deleted; + private static void OnDeleted(ISectionService app, EventArgs args) { if (Deleted != null) { @@ -271,8 +271,8 @@ namespace Umbraco.Web.Services } } - internal static event TypedEventHandler New; - private static void OnNew(Section app, EventArgs args) + internal static event TypedEventHandler New; + private static void OnNew(ISectionService app, EventArgs args) { if (New != null) { diff --git a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs index 058df58c17..2bba17a729 100644 --- a/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs +++ b/src/Umbraco.Web/Trees/LegacyTreeDataConverter.cs @@ -55,11 +55,6 @@ namespace Umbraco.Web.Trees new LegacyUrlAction( "create.aspx?nodeId=" + nodeId + "&nodeType=" + nodeType + "&nodeName=" + nodeName + "&rnd=" + DateTime.UtcNow.Ticks, Current.Services.TextService.Localize("actions/create"))); - case ActionProtect actionProtect: - return Attempt.Succeed( - new LegacyUrlAction( - "dialogs/protectPage.aspx?mode=cut&nodeId=" + nodeId + "&rnd=" + DateTime.UtcNow.Ticks, - Current.Services.TextService.Localize("actions/protect"))); } return Attempt.Fail(); } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 2709335365..5c1e6ba730 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -108,7 +108,10 @@ + + + @@ -157,6 +160,7 @@ + @@ -656,7 +660,7 @@ - + @@ -1014,9 +1018,6 @@ ASPXCodeBehind - - ASPXCodeBehind - diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs deleted file mode 100644 index 07d13230e5..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs +++ /dev/null @@ -1,745 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Web; -using System.Web.Security; -using System.Web.UI.WebControls; -using Umbraco.Core; -using Umbraco.Core.Logging; -using umbraco.controls; -using Umbraco.Core.Models; -using Umbraco.Core.Security; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web.UI.Pages; -using MembershipProviderExtensions = Umbraco.Core.Security.MembershipProviderExtensions; - -namespace umbraco.presentation.umbraco.dialogs -{ - /// - /// Summary description for protectPage. - /// - public partial class protectPage : UmbracoEnsuredPage - { - public protectPage() - { - CurrentApp = Constants.Applications.Content.ToString(); - } - - protected Literal jsShowWindow; - protected DualSelectbox _memberGroups = new DualSelectbox(); - protected ContentPicker loginPagePicker = new ContentPicker(); - protected ContentPicker errorPagePicker = new ContentPicker(); - - private const string MemberIdRuleType = "MemberId"; // moved from Constants-Conventions.cs - - - override protected void OnInit(EventArgs e) - { - base.OnInit(e); - } - - protected void selectMode(object sender, EventArgs e) - { - p_mode.Visible = false; - p_setup.Visible = true; - - if (rb_simple.Checked) - { - pane_advanced.Visible = false; - pane_simple.Visible = true; - bt_protect.CommandName = "simple"; - } - else - { - pane_advanced.Visible = true; - pane_simple.Visible = false; - bt_protect.CommandName = "advanced"; - } - } - - private ProtectionType GetProtectionType(int documentId) - { - var content = Services.ContentService.GetById(documentId); - if (content == null) return ProtectionType.NotProtected; - - var entry = Services.PublicAccessService.GetEntryForContent(content); - if (entry == null) return ProtectionType.NotProtected; - - //legacy states that if it is protected by a member id then it is 'simple' - return entry.Rules.Any(x => x.RuleType == MemberIdRuleType) - ? ProtectionType.Simple - : ProtectionType.Advanced; - } - - private enum ProtectionType - { - NotProtected, - Simple, - Advanced - } - - private int GetErrorPage(string path) - { - var entry = Services.PublicAccessService.GetEntryForContent(path); - if (entry == null) return -1; - var entity = Services.EntityService.Get(entry.NoAccessNodeId, UmbracoObjectTypes.Document, false); - return entity.Id; - } - - private int GetLoginPage(string path) - { - var entry = Services.PublicAccessService.GetEntryForContent(path); - if (entry == null) return -1; - var entity = Services.EntityService.Get(entry.LoginNodeId, UmbracoObjectTypes.Document, false); - return entity.Id; - } - - private MembershipUser GetAccessingMembershipUser(int documentId) - { - var content = Services.ContentService.GetById(documentId); - if (content == null) return null; - var entry = Services.PublicAccessService.GetEntryForContent(content); - if (entry == null) return null; - //legacy would throw an exception here if it was not 'simple' and simple means based on a username - if (entry.Rules.All(x => x.RuleType != Constants.Conventions.PublicAccess.MemberUsernameRuleType)) - { - throw new Exception("Document isn't protected using Simple mechanism. Use GetAccessingMemberGroups instead"); - } - var provider = MembershipProviderExtensions.GetMembersMembershipProvider(); - var usernameRule = entry.Rules.First(x => x.RuleType == Constants.Conventions.PublicAccess.MemberUsernameRuleType); - return provider.GetUser(usernameRule.RuleValue, false); - } - - private bool IsProtectedByMembershipRole(int documentId, string role) - { - var content = Services.ContentService.GetById(documentId); - var entry = Services.PublicAccessService.GetEntryForContent(content); - if (entry == null) return false; - return entry.Rules - .Any(x => x.RuleType == Constants.Conventions.PublicAccess.MemberRoleRuleType - && x.RuleValue == role); - } - - private void ProtectPage(bool Simple, int DocumentId, int LoginDocumentId, int ErrorDocumentId) - { - var content = Current.Services.ContentService.GetById(DocumentId); - var loginContent = Services.ContentService.GetById(LoginDocumentId); - if (loginContent == null) throw new NullReferenceException("No content item found with id " + LoginDocumentId); - var noAccessContent = Services.ContentService.GetById(ErrorDocumentId); - if (noAccessContent == null) throw new NullReferenceException("No content item found with id " + ErrorDocumentId); - - var entry = Services.PublicAccessService.GetEntryForContent(content.Id.ToString()); - if (entry != null) - { - if (Simple) - { - // if using simple mode, make sure that all existing groups are removed - entry.ClearRules(); - } - - //ensure the correct ids are applied - entry.LoginNodeId = loginContent.Id; - entry.NoAccessNodeId = noAccessContent.Id; - } - else - { - entry = new PublicAccessEntry(content, - Services.ContentService.GetById(LoginDocumentId), - Services.ContentService.GetById(ErrorDocumentId), - new List()); - } - Services.PublicAccessService.Save(entry); - } - - private void AddMembershipRoleToDocument(int documentId, string role) - { - //event - var content = Current.Services.ContentService.GetById(documentId); - - var entry = Services.PublicAccessService.AddRule( - content, - Constants.Conventions.PublicAccess.MemberRoleRuleType, - role); - - if (entry.Success == false && entry.Result.Entity == null) - { - throw new Exception("Document is not protected!"); - } - } - - private void AddMembershipUserToDocument(int documentId, string membershipUserName) - { - //event - var content = Current.Services.ContentService.GetById(documentId); - var entry = Services.PublicAccessService.AddRule( - content, - Constants.Conventions.PublicAccess.MemberUsernameRuleType, - membershipUserName); - - if (entry.Success == false && entry.Result.Entity == null) - { - throw new Exception("Document is not protected!"); - } - } - - private void RemoveMembershipRoleFromDocument(int documentId, string role) - { - var content = Current.Services.ContentService.GetById(documentId); - Services.PublicAccessService.RemoveRule( - content, - Constants.Conventions.PublicAccess.MemberRoleRuleType, - role); - } - - private void RemoveProtection(int documentId) - { - var content = Current.Services.ContentService.GetById(documentId); - var entry = Services.PublicAccessService.GetEntryForContent(content); - if (entry != null) - { - Services.PublicAccessService.Delete(entry); - } - } - - protected void Page_Load(object sender, EventArgs e) - { - // Check for editing - int documentId = int.Parse(Request.GetItemAsString("nodeId")); - var content = Current.Services.ContentService.GetById(documentId); - jsShowWindow.Text = ""; - - ph_errorpage.Controls.Add(errorPagePicker); - ph_loginpage.Controls.Add(loginPagePicker); - - pp_login.Text = Services.TextService.Localize("login"); - pp_pass.Text = Services.TextService.Localize("password"); - pp_loginPage.Text = Services.TextService.Localize("paLoginPage"); - pp_errorPage.Text = Services.TextService.Localize("paErrorPage"); - - pane_chooseMode.Text = Services.TextService.Localize("publicAccess/paHowWould"); - pane_pages.Text = Services.TextService.Localize("publicAccess/paSelectPages"); - pane_simple.Text = Services.TextService.Localize("publicAccess/paSimple"); - pane_advanced.Text = Services.TextService.Localize("publicAccess/paAdvanced"); - - if (IsPostBack == false) - { - if (Services.PublicAccessService.IsProtected(documentId.ToString()) - && GetProtectionType(documentId) != ProtectionType.NotProtected) - { - bt_buttonRemoveProtection.Visible = true; - bt_buttonRemoveProtection.Attributes.Add("onClick", "return confirm('" + Services.TextService.Localize("areyousure") + "')"); - - // Get login and error pages - int errorPage = GetErrorPage(content.Path); - int loginPage = GetLoginPage(content.Path); - try - { - var loginPageContent = Current.Services.ContentService.GetById(loginPage); - if (loginPageContent != null) - { - loginPagePicker.Value = loginPage.ToString(CultureInfo.InvariantCulture); - } - var errorPageContent = Current.Services.ContentService.GetById(errorPage); - errorPagePicker.Value = errorPage.ToString(CultureInfo.InvariantCulture); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred initializing the protect page editor"); - } - - if (GetProtectionType(documentId) == ProtectionType.Simple) - { - MembershipUser m = GetAccessingMembershipUser(documentId); - if (m != null) - { - pane_simple.Visible = true; - pp_pass.Visible = false; - simpleLogin.Visible = false; - SimpleLoginLabel.Visible = true; - SimpleLoginLabel.Text = m.UserName; - pane_advanced.Visible = false; - bt_protect.CommandName = "simple"; - } - - } - else if (GetProtectionType(documentId) == ProtectionType.Advanced) - { - pane_simple.Visible = false; - pane_advanced.Visible = true; - bt_protect.CommandName = "advanced"; - } - - p_mode.Visible = false; - p_setup.Visible = true; - } - } - - // Load up membergrouops - _memberGroups.ID = "Membergroups"; - _memberGroups.Width = 175; - _memberGroups.Height = 165; - var selectedGroups = ""; - - // get roles from the membership provider - var roles = Roles.GetAllRoles().OrderBy(x => x).ToArray(); - - if (roles.Length > 0) - { - foreach (var role in roles) - { - var listItem = new ListItem(role, role); - _memberGroups.Items.Add(listItem); - if (IsPostBack) continue; - - // first time, initialize selected roles - if (IsProtectedByMembershipRole(documentId, role)) - selectedGroups += role + ","; - } - } - else - { - p_noGroupsFound.Visible = true; - rb_advanced.Enabled = false; - } - - _memberGroups.Value = selectedGroups; - groupsSelector.Controls.Add(_memberGroups); - - - bt_protect.Text = Services.TextService.Localize("buttons", "select"); - bt_buttonRemoveProtection.Text = Services.TextService.Localize("paRemoveProtection"); - - // Put user code to initialize the page here - } - - protected void ChangeOnClick(object sender, EventArgs e) - { - SimpleLoginNameValidator.IsValid = true; - SimpleLoginLabel.Visible = false; - simpleLogin.Visible = true; - pp_pass.Visible = true; - } - - protected void protect_Click(object sender, CommandEventArgs e) - { - if (string.IsNullOrEmpty(errorPagePicker.Value) || errorPagePicker.Value == "-1") - cv_errorPage.IsValid = false; - - if (string.IsNullOrEmpty(loginPagePicker.Value) || loginPagePicker.Value == "-1") - cv_loginPage.IsValid = false; - - //reset - SimpleLoginNameValidator.IsValid = true; - - var provider = Umbraco.Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); - var pageId = int.Parse(Request.GetItemAsString("nodeId")); - - if (e.CommandName == "simple") - { - var memberLogin = simpleLogin.Visible ? simpleLogin.Text : SimpleLoginLabel.Text; - - var member = provider.GetUser(memberLogin, false); - if (member == null) - { - var tempEmail = "u" + Guid.NewGuid().ToString("N") + "@example.com"; - - // this needs to work differently depending on umbraco members or external membership provider - if (provider.IsUmbracoMembershipProvider() == false) - { - member = provider.CreateUser(memberLogin, simplePassword.Text, tempEmail); - } - else - { - //if it's the umbraco membership provider, then we need to tell it what member type to create it with - if (Current.Services.MemberTypeService.Get(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) - //if (MemberType.GetByAlias(Constants.Conventions.MemberTypes.SystemDefaultProtectType) == null) - { - var defm = new MemberType(null, Constants.Conventions.MemberTypes.SystemDefaultProtectType); - Current.Services.MemberTypeService.Save(defm); - } - var castedProvider = provider.AsUmbracoMembershipProvider(); - MembershipCreateStatus status; - member = castedProvider.CreateUser(Constants.Conventions.MemberTypes.SystemDefaultProtectType, - memberLogin, simplePassword.Text, tempEmail, null, null, true, null, out status); - if (status != MembershipCreateStatus.Success) - { - SimpleLoginNameValidator.IsValid = false; - SimpleLoginNameValidator.ErrorMessage = "Could not create user: " + status; - SimpleLoginNameValidator.Text = "Could not create user: " + status; - return; - } - } - } - else if (pp_pass.Visible) - { - SimpleLoginNameValidator.IsValid = false; - SimpleLoginLabel.Visible = true; - SimpleLoginLabel.Text = memberLogin; - simpleLogin.Visible = false; - pp_pass.Visible = false; - return; - } - - // Create or find a memberGroup - var simpleRoleName = "__umbracoRole_" + member.UserName; - if (Roles.RoleExists(simpleRoleName) == false) - { - Roles.CreateRole(simpleRoleName); - } - if (Roles.IsUserInRole(member.UserName, simpleRoleName) == false) - { - Roles.AddUserToRole(member.UserName, simpleRoleName); - } - - ProtectPage(true, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); - AddMembershipRoleToDocument(pageId, simpleRoleName); - AddMembershipUserToDocument(pageId, member.UserName); - } - else if (e.CommandName == "advanced") - { - if (cv_errorPage.IsValid && cv_loginPage.IsValid) - { - ProtectPage(false, pageId, int.Parse(loginPagePicker.Value), int.Parse(errorPagePicker.Value)); - - foreach (ListItem li in _memberGroups.Items) - if (("," + _memberGroups.Value + ",").IndexOf("," + li.Value + ",", StringComparison.Ordinal) > -1) - AddMembershipRoleToDocument(pageId, li.Value); - else - RemoveMembershipRoleFromDocument(pageId, li.Value); - } - else - { - return; - } - } - - var content = Services.ContentService.GetById(pageId); - var text = content == null ? "" : content.Name; - feedback_text.Text = HttpUtility.HtmlEncode(Services.TextService.Localize("publicAccess/paIsProtected", new[] { text })); - - p_setup.Visible = false; - p_feedback.Visible = true; - - //reloads the current node in the tree - ClientTools.SyncTree(content.Path, true); - //reloads the current node's children in the tree - ClientTools.ReloadActionNode(false, true); - } - - - protected void buttonRemoveProtection_Click(object sender, System.EventArgs e) - { - int pageId = int.Parse(Request.GetItemAsString("nodeId")); - p_setup.Visible = false; - - RemoveProtection(pageId); - - var content = Services.ContentService.GetById(pageId); - var text = content == null ? "" : content.Name; - feedback_text.Text = HttpUtility.HtmlEncode(Services.TextService.Localize("publicAccess/paIsRemoved", new[] { text })); - p_feedback.Visible = true; - - - //reloads the current node in the tree - ClientTools.SyncTree(content.Path, true); - //reloads the current node's children in the tree - ClientTools.ReloadActionNode(false, true); - } - - protected CustomValidator SimpleLoginNameValidator; - protected Label SimpleLoginLabel; - - /// - /// tempFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden tempFile; - - /// - /// p_feedback control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_feedback; - - /// - /// p_mode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_mode; - - /// - /// p_setup control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_setup; - - /// - /// pane_chooseMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_chooseMode; - - /// - /// rb_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_simple; - - /// - /// rb_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RadioButton rb_advanced; - - /// - /// p_noGroupsFound control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Panel p_noGroupsFound; - - /// - /// bt_selectMode control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_selectMode; - - /// - /// pane_simple control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_simple; - - /// - /// PropertyPanel1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel1; - - /// - /// pp_login control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_login; - - /// - /// simpleLogin control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simpleLogin; - - /// - /// pp_pass control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_pass; - - /// - /// simplePassword control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox simplePassword; - - /// - /// pane_advanced control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_advanced; - - /// - /// PropertyPanel3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel3; - - /// - /// PropertyPanel2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel PropertyPanel2; - - /// - /// groupsSelector control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder groupsSelector; - - /// - /// pane_pages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane_pages; - - /// - /// pp_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_loginPage; - - /// - /// ph_loginpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_loginpage; - - /// - /// cv_loginPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_loginPage; - - /// - /// pp_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_errorPage; - - /// - /// ph_errorpage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder ph_errorpage; - - /// - /// cv_errorPage control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator cv_errorPage; - - /// - /// bt_protect control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_protect; - - /// - /// bt_buttonRemoveProtection control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button bt_buttonRemoveProtection; - - /// - /// errorId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden errorId; - - /// - /// loginId control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.HtmlControls.HtmlInputHidden loginId; - - /// - /// js control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder js; - - /// - /// feedback_text control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal feedback_text; - - - } -}