Completes: U4-5846 Remove cmsTemplate.master column and transfer those values over to umbracoNode.parentId, U4-5847 Ensure the path is set correctly for templates in the db, almost done the other template service tasks too.

This commit is contained in:
Shannon
2014-12-05 17:29:47 +11:00
parent 2961370b83
commit d4fd58e038
14 changed files with 163 additions and 74 deletions

View File

@@ -1,2 +1,2 @@
# Usage: on line 2 put the release version, on line 3 put the version comment (example: beta)
7.2.0
7.3.0

View File

@@ -5,7 +5,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.2.0");
private static readonly Version Version = new Version("7.3.0");
/// <summary>
/// Gets the current version of Umbraco.

View File

@@ -15,12 +15,7 @@ namespace Umbraco.Core.Models.Rdbms
[Column("nodeId")]
[Index(IndexTypes.UniqueNonClustered)]
[ForeignKey(typeof(NodeDto), Name = "FK_cmsTemplate_umbracoNode")]
public int NodeId { get; set; }
[Column("master")]
[NullSetting(NullSetting = NullSettings.Null)]
[ForeignKey(typeof(NodeDto), Name = "FK_cmsTemplate_cmsTemplate")]
public int? Master { get; set; }
public int NodeId { get; set; }
[Column("alias")]
[Length(100)]

View File

@@ -187,20 +187,30 @@ namespace Umbraco.Core.Models
public void SetMasterTemplate(ITemplate masterTemplate)
{
MasterTemplateId = new Lazy<int>(() => masterTemplate.Id);
MasterTemplateAlias = masterTemplate.Alias;
if (masterTemplate == null)
{
MasterTemplateId = new Lazy<int>(() => -1);
MasterTemplateAlias = null;
}
else
{
MasterTemplateId = new Lazy<int>(() => masterTemplate.Id);
MasterTemplateAlias = masterTemplate.Alias;
}
}
public override object DeepClone()
{
var clone = (Template)base.DeepClone();
//We cannot call in to the base classes to clone because the base File class treats Alias, Name.. differently so we need to manually do the clone
//need to manually assign since they are readonly properties
clone._alias = Alias;
clone._name = Name;
//Memberwise clone on Entity will work since it doesn't have any deep elements
// for any sub class this will work for standard properties as well that aren't complex object's themselves.
var clone = (Template)MemberwiseClone();
//Automatically deep clone ref properties that are IDeepCloneable
DeepCloneHelper.DeepCloneRefProperties(this, clone);
clone.ResetDirtyProperties(false);
return clone;
}

View File

@@ -63,8 +63,8 @@ namespace Umbraco.Core.Persistence.Factories
}
//TODO: Change this to ParentId: http://issues.umbraco.org/issue/U4-5846
if(dto.Master.HasValue)
template.MasterTemplateId = new Lazy<int>(() => dto.Master.Value);
if(dto.NodeDto.ParentId > 0)
template.MasterTemplateId = new Lazy<int>(() => dto.NodeDto.ParentId);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
@@ -81,9 +81,9 @@ namespace Umbraco.Core.Persistence.Factories
NodeDto = BuildNodeDto(entity)
};
if (entity.MasterTemplateId != null && entity.MasterTemplateId.Value != default(int))
if (entity.MasterTemplateId != null && entity.MasterTemplateId.Value > 0)
{
dto.Master = entity.MasterTemplateId.Value;
dto.NodeDto.ParentId = entity.MasterTemplateId.Value;
}
if (entity.HasIdentity)

View File

@@ -38,8 +38,7 @@ namespace Umbraco.Core.Persistence.Mappers
if(PropertyInfoCache.IsEmpty)
{
CacheMap<Template, TemplateDto>(src => src.Id, dto => dto.NodeId);
CacheMap<Template, TemplateDto>(src => src.MasterTemplateId, dto => dto.Master);
CacheMap<Template, NodeDto>(src => src.MasterTemplateId, dto => dto.ParentId);
CacheMap<Template, TemplateDto>(src => src.Alias, dto => dto.Alias);
CacheMap<Template, TemplateDto>(src => src.Content, dto => dto.Design);
}

