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; }
}
}