Fixes issue with ThreadStatic db

Uses IScopeContext which wraps either HttpContext or CallContext which is created/injected via IOC factory.
This commit is contained in:
Shannon
2016-04-29 17:08:34 +02:00
parent 5c24a3a734
commit bd31dd6caf
37 changed files with 270 additions and 95 deletions

View File

@@ -0,0 +1,25 @@
using System.Runtime.Remoting.Messaging;
namespace Umbraco.Core
{
/// <summary>
/// A place to get/retrieve data in a current context (i.e. http, thread, etc...)
/// </summary>
internal class CallContextScope : IScopeContext
{
public object GetData(string key)
{
return CallContext.GetData(key);
}
public void SetData(string key, object data)
{
CallContext.SetData(key, data);
}
public void ClearData(string key)
{
CallContext.FreeNamedDataSlot(key);
}
}
}

View File

@@ -0,0 +1,16 @@
using System.Web;
using Umbraco.Core.Persistence;
namespace Umbraco.Core
{
/// <summary>
/// Default scope context factory
/// </summary>
internal class DefaultScopeContextFactory : IScopeContextFactory
{
public IScopeContext GetContext()
{
return HttpContext.Current == null ? (IScopeContext)new CallContextScope() : new HttpContextScope();
}
}
}

View File

@@ -24,6 +24,8 @@ namespace Umbraco.Core.DependencyInjection
container.Register<ISqlSyntaxProvider, SqlCeSyntaxProvider>("SqlCeSyntaxProvider");
container.Register<ISqlSyntaxProvider, SqlServerSyntaxProvider>("SqlServerSyntaxProvider");
container.RegisterSingleton<IScopeContextFactory, DefaultScopeContextFactory>();
// register database factory
// will be initialized with syntax providers and a logger, and will try to configure
// from the default connection string name, if possible, else will remain non-configured

View File

@@ -0,0 +1,25 @@
using System.Web;
namespace Umbraco.Core
{
/// <summary>
/// A place to get/retrieve data in a current context (i.e. http, thread, etc...)
/// </summary>
internal class HttpContextScope : IScopeContext
{
public object GetData(string key)
{
return HttpContext.Current.Items[key];
}
public void SetData(string key, object data)
{
HttpContext.Current.Items[key] = data;
}
public void ClearData(string key)
{
HttpContext.Current.Items.Remove(key);
}
}
}

View File

@@ -0,0 +1,12 @@
namespace Umbraco.Core
{
/// <summary>
/// A place to get/retrieve data in a current context (i.e. http, thread, etc...)
/// </summary>
internal interface IScopeContext
{
object GetData(string key);
void SetData(string key, object data);
void ClearData(string key);
}
}

View File