View File

@@ -0,0 +1,81 @@
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
{
/// <summary>
/// Remove the master column after we've migrated all of the values into the 'ParentId' and Path column of Umbraco node
/// </summary>
[Migration("7.3.0", 0, GlobalSettings.UmbracoMigrationName)]
public class MigrateAndRemoveTemplateMasterColumn : MigrationBase
{
public override void Up()
{
//update the parentId column for all templates to be correct so it matches the current 'master' template
//NOTE: we are using dynamic because we need to get the data in a column that no longer exists in the schema
var templates = Context.Database.Fetch<dynamic>(new Sql().Select("*").From<TemplateDto>());
foreach (var template in templates)
{
Update.Table("umbracoNode").Set(new {parentID = template.master ?? -1}).Where(new {id = template.nodeId});
//now build the correct path for the template
Update.Table("umbracoNode").Set(new { path = BuildPath (template, templates)}).Where(new { id = template.nodeId });
}
//now remove the master column and key
if (this.Context.CurrentDatabaseProvider == DatabaseProviders.MySql)
{
Delete.ForeignKey().FromTable("cmsTemplate").ForeignColumn("master").ToTable("umbracoUser").PrimaryColumn("id");
}
else
{
//These are the old aliases, before removing them, check they exist
var constraints = SqlSyntaxContext.SqlSyntaxProvider.GetConstraintsPerColumn(Context.Database).Distinct().ToArray();
if (constraints.Any(x => x.Item1.InvariantEquals("cmsTemplate") && x.Item3.InvariantEquals("FK_cmsTemplate_cmsTemplate")))
{
Delete.ForeignKey("FK_cmsTemplate_cmsTemplate").OnTable("cmsTemplate");
}
//TODO: Hopefully it's not named something else silly in some crazy old versions
}
var columns = SqlSyntaxContext.SqlSyntaxProvider.GetColumnsInSchema(Context.Database).Distinct().ToArray();
if (columns.Any(x => x.ColumnName.InvariantEquals("master") && x.TableName.InvariantEquals("cmsTemplate")))
{
Delete.Column("master").FromTable("cmsTemplate");
}
}
public override void Down()
{
}
private string BuildPath(dynamic template, IEnumerable<dynamic> allTemplates)
{
if (template.master == null)
{
return string.Format("-1,{0}", template.nodeId);
}
var parent = allTemplates.FirstOrDefault(x => x.nodeId == template.master);
if (parent == null)
{
//this shouldn't happen but i suppose it could if people have bad data
return string.Format("-1,{0}", template.nodeId);
}
//recurse
var parentPath = BuildPath(parent, allTemplates);
var path = parentPath + string.Format(",{0}", template.nodeId);
return path;
}
}
}

View File

