Files
Umbraco-CMS/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs

426 lines
19 KiB
C#
Raw Normal View History

2016-10-13 21:08:07 +02:00
using System;
using System.Configuration;
using System.Data.SqlServerCe;
2018-03-30 19:31:42 +02:00
using System.Threading;
2016-10-13 21:08:07 +02:00
using System.Web.Routing;
using System.Xml;
2020-09-17 12:52:25 +02:00
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
2016-10-13 21:08:07 +02:00
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Cache;
2020-09-08 13:03:43 +02:00
using Umbraco.Core.Configuration.Models;
2016-10-13 21:08:07 +02:00
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Web.Security;
using Umbraco.Web.Routing;
using File = System.IO.File;
using Umbraco.Web.Composing;
2016-11-08 17:38:05 +01:00
using Umbraco.Core.Persistence.Mappers;
2017-05-12 14:49:44 +02:00
using Umbraco.Core.Scoping;
2016-12-16 10:40:14 +01:00
using Umbraco.Tests.Testing;
using Umbraco.Core.Migrations.Install;
2017-10-17 17:43:15 +02:00
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Persistance.SqlCe;
using Umbraco.Tests.LegacyXmlPublishedCache;
using Umbraco.Web.WebApi;
using Umbraco.Tests.Common;
2020-08-24 16:06:09 +02:00
using Umbraco.Tests.Common.Builders;
2016-10-13 21:08:07 +02:00
namespace Umbraco.Tests.TestHelpers
{
/// <summary>
/// Provides a base class for all Umbraco tests that require a database.
/// </summary>
/// <remarks>
2016-12-16 18:45:41 +01:00
/// <para>Can provide a SqlCE database populated with the Umbraco schema. The database should
2016-12-14 14:06:30 +01:00
/// be accessed through the UmbracoDatabaseFactory.</para>
2016-10-13 21:08:07 +02:00
/// <para>Provides an Umbraco context and Xml content.</para>
/// <para>fixme what else?</para>
/// </remarks>
2018-03-30 19:31:42 +02:00
[Apartment(ApartmentState.STA)] // why?
[UmbracoTest(WithApplication = true)]
public abstract class TestWithDatabaseBase : UmbracoTestBase
2016-10-13 21:08:07 +02:00
{
private string _databasePath;
private static byte[] _databaseBytes;
protected PublishedContentTypeCache ContentTypesCache { get; private set; }
protected override ISqlSyntaxProvider SqlSyntax => GetSyntaxProvider();
protected IVariationContextAccessor VariationContextAccessor => new TestVariationContextAccessor();
2016-10-13 21:08:07 +02:00
2017-05-30 19:41:37 +02:00
internal ScopeProvider ScopeProvider => Current.ScopeProvider as ScopeProvider;
2016-10-13 21:08:07 +02:00
2018-11-28 12:59:40 +01:00
protected ISqlContext SqlContext => Factory.GetInstance<ISqlContext>();
2016-10-13 21:08:07 +02:00
public override void SetUp()
{
// Ensure the data directory is set before continuing
var path = TestHelper.WorkingDirectory;
2016-10-13 21:08:07 +02:00
AppDomain.CurrentDomain.SetData("DataDirectory", path);
base.SetUp();
2016-10-13 21:08:07 +02:00
}
protected override void Compose()
{
base.Compose();
2018-11-28 12:59:40 +01:00
Composition.Register<ISqlSyntaxProvider, SqlCeSyntaxProvider>();
Composition.Register(factory => PublishedSnapshotService);
Composition.Register(factory => DefaultCultureAccessor);
2016-10-13 21:08:07 +02:00
2018-11-28 17:35:12 +01:00
Composition.WithCollectionBuilder<DataEditorCollectionBuilder>()
2016-11-08 17:38:05 +01:00
.Clear()
2018-11-28 11:05:41 +01:00
.Add(() => Composition.TypeLoader.GetDataEditors());
2016-10-13 21:08:07 +02:00
Composition.WithCollectionBuilder<UmbracoApiControllerTypeCollectionBuilder>()
.Add(Composition.TypeLoader.GetUmbracoApiControllers());
2018-11-29 10:35:16 +01:00
Composition.RegisterUnique(f =>
2016-10-13 21:08:07 +02:00
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None)
2016-10-13 21:08:07 +02:00
return TestObjects.GetDatabaseFactoryMock();
2019-03-29 08:30:51 +01:00
var lazyMappers = new Lazy<IMapperCollection>(f.GetInstance<IMapperCollection>);
2020-09-21 14:35:28 +02:00
var factory = new UmbracoDatabaseFactory(f.GetInstance<ILogger<UmbracoDatabaseFactory>>(), f.GetInstance<ILoggerFactory>(), GetDbConnectionString(), GetDbProviderName(), lazyMappers, TestHelper.DbProviderFactoryCreator);
2016-10-13 21:08:07 +02:00
factory.ResetForTests();
return factory;
});
}
2018-03-30 19:31:42 +02:00
[OneTimeTearDown]
2016-10-13 21:08:07 +02:00
public void FixtureTearDown()
{
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile();
2016-10-13 21:08:07 +02:00
}
public override void TearDown()
{
2018-11-28 12:59:40 +01:00
var profilingLogger = Factory.TryGetInstance<IProfilingLogger>();
var timer = profilingLogger?.TraceDuration<TestWithDatabaseBase>("teardown"); // FIXME: move that one up
2016-11-08 17:38:05 +01:00
try
2016-10-13 21:08:07 +02:00
{
// FIXME: should we first kill all scopes?
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest)
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile();
2016-10-13 21:08:07 +02:00
AppDomain.CurrentDomain.SetData("DataDirectory", null);
// make sure we dispose of the service to unbind events
2017-10-31 12:48:24 +01:00
PublishedSnapshotService?.Dispose();
PublishedSnapshotService = null;
2016-10-13 21:08:07 +02:00
}
2016-11-08 17:38:05 +01:00
finally
{
timer?.Dispose();
}
2016-10-13 21:08:07 +02:00
base.TearDown();
}
private void CreateAndInitializeDatabase()
{
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Create database."))
{
CreateSqlCeDatabase(); // TODO: faster!
2016-10-13 21:08:07 +02:00
}
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Initialize database."))
{
InitializeDatabase(); // TODO: faster!
2016-10-13 21:08:07 +02:00
}
}
protected virtual ISqlSyntaxProvider GetSyntaxProvider()
{
return new SqlCeSyntaxProvider();
}
protected virtual string GetDbProviderName()
{
return Constants.DbProviderNames.SqlCe;
}
protected virtual string GetDbConnectionString()
{
Merge remote-tracking branch 'origin/netcore/netcore' into netcore/feature/migrate-logging Signed-off-by: Bjarke Berg <mail@bergmania.dk> # Conflicts: # src/Umbraco.ModelsBuilder.Embedded/LiveModelsProvider.cs # src/Umbraco.ModelsBuilder.Embedded/PureLiveModelFactory.cs # src/Umbraco.Tests.Integration/Implementations/TestHelper.cs # src/Umbraco.Tests.Integration/Persistence/Repositories/TemplateRepositoryTest.cs # src/Umbraco.Tests.Integration/RuntimeTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/ColorListValidatorTest.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/EnsureUniqueValuesValidatorTest.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/PropertyEditors/MultiValuePropertyEditorTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/NestedContentTests.cs # src/Umbraco.Tests.UnitTests/Umbraco.Core/Published/PropertyCacheLevelTests.cs # src/Umbraco.Tests/Components/ComponentTests.cs # src/Umbraco.Tests/IO/ShadowFileSystemTests.cs # src/Umbraco.Tests/Models/VariationTests.cs # src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs # src/Umbraco.Tests/Persistence/DatabaseContextTests.cs # src/Umbraco.Tests/Persistence/Repositories/MediaRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/MediaTypeRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/MemberRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/MemberTypeRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/PublicAccessRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/TagRepositoryTest.cs # src/Umbraco.Tests/Persistence/Repositories/UserRepositoryTest.cs # src/Umbraco.Tests/Persistence/SqlCeTableByTableTest.cs # src/Umbraco.Tests/Published/ConvertersTests.cs # src/Umbraco.Tests/PublishedContent/SolidPublishedSnapshot.cs # src/Umbraco.Tests/Routing/ContentFinderByIdTests.cs # src/Umbraco.Tests/Routing/ContentFinderByUrlAndTemplateTests.cs # src/Umbraco.Tests/Routing/DomainsAndCulturesTests.cs # src/Umbraco.Tests/Routing/UmbracoModuleTests.cs # src/Umbraco.Tests/Scoping/ScopeEventDispatcherTests.cs # src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs # src/Umbraco.Tests/Services/ContentServicePerformanceTest.cs # src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs
2020-09-23 07:17:05 +02:00
return @"DataSource=|DataDirectory|UmbracoNPocoTests.sdf;Flush Interval=1;";
2016-10-13 21:08:07 +02:00
}
/// <summary>
/// Creates the SqlCe database if required
/// </summary>
protected virtual void CreateSqlCeDatabase()
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None)
2016-10-13 21:08:07 +02:00
return;
var path = TestHelper.WorkingDirectory;
2016-10-13 21:08:07 +02:00
//Get the connectionstring settings from config
2017-05-12 14:49:44 +02:00
var settings = ConfigurationManager.ConnectionStrings[Constants.System.UmbracoConnectionName];
2016-10-13 21:08:07 +02:00
ConfigurationManager.AppSettings.Set(
2017-05-12 14:49:44 +02:00
Constants.System.UmbracoConnectionName,
2016-10-13 21:08:07 +02:00
GetDbConnectionString());
_databasePath = string.Concat(path, "\\UmbracoNPocoTests.sdf");
//create a new database file if
// - is the first test in the session
// - the database file doesn't exist
// - NewDbFileAndSchemaPerTest
// - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture
//if this is the first test in the session, always ensure a new db file is created
2016-12-16 18:45:41 +01:00
if (FirstTestInSession
|| File.Exists(_databasePath) == false
|| Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest
|| Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest
2016-12-16 10:40:14 +01:00
|| (FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture))
2016-10-13 21:08:07 +02:00
{
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Remove database file"))
{
RemoveDatabaseFile(null, ex =>
{
//if this doesn't work we have to make sure everything is reset! otherwise
// well run into issues because we've already set some things up
TearDown();
throw ex;
});
}
//Create the Sql CE database
using (ProfilingLogger.TraceDuration<TestWithDatabaseBase>("Create database file"))
{
2016-11-05 19:23:55 +01:00
if (Options.Database != UmbracoTestOptions.Database.NewEmptyPerTest && _databaseBytes != null)
2016-10-13 21:08:07 +02:00
{
File.WriteAllBytes(_databasePath, _databaseBytes);
}
else
{
using (var engine = new SqlCeEngine(settings.ConnectionString))
{
engine.CreateDatabase();
}
}
}
}
}
2018-04-30 21:29:49 +02:00
protected IDefaultCultureAccessor DefaultCultureAccessor { get; set; }
2018-04-27 17:36:50 +02:00
2017-10-31 12:48:24 +01:00
protected IPublishedSnapshotService PublishedSnapshotService { get; set; }
2017-07-17 17:59:46 +02:00
protected override void Initialize() // FIXME: should NOT be here!
2016-10-13 21:08:07 +02:00
{
2016-11-07 20:50:14 +01:00
base.Initialize();
2018-04-30 21:29:49 +02:00
DefaultCultureAccessor = new TestDefaultCultureAccessor();
2018-04-27 17:36:50 +02:00
2016-11-25 14:08:23 +01:00
CreateAndInitializeDatabase();
2017-10-31 12:48:24 +01:00
// ensure we have a PublishedSnapshotService
if (PublishedSnapshotService == null)
2016-10-13 21:08:07 +02:00
{
2017-10-31 12:48:24 +01:00
PublishedSnapshotService = CreatePublishedSnapshotService();
2016-10-13 21:08:07 +02:00
}
}
2020-09-08 13:03:43 +02:00
protected virtual IPublishedSnapshotService CreatePublishedSnapshotService(GlobalSettings globalSettings = null)
2017-07-17 17:59:46 +02:00
{
2019-01-17 11:01:23 +01:00
var cache = NoAppCache.Instance;
2017-07-17 17:59:46 +02:00
2020-09-10 09:04:17 +02:00
ContentTypesCache ??= new PublishedContentTypeCache(
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IContentTypeService>(),
Factory.GetInstance<IMediaTypeService>(),
Factory.GetInstance<IMemberTypeService>(),
Factory.GetInstance<IPublishedContentTypeFactory>(),
Factory.GetInstance<ILogger<PublishedContentTypeCache>>());
2017-07-17 17:59:46 +02:00
// testing=true so XmlStore will not use the file nor the database
2017-10-31 12:48:24 +01:00
var publishedSnapshotAccessor = new UmbracoContextPublishedSnapshotAccessor(Umbraco.Web.Composing.Current.UmbracoContextAccessor);
2018-04-30 21:29:49 +02:00
var variationContextAccessor = new TestVariationContextAccessor();
var service = new XmlPublishedSnapshotService(
ServiceContext,
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IPublishedContentTypeFactory>(),
ScopeProvider,
cache, publishedSnapshotAccessor, variationContextAccessor,
Factory.GetInstance<IUmbracoContextAccessor>(),
2018-11-28 12:59:40 +01:00
Factory.GetInstance<IDocumentRepository>(), Factory.GetInstance<IMediaRepository>(), Factory.GetInstance<IMemberRepository>(),
2018-04-30 21:29:49 +02:00
DefaultCultureAccessor,
Factory.GetInstance<ILoggerFactory>(),
2020-09-08 13:03:43 +02:00
globalSettings ?? TestObjects.GetGlobalSettings(),
HostingEnvironment,
HostingLifetime,
ShortStringHelper,
new SiteDomainHelper(),
Factory.GetInstance<IEntityXmlSerializer>(),
ContentTypesCache,
null, true, Options.PublishedRepositoryEvents);
2017-07-17 17:59:46 +02:00
// initialize PublishedCacheService content with an Xml source
service.XmlStore.GetXmlDocument = () =>
{
var doc = new XmlDocument();
doc.LoadXml(GetXmlContent(0));
return doc;
};
return service;
}
2016-10-13 21:08:07 +02:00
/// <summary>
/// Creates the tables and data for the database
/// </summary>
protected virtual void InitializeDatabase()
{
2016-11-05 19:23:55 +01:00
if (Options.Database == UmbracoTestOptions.Database.None || Options.Database == UmbracoTestOptions.Database.NewEmptyPerTest)
2016-10-13 21:08:07 +02:00
return;
//create the schema and load default data if:
// - is the first test in the session
// - NewDbFileAndSchemaPerTest
// - _isFirstTestInFixture + DbInitBehavior.NewDbFileAndSchemaPerFixture
if (_databaseBytes == null &&
2016-12-16 10:40:14 +01:00
(FirstTestInSession
2016-11-05 19:23:55 +01:00
|| Options.Database == UmbracoTestOptions.Database.NewSchemaPerTest
2017-05-12 14:49:44 +02:00
|| FirstTestInFixture && Options.Database == UmbracoTestOptions.Database.NewSchemaPerFixture))
2016-10-13 21:08:07 +02:00
{
2017-05-12 14:49:44 +02:00
using (var scope = ScopeProvider.CreateScope())
{
var schemaHelper = new DatabaseSchemaCreator(scope.Database, LoggerFactory.CreateLogger<DatabaseSchemaCreator>(), LoggerFactory, UmbracoVersion);
2017-05-12 14:49:44 +02:00
//Create the umbraco database and its base data
schemaHelper.InitializeDatabaseSchema();
//Special case, we need to create the xml cache tables manually since they are not part of the default
//setup.
//TODO: Remove this when we update all tests to use nucache
schemaHelper.CreateTable<ContentXmlDto>();
schemaHelper.CreateTable<PreviewXmlDto>();
2017-05-12 14:49:44 +02:00
scope.Complete();
}
2016-10-13 21:08:07 +02:00
_databaseBytes = File.ReadAllBytes(_databasePath);
}
}
// FIXME: is this needed?
2016-12-16 14:18:37 +01:00
private void CloseDbConnections(IUmbracoDatabase database)
2016-10-13 21:08:07 +02:00
{
//Ensure that any database connections from a previous test is disposed.
//This is really just double safety as its also done in the TearDown.
database?.Dispose();
2017-06-20 12:12:29 +02:00
//SqlCeContextGuardian.CloseBackgroundConnection();
2016-10-13 21:08:07 +02:00
}
2016-12-16 14:18:37 +01:00
private void RemoveDatabaseFile(IUmbracoDatabase database, Action<Exception> onFail = null)
2016-10-13 21:08:07 +02:00
{
if (database != null) CloseDbConnections(database);
2017-05-12 14:49:44 +02:00
RemoveDatabaseFile(onFail);
}
private void RemoveDatabaseFile(Action<Exception> onFail = null)
{
var path = TestHelper.WorkingDirectory;
2016-10-13 21:08:07 +02:00
try
{
var filePath = string.Concat(path, "\\UmbracoNPocoTests.sdf");
if (File.Exists(filePath))
File.Delete(filePath);
}
catch (Exception ex)
{
LoggerFactory.CreateLogger<TestWithDatabaseBase>().LogError(ex, "Could not remove the old database file");
2016-10-13 21:08:07 +02:00
// swallow this exception - that's because a sub class might require further teardown logic
onFail?.Invoke(ex);
}
}
2020-09-08 13:03:43 +02:00
protected IUmbracoContext GetUmbracoContext(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, GlobalSettings globalSettings = null, IPublishedSnapshotService snapshotService = null)
2016-10-13 21:08:07 +02:00
{
// ensure we have a PublishedCachesService
var service = snapshotService ?? PublishedSnapshotService as XmlPublishedSnapshotService;
2016-10-13 21:08:07 +02:00
if (service == null)
throw new Exception("Not a proper XmlPublishedCache.PublishedCachesService.");
if (service is XmlPublishedSnapshotService)
2016-10-13 21:08:07 +02:00
{
2018-04-27 11:38:50 +10:00
// re-initialize PublishedCacheService content with an Xml source with proper template id
((XmlPublishedSnapshotService)service).XmlStore.GetXmlDocument = () =>
2018-04-27 11:38:50 +10:00
{
var doc = new XmlDocument();
doc.LoadXml(GetXmlContent(templateId));
return doc;
};
}
2016-10-13 21:08:07 +02:00
var httpContext = GetHttpContextFactory(url, routeData).HttpContext;
var httpContextAccessor = TestHelper.GetHttpContextAccessor(httpContext);
2016-10-18 17:09:26 +02:00
var umbracoContext = new UmbracoContext(
httpContextAccessor,
2016-10-13 21:08:07 +02:00
service,
Mock.Of<IBackOfficeSecurity>(),
2020-09-21 21:06:24 +02:00
globalSettings ?? new GlobalSettings(),
HostingEnvironment,
2019-12-04 14:03:39 +01:00
new TestVariationContextAccessor(),
UriUtility,
new AspNetCookieManager(httpContextAccessor));
2016-10-13 21:08:07 +02:00
if (setSingleton)
2017-05-30 18:13:11 +02:00
Umbraco.Web.Composing.Current.UmbracoContextAccessor.UmbracoContext = umbracoContext;
2016-10-13 21:08:07 +02:00
return umbracoContext;
}
protected virtual string GetXmlContent(int templateId)
{
return @"<?xml version=""1.0"" encoding=""utf-8""?>
<!DOCTYPE root[
<!ELEMENT Home ANY>
<!ATTLIST Home id ID #REQUIRED>
<!ELEMENT CustomDocument ANY>
<!ATTLIST CustomDocument id ID #REQUIRED>
]>
<root id=""-1"">
2017-07-20 11:21:28 +02:00
<Home id=""1046"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""1"" createDate=""2012-06-12T14:13:17"" updateDate=""2012-07-20T18:50:43"" nodeName=""Home"" urlName=""home"" writerName=""admin"" creatorName=""admin"" path=""-1,1046"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[this/is/my/alias, anotheralias]]></umbracoUrlAlias>
<umbracoNaviHide>1</umbracoNaviHide>
<Home id=""1173"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:06:45"" updateDate=""2012-07-20T19:07:31"" nodeName=""Sub1"" urlName=""sub1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173"" isDoc="""">
<content><![CDATA[<div>This is some content</div>]]></content>
<umbracoUrlAlias><![CDATA[page2/alias, 2ndpagealias]]></umbracoUrlAlias>
<Home id=""1174"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-20T18:07:54"" updateDate=""2012-07-20T19:10:27"" nodeName=""Sub2"" urlName=""sub2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1174"" isDoc="""">
<content><![CDATA[]]></content>
<umbracoUrlAlias><![CDATA[only/one/alias]]></umbracoUrlAlias>
<creatorName><![CDATA[Custom data with same property name as the member name]]></creatorName>
</Home>
<Home id=""1176"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:08"" updateDate=""2012-07-20T19:10:52"" nodeName=""Sub 3"" urlName=""sub-3"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1176"" isDoc="""">
<content><![CDATA[]]></content>
</Home>
<CustomDocument id=""1177"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""custom sub 1"" urlName=""custom-sub-1"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1177"" isDoc="""" />
<CustomDocument id=""1178"" parentID=""1173"" level=""3"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""4"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-16T14:23:35"" nodeName=""custom sub 2"" urlName=""custom-sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1173,1178"" isDoc="""" />
</Home>
<Home id=""1175"" parentID=""1046"" level=""2"" writerID=""0"" creatorID=""0"" nodeType=""1044"" template=""" + templateId + @""" sortOrder=""3"" createDate=""2012-07-20T18:08:01"" updateDate=""2012-07-20T18:49:32"" nodeName=""Sub 2"" urlName=""sub-2"" writerName=""admin"" creatorName=""admin"" path=""-1,1046,1175"" isDoc=""""><content><![CDATA[]]></content>
</Home>
</Home>
<CustomDocument id=""1172"" parentID=""-1"" level=""1"" writerID=""0"" creatorID=""0"" nodeType=""1234"" template=""" + templateId + @""" sortOrder=""2"" createDate=""2012-07-16T15:26:59"" updateDate=""2012-07-18T14:23:35"" nodeName=""Test"" urlName=""test-page"" writerName=""admin"" creatorName=""admin"" path=""-1,1172"" isDoc="""" />
2016-10-13 21:08:07 +02:00
</root>";
}
}
2017-07-20 11:21:28 +02:00
}