@@ -0,0 +1,10 @@
namespace Umbraco.Core
{
/// <summary>
/// Gets an IScopedContext
/// </summary>
internal interface IScopeContextFactory
{
IScopeContext GetContext();
}
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Configuration;
using System.Data.Common;
using System.Linq;
using System.Web;
using System.Threading;
using NPoco;
using NPoco.FluentMappings;
using Umbraco.Core.Configuration;
@@ -26,11 +26,12 @@ namespace Umbraco.Core.Persistence
/// </remarks>
internal class DefaultDatabaseFactory : DisposableObject, IDatabaseFactory
{
private readonly IScopeContextFactory _scopeContextFactory;
private readonly ISqlSyntaxProvider[] _sqlSyntaxProviders;
private readonly ILogger _logger;
private bool _configured;
private readonly ILogger _logger;
private DatabaseFactory _databaseFactory;
private const string HttpItemKey = "Umbraco.Core.Persistence.DefaultDatabaseFactory";
private DatabaseFactory _databaseFactory;
private IPocoDataFactory _pocoDataFactory;
private string _connectionString;
private string _providerName;
@@ -39,9 +40,9 @@ namespace Umbraco.Core.Persistence
private ISqlSyntaxProvider _sqlSyntax;
private RetryPolicy _connectionRetryPolicy;
private RetryPolicy _commandRetryPolicy;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
// fixme - what needs to be private fields vs public properties?
public bool Configured => _configured;
public bool Configured { get; private set; }
public ISqlSyntaxProvider SqlSyntax
{
get
@@ -51,20 +52,16 @@ namespace Umbraco.Core.Persistence
}
}
// very important to have ThreadStatic,
// see: http://issues.umbraco.org/issue/U4-2172
[ThreadStatic]
private static Lazy<UmbracoDatabase> _nonHttpInstance;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultDatabaseFactory"/> with the default connection, and a logger.
/// </summary>
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger.</param>
/// <param name="scopeContextFactory"></param>
/// <remarks>Used by LightInject.</remarks>
public DefaultDatabaseFactory(IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger)
: this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger)
{
public DefaultDatabaseFactory(IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory)
: this(GlobalSettings.UmbracoConnectionName, sqlSyntaxProviders, logger, scopeContextFactory)
{
if (Configured == false)
DatabaseContext.GiveLegacyAChance(this, logger);
}
@@ -75,15 +72,20 @@ namespace Umbraco.Core.Persistence
/// <param name="connectionStringName">The name of the connection string in web.config.</param>
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger</param>
/// <param name="scopeContextFactory"></param>
/// <remarks>Used by the other ctor and in tests.</remarks>
public DefaultDatabaseFactory(string connectionStringName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger)
{
public DefaultDatabaseFactory(string connectionStringName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory)
{
if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (scopeContextFactory == null) throw new ArgumentNullException(nameof(scopeContextFactory));
if (string.IsNullOrWhiteSpace(connectionStringName)) throw new ArgumentException("Value cannot be null nor empty.", nameof(connectionStringName));
_sqlSyntaxProviders = sqlSyntaxProviders.ToArray();
_logger = logger;
_scopeContextFactory = scopeContextFactory;
_logger.Debug<DefaultDatabaseFactory>("Created!");
var settings = ConfigurationManager.ConnectionStrings[connectionStringName];
if (settings == null)
@@ -97,61 +99,73 @@ namespace Umbraco.Core.Persistence
/// </summary>
/// <param name="connectionString">The database connection string.</param>
/// <param name="providerName">The name of the database provider.</param>
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="sqlSyntaxProviders">The collection of available sql syntax providers.</param>
/// <param name="logger">A logger.</param>
/// <param name="scopeContextFactory"></param>
/// <remarks>Used in tests.</remarks>
public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger)
{
public DefaultDatabaseFactory(string connectionString, string providerName, IEnumerable<ISqlSyntaxProvider> sqlSyntaxProviders, ILogger logger, IScopeContextFactory scopeContextFactory)
{
if (sqlSyntaxProviders == null) throw new ArgumentNullException(nameof(sqlSyntaxProviders));
if (logger == null) throw new ArgumentNullException(nameof(logger));
if (scopeContextFactory == null) throw new ArgumentNullException(nameof(scopeContextFactory));
_sqlSyntaxProviders = sqlSyntaxProviders.ToArray();
_logger = logger;
_scopeContextFactory = scopeContextFactory;
_logger.Debug<DefaultDatabaseFactory>("Created!");
if (string.IsNullOrWhiteSpace(connectionString) || string.IsNullOrWhiteSpace(providerName))
return; // not configured
Configure(connectionString, providerName);
}
}
public void Configure(string connectionString, string providerName)
{
if (_configured) throw new InvalidOperationException("Already configured.");
using (new WriteLock(_lock))
{
_logger.Debug<DefaultDatabaseFactory>("Configuring!");
Mandate.ParameterNotNullOrEmpty(connectionString, nameof(connectionString));
Mandate.ParameterNotNullOrEmpty(providerName, nameof(providerName));
if (Configured) throw new InvalidOperationException("Already configured.");
_connectionString = connectionString;
_providerName = providerName;
Mandate.ParameterNotNullOrEmpty(connectionString, nameof(connectionString));
Mandate.ParameterNotNullOrEmpty(providerName, nameof(providerName));
_connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString);
_commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString);
_connectionString = connectionString;
_providerName = providerName;
_dbProviderFactory = DbProviderFactories.GetFactory(_providerName);
if (_dbProviderFactory == null)
throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
_databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName);
if (_databaseType == null)
throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\".");
_connectionRetryPolicy = RetryPolicyFactory.GetDefaultSqlConnectionRetryPolicyByConnectionString(_connectionString);
_commandRetryPolicy = RetryPolicyFactory.GetDefaultSqlCommandRetryPolicyByConnectionString(_connectionString);
_sqlSyntax = GetSqlSyntaxProvider(_providerName);
if (_sqlSyntax == null)
throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\".");
_dbProviderFactory = DbProviderFactories.GetFactory(_providerName);
if (_dbProviderFactory == null)
throw new Exception($"Can't find a provider factory for provider name \"{_providerName}\".");
_databaseType = DatabaseType.Resolve(_dbProviderFactory.GetType().Name, _providerName);
if (_databaseType == null)
throw new Exception($"Can't find an NPoco database type for provider name \"{_providerName}\".");
// ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database
// so that everything NPoco is properly cached for the lifetime of the application
var mappers = new MapperCollection { new PocoMapper() };
var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init());
_pocoDataFactory = factory;
var config = new FluentConfig(xmappers => factory);
_sqlSyntax = GetSqlSyntaxProvider(_providerName);
if (_sqlSyntax == null)
throw new Exception($"Can't find a sql syntax provider for provider name \"{_providerName}\".");
// create the database factory
_databaseFactory = DatabaseFactory.Config(x => x
.UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances
.WithFluentConfig(config)); // with proper configuration
// ensure we have only 1 set of mappers, and 1 PocoDataFactory, for all database
// so that everything NPoco is properly cached for the lifetime of the application
var mappers = new MapperCollection { new PocoMapper() };
var factory = new FluentPocoDataFactory((type, iPocoDataFactory) => new PocoDataBuilder(type, mappers).Init());
_pocoDataFactory = factory;
var config = new FluentConfig(xmappers => factory);
_nonHttpInstance = new Lazy<UmbracoDatabase>(() => (UmbracoDatabase) _databaseFactory.GetDatabase());
_configured = true;
// create the database factory
_databaseFactory = DatabaseFactory.Config(x => x
.UsingDatabase(CreateDatabaseInstance) // creating UmbracoDatabase instances
.WithFluentConfig(config)); // with proper configuration
if (_databaseFactory == null) throw new NullReferenceException("The call to DatabaseFactory.Config yielded a null DatabaseFactory instance");
_logger.Debug<DefaultDatabaseFactory>("Created _nonHttpInstance");
Configured = true;
}
}
// gets the sql syntax provider that corresponds, from attribute
@@ -174,19 +188,22 @@ namespace Umbraco.Core.Persistence
/// Gets a value indicating whether it is possible to connect to the database.
/// </summary>
/// <returns></returns>
public bool CanConnect => _configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName);
public bool CanConnect => Configured && DbConnectionExtensions.IsConnectionAvailable(_connectionString, _providerName);
private void EnsureConfigured()
{
if (_configured == false)
throw new InvalidOperationException("Not configured.");
using (new ReadLock(_lock))
{
if (Configured == false)
throw new InvalidOperationException("Not configured.");
}
}
// method used by NPoco's DatabaseFactory to actually create the database instance
private UmbracoDatabase CreateDatabaseInstance()
{
return new UmbracoDatabase(_connectionString, _sqlSyntax, _databaseType, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy);
}
{
return new UmbracoDatabase(_connectionString, _sqlSyntax, _databaseType, _dbProviderFactory, _logger, _connectionRetryPolicy, _commandRetryPolicy);
}
/// <summary>
/// Gets (creates or retrieves) the "ambient" database connection.
@@ -196,43 +213,29 @@ namespace Umbraco.Core.Persistence
{
EnsureConfigured();
// no http context, create the thread-static singleton object
if (HttpContext.Current == null)
{
return _nonHttpInstance.Value;
}
var scope = _scopeContextFactory.GetContext();
// we have an http context, so only create one per request
var db = HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] as UmbracoDatabase;
if (db == null) HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = db = (UmbracoDatabase) _databaseFactory.GetDatabase();
return db;
}
protected override void DisposeResources()
{
// check if it's in scope
var db = scope.GetData(HttpItemKey) as UmbracoDatabase;
if (db != null) return db;
db = (UmbracoDatabase) _databaseFactory.GetDatabase();
scope.SetData(HttpItemKey, db);
return db;
}
protected override void DisposeResources()
{
// this is weird, because _nonHttpInstance is thread-static, so we would need
// to dispose the factory in each thread where a database has been used - else
// it only disposes the current thread's database instance.
//
// besides, we don't really want to dispose the factory, which is a singleton...
UmbracoDatabase db = null;
if (HttpContext.Current == null)
{
if (_nonHttpInstance.IsValueCreated)
{
db = _nonHttpInstance.Value;
_nonHttpInstance = null;
}
}
else
{
db = HttpContext.Current.Items[typeof(DefaultDatabaseFactory)] as UmbracoDatabase;
HttpContext.Current.Items[typeof (DefaultDatabaseFactory)] = null;
}
db?.Dispose();
}
}
var scope = _scopeContextFactory.GetContext();
var db = scope.GetData(HttpItemKey) as UmbracoDatabase;
scope.ClearData(HttpItemKey);
db?.Dispose();
Configured = false;
}
}
}