@@ -85,17 +85,21 @@ namespace Umbraco.Core.Persistence.Repositories
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql);
if (dtos.Count == 0) return Enumerable.Empty<ITemplate>();
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIdsSql = new Sql()
.Select("nodeId,alias," + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("master"))
.Select("nodeId,alias,parentID")
.From<TemplateDto>()
.Where<TemplateDto>(t => t.Master > 0);
.InnerJoin<NodeDto>()
.On<TemplateDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(t => t.ParentId > 0);
var childIds = Database.Fetch<dynamic>(childIdsSql)
.Select(x => new UmbracoEntity
{
Id = x.nodeId,
ParentId = x.master,
ParentId = x.parentID,
Name = x.alias
});
@@ -110,17 +114,21 @@ namespace Umbraco.Core.Persistence.Repositories
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql);
if (dtos.Count == 0) return Enumerable.Empty<ITemplate>();
//look up the simple template definitions that have a master template assigned, this is used
// later to populate the template item's properties
var childIdsSql = new Sql()
.Select("nodeId,alias," + SqlSyntaxContext.SqlSyntaxProvider.GetQuotedColumnName("master"))
.Select("nodeId,alias,parentID")
.From<TemplateDto>()
.Where<TemplateDto>(t => t.Master > 0);
.InnerJoin<NodeDto>()
.On<TemplateDto, NodeDto>(dto => dto.NodeId, dto => dto.NodeId)
.Where<NodeDto>(t => t.ParentId > 0);
var childIds = Database.Fetch<dynamic>(childIdsSql)
.Select(x => new UmbracoEntity
{
Id = x.nodeId,
ParentId = x.master,
ParentId = x.parentID,
Name = x.alias
});
@@ -309,9 +317,10 @@ namespace Umbraco.Core.Persistence.Repositories
// to it, then in the PersistDeletedTemplate we wouldn't recurse the underlying function, we'd just call
// PersistDeletedItem with a Template object and clear it's cache.
var sql = new Sql();
sql.Select("*").From<TemplateDto>().Where<TemplateDto>(dto => dto.Master != null || dto.NodeId == entity.Id);
var dtos = Database.Fetch<TemplateDto>(sql);
var sql = GetBaseQuery(false).Where<NodeDto>(dto => dto.ParentId > 0 || dto.NodeId == entity.Id);
var dtos = Database.Fetch<TemplateDto, NodeDto>(sql);
var self = dtos.Single(x => x.NodeId == entity.Id);
var allChildren = dtos.Except(new[] { self });
var hierarchy = GenerateTemplateHierarchy(self, allChildren);
@@ -353,14 +362,14 @@ namespace Umbraco.Core.Persistence.Repositories
var factory = new TemplateFactory(_viewsFileSystem, _masterpagesFileSystem, _templateConfig);
var template = factory.BuildEntity(dto, childDefinitions);
if (dto.Master.HasValue)
if (dto.NodeDto.ParentId > 0)
{
//TODO: Fix this n+1 query!
var masterTemplate = Get(dto.Master.Value);
var masterTemplate = Get(dto.NodeDto.ParentId);
if (masterTemplate != null)
{
template.MasterTemplateAlias = masterTemplate.Alias;
template.MasterTemplateId = new Lazy<int>(() => dto.Master.Value);
template.MasterTemplateId = new Lazy<int>(() => dto.NodeDto.ParentId);
}
}
@@ -409,7 +418,7 @@ namespace Umbraco.Core.Persistence.Repositories
private static List<TemplateDto> GenerateTemplateHierarchy(TemplateDto template, IEnumerable<TemplateDto> allChildTemplates)
{
var hierarchy = new List<TemplateDto> { template };
foreach (var t in allChildTemplates.Where(x => x.Master == template.NodeId))
foreach (var t in allChildTemplates.Where(x => x.NodeDto.ParentId == template.NodeId))
{
hierarchy.AddRange(GenerateTemplateHierarchy(t, allChildTemplates));
}
@@ -418,27 +427,20 @@ namespace Umbraco.Core.Persistence.Repositories
private void PopulateViewTemplate(ITemplate template, string fileName)
{
string content = string.Empty;
string path = string.Empty;
string content;
using (var stream = _viewsFileSystem.OpenFile(fileName))
using (var reader = new StreamReader(stream, Encoding.UTF8))
{
content = reader.ReadToEnd();
}
template.UpdateDate = _viewsFileSystem.GetLastModified(path).UtcDateTime;
//Currently set with db values, but will eventually be changed
//template.CreateDate = _viewsFileSystem.GetCreated(path).UtcDateTime;
//template.Key = new FileInfo(path).Name.EncodeAsGuid();
template.UpdateDate = _viewsFileSystem.GetLastModified(fileName).UtcDateTime;
template.Content = content;
}
private void PopulateMasterpageTemplate(ITemplate template, string fileName)
{
string content = string.Empty;
string path = string.Empty;
string content;
using (var stream = _masterpagesFileSystem.OpenFile(fileName))
using (var reader = new StreamReader(stream, Encoding.UTF8))
@@ -446,12 +448,7 @@ namespace Umbraco.Core.Persistence.Repositories
content = reader.ReadToEnd();
}
template.UpdateDate = _masterpagesFileSystem.GetLastModified(path).UtcDateTime;
//Currently set with db values, but will eventually be changed
//template.CreateDate = _masterpagesFileSystem.GetCreated(path).UtcDateTime;
//template.Key = new FileInfo(path).Name.EncodeAsGuid();
template.Path = path;
template.UpdateDate = _masterpagesFileSystem.GetLastModified(fileName).UtcDateTime;
template.Content = content;
}
@@ -459,8 +456,7 @@ namespace Umbraco.Core.Persistence.Repositories
public ITemplate Get(string alias)
{
var sql = GetBaseQuery(false)
.Where<TemplateDto>(x => x.Alias == alias);
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.Alias == alias);
var dto = Database.Fetch<TemplateDto, NodeDto>(sql).FirstOrDefault();
@@ -497,12 +493,12 @@ namespace Umbraco.Core.Persistence.Repositories
List<TemplateDto> found;
if (masterTemplateId == -1)
{
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.Master == null);
var sql = GetBaseQuery(false).Where<NodeDto>(x => x.ParentId <= 0);
found = Database.Fetch<TemplateDto, NodeDto>(sql);
}
else
{
var sql = GetBaseQuery(false).Where<TemplateDto>(x => x.Master == masterTemplateId);
var sql = GetBaseQuery(false).Where<NodeDto>(x => x.ParentId == masterTemplateId);
found = Database.Fetch<TemplateDto, NodeDto>(sql);
}
@@ -534,20 +530,19 @@ namespace Umbraco.Core.Persistence.Repositories
}
//then we need to get all template Dto's because those contain the master property
var sql = new Sql();
sql.Select("*").From<TemplateDto>();
var allDtos = Database.Fetch<TemplateDto>(sql).ToArray();
var sql = GetBaseQuery(false);
var allDtos = Database.Fetch<TemplateDto, NodeDto>(sql).ToArray();
var selfDto = allDtos.Single(x => x.NodeId == selfTemplate.Id);
//need to get the top-most node of the current tree
var top = selfDto;
while (top.Master.HasValue && top.Master.Value != -1)
while (top.NodeDto.ParentId > 0)
{
top = allDtos.Single(x => x.NodeId == top.Master.Value);
top = allDtos.Single(x => x.NodeId == top.NodeDto.ParentId);
}
var topNode = new TemplateNode(allTemplates.Single(x => x.Id == top.NodeId));
var childIds = allDtos.Where(x => x.Master == top.NodeId).Select(x => x.NodeId);
var childIds = allDtos.Where(x => x.NodeDto.ParentId == top.NodeId).Select(x => x.NodeId);
//This now creates the hierarchy recursively
topNode.Children = CreateChildren(topNode, childIds, allTemplates, allDtos);
@@ -602,7 +597,7 @@ namespace Umbraco.Core.Persistence.Repositories
children.Add(child);
//get this node's children
var kids = allDtos.Where(x => x.Master == i).Select(x => x.NodeId).ToArray();
var kids = allDtos.Where(x => x.NodeDto.ParentId == i).Select(x => x.NodeId).ToArray();
//recurse
child.Children = CreateChildren(child, kids, allTemplates, allDtos);

