diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs
index dd352c2c86..c5741a74fc 100644
--- a/src/Umbraco.Core/Models/ServerRegistration.cs
+++ b/src/Umbraco.Core/Models/ServerRegistration.cs
@@ -6,6 +6,100 @@ using Umbraco.Core.Sync;
namespace Umbraco.Core.Models
{
+ internal class Macro : Entity, IAggregateRoot
+ {
+ ///
+ /// Creates an item with pre-filled properties
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Macro(int id, bool useInEditor, int refreshRate, string @alias, string name, string controlType, string controlAssembly, string xsltPath, bool cacheByPage, bool cachePersonalized, bool dontRender, string scriptPath)
+ {
+ Id = id;
+ UseInEditor = useInEditor;
+ RefreshRate = refreshRate;
+ Alias = alias;
+ Name = name;
+ ControlType = controlType;
+ ControlAssembly = controlAssembly;
+ XsltPath = xsltPath;
+ CacheByPage = cacheByPage;
+ CachePersonalized = cachePersonalized;
+ DontRender = dontRender;
+ ScriptPath = scriptPath;
+ }
+
+ ///
+ /// Creates an instance for persisting a new item
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Macro(string @alias, string name,
+ string controlType = "",
+ string controlAssembly = "",
+ string xsltPath = "",
+ string scriptPath = "",
+ bool cacheByPage = false,
+ bool cachePersonalized = false,
+ bool dontRender = true,
+ bool useInEditor = false,
+ int refreshRate = 0)
+ {
+ UseInEditor = useInEditor;
+ RefreshRate = refreshRate;
+ Alias = alias;
+ Name = name;
+ ControlType = controlType;
+ ControlAssembly = controlAssembly;
+ XsltPath = xsltPath;
+ CacheByPage = cacheByPage;
+ CachePersonalized = cachePersonalized;
+ DontRender = dontRender;
+ ScriptPath = scriptPath;
+ }
+
+ public bool UseInEditor { get; set; }
+
+ public int RefreshRate { get; set; }
+
+ public string Alias { get; set; }
+
+ public string Name { get; set; }
+
+ public string ControlType { get; set; }
+
+ public string ControlAssembly { get; set; }
+
+ public string XsltPath { get; set; }
+
+ public bool CacheByPage { get; set; }
+
+ public bool CachePersonalized { get; set; }
+
+ public bool DontRender { get; set; }
+
+ public string ScriptPath { get; set; }
+ }
+
internal class ServerRegistration : Entity, IServerAddress, IAggregateRoot
{
private string _serverAddress;
diff --git a/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs
new file mode 100644
index 0000000000..1a64ef7cf5
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Factories/MacroFactory.cs
@@ -0,0 +1,44 @@
+using System.Globalization;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Factories
+{
+ internal class MacroFactory : IEntityFactory
+ {
+ #region Implementation of IEntityFactory
+
+ public Macro BuildEntity(MacroDto dto)
+ {
+ var model = new Macro(dto.Id, dto.UseInEditor, dto.RefreshRate, dto.Alias, dto.Name, dto.ScriptType, dto.ScriptAssembly, dto.Xslt, dto.CacheByPage, dto.CachePersonalized, dto.DontRender, dto.Python);
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ model.ResetDirtyProperties(false);
+ return model;
+ }
+
+ public MacroDto BuildDto(Macro entity)
+ {
+ var dto = new MacroDto()
+ {
+ Alias = entity.Alias,
+ CacheByPage = entity.CacheByPage,
+ CachePersonalized = entity.CachePersonalized,
+ DontRender = entity.DontRender,
+ Name = entity.Name,
+ Python = entity.ScriptPath,
+ RefreshRate = entity.RefreshRate,
+ ScriptAssembly = entity.ControlAssembly,
+ ScriptType = entity.ControlType,
+ UseInEditor = entity.UseInEditor,
+ Xslt = entity.XsltPath
+ };
+ if (entity.HasIdentity)
+ dto.Id = short.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
+
+ return dto;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
index fc065102e0..d8ddc1fc70 100644
--- a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
@@ -4,7 +4,6 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
{
-
internal class ServerRegistrationFactory : IEntityFactory
{
#region Implementation of IEntityFactory
diff --git a/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs
new file mode 100644
index 0000000000..5db94eaf12
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Mappers/MacroMapper.cs
@@ -0,0 +1,44 @@
+using System.Collections.Concurrent;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.Rdbms;
+
+namespace Umbraco.Core.Persistence.Mappers
+{
+ [MapperFor(typeof(Macro))]
+ internal sealed class MacroMapper : BaseMapper
+ {
+ private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
+
+ //NOTE: its an internal class but the ctor must be public since we're using Activator.CreateInstance to create it
+ // otherwise that would fail because there is no public constructor.
+ public MacroMapper()
+ {
+ BuildMap();
+ }
+
+ #region Overrides of BaseMapper
+
+ internal override ConcurrentDictionary PropertyInfoCache
+ {
+ get { return PropertyInfoCacheInstance; }
+ }
+
+ internal override void BuildMap()
+ {
+ CacheMap(src => src.Id, dto => dto.Id);
+ CacheMap(src => src.Alias, dto => dto.Alias);
+ CacheMap(src => src.CacheByPage, dto => dto.CacheByPage);
+ CacheMap(src => src.CachePersonalized, dto => dto.CachePersonalized);
+ CacheMap(src => src.ControlAssembly, dto => dto.ScriptAssembly);
+ CacheMap(src => src.ControlType, dto => dto.ScriptType);
+ CacheMap(src => src.DontRender, dto => dto.DontRender);
+ CacheMap(src => src.Name, dto => dto.Name);
+ CacheMap(src => src.RefreshRate, dto => dto.RefreshRate);
+ CacheMap(src => src.ScriptPath, dto => dto.Python);
+ CacheMap(src => src.UseInEditor, dto => dto.UseInEditor);
+ CacheMap(src => src.XsltPath, dto => dto.Xslt);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs
new file mode 100644
index 0000000000..f0ed6ae0fb
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Repositories/MacroRepository.cs
@@ -0,0 +1,128 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Umbraco.Core.Models;
+using Umbraco.Core.Models.EntityBase;
+using Umbraco.Core.Models.Rdbms;
+using Umbraco.Core.Persistence.Caching;
+using Umbraco.Core.Persistence.Factories;
+using Umbraco.Core.Persistence.Querying;
+using Umbraco.Core.Persistence.UnitOfWork;
+
+namespace Umbraco.Core.Persistence.Repositories
+{
+ internal class MacroRepository : PetaPocoRepositoryBase
+ {
+ public MacroRepository(IDatabaseUnitOfWork work) : base(work)
+ {
+ }
+
+ public MacroRepository(IDatabaseUnitOfWork work, IRepositoryCacheProvider cache) : base(work, cache)
+ {
+ }
+
+ protected override Macro PerformGet(int id)
+ {
+ var sql = GetBaseQuery(false);
+ sql.Where(GetBaseWhereClause(), new { Id = id });
+
+ var macroDto = Database.First(sql);
+ if (macroDto == null)
+ return null;
+
+ var factory = new MacroFactory();
+ var entity = factory.BuildEntity(macroDto);
+
+ //on initial construction we don't want to have dirty properties tracked
+ // http://issues.umbraco.org/issue/U4-1946
+ entity.ResetDirtyProperties(false);
+
+ return entity;
+ }
+
+ protected override IEnumerable PerformGetAll(params int[] ids)
+ {
+ if (ids.Any())
+ {
+ foreach (var id in ids)
+ {
+ yield return Get(id);
+ }
+ }
+ else
+ {
+ var macroDtos = Database.Fetch("WHERE id > 0");
+ foreach (var macroDto in macroDtos)
+ {
+ yield return Get(macroDto.Id);
+ }
+ }
+ }
+
+ protected override IEnumerable PerformGetByQuery(IQuery query)
+ {
+ var sqlClause = GetBaseQuery(false);
+ var translator = new SqlTranslator(sqlClause, query);
+ var sql = translator.Translate();
+
+ var dtos = Database.Fetch(sql);
+
+ foreach (var dto in dtos)
+ {
+ yield return Get(dto.Id);
+ }
+ }
+
+ protected override Sql GetBaseQuery(bool isCount)
+ {
+ var sql = new Sql();
+ sql.Select(isCount ? "COUNT(*)" : "*")
+ .From();
+ return sql;
+ }
+
+ protected override string GetBaseWhereClause()
+ {
+ return "id = @Id";
+ }
+
+ protected override IEnumerable GetDeleteClauses()
+ {
+ var list = new List
+ {
+ "DELETE FROM cmsMacro WHERE id = @Id"
+ };
+ return list;
+ }
+
+ protected override Guid NodeObjectTypeId
+ {
+ get { throw new NotImplementedException(); }
+ }
+
+ protected override void PersistNewItem(Macro entity)
+ {
+ ((Entity)entity).AddingEntity();
+
+ var factory = new MacroFactory();
+ var dto = factory.BuildDto(entity);
+
+ var id = Convert.ToInt32(Database.Insert(dto));
+ entity.Id = id;
+
+ ((ICanBeDirty)entity).ResetDirtyProperties();
+ }
+
+ protected override void PersistUpdatedItem(Macro entity)
+ {
+ ((Entity)entity).UpdatingEntity();
+
+ var factory = new MacroFactory();
+ var dto = factory.BuildDto(entity);
+
+ Database.Update(dto);
+
+ ((ICanBeDirty)entity).ResetDirtyProperties();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
index f4abb60ffd..f2df870e02 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
@@ -11,7 +11,6 @@ using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Persistence.Repositories
{
-
internal class ServerRegistrationRepository : PetaPocoRepositoryBase
{
public ServerRegistrationRepository(IDatabaseUnitOfWork work)
diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj
index a0f7f0af55..7b4f5f2baa 100644
--- a/src/Umbraco.Core/Umbraco.Core.csproj
+++ b/src/Umbraco.Core/Umbraco.Core.csproj
@@ -374,6 +374,7 @@
+
@@ -394,7 +395,10 @@
+
+
+
diff --git a/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs b/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs
index ef1bda3c5c..fbd5bb30a1 100644
--- a/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs
+++ b/src/Umbraco.Tests/Persistence/Repositories/ServerRegistrationRepositoryTest.cs
@@ -10,6 +10,269 @@ using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Persistence.Repositories
{
+ [TestFixture]
+ public class MacroRepositoryTest : BaseDatabaseFactoryTest
+ {
+ [SetUp]
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ CreateTestData();
+ }
+
+ [Test]
+ public void Cannot_Add_Duplicate_Macros()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+
+ // Act
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml");
+ repository.AddOrUpdate(macro);
+
+ Assert.Throws(unitOfWork.Commit);
+ }
+
+ }
+
+ [Test]
+ public void Cannot_Update_To_Duplicate_Macro_Alias()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+
+ // Act
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ var macro = repository.Get(1);
+ macro.Alias = "test1";
+ repository.AddOrUpdate(macro);
+ Assert.Throws(unitOfWork.Commit);
+ }
+
+ }
+
+ [Test]
+ public void Can_Instantiate_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+
+ // Act
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Assert
+ Assert.That(repository, Is.Not.Null);
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Get_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var macro = repository.Get(1);
+
+ // Assert
+ Assert.That(macro, Is.Not.Null);
+ Assert.That(macro.HasIdentity, Is.True);
+ Assert.That(macro.Alias, Is.EqualTo("test1"));
+ Assert.That(macro.CacheByPage, Is.EqualTo(false));
+ Assert.That(macro.CachePersonalized, Is.EqualTo(false));
+ Assert.That(macro.ControlAssembly, Is.EqualTo("MyAssembly1"));
+ Assert.That(macro.ControlType, Is.EqualTo("~/usercontrol/test1.ascx"));
+ Assert.That(macro.DontRender, Is.EqualTo(true));
+ Assert.That(macro.Name, Is.EqualTo("Test1"));
+ Assert.That(macro.RefreshRate, Is.EqualTo(0));
+ Assert.That(macro.ScriptPath, Is.EqualTo("~/views/macropartials/test1.cshtml"));
+ Assert.That(macro.UseInEditor, Is.EqualTo(false));
+ Assert.That(macro.XsltPath, Is.EqualTo("test1.xslt"));
+ }
+
+
+ }
+
+ [Test]
+ public void Can_Perform_GetAll_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var macros = repository.GetAll();
+
+ // Assert
+ Assert.That(macros.Count(), Is.EqualTo(3));
+ }
+
+ }
+
+ [Test]
+ public void Can_Perform_GetByQuery_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var query = Query.Builder.Where(x => x.Alias.ToUpper() == "TEST1");
+ var result = repository.GetByQuery(query);
+
+ // Assert
+ Assert.AreEqual(1, result.Count());
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Count_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var query = Query.Builder.Where(x => x.Name.StartsWith("Test"));
+ int count = repository.Count(query);
+
+ // Assert
+ Assert.That(count, Is.EqualTo(2));
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Add_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var macro = new Macro("test", "Test", "~/usercontrol/blah.ascx", "MyAssembly", "test.xslt", "~/views/macropartials/test.cshtml");
+ repository.AddOrUpdate(macro);
+ unitOfWork.Commit();
+
+ // Assert
+ Assert.That(macro.HasIdentity, Is.True);
+ Assert.That(macro.Id, Is.EqualTo(4));//With 3 existing entries the Id should be 4
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Update_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var macro = repository.Get(2);
+ macro.Name = "Hello";
+ macro.RefreshRate = 1234;
+ macro.CacheByPage = true;
+ macro.CachePersonalized = true;
+ macro.ControlAssembly = "";
+ macro.ControlType = "";
+ macro.DontRender = false;
+ macro.ScriptPath = "~/newpath.cshtml";
+ macro.UseInEditor = true;
+ macro.XsltPath = "";
+
+ repository.AddOrUpdate(macro);
+ unitOfWork.Commit();
+
+ var macroUpdated = repository.Get(2);
+
+ // Assert
+ Assert.That(macroUpdated, Is.Not.Null);
+ Assert.That(macroUpdated.Name, Is.EqualTo("Hello"));
+ Assert.That(macroUpdated.RefreshRate, Is.EqualTo(1234));
+ Assert.That(macroUpdated.CacheByPage, Is.EqualTo(true));
+ Assert.That(macroUpdated.CachePersonalized, Is.EqualTo(true));
+ Assert.That(macroUpdated.ControlAssembly, Is.EqualTo(""));
+ Assert.That(macroUpdated.ControlType, Is.EqualTo(""));
+ Assert.That(macroUpdated.DontRender, Is.EqualTo(false));
+ Assert.That(macroUpdated.ScriptPath, Is.EqualTo("~/newpath.cshtml"));
+ Assert.That(macroUpdated.UseInEditor, Is.EqualTo(true));
+ Assert.That(macroUpdated.XsltPath, Is.EqualTo(""));
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Delete_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var macro = repository.Get(3);
+ Assert.IsNotNull(macro);
+ repository.Delete(macro);
+ unitOfWork.Commit();
+
+ var exists = repository.Exists(3);
+
+ // Assert
+ Assert.That(exists, Is.False);
+ }
+ }
+
+ [Test]
+ public void Can_Perform_Exists_On_Repository()
+ {
+ // Arrange
+ var provider = new PetaPocoUnitOfWorkProvider();
+ var unitOfWork = provider.GetUnitOfWork();
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ // Act
+ var exists = repository.Exists(3);
+ var doesntExist = repository.Exists(10);
+
+ // Assert
+ Assert.That(exists, Is.True);
+ Assert.That(doesntExist, Is.False);
+ }
+ }
+
+ [TearDown]
+ public override void TearDown()
+ {
+ base.TearDown();
+ }
+
+ public void CreateTestData()
+ {
+ var provider = new PetaPocoUnitOfWorkProvider();
+ using (var unitOfWork = provider.GetUnitOfWork())
+ using (var repository = new MacroRepository(unitOfWork))
+ {
+ repository.AddOrUpdate(new Macro("test1", "Test1", "~/usercontrol/test1.ascx", "MyAssembly1", "test1.xslt", "~/views/macropartials/test1.cshtml"));
+ repository.AddOrUpdate(new Macro("test2", "Test2", "~/usercontrol/test2.ascx", "MyAssembly2", "test2.xslt", "~/views/macropartials/test2.cshtml"));
+ repository.AddOrUpdate(new Macro("test3", "Test3", "~/usercontrol/test3.ascx", "MyAssembly3", "test3.xslt", "~/views/macropartials/test3.cshtml"));
+ unitOfWork.Commit();
+ }
+
+ }
+ }
+
[TestFixture]
public class ServerRegistrationRepositoryTest : BaseDatabaseFactoryTest
{