View File

@@ -33,7 +33,7 @@ namespace Umbraco.Core.Persistence.UnitOfWork
/// <see cref="DefaultDatabaseFactory"/> with the default connection name, and default sql syntax providers.</para>
/// </remarks>
internal NPocoUnitOfWorkProvider(ILogger logger)
: this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger))
: this(new DefaultDatabaseFactory(GlobalSettings.UmbracoConnectionName, GetDefaultSqlSyntaxProviders(logger), logger, new DefaultScopeContextFactory()))
{ }
// this should NOT be here, all tests should supply the appropriate providers,

View File

@@ -377,10 +377,15 @@
<Compile Include="Models\PublishedContent\PublishedContentWithKeyWrapped.cs" />
<Compile Include="Models\Rdbms\AccessDto.cs" />
<Compile Include="ObjectResolution\ContainerSingleObjectResolver.cs" />
<Compile Include="CallContextScope.cs" />
<Compile Include="Persistence\Constants-DbProviderNames.cs" />
<Compile Include="DefaultScopeContextFactory.cs" />
<Compile Include="Persistence\FaultHandling\RetryDbConnection.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="HttpContextScope.cs" />
<Compile Include="IScopeContext.cs" />
<Compile Include="IScopeContextFactory.cs" />
<Compile Include="Persistence\IUmbracoDatabaseConfig.cs" />
<Compile Include="Persistence\Mappers\AuditItemMapper.cs" />
<Compile Include="Persistence\Mappers\IMappingResolver.cs" />

