diff --git a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj index d4b49b24d..c8e3adf1f 100644 --- a/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj +++ b/src/Orchard.Tests.Modules/Orchard.Tests.Modules.csproj @@ -59,6 +59,7 @@ False ..\..\lib\autofac\Autofac.dll + False ..\..\lib\fluentnhibernate\FluentNHibernate.dll @@ -127,6 +128,7 @@ + @@ -148,6 +150,10 @@ {C0C45321-B51D-4D8D-9B7B-AA4C2E0B2962} Orchard.CodeGeneration + + {17F86780-9A1F-4AA1-86F1-875EEC2730C7} + Orchard.Modules + {D10AD48F-407D-4DB5-A328-173EC7CB010F} Orchard.Roles @@ -156,6 +162,10 @@ {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} Orchard.Setup + + {CDE24A24-01D3-403C-84B9-37722E18DFB7} + Orchard.Themes + {79AED36E-ABD0-4747-93D3-8722B042454B} Orchard.Users diff --git a/src/Orchard.Tests.Modules/Themes/Services/ThemeServiceTests.cs b/src/Orchard.Tests.Modules/Themes/Services/ThemeServiceTests.cs new file mode 100644 index 000000000..141c94613 --- /dev/null +++ b/src/Orchard.Tests.Modules/Themes/Services/ThemeServiceTests.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Xml.Linq; +using Autofac; +using NHibernate; +using NUnit.Framework; +using Orchard.Caching; +using Orchard.ContentManagement; +using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.MetaData; +using Orchard.ContentManagement.MetaData.Models; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.ContentManagement.Records; +using Orchard.Core.Settings.Descriptor.Records; +using Orchard.Core.Settings.Handlers; +using Orchard.Core.Settings.Metadata; +using Orchard.Core.Settings.Models; +using Orchard.Core.Settings.Services; +using Orchard.Data; +using Orchard.DisplayManagement; +using Orchard.DisplayManagement.Descriptors; +using Orchard.DisplayManagement.Implementation; +using Orchard.Environment; +using Orchard.Environment.AutofacUtil.DynamicProxy2; +using Orchard.Environment.Descriptor; +using Orchard.Environment.Descriptor.Models; +using Orchard.Environment.Extensions; +using Orchard.Environment.Extensions.Models; +using Orchard.Localization; +using Orchard.Modules; +using Orchard.Modules.Services; +using Orchard.Security; +using Orchard.Security.Permissions; +using Orchard.Settings; +using Orchard.Tests.Stubs; +using Orchard.Themes; +using Orchard.Themes.Handlers; +using Orchard.Themes.Models; +using Orchard.Themes.Services; +using Orchard.UI.Notify; + +namespace Orchard.Tests.Modules.Themes.Services { + [TestFixture] + public class ThemeServiceTests { + private IThemeService _themeService; + private IContainer _container; + private ISessionFactory _sessionFactory; + private ISession _session; + + [TestFixtureSetUp] + public void InitFixture() { + var databaseFileName = System.IO.Path.GetTempFileName(); + _sessionFactory = DataUtility.CreateSessionFactory(databaseFileName, + typeof(ThemeSiteSettingsPartRecord), + typeof(SiteSettingsPartRecord), + typeof(ContentItemVersionRecord), + typeof(ContentItemRecord), + typeof(ContentTypeRecord)); + } + + [TestFixtureTearDown] + public void TermFixture() { } + + [SetUp] + public void Init() { + var context = new DynamicProxyContext(); + var builder = new ContainerBuilder(); + builder.RegisterModule(new SettingsModule()); + builder.RegisterType().EnableDynamicProxy(context).As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType(typeof(SettingsFormatter)) + .As(typeof(IMapper)) + .As(typeof(IMapper)); + _session = _sessionFactory.OpenSession(); + builder.RegisterInstance(new TestSessionLocator(_session)).As(); + _container = builder.Build(); + _themeService = _container.Resolve(); + } + + //todo: test theme feature enablement + + [Test] + public void ThemeWithNoBaseThemeCanBeSetAsSiteTheme() { + _themeService.SetSiteTheme("ThemeOne"); + var siteTheme = _themeService.GetSiteTheme(); + Assert.That(siteTheme.ThemeName, Is.EqualTo("ThemeOne")); + } + + [Test] + public void ThemeWithAvailableBaseThemeCanBeSetAsSiteTheme() { + _themeService.SetSiteTheme("ThemeTwo"); + var siteTheme = _themeService.GetSiteTheme(); + Assert.That(siteTheme.ThemeName, Is.EqualTo("ThemeTwo")); + Assert.That(siteTheme.BaseTheme, Is.EqualTo("ThemeOne")); + } + + [Test] + public void ThemeWithUnavailableBaseThemeCanBeSetAsSiteTheme() { + _themeService.SetSiteTheme("ThemeOne"); + _themeService.SetSiteTheme("ThemeThree"); + var siteTheme = _themeService.GetSiteTheme(); + Assert.That(siteTheme.ThemeName, Is.EqualTo("ThemeOne")); + } + + #region Stubs + + public class TestSessionLocator : ISessionLocator { + private readonly ISession _session; + + public TestSessionLocator(ISession session) { + _session = session; + } + + public ISession For(Type entityType) { + return _session; + } + } + + public class StubAuthorizer : IAuthorizer { + public bool Authorize(Permission permission) { + return true; + } + public bool Authorize(Permission permission, LocalizedString message) { + return true; + } + public bool Authorize(Permission permission, IContent content, LocalizedString message) { + return true; + } + } + + public class StubExtensionManager : IExtensionManager { + public IEnumerable AvailableExtensions() { + var extensions = new[] { + new ExtensionDescriptor {Name = "ThemeOne", ExtensionType = "Theme"}, + new ExtensionDescriptor {Name = "ThemeTwo", BaseTheme = "ThemeOne", ExtensionType = "Theme"}, + new ExtensionDescriptor {Name = "ThemeThree", BaseTheme = "TheThemeThatIsntThere", ExtensionType = "Theme"}, + }; + + foreach (var extension in extensions) { + extension.Features = new[] { new FeatureDescriptor { Extension = extension, Name = extension.Name } }; + yield return extension; + } + } + + public IEnumerable AvailableFeatures() { + return AvailableExtensions().SelectMany(ed => ed.Features); + } + + public IEnumerable LoadFeatures(IEnumerable featureDescriptors) { + return featureDescriptors.Select(FrameworkFeature); + } + + private static Feature FrameworkFeature(FeatureDescriptor descriptor) { + return new Feature { + Descriptor = descriptor + }; + } + + public void InstallExtension(string extensionType, HttpPostedFileBase extensionBundle) { + throw new NotImplementedException(); + } + + public void UninstallExtension(string extensionType, string extensionName) { + throw new NotImplementedException(); + } + + public void Monitor(Action monitor) { + throw new NotImplementedException(); + } + } + + public class StubShellDescriptorManager : IShellDescriptorManager { + private readonly ShellDescriptorRecord _shellDescriptorRecord = new ShellDescriptorRecord(); + public ShellDescriptor GetShellDescriptor() { + return GetShellDescriptorFromRecord(_shellDescriptorRecord); + } + + public void UpdateShellDescriptor(int priorSerialNumber, IEnumerable enabledFeatures, IEnumerable parameters) { + _shellDescriptorRecord.Features.Clear(); + foreach (var feature in enabledFeatures) { + _shellDescriptorRecord.Features.Add(new ShellFeatureRecord { Name = feature.Name, ShellDescriptorRecord = null }); + } + + _shellDescriptorRecord.Parameters.Clear(); + foreach (var parameter in parameters) { + _shellDescriptorRecord.Parameters.Add(new ShellParameterRecord { + Component = parameter.Component, + Name = parameter.Name, + Value = parameter.Value, + ShellDescriptorRecord = null + }); + } + } + + private static ShellDescriptor GetShellDescriptorFromRecord(ShellDescriptorRecord shellDescriptorRecord) { + return new ShellDescriptor { + SerialNumber = shellDescriptorRecord.SerialNumber, + Features = shellDescriptorRecord.Features.Select(descriptorFeatureRecord => new ShellFeature {Name = descriptorFeatureRecord.Name}).ToList(), + Parameters = shellDescriptorRecord.Parameters.Select(descriptorParameterRecord => new ShellParameter { + Component = descriptorParameterRecord.Component, Name = descriptorParameterRecord.Name, Value = descriptorParameterRecord.Value + }).ToList() + }; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs index dee0f004e..388086e3c 100644 --- a/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs +++ b/src/Orchard.Tests/DisplayManagement/DefaultDisplayManagerTests.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Web; using System.Web.Mvc; using Autofac; @@ -10,7 +8,6 @@ using NUnit.Framework; using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Implementation; using Orchard.DisplayManagement.Shapes; -using Orchard.Tests.Stubs; using Orchard.Themes; namespace Orchard.Tests.DisplayManagement { @@ -42,6 +39,7 @@ namespace Orchard.Tests.DisplayManagement { public string HomePage { get; set; } public string Tags { get; set; } public string Zones { get; set; } + public string BaseTheme { get; set; } } diff --git a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs index 362b3d0ce..5fd304468 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/SetupMode.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Web; -using System.Web.Mvc; using System.Web.Routing; using Autofac; using Orchard.Commands; @@ -9,7 +8,6 @@ using Orchard.Commands.Builtin; using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; using Orchard.ContentManagement.MetaData.Builders; -using Orchard.Core.Shapes; using Orchard.Data.Migration.Interpreters; using Orchard.Data.Providers; using Orchard.Data.Migration; @@ -18,13 +16,10 @@ using Orchard.DisplayManagement.Descriptors; using Orchard.DisplayManagement.Descriptors.ShapeAttributeStrategy; using Orchard.DisplayManagement.Descriptors.ShapeTemplateStrategy; using Orchard.DisplayManagement.Implementation; -using Orchard.DisplayManagement.Shapes; using Orchard.Environment; -using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Localization; using Orchard.Mvc; -using Orchard.Mvc.Filters; using Orchard.Mvc.ModelBinders; using Orchard.Mvc.Routes; using Orchard.Mvc.ViewEngines; @@ -116,6 +111,7 @@ namespace Orchard.Setup { public string HomePage { get; set; } public string Tags { get; set; } public string Zones { get; set; } + public string BaseTheme { get; set; } } private readonly SafeModeTheme _theme = new SafeModeTheme { diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Models/Theme.cs b/src/Orchard.Web/Modules/Orchard.Themes/Models/Theme.cs index 6b5b2f8f0..70f24ec98 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Models/Theme.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/Models/Theme.cs @@ -8,5 +8,6 @@ public string HomePage { get; set; } public string Tags { get; set; } public string Zones { get; set; } + public string BaseTheme { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Services/ThemeService.cs b/src/Orchard.Web/Modules/Orchard.Themes/Services/ThemeService.cs index c8b4ea08b..c8def13a6 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Services/ThemeService.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/Services/ThemeService.cs @@ -33,9 +33,9 @@ namespace Orchard.Themes.Services { protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; } public ITheme GetSiteTheme() { - string currentThemeName = CurrentSite.As().Record.CurrentThemeName; + string currentThemeName = CurrentSite.As().CurrentThemeName; - if (String.IsNullOrEmpty(currentThemeName)) { + if (string.IsNullOrEmpty(currentThemeName)) { return null; } @@ -43,24 +43,35 @@ namespace Orchard.Themes.Services { } public void SetSiteTheme(string themeName) { - if (GetThemeByName(themeName) != null) { + if (string.IsNullOrWhiteSpace(themeName)) + return; - var currentTheme = CurrentSite.As().Record.CurrentThemeName; + //todo: (heskew) need messages given in addition to all of these early returns so something meaningful can be presented to the user + var themeToSet = GetThemeByName(themeName); + if (themeToSet == null) + return; - if ( !String.IsNullOrEmpty(currentTheme) ) { - _moduleService.DisableFeatures(new[] {currentTheme}, true); - } - - if ( !String.IsNullOrEmpty(themeName) ) { - _moduleService.EnableFeatures(new[] {themeName}, true); - } - - CurrentSite.As().Record.CurrentThemeName = themeName; + //if there's a base theme, it needs to be present + ITheme baseTheme = null; + if (!string.IsNullOrWhiteSpace(themeToSet.BaseTheme)) { + baseTheme = GetThemeByName(themeToSet.BaseTheme); + if (baseTheme == null) + return; } + + var currentTheme = CurrentSite.As().CurrentThemeName; + if ( !string.IsNullOrEmpty(currentTheme) ) + _moduleService.DisableFeatures(new[] {currentTheme}, true); + + if (baseTheme != null) + _moduleService.EnableFeatures(new[] {themeToSet.BaseTheme}); + + _moduleService.EnableFeatures(new[] {themeToSet.ThemeName}, true); + + CurrentSite.As().Record.CurrentThemeName = themeToSet.ThemeName; } public ITheme GetRequestTheme(RequestContext requestContext) { - var requestTheme = _themeSelectors .Select(x => x.GetTheme(requestContext)) .Where(x => x != null) @@ -80,7 +91,7 @@ namespace Orchard.Themes.Services { public ITheme GetThemeByName(string name) { foreach (var descriptor in _extensionManager.AvailableExtensions()) { - if (String.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase)) { + if (string.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase)) { return CreateTheme(descriptor); } } @@ -91,11 +102,10 @@ namespace Orchard.Themes.Services { /// Loads only enabled themes /// public IEnumerable GetInstalledThemes() { - var themes = new List(); foreach (var descriptor in _extensionManager.AvailableExtensions()) { - - if (!String.Equals(descriptor.ExtensionType, "Theme", StringComparison.OrdinalIgnoreCase)) { + + if (!string.Equals(descriptor.ExtensionType, "Theme", StringComparison.OrdinalIgnoreCase)) { continue; } @@ -116,16 +126,17 @@ namespace Orchard.Themes.Services { _extensionManager.UninstallExtension("Theme", themeName); } - private ITheme CreateTheme(ExtensionDescriptor descriptor) { + private static ITheme CreateTheme(ExtensionDescriptor descriptor) { return new Theme { - Author = descriptor.Author ?? String.Empty, - Description = descriptor.Description ?? String.Empty, - DisplayName = descriptor.DisplayName ?? String.Empty, - HomePage = descriptor.WebSite ?? String.Empty, + Author = descriptor.Author ?? "", + Description = descriptor.Description ?? "", + DisplayName = descriptor.DisplayName ?? "", + HomePage = descriptor.WebSite ?? "", ThemeName = descriptor.Name, - Version = descriptor.Version ?? String.Empty, - Tags = descriptor.Tags ?? String.Empty, - Zones = descriptor.Zones ?? String.Empty, + Version = descriptor.Version ?? "", + Tags = descriptor.Tags ?? "", + Zones = descriptor.Zones ?? "", + BaseTheme = descriptor.BaseTheme ?? "", }; } } diff --git a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs index 7bf74cdcf..726d8099d 100644 --- a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs @@ -127,6 +127,7 @@ namespace Orchard.Environment.Extensions.Folders { Tags = GetValue(fields, "tags"), AntiForgery = GetValue(fields, "antiforgery"), Zones = GetValue(fields, "zones"), + BaseTheme = GetValue(fields, "basetheme"), }; extensionDescriptor.Features = GetFeaturesForExtension(GetMapping(fields, "features"), extensionDescriptor); diff --git a/src/Orchard/Environment/Extensions/Models/ExtensionDescriptor.cs b/src/Orchard/Environment/Extensions/Models/ExtensionDescriptor.cs index ec8a96347..667b8ab1e 100644 --- a/src/Orchard/Environment/Extensions/Models/ExtensionDescriptor.cs +++ b/src/Orchard/Environment/Extensions/Models/ExtensionDescriptor.cs @@ -27,6 +27,7 @@ namespace Orchard.Environment.Extensions.Models { public string Tags { get; set; } public string AntiForgery { get; set; } public string Zones { get; set; } + public string BaseTheme { get; set; } public IEnumerable Features { get; set; } } diff --git a/src/Orchard/Themes/ITheme.cs b/src/Orchard/Themes/ITheme.cs index b144049d6..178a651e6 100644 --- a/src/Orchard/Themes/ITheme.cs +++ b/src/Orchard/Themes/ITheme.cs @@ -11,5 +11,6 @@ string HomePage { get; set; } string Tags { get; set; } string Zones { get; set; } + string BaseTheme { get; set; } } }