View File

@@ -390,6 +390,7 @@
<Compile Include="Persistence\Mappers\TagMapper.cs" />
<Compile Include="Persistence\Mappers\TemplateMapper.cs" />
<Compile Include="Persistence\Migrations\DataLossException.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenThreeZero\RemoveTemplateMasterColumn.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\AddMissingForeignKeyForContentType.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenTwoZero\AlterDataTypePreValueTable.cs" />
<Compile Include="Persistence\Migrations\Upgrades\TargetVersionSevenOneZero\AssignMissingPrimaryForMySqlKeys.cs" />

View File

@@ -2,23 +2,25 @@ using System;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core.Serialization;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class TemplateTests
public class TemplateTests : BaseUmbracoConfigurationTest
{
[Test]
public void Can_Deep_Clone()
{
var item = new Template("-1,2,3", "Test", "test")
var item = new Template("Test", "test")
{
Id = 3,
CreateDate = DateTime.Now,
Key = Guid.NewGuid(),
UpdateDate = DateTime.Now,
Content = "blah",
Path = "-1,3",
IsMasterTemplate = true,
MasterTemplateAlias = "master",
MasterTemplateId = new Lazy<int>(() => 88)
};
@@ -27,6 +29,8 @@ namespace Umbraco.Tests.Models
Assert.AreNotSame(clone, item);
Assert.AreEqual(clone, item);
Assert.AreEqual(clone.Path, item.Path);
Assert.AreEqual(clone.IsMasterTemplate, item.IsMasterTemplate);
Assert.AreEqual(clone.CreateDate, item.CreateDate);
Assert.AreEqual(clone.Alias, item.Alias);
Assert.AreEqual(clone.Id, item.Id);
@@ -49,7 +53,7 @@ namespace Umbraco.Tests.Models
{
var ss = new SerializationService(new JsonNetSerializer());
var item = new Template("-1,2,3", "Test", "test")
var item = new Template("Test", "test")
{
Id = 3,
CreateDate = DateTime.Now,

View File

@@ -33,8 +33,8 @@ namespace Umbraco.Tests.Persistence.Querying
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("umbracoNode"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsTemplate"))));
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55554, Alias = "testTemplate1", Design = "", Master = null, PrimaryKey = 22221});
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55555, Alias = "testTemplate2", Design = "", Master = null, PrimaryKey = 22222 });
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55554, Alias = "testTemplate1", Design = "", PrimaryKey = 22221});
DatabaseContext.Database.Insert("cmsTemplate", "pk", false, new TemplateDto { NodeId = 55555, Alias = "testTemplate2", Design = "", PrimaryKey = 22222 });
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} OFF ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsTemplate"))));
DatabaseContext.Database.Execute(new Sql(string.Format("SET IDENTITY_INSERT {0} ON ", SqlSyntaxContext.SqlSyntaxProvider.GetQuotedTableName("cmsContentType"))));