View File

@@ -31,7 +31,7 @@ namespace Umbraco.Tests.Persistence
_sqlCeSyntaxProvider = new SqlCeSyntaxProvider();
_sqlSyntaxProviders = new[] { (ISqlSyntaxProvider) _sqlCeSyntaxProvider };
_logger = Mock.Of<ILogger>();
var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger);
var dbFactory = new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, _sqlSyntaxProviders, _logger, new TestScopeContextFactory());
_dbContext = new DatabaseContext(dbFactory, _logger);
}
@@ -79,7 +79,7 @@ namespace Umbraco.Tests.Persistence
engine.CreateDatabase();
// re-create the database factory and database context with proper connection string
var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger);
var dbFactory = new DefaultDatabaseFactory(engine.LocalConnectionString, Constants.DbProviderNames.SqlCe, _sqlSyntaxProviders, _logger, new TestScopeContextFactory());
_dbContext = new DatabaseContext(dbFactory, _logger);
// create application context

View File

@@ -6,6 +6,7 @@ using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests.Persistence.FaultHandling
{
@@ -19,7 +20,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=x;password=umbraco";
const string providerName = Constants.DbProviderNames.SqlServer;
var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null)) };
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>());
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestScopeContextFactory());
var database = factory.GetDatabase();
//Act
@@ -34,7 +35,7 @@ namespace Umbraco.Tests.Persistence.FaultHandling
const string connectionString = @"server=.\SQLEXPRESS;database=EmptyForTest;user id=umbraco;password=umbraco";
const string providerName = Constants.DbProviderNames.SqlServer;
var sqlSyntax = new[] { new SqlServerSyntaxProvider(new Lazy<IDatabaseFactory>(() => null)) };
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>());
var factory = new DefaultDatabaseFactory(connectionString, providerName, sqlSyntax, Mock.Of<ILogger>(), new TestScopeContextFactory());
var database = factory.GetDatabase();
//Act

View File

@@ -1333,7 +1333,11 @@ namespace Umbraco.Tests.Services
[Test]
public void Can_Save_Lazy_Content()
{
var databaseFactory = new DefaultDatabaseFactory(Umbraco.Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger);
var databaseFactory = new DefaultDatabaseFactory(
Core.Configuration.GlobalSettings.UmbracoConnectionName,
TestObjects.GetDefaultSqlSyntaxProviders(Logger),
Logger,
new TestScopeContextFactory());
var provider = new NPocoUnitOfWorkProvider(databaseFactory);
var unitOfWork = provider.GetUnitOfWork();
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");

View File

@@ -107,7 +107,7 @@ namespace Umbraco.Tests.TestHelpers
// use a mock factory; otherwise use a real factory.
var databaseFactory = DatabaseTestBehavior == DatabaseBehavior.NoDatabasePerFixture
? TestObjects.GetIDatabaseFactoryMock()
: new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger);
: new DefaultDatabaseFactory(GetDbConnectionString(), GetDbProviderName(), sqlSyntaxProviders, Logger, new TestScopeContextFactory());
// so, using the above code to create a mock IDatabaseFactory if we don't have a real database
// but, that will NOT prevent _appContext from NOT being configured, because it cannot connect

View File

@@ -218,7 +218,10 @@ namespace Umbraco.Tests.TestHelpers
var evtMsgs = new TransientMessagesFactory();
ApplicationContext.Current = new ApplicationContext(
//assign the db context
new DatabaseContext(new DefaultDatabaseFactory(Core.Configuration.GlobalSettings.UmbracoConnectionName, TestObjects.GetDefaultSqlSyntaxProviders(Logger), Logger), Logger),
new DatabaseContext(new DefaultDatabaseFactory(
Core.Configuration.GlobalSettings.UmbracoConnectionName,
TestObjects.GetDefaultSqlSyntaxProviders(Logger),
Logger, new TestScopeContextFactory()), Logger),
//assign the service context
TestObjects.GetServiceContext(
Container.GetInstance<RepositoryFactory>(),

View File

@@ -0,0 +1,27 @@
using System.Collections.Generic;
using Umbraco.Core;
namespace Umbraco.Tests.TestHelpers
{
internal class TestScopeContext : IScopeContext
{
private readonly Dictionary<string, object> _d = new Dictionary<string, object>();
public object GetData(string key)
{
if (_d.ContainsKey(key)) return _d[key];
return null;
}
public void SetData(string key, object data)
{
_d[key] = data;
}
public void ClearData(string key)
{
if (_d.ContainsKey(key))
_d.Remove(key);
}
}
}

View File

@@ -0,0 +1,20 @@
using Umbraco.Core;
namespace Umbraco.Tests.TestHelpers
{
internal class TestScopeContextFactory : IScopeContextFactory
{
private readonly bool _transient = false;
private TestScopeContext _ctx;
public TestScopeContextFactory(bool transient = false)
{
_transient = transient;
}
public IScopeContext GetContext()
{
return _transient ? new TestScopeContext() : (_ctx ?? (_ctx = new TestScopeContext()));
}
}
}

View File

@@ -187,6 +187,8 @@
<Compile Include="Persistence\Migrations\MigrationStartupHandlerTests.cs" />
<Compile Include="TestHelpers\TestObjects-Mocks.cs" />
<Compile Include="TestHelpers\TestObjects.cs" />
<Compile Include="TestHelpers\TestScopeContext.cs" />
<Compile Include="TestHelpers\TestScopeContextFactory.cs" />
<Compile Include="Web\AngularIntegration\AngularAntiForgeryTests.cs" />
<Compile Include="Web\AngularIntegration\ContentModelSerializationTests.cs" />
<Compile Include="Web\AngularIntegration\JsInitializationTests.cs" />

View File

@@ -0,0 +1 @@
// partialView

View File

@@ -0,0 +1 @@
// partialView

View File

@@ -0,0 +1 @@
// partialView

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
body {background:#EE7600; color:#FFF;}

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
body { color:#000; } .bold {font-weight:bold;}

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax.js"/>

View File

@@ -0,0 +1 @@
// script

View File

@@ -0,0 +1 @@
// script

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax.js"/>

View File

@@ -0,0 +1 @@
// script

View File

@@ -0,0 +1 @@
Umbraco.Sys.registerNamespace("Umbraco.Utils");

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax.js"/>

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax.js"/>

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax.js"/>

View File

@@ -0,0 +1 @@
/// <reference name="MicrosoftAjax-Updated.js"/>