From 531306a09102d79665660f9689ee04b684c3af2f Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 10 Jan 2014 17:03:00 +1100 Subject: [PATCH] Created notifications repository and service with unit tests, then need to start integrating that so notifications are properly sent. --- src/Umbraco.Core/Models/Notification.cs | 23 +++ .../Repositories/NotificationsRepository.cs | 85 ++++++++++ .../Services/INotificationService.cs | 66 ++++++++ .../Services/NotificationService.cs | 103 +++++++++++++ src/Umbraco.Core/Umbraco.Core.csproj | 4 + .../Repositories/ContentRepositoryTest.cs | 1 - .../NotificationsRepositoryTest.cs | 145 ++++++++++++++++++ src/Umbraco.Tests/Umbraco.Tests.csproj | 1 + src/umbraco.cms/businesslogic/CMSNode.cs | 1 + 9 files changed, 428 insertions(+), 1 deletion(-) create mode 100644 src/Umbraco.Core/Models/Notification.cs create mode 100644 src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs create mode 100644 src/Umbraco.Core/Services/INotificationService.cs create mode 100644 src/Umbraco.Core/Services/NotificationService.cs create mode 100644 src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs diff --git a/src/Umbraco.Core/Models/Notification.cs b/src/Umbraco.Core/Models/Notification.cs new file mode 100644 index 0000000000..e5d3236b5e --- /dev/null +++ b/src/Umbraco.Core/Models/Notification.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Umbraco.Core.Models +{ + internal class Notification + { + public Notification(int entityId, int userId, string action, Guid entityType) + { + EntityId = entityId; + UserId = userId; + Action = action; + EntityType = entityType; + } + + public int EntityId { get; private set; } + public int UserId { get; private set; } + public string Action { get; private set; } + public Guid EntityType { get; private set; } + } +} diff --git a/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs new file mode 100644 index 0000000000..a15a7e1521 --- /dev/null +++ b/src/Umbraco.Core/Persistence/Repositories/NotificationsRepository.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.UnitOfWork; + +namespace Umbraco.Core.Persistence.Repositories +{ + internal class NotificationsRepository + { + private readonly IDatabaseUnitOfWork _unitOfWork; + + public NotificationsRepository(IDatabaseUnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public IEnumerable GetUserNotifications(IUser user) + { + var sql = new Sql() + .Select("DISTINCT umbracoNode.id, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => dto.UserId == (int)user.Id) + .OrderBy(dto => dto.NodeId); + + var dtos = _unitOfWork.Database.Fetch(sql); + //need to map the results + return dtos.Select(d => new Notification(d.id, d.userId, d.action, d.nodeObjectType)).ToList(); + } + + public IEnumerable GetEntityNotifications(IEntity entity) + { + var sql = new Sql() + .Select("DISTINCT umbracoNode.id, umbracoUser2NodeNotify.userId, umbracoNode.nodeObjectType, umbracoUser2NodeNotify.action") + .From() + .InnerJoin() + .On(dto => dto.NodeId, dto => dto.NodeId) + .Where(dto => dto.NodeId == entity.Id) + .OrderBy(dto => dto.NodeId); + + var dtos = _unitOfWork.Database.Fetch(sql); + //need to map the results + return dtos.Select(d => new Notification(d.id, d.userId, d.action, d.nodeObjectType)).ToList(); + } + + public int DeleteNotifications(IEntity entity) + { + return _unitOfWork.Database.Delete("WHERE nodeId = @nodeId", new { nodeId = entity.Id }); + } + + public int DeleteNotifications(IUser user) + { + return _unitOfWork.Database.Delete("WHERE userId = @userId", new { userId = user.Id }); + } + + public int DeleteNotifications(IUser user, IEntity entity) + { + // delete all settings on the node for this user + return _unitOfWork.Database.Delete("WHERE userId = @userId AND nodeId = @nodeId", new { userId = user.Id, nodeId = entity.Id }); + } + + public Notification CreateNotification(IUser user, IEntity entity, string action) + { + var sql = new Sql() + .Select("DISTINCT nodeObjectType") + .From() + .Where(nodeDto => nodeDto.NodeId == entity.Id); + var nodeType = _unitOfWork.Database.ExecuteScalar(sql); + + var dto = new User2NodeNotifyDto() + { + Action = action, + NodeId = entity.Id, + UserId = (int)user.Id + }; + _unitOfWork.Database.Insert(dto); + return new Notification(dto.NodeId, dto.UserId, dto.Action, nodeType); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/INotificationService.cs b/src/Umbraco.Core/Services/INotificationService.cs new file mode 100644 index 0000000000..801424d3b0 --- /dev/null +++ b/src/Umbraco.Core/Services/INotificationService.cs @@ -0,0 +1,66 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence; +using umbraco.interfaces; + +namespace Umbraco.Core.Services +{ + internal interface INotificationService : IService + { + /// + /// Sends the notifications for the specified user regarding the specified node and action. + /// + /// + /// + /// + void SendNotifications(IEntity entity, IUser user, IAction action); + + /// + /// Gets the notifications for the user + /// + /// + /// + IEnumerable GetUserNotifications(IUser user); + + /// + /// Returns the notifications for an entity + /// + /// + /// + IEnumerable GetEntityNotifications(IEntity entity); + + /// + /// Deletes notifications by entity + /// + /// + void DeleteNotifications(IEntity entity); + + /// + /// Deletes notifications by user + /// + /// + void DeleteNotifications(IUser user); + + /// + /// Delete notifications by user and entity + /// + /// + /// + void DeleteNotifications(IUser user, IEntity entity); + + /// + /// Creates a new notification + /// + /// + /// + /// The action letter - note: this is a string for future compatibility + /// + Notification CreateNotification(IUser user, IEntity entity, string action); + } +} diff --git a/src/Umbraco.Core/Services/NotificationService.cs b/src/Umbraco.Core/Services/NotificationService.cs new file mode 100644 index 0000000000..31c534470b --- /dev/null +++ b/src/Umbraco.Core/Services/NotificationService.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using Umbraco.Core.Models; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using umbraco.interfaces; + +namespace Umbraco.Core.Services +{ + internal class NotificationService : INotificationService + { + private readonly IDatabaseUnitOfWorkProvider _uowProvider; + + public NotificationService(IDatabaseUnitOfWorkProvider provider) + { + _uowProvider = provider; + } + + /// + /// Sends the notifications for the specified user regarding the specified node and action. + /// + /// + /// + /// + public void SendNotifications(IEntity entity, IUser user, IAction action) + { + throw new NotImplementedException(); + } + + /// + /// Gets the notifications for the user + /// + /// + /// + public IEnumerable GetUserNotifications(IUser user) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + return repository.GetUserNotifications(user); + } + + /// + /// Deletes notifications by entity + /// + /// + public IEnumerable GetEntityNotifications(IEntity entity) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + return repository.GetEntityNotifications(entity); + } + + /// + /// Deletes notifications by entity + /// + /// + public void DeleteNotifications(IEntity entity) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + repository.DeleteNotifications(entity); + } + + /// + /// Deletes notifications by user + /// + /// + public void DeleteNotifications(IUser user) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + repository.DeleteNotifications(user); + } + + /// + /// Delete notifications by user and entity + /// + /// + /// + public void DeleteNotifications(IUser user, IEntity entity) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + repository.DeleteNotifications(user, entity); + } + + /// + /// Creates a new notification + /// + /// + /// + /// The action letter - note: this is a string for future compatibility + /// + public Notification CreateNotification(IUser user, IEntity entity, string action) + { + var uow = _uowProvider.GetUnitOfWork(); + var repository = new NotificationsRepository(uow); + return repository.CreateNotification(user, entity, action); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 38068e7082..de971f6269 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -192,6 +192,7 @@ + @@ -205,6 +206,7 @@ + @@ -768,6 +770,7 @@ + @@ -776,6 +779,7 @@ + diff --git a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs index 55a978f6b2..d7f51257a3 100644 --- a/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs +++ b/src/Umbraco.Tests/Persistence/Repositories/ContentRepositoryTest.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core.Models.Rdbms; using Umbraco.Core.Persistence; diff --git a/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs new file mode 100644 index 0000000000..643c8d8b50 --- /dev/null +++ b/src/Umbraco.Tests/Persistence/Repositories/NotificationsRepositoryTest.cs @@ -0,0 +1,145 @@ +using System; +using System.Globalization; +using System.Linq; +using Moq; +using NUnit.Framework; +using Umbraco.Core; +using Umbraco.Core.Models.EntityBase; +using Umbraco.Core.Models.Membership; +using Umbraco.Core.Models.Rdbms; +using Umbraco.Core.Persistence.Repositories; +using Umbraco.Core.Persistence.UnitOfWork; +using Umbraco.Tests.TestHelpers; + +namespace Umbraco.Tests.Persistence.Repositories +{ + [TestFixture] + public class NotificationsRepositoryTest : BaseDatabaseFactoryTest + { + [Test] + public void CreateNotification() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repo = new NotificationsRepository(unitOfWork); + + var node = new NodeDto {CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,123", SortOrder = 1, Text = "hello", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0}; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var user = Mock.Of(e => e.Id == (object)node.UserId); + + var notification = repo.CreateNotification(user, entity, "A"); + + Assert.AreEqual("A", notification.Action); + Assert.AreEqual(node.NodeId, notification.EntityId); + Assert.AreEqual(node.NodeObjectType, notification.EntityType); + Assert.AreEqual(node.UserId, notification.UserId); + } + + [Test] + public void GetUserNotifications() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repo = new NotificationsRepository(unitOfWork); + + var userDto = new UserDto { ContentStartId = -1, DefaultPermissions = "", Email = "test" , Login = "test" , MediaStartId = -1, Password = "test" , Type = 1, UserName = "test" , UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + + var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var userAdmin = Mock.Of(e => e.Id == (object)0); + + for (var i = 0; i < 10; i++) + { + var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + } + + var notifications = repo.GetUserNotifications(userAdmin); + + Assert.AreEqual(5, notifications.Count()); + } + + [Test] + public void GetEntityNotifications() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repo = new NotificationsRepository(unitOfWork); + + var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node1); + var entity1 = Mock.Of(e => e.Id == node1.NodeId); + var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node2); + var entity2 = Mock.Of(e => e.Id == node2.NodeId); + + for (var i = 0; i < 10; i++) + { + var userDto = new UserDto { ContentStartId = -1, DefaultPermissions = "", Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + } + + var notifications = repo.GetEntityNotifications(entity1); + + Assert.AreEqual(5, notifications.Count()); + } + + [Test] + public void Delete_By_Entity() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repo = new NotificationsRepository(unitOfWork); + + var node1 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,1", SortOrder = 1, Text = "hello1", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node1); + var entity1 = Mock.Of(e => e.Id == node1.NodeId); + var node2 = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1,2", SortOrder = 1, Text = "hello2", Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + unitOfWork.Database.Insert(node2); + var entity2 = Mock.Of(e => e.Id == node2.NodeId); + + for (var i = 0; i < 10; i++) + { + var userDto = new UserDto { ContentStartId = -1, DefaultPermissions = "", Email = "test" + i, Login = "test" + i, MediaStartId = -1, Password = "test", Type = 1, UserName = "test" + i, UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var notification = repo.CreateNotification(userNew, (i % 2 == 0) ? entity1 : entity2, i.ToString(CultureInfo.InvariantCulture)); + } + + var delCount = repo.DeleteNotifications(entity1); + + Assert.AreEqual(5, delCount); + } + + [Test] + public void Delete_By_User() + { + var provider = new PetaPocoUnitOfWorkProvider(); + var unitOfWork = provider.GetUnitOfWork(); + var repo = new NotificationsRepository(unitOfWork); + + var userDto = new UserDto { ContentStartId = -1, DefaultPermissions = "", Email = "test", Login = "test", MediaStartId = -1, Password = "test", Type = 1, UserName = "test", UserLanguage = "en" }; + unitOfWork.Database.Insert(userDto); + + var userNew = Mock.Of(e => e.Id == (object)userDto.Id); + var userAdmin = Mock.Of(e => e.Id == (object)0); + + for (var i = 0; i < 10; i++) + { + var node = new NodeDto { CreateDate = DateTime.Now, Level = 1, NodeObjectType = Guid.Parse(Constants.ObjectTypes.ContentItem), ParentId = -1, Path = "-1," + i, SortOrder = 1, Text = "hello" + i, Trashed = false, UniqueId = Guid.NewGuid(), UserId = 0 }; + var result = unitOfWork.Database.Insert(node); + var entity = Mock.Of(e => e.Id == node.NodeId); + var notification = repo.CreateNotification((i % 2 == 0) ? userAdmin : userNew, entity, i.ToString(CultureInfo.InvariantCulture)); + } + + var delCount = repo.DeleteNotifications(userAdmin); + + Assert.AreEqual(5, delCount); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 1f5d92722c..7493eac540 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -210,6 +210,7 @@ + diff --git a/src/umbraco.cms/businesslogic/CMSNode.cs b/src/umbraco.cms/businesslogic/CMSNode.cs index 69fe7a1192..c92f82ec95 100644 --- a/src/umbraco.cms/businesslogic/CMSNode.cs +++ b/src/umbraco.cms/businesslogic/CMSNode.cs @@ -21,6 +21,7 @@ using umbraco.cms.businesslogic.workflow; using umbraco.cms.businesslogic.Tags; using File = System.IO.File; using Media = umbraco.cms.businesslogic.media.Media; +using Notification = umbraco.cms.businesslogic.workflow.Notification; using Task = umbraco.cms.businesslogic.task.Task; namespace umbraco.cms.businesslogic