View File

@@ -2544,7 +2544,7 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.0\x86\*.* "$(TargetDir)x86\"
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7200</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7200</IISUrl>
<IISUrl>http://localhost:7300</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>

View File

@@ -163,6 +163,8 @@ namespace Umbraco.Web.WebServices
[HttpPost]
public JsonResult SaveTemplate(string templateName, string templateAlias, string templateContents, int templateId, int masterTemplateId)
{
//TODO: Change this over to use the new API - Also this will be migrated to a TemplateEditor or ViewEditor when it's all moved to angular
Template t;
bool pathChanged = false;
try
@@ -174,11 +176,12 @@ namespace Umbraco.Web.WebServices
Design = templateContents
};
//check if the master page has changed
if (t.MasterTemplate != masterTemplateId)
//check if the master page has changed - we need to normalize both - if it's 0 or -1, then make it 0... this is easy
// to do with Math.Max
if (Math.Max(t.MasterTemplate, 0) != Math.Max(masterTemplateId, 0))
{
pathChanged = true;
t.MasterTemplate = masterTemplateId;
t.MasterTemplate = Math.Max(masterTemplateId, 0);
pathChanged = true;
}
}
catch (ArgumentException ex)

View File

@@ -88,6 +88,7 @@ namespace umbraco.cms.businesslogic.template
#region Constructors
internal Template(ITemplate template)
: base(template.Id, true)
{
TemplateEntity = template;
}
@@ -116,7 +117,7 @@ namespace umbraco.cms.businesslogic.template
public string GetRawText()
{
return TemplateEntity.Content;
return TemplateEntity.Name;
//return base.Text;
}
@@ -149,7 +150,7 @@ namespace umbraco.cms.businesslogic.template
set
{
FlushCache();
base.Text = value;
TemplateEntity.Name = value;
}
}