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.Persistence.UnitOfWork; namespace Umbraco.Core.Services { public class PublicAccessService : ScopeRepositoryService, IPublicAccessService { public PublicAccessService(IScopeUnitOfWorkProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory) : base(provider, logger, eventMessagesFactory) { } /// /// Gets all defined entries and associated rules /// /// public IEnumerable GetAll() { using (var uow = UowProvider.CreateUnitOfWork(readOnly: true)) { var repo = uow.CreateRepository(); return repo.GetAll(); } } /// /// 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 uow = UowProvider.CreateUnitOfWork()) { var repo = uow.CreateRepository(); //This will retrieve from cache! var entries = repo.GetAll().ToArray(); uow.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 uow = UowProvider.CreateUnitOfWork()) { var repo = uow.CreateRepository(); entry = repo.GetAll().FirstOrDefault(x => x.ProtectedNodeId == content.Id); if (entry == null) return OperationStatus.Attempt.Cannot(evtMsgs); // 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 return OperationStatus.Attempt.Succeed(evtMsgs, entry); } if (Saving.IsRaisedEventCancelled(new SaveEventArgs(entry, evtMsgs), this)) return OperationStatus.Attempt.Cancel(evtMsgs, entry); // causes rollback repo.AddOrUpdate(entry); uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(entry, false, evtMsgs), this); return OperationStatus.Attempt.Succeed(evtMsgs, entry); } /// /// Removes a rule /// /// /// /// public Attempt RemoveRule(IContent content, string ruleType, string ruleValue) { var evtMsgs = EventMessagesFactory.Get(); PublicAccessEntry entry; using (var uow = UowProvider.CreateUnitOfWork()) { var repo = uow.CreateRepository(); entry = repo.GetAll().FirstOrDefault(x => x.ProtectedNodeId == content.Id); if (entry == null) return Attempt.Fail(); // causes rollback var existingRule = entry.Rules.FirstOrDefault(x => x.RuleType == ruleType && x.RuleValue == ruleValue); if (existingRule == null) return Attempt.Fail(); // causes rollback entry.RemoveRule(existingRule); if (Saving.IsRaisedEventCancelled(new SaveEventArgs(entry, evtMsgs), this)) return OperationStatus.Attempt.Cancel(evtMsgs); // causes rollback repo.AddOrUpdate(entry); uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(entry, false, evtMsgs), this); return OperationStatus.Attempt.Succeed(evtMsgs); } /// /// Saves the entry /// /// public Attempt Save(PublicAccessEntry entry) { var evtMsgs = EventMessagesFactory.Get(); if (Saving.IsRaisedEventCancelled( new SaveEventArgs(entry, evtMsgs), this)) { return OperationStatus.Attempt.Cancel(evtMsgs); } using (var uow = UowProvider.CreateUnitOfWork()) { var repo = uow.CreateRepository(); repo.AddOrUpdate(entry); uow.Complete(); } Saved.RaiseEvent(new SaveEventArgs(entry, false, evtMsgs), this); return OperationStatus.Attempt.Succeed(evtMsgs); } /// /// Deletes the entry and all associated rules /// /// public Attempt Delete(PublicAccessEntry entry) { var evtMsgs = EventMessagesFactory.Get(); if (Deleting.IsRaisedEventCancelled( new DeleteEventArgs(entry, evtMsgs), this)) { return OperationStatus.Attempt.Cancel(evtMsgs); } using (var uow = UowProvider.CreateUnitOfWork()) { var repo = uow.CreateRepository(); repo.Delete(entry); uow.Complete(); } Deleted.RaiseEvent(new DeleteEventArgs(entry, false, evtMsgs), this); return OperationStatus.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; } }