using System; using System.Collections.Generic; using System.Linq; using Umbraco.Core.Events; using Umbraco.Core.Logging; using Umbraco.Core.Models; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { public class PublicAccessService : ScopeRepositoryService, IPublicAccessService { private readonly IPublicAccessRepository _publicAccessRepository; public PublicAccessService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IPublicAccessRepository publicAccessRepository) : base(provider, logger, eventMessagesFactory) { _publicAccessRepository = publicAccessRepository; } /// /// Gets all defined entries and associated rules /// /// public IEnumerable GetAll() { using (var scope = ScopeProvider.CreateScope(autoComplete: true)) { return _publicAccessRepository.GetMany(); } } /// /// Gets the entry defined for the content item's path /// /// /// Returns null if no entry is found public PublicAccessEntry GetEntryForContent(IContent content) { return GetEntryForContent(content.Path.EnsureEndsWith("," + content.Id)); } /// /// Gets the entry defined for the content item based on a content path /// /// /// Returns null if no entry is found /// /// NOTE: This method get's called *very* often! This will return the results from cache /// public PublicAccessEntry GetEntryForContent(string contentPath) { //Get all ids in the path for the content item and ensure they all // parse to ints that are not -1. var ids = contentPath.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) .Select(x => int.TryParse(x, out int val) ? val : -1) .Where(x => x != -1) .ToList(); //start with the deepest id ids.Reverse(); using (var scope = ScopeProvider.CreateScope()) { //This will retrieve from cache! var entries = _publicAccessRepository.GetMany().ToArray(); scope.Complete(); foreach (var id in ids) { var found = entries.FirstOrDefault(x => x.ProtectedNodeId == id); if (found != null) return found; } } return null; } /// /// Returns true if the content has an entry for it's path /// /// /// public Attempt IsProtected(IContent content) { var result = GetEntryForContent(content); return Attempt.If(result != null, result); } /// /// Returns true if the content has an entry based on a content path /// /// /// public Attempt IsProtected(string contentPath) { var result = GetEntryForContent(contentPath); return Attempt.If(result != null, result); } /// /// Adds a rule /// /// /// /// /// public Attempt> AddRule(IContent content, string ruleType, string ruleValue) { var evtMsgs = EventMessagesFactory.Get(); PublicAccessEntry entry; using (var scope = ScopeProvider.CreateScope()) { entry = _publicAccessRepository.GetMany().FirstOrDefault(x => x.ProtectedNodeId == content.Id); if (entry == null) return OperationResult.Attempt.Cannot(evtMsgs); // causes rollback // causes rollback var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue); if (existingRule == null) { entry.AddRule(ruleValue, ruleType); } else { //If they are both the same already then there's nothing to update, exit //If they are both the same already then there's nothing to update, exit return OperationResult.Attempt.Succeed(evtMsgs, entry); } var saveEventArgs = new SaveEventArgs(entry, evtMsgs); if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs, entry); } _publicAccessRepository.Save(entry); scope.Complete(); saveEventArgs.CanCancel = false; scope.Events.Dispatch(Saved, this, saveEventArgs); } return OperationResult.Attempt.Succeed(evtMsgs, entry); } /// /// Removes a rule /// /// /// /// public Attempt RemoveRule(IContent content, string ruleType, string ruleValue) { var evtMsgs = EventMessagesFactory.Get(); PublicAccessEntry entry; using (var scope = ScopeProvider.CreateScope()) { entry = _publicAccessRepository.GetMany().FirstOrDefault(x => x.ProtectedNodeId == content.Id); if (entry == null) return Attempt.Fail(); // causes rollback // causes rollback var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue); if (existingRule == null) return Attempt.Fail(); // causes rollback // causes rollback entry.RemoveRule(existingRule); var saveEventArgs = new SaveEventArgs(entry, evtMsgs); if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); } _publicAccessRepository.Save(entry); scope.Complete(); saveEventArgs.CanCancel = false; scope.Events.Dispatch(Saved, this, saveEventArgs); } return OperationResult.Attempt.Succeed(evtMsgs); } /// /// Saves the entry /// /// public Attempt Save(PublicAccessEntry entry) { var evtMsgs = EventMessagesFactory.Get(); using (var scope = ScopeProvider.CreateScope()) { var saveEventArgs = new SaveEventArgs(entry, evtMsgs); if (scope.Events.DispatchCancelable(Saving, this, saveEventArgs)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); } _publicAccessRepository.Save(entry); scope.Complete(); saveEventArgs.CanCancel = false; scope.Events.Dispatch(Saved, this, saveEventArgs); } return OperationResult.Attempt.Succeed(evtMsgs); } /// /// Deletes the entry and all associated rules /// /// public Attempt Delete(PublicAccessEntry entry) { var evtMsgs = EventMessagesFactory.Get(); using (var scope = ScopeProvider.CreateScope()) { var deleteEventArgs = new DeleteEventArgs(entry, evtMsgs); if (scope.Events.DispatchCancelable(Deleting, this, deleteEventArgs)) { scope.Complete(); return OperationResult.Attempt.Cancel(evtMsgs); } _publicAccessRepository.Delete(entry); scope.Complete(); deleteEventArgs.CanCancel = false; scope.Events.Dispatch(Deleted, this, deleteEventArgs); } return OperationResult.Attempt.Succeed(evtMsgs); } /// /// Occurs before Save /// public static event TypedEventHandler> Saving; /// /// Occurs after Save /// public static event TypedEventHandler> Saved; /// /// Occurs before Delete /// public static event TypedEventHandler> Deleting; /// /// Occurs after Delete /// public static event TypedEventHandler> Deleted; } }