diff --git a/.hgignore b/.hgignore index 2f0b5b1d9..91673c801 100644 --- a/.hgignore +++ b/.hgignore @@ -14,3 +14,4 @@ glob:desktop.ini glob:src/Orchard.Azure.suo glob:src/Orchard.5.0.ReSharper glob:log.xml +glob:profiling diff --git a/AzurePackage.proj b/AzurePackage.proj index ad02d137d..0dade831c 100644 --- a/AzurePackage.proj +++ b/AzurePackage.proj @@ -109,6 +109,23 @@ + + + + + + + + + + $(MSBuildProjectDirectory)\lib $(MSBuildProjectDirectory)\src $(MSBuildProjectDirectory)\build + $(MSBuildProjectDirectory)\artifacts\Source + $(MSBuildProjectDirectory)\artifacts\WebPI $(BuildFolder)\Compile $(CompileFolder)\_PublishedWebsites @@ -116,6 +118,20 @@ + + + + + + + + @@ -146,8 +162,10 @@ - - + + + + diff --git a/build.cmd b/build.cmd index 1e1be68bc..eeddd1991 100644 --- a/build.cmd +++ b/build.cmd @@ -1,3 +1,3 @@ if "%~1"=="" build Build msbuild /t:%~1 Orchard.proj -msbuild /t:%~1 AzurePackage.proj + diff --git a/src/Orchard.5.0.ReSharper b/src/Orchard.5.0.ReSharper new file mode 100644 index 000000000..21ba39891 --- /dev/null +++ b/src/Orchard.5.0.ReSharper @@ -0,0 +1,103 @@ + + + + + SOLUTION + + + END_OF_LINE + END_OF_LINE + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + END_OF_LINE + END_OF_LINE + + public + protected + internal + private + new + abstract + virtual + override + sealed + static + readonly + extern + unsafe + volatile + + END_OF_LINE + END_OF_LINE + + + + $object$_On$event$ + $event$Handler + + + + + + + + + + + + + + + + + + + + + + + + + $object$_On$event$ + $event$Handler + + + + + + + + + + + + + + $object$_On$event$ + $event$Handler + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs b/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs index ccd2a82cb..8c63d172c 100644 --- a/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs +++ b/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs @@ -1,5 +1,4 @@ -using System.IO; -using System.Linq; +using System.Linq; using Microsoft.WindowsAzure; using NUnit.Framework; using Orchard.Azure.Environment.Configuration; @@ -15,7 +14,7 @@ namespace Orchard.Azure.Tests.Environment.Configuration { CloudStorageAccount devAccount; CloudStorageAccount.TryParse("UseDevelopmentStorage=true", out devAccount); - Loader = new AzureShellSettingsManager(devAccount); + Loader = new AzureShellSettingsManager(devAccount, new Moq.Mock().Object); } [SetUp] diff --git a/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs b/src/Orchard.Azure.Tests/FileSystems/Media/AzureBlobStorageProviderTests.cs similarity index 94% rename from src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs rename to src/Orchard.Azure.Tests/FileSystems/Media/AzureBlobStorageProviderTests.cs index 8c43b9a15..2b2c5baab 100644 --- a/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs +++ b/src/Orchard.Azure.Tests/FileSystems/Media/AzureBlobStorageProviderTests.cs @@ -1,11 +1,12 @@ using System; using System.IO; using NUnit.Framework; -using Orchard.Azure.Storage; +using Orchard.Azure.FileSystems.Media; using Microsoft.WindowsAzure; using System.Linq; +using Orchard.Environment.Configuration; -namespace Orchard.Azure.Tests.Storage { +namespace Orchard.Azure.Tests.FileSystems.Media { [TestFixture] public class AzureBlobStorageProviderTests : AzureVirtualEnvironmentTest { @@ -15,7 +16,7 @@ namespace Orchard.Azure.Tests.Storage { CloudStorageAccount devAccount; CloudStorageAccount.TryParse("UseDevelopmentStorage=true", out devAccount); - _azureBlobStorageProvider = new AzureBlobStorageProvider("default", devAccount); + _azureBlobStorageProvider = new AzureBlobStorageProvider(new ShellSettings { Name = "default" }, devAccount); } [SetUp] @@ -150,4 +151,4 @@ namespace Orchard.Azure.Tests.Storage { Assert.AreEqual(teststring, content); } } -} +} \ No newline at end of file diff --git a/src/Orchard.Azure.Tests/Orchard.Azure.Tests.csproj b/src/Orchard.Azure.Tests/Orchard.Azure.Tests.csproj index 393e8b820..1e1dc42b3 100644 --- a/src/Orchard.Azure.Tests/Orchard.Azure.Tests.csproj +++ b/src/Orchard.Azure.Tests/Orchard.Azure.Tests.csproj @@ -34,6 +34,10 @@ + + False + ..\..\lib\moq\Moq.dll + False ..\..\lib\nunit\nunit.framework.dll @@ -56,7 +60,7 @@ - + diff --git a/src/Orchard.Azure/AzureFileSystem.cs b/src/Orchard.Azure/AzureFileSystem.cs index 4f66b1bc6..c2c16eded 100644 --- a/src/Orchard.Azure/AzureFileSystem.cs +++ b/src/Orchard.Azure/AzureFileSystem.cs @@ -4,26 +4,26 @@ using System.Linq; using Microsoft.WindowsAzure; using Microsoft.WindowsAzure.StorageClient; using System.IO; -using Orchard.Storage; +using Orchard.FileSystems.Media; namespace Orchard.Azure { public class AzureFileSystem { public string ContainerName { get; protected set; } private readonly CloudStorageAccount _storageAccount; - private readonly string _shellName; + private readonly string _root; public CloudBlobClient BlobClient { get; private set; } public CloudBlobContainer Container { get; private set; } - public AzureFileSystem(string containerName, string shellName, bool isPrivate) - : this(containerName, shellName, isPrivate, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) { + public AzureFileSystem(string containerName, string root, bool isPrivate) + : this(containerName, root, isPrivate, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) { } - public AzureFileSystem(string containerName, string shellName, bool isPrivate, CloudStorageAccount storageAccount) { + public AzureFileSystem(string containerName, string root, bool isPrivate, CloudStorageAccount storageAccount) { // Setup the connection to custom storage accountm, e.g. Development Storage _storageAccount = storageAccount; ContainerName = containerName; - _shellName = shellName; + _root = String.IsNullOrEmpty(root) || root == "/" ? String.Empty : root + "/"; BlobClient = _storageAccount.CreateCloudBlobClient(); // Get and create the container if it does not exist @@ -47,20 +47,20 @@ namespace Orchard.Azure { public IStorageFile GetFile(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); Container.EnsureBlobExists(path); return new AzureBlobFileStorage(Container.GetBlockBlobReference(path)); } public bool FileExists(string path) { - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); return Container.BlobExists(path); } public IEnumerable ListFiles(string path) { EnsurePathIsRelative(path); - string prefix = String.Concat(Container.Name, "/", _shellName, "/", path); + string prefix = String.Concat(Container.Name, "/", _root, path); if ( !prefix.EndsWith("/") ) prefix += "/"; @@ -71,7 +71,7 @@ namespace Orchard.Azure { public IEnumerable ListFolders(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); if ( !Container.DirectoryExists(path) ) { try { @@ -91,7 +91,7 @@ namespace Orchard.Azure { public void CreateFolder(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); Container.EnsureDirectoryDoesNotExist(path); Container.GetDirectoryReference(path); @@ -99,7 +99,7 @@ namespace Orchard.Azure { public void DeleteFolder(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); Container.EnsureDirectoryExists(path); foreach ( var blob in Container.GetDirectoryReference(path).ListBlobs() ) { @@ -107,7 +107,7 @@ namespace Orchard.Azure { ( (CloudBlob)blob ).Delete(); if ( blob is CloudBlobDirectory ) - DeleteFolder(blob.Uri.ToString().Substring(Container.Uri.ToString().Length + 2 + _shellName.Length)); + DeleteFolder(blob.Uri.ToString().Substring(Container.Uri.ToString().Length + 1 + _root.Length)); } } @@ -122,7 +122,7 @@ namespace Orchard.Azure { if ( !newPath.EndsWith("/") ) newPath += "/"; - foreach ( var blob in Container.GetDirectoryReference(_shellName + "/" + path).ListBlobs() ) { + foreach ( var blob in Container.GetDirectoryReference(_root + path).ListBlobs() ) { if ( blob is CloudBlob ) { string filename = Path.GetFileName(blob.Uri.ToString()); string source = String.Concat(path, filename); @@ -142,7 +142,7 @@ namespace Orchard.Azure { public void DeleteFile(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); Container.EnsureBlobExists(path); var blob = Container.GetBlockBlobReference(path); @@ -151,10 +151,10 @@ namespace Orchard.Azure { public void RenameFile(string path, string newPath) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); EnsurePathIsRelative(newPath); - newPath = String.Concat(_shellName, "/", newPath); + newPath = String.Concat(_root, newPath); Container.EnsureBlobExists(path); Container.EnsureBlobDoesNotExist(newPath); @@ -167,7 +167,7 @@ namespace Orchard.Azure { public IStorageFile CreateFile(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); if ( Container.BlobExists(path) ) { throw new ArgumentException("File " + path + " already exists"); @@ -180,7 +180,7 @@ namespace Orchard.Azure { public string GetPublicUrl(string path) { EnsurePathIsRelative(path); - path = String.Concat(_shellName, "/", path); + path = String.Concat(_root, path); Container.EnsureBlobExists(path); return Container.GetBlockBlobReference(path).Uri.ToString(); } diff --git a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs b/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs index 9f98c658e..bd14c2a56 100644 --- a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs +++ b/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs @@ -13,6 +13,7 @@ namespace Orchard.Azure.Environment.Configuration { public class AzureShellSettingsManager : IShellSettingsManager { public const string ContainerName = "sites"; // container names must be lower cased + private readonly IShellSettingsManagerEventHandler _events; private readonly CloudStorageAccount _storageAccount; public CloudBlobClient BlobClient { get; private set; } @@ -21,14 +22,16 @@ namespace Orchard.Azure.Environment.Configuration { Localizer T { get; [UsedImplicitly] set; } - public AzureShellSettingsManager() : this(CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) + public AzureShellSettingsManager(IShellSettingsManagerEventHandler events) + : this(CloudStorageAccount.FromConfigurationSetting("DataConnectionString"), events) { } - public AzureShellSettingsManager(CloudStorageAccount storageAccount) + public AzureShellSettingsManager(CloudStorageAccount storageAccount, IShellSettingsManagerEventHandler events) { // Setup the connection to custom storage accountm, e.g. Development Storage _storageAccount = storageAccount; + _events = events; BlobClient = _storageAccount.CreateCloudBlobClient(); @@ -54,6 +57,8 @@ namespace Orchard.Azure.Environment.Configuration { var filePath =String.Concat(settings.Name, "/", "Settings.txt"); var blob = Container.GetBlockBlobReference(filePath); blob.UploadText(ComposeSettings(settings)); + + _events.Saved(settings); } IEnumerable LoadSettings() { diff --git a/src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs b/src/Orchard.Azure/FileSystems/AppData/AzureAppDataFolder.cs similarity index 87% rename from src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs rename to src/Orchard.Azure/FileSystems/AppData/AzureAppDataFolder.cs index e108a5f05..ee4a5b190 100644 --- a/src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs +++ b/src/Orchard.Azure/FileSystems/AppData/AzureAppDataFolder.cs @@ -3,14 +3,15 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Orchard.Environment.Configuration; +using Orchard.FileSystems.AppData; -namespace Orchard.Azure.Environment.Configuration { +namespace Orchard.Azure.FileSystems.AppData { public class AzureAppDataFolder : IAppDataFolder { private readonly AzureFileSystem _fs; - public AzureAppDataFolder(string shellName) { - _fs = new AzureFileSystem("appdata", shellName, true); + public AzureAppDataFolder() { + _fs = new AzureFileSystem("appdata", null, true); } public void CreateFile(string path, string content) { @@ -62,4 +63,4 @@ namespace Orchard.Azure.Environment.Configuration { throw new NotImplementedException(); } } -} +} \ No newline at end of file diff --git a/src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs b/src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs new file mode 100644 index 000000000..e6fe9acac --- /dev/null +++ b/src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs @@ -0,0 +1,14 @@ +using Microsoft.WindowsAzure; +using Orchard.Environment.Configuration; +using Orchard.FileSystems.Media; + +namespace Orchard.Azure.FileSystems.Media { + public class AzureBlobStorageProvider : AzureFileSystem, IStorageProvider { + + public AzureBlobStorageProvider(ShellSettings shellSettings) + : this(shellSettings, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) { + } + + public AzureBlobStorageProvider(ShellSettings shellSettings, CloudStorageAccount storageAccount) : base("media", shellSettings.Name, false, storageAccount) { } + } +} \ No newline at end of file diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/Diagnostics.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/Diagnostics.config index 124536a7d..d603238d2 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/Diagnostics.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/Diagnostics.config @@ -3,25 +3,25 @@ - + - + - + - + diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config index b56b3523b..1a081a020 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config @@ -14,19 +14,13 @@ - - - + type="Orchard.Azure.FileSystems.Media.AzureBlobStorageProvider, Orchard.Azure" + service="Orchard.FileSystems.Media.IStorageProvider"> - - - + type="Orchard.Azure.FileSystems.AppData.AzureAppDataFolder, Orchard.Azure" + service="Orchard.FileSystems.AppData.IAppDataFolder"> diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj b/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj index 0f11fc754..2d5045c4d 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj +++ b/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj @@ -34,20 +34,12 @@ False - ..\..\lib\autofac\Autofac.dll + ..\..\..\lib\autofac\Autofac.dll False ..\..\..\lib\autofac\Autofac.Configuration.dll - - False - ..\..\lib\autofac\Autofac.Integration.Web.dll - - - False - ..\..\lib\autofac\Autofac.Integration.Web.Mvc.dll - False ..\..\..\lib\Castle Windsor 2.0\bin\Castle.Core.dll @@ -59,10 +51,6 @@ - - False - ..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll - @@ -74,10 +62,6 @@ 3.5 - - False - ..\..\lib\sqlite\x64\System.Data.SQLite.DLL - False ..\..\..\..\..\..\Program Files (x86)\Microsoft ASP.NET\ASP.NET MVC 2\\Assemblies\System.Web.Mvc.dll @@ -96,14 +80,6 @@ - - False - bin\Orchard.Framework.dll - - - False - bin\Orchard.Azure.dll - @@ -123,6 +99,16 @@ + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + + + {2505AA84-65A6-43D0-9C27-4F44FD576284} + Orchard.Azure + + diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Web.config b/src/Orchard.Azure/Orchard.Azure.Web/Web.config index b1de2f004..722cd267a 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Web.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Web.config @@ -8,151 +8,205 @@ \Windows\Microsoft.Net\Framework\v2.x\Config --> - - - -
- -
-
-
-
- - - - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Orchard.Azure/Orchard.Azure.csproj b/src/Orchard.Azure/Orchard.Azure.csproj index 21becee28..bdd1d9fc8 100644 --- a/src/Orchard.Azure/Orchard.Azure.csproj +++ b/src/Orchard.Azure/Orchard.Azure.csproj @@ -51,9 +51,9 @@ - + - + diff --git a/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs b/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs deleted file mode 100644 index 938a16602..000000000 --- a/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Microsoft.WindowsAzure; -using Orchard.Storage; - -namespace Orchard.Azure.Storage { - - public class AzureBlobStorageProvider : AzureFileSystem, IStorageProvider { - - public AzureBlobStorageProvider(string shellName) - : this(shellName, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) { - } - - public AzureBlobStorageProvider(string shellName, CloudStorageAccount storageAccount) : base("media", shellName, false, storageAccount) { } - } -} - - diff --git a/src/Orchard.Core.Tests/Common/Providers/CommonAspectProviderTests.cs b/src/Orchard.Core.Tests/Common/Providers/CommonAspectProviderTests.cs index 294b75f9b..d62bae234 100644 --- a/src/Orchard.Core.Tests/Common/Providers/CommonAspectProviderTests.cs +++ b/src/Orchard.Core.Tests/Common/Providers/CommonAspectProviderTests.cs @@ -6,6 +6,7 @@ using JetBrains.Annotations; using Moq; using NUnit.Framework; using Orchard.ContentManagement.Aspects; +using Orchard.ContentManagement.MetaData.Records; using Orchard.Core.Common; using Orchard.Core.Common.Handlers; using Orchard.Core.Common.Models; @@ -28,6 +29,7 @@ namespace Orchard.Core.Tests.Common.Providers { public override void Register(ContainerBuilder builder) { builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -45,6 +47,8 @@ namespace Orchard.Core.Tests.Common.Providers { get { return new[] { typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(CommonRecord), diff --git a/src/Orchard.Core.Tests/Common/Services/RoutableServiceTests.cs b/src/Orchard.Core.Tests/Common/Services/RoutableServiceTests.cs index 1a3b18f47..69bb981db 100644 --- a/src/Orchard.Core.Tests/Common/Services/RoutableServiceTests.cs +++ b/src/Orchard.Core.Tests/Common/Services/RoutableServiceTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; using Orchard.ContentManagement; using Orchard.ContentManagement.Drivers; using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.MetaData.Records; using Orchard.ContentManagement.Records; using Orchard.Core.Common.Models; using Orchard.Core.Common.Services; @@ -26,6 +27,8 @@ namespace Orchard.Core.Tests.Common.Services { public override void Register(ContainerBuilder builder) { builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -68,7 +71,7 @@ namespace Orchard.Core.Tests.Common.Services { Assert.That(_routableService.IsSlugValid("a" + c + "b"), Is.False); } } - + [Test] public void VeryLongStringTruncatedTo1000Chars() { @@ -140,18 +143,15 @@ namespace Orchard.Core.Tests.Common.Services { } [Test] - public void GeneratedSlugsShouldBeUniqueAmongContentType() - { + public void GeneratedSlugsShouldBeUniqueAmongContentType() { var contentManager = _container.Resolve(); - var thing1 = contentManager.Create(ThingDriver.ContentType.Name, t => - { + var thing1 = contentManager.Create(ThingDriver.ContentType.Name, t => { t.As().Record = new RoutableRecord(); t.Title = "This Is Some Interesting Title"; }); - - var thing2 = contentManager.Create(ThingDriver.ContentType.Name , t => - { + + var thing2 = contentManager.Create(ThingDriver.ContentType.Name, t => { t.As().Record = new RoutableRecord(); t.Title = "This Is Some Interesting Title"; }); @@ -160,18 +160,15 @@ namespace Orchard.Core.Tests.Common.Services { } [Test] - public void SlugsCanBeDuplicatedAccrossContentTypes() - { + public void SlugsCanBeDuplicatedAccrossContentTypes() { var contentManager = _container.Resolve(); - var thing = contentManager.Create(ThingDriver.ContentType.Name, t => - { + var thing = contentManager.Create(ThingDriver.ContentType.Name, t => { t.As().Record = new RoutableRecord(); t.Title = "This Is Some Interesting Title"; }); - var stuff = contentManager.Create(StuffDriver.ContentType.Name, s => - { + var stuff = contentManager.Create(StuffDriver.ContentType.Name, s => { s.As().Record = new RoutableRecord(); s.Title = "This Is Some Interesting Title"; }); @@ -184,9 +181,11 @@ namespace Orchard.Core.Tests.Common.Services { get { return new[] { typeof(RoutableRecord), + typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), - typeof(ContentTypeRecord), typeof(CommonRecord), typeof(CommonVersionRecord), }; @@ -195,8 +194,7 @@ namespace Orchard.Core.Tests.Common.Services { [UsedImplicitly] public class ThingHandler : ContentHandler { - public ThingHandler() - { + public ThingHandler() { Filters.Add(new ActivatingFilter(ThingDriver.ContentType.Name)); Filters.Add(new ActivatingFilter>(ThingDriver.ContentType.Name)); Filters.Add(new ActivatingFilter(ThingDriver.ContentType.Name)); @@ -217,7 +215,7 @@ namespace Orchard.Core.Tests.Common.Services { set { this.As().Slug = value; } } } - + public class ThingDriver : ContentItemDriver { public readonly static ContentType ContentType = new ContentType { Name = "thing", @@ -226,10 +224,8 @@ namespace Orchard.Core.Tests.Common.Services { } [UsedImplicitly] - public class StuffHandler : ContentHandler - { - public StuffHandler() - { + public class StuffHandler : ContentHandler { + public StuffHandler() { Filters.Add(new ActivatingFilter(StuffDriver.ContentType.Name)); Filters.Add(new ActivatingFilter>(StuffDriver.ContentType.Name)); Filters.Add(new ActivatingFilter(StuffDriver.ContentType.Name)); @@ -237,27 +233,22 @@ namespace Orchard.Core.Tests.Common.Services { } } - public class Stuff : ContentPart - { + public class Stuff : ContentPart { public int Id { get { return ContentItem.Id; } } - public string Title - { + public string Title { get { return this.As().Title; } set { this.As().Title = value; } } - public string Slug - { + public string Slug { get { return this.As().Slug; } set { this.As().Slug = value; } } } - public class StuffDriver : ContentItemDriver - { - public readonly static ContentType ContentType = new ContentType - { + public class StuffDriver : ContentItemDriver { + public readonly static ContentType ContentType = new ContentType { Name = "stuff", DisplayName = "Stuff" }; diff --git a/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs b/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs index 7be62219d..e8cdd485a 100644 --- a/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs +++ b/src/Orchard.Core.Tests/Feeds/Controllers/FeedControllerTests.cs @@ -27,7 +27,8 @@ namespace Orchard.Core.Tests.Feeds.Controllers { var controller = new FeedController( Enumerable.Empty(), Enumerable.Empty(), - Enumerable.Empty()) { + new StubItemBuilder() + ) { ValueProvider = Values.From(new { }) }; @@ -54,7 +55,8 @@ namespace Orchard.Core.Tests.Feeds.Controllers { var controller = new FeedController( new[] { queryProvider.Object }, new[] { formatProvider.Object }, - Enumerable.Empty()) { + new StubItemBuilder() + ) { ValueProvider = Values.From(new { }) }; @@ -86,15 +88,20 @@ namespace Orchard.Core.Tests.Feeds.Controllers { } } + class StubItemBuilder : IFeedItemBuilder { + public void Populate(FeedContext context) { + } + } + [Test] public void RssFeedShouldBeStructuredAppropriately() { var query = new StubQuery(Enumerable.Empty()); var builder = new ContainerBuilder(); - //builder.RegisterModule(new ImplicitCollectionSupportModule()); builder.RegisterType(); builder.RegisterType().As(); builder.RegisterInstance(query).As(); + builder.RegisterInstance(new StubItemBuilder()).As(); var container = builder.Build(); var controller = container.Resolve(); @@ -117,9 +124,9 @@ namespace Orchard.Core.Tests.Feeds.Controllers { }); var builder = new ContainerBuilder(); - //builder.RegisterModule(new ImplicitCollectionSupportModule()); builder.RegisterType(); builder.RegisterType().As(); + builder.RegisterInstance(new StubItemBuilder()).As(); builder.RegisterInstance(query).As(); var container = builder.Build(); diff --git a/src/Orchard.Core.Tests/Scheduling/ScheduledTaskExecutorTests.cs b/src/Orchard.Core.Tests/Scheduling/ScheduledTaskExecutorTests.cs index 887fa244f..e7a7955dd 100644 --- a/src/Orchard.Core.Tests/Scheduling/ScheduledTaskExecutorTests.cs +++ b/src/Orchard.Core.Tests/Scheduling/ScheduledTaskExecutorTests.cs @@ -4,6 +4,7 @@ using Autofac; using Moq; using NUnit.Framework; using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData.Records; using Orchard.ContentManagement.Records; using Orchard.Core.Scheduling.Models; using Orchard.Core.Scheduling.Services; @@ -28,7 +29,9 @@ namespace Orchard.Core.Tests.Scheduling { _handler = new StubTaskHandler(); builder.RegisterInstance(new Mock().Object); builder.RegisterType().As(); - builder.RegisterType().As().Named("ScheduledTaskExecutor", typeof (IBackgroundTask)); + builder.RegisterType().As(); + + builder.RegisterType().As().Named("ScheduledTaskExecutor", typeof(IBackgroundTask)); builder.RegisterInstance(_handler).As(); } @@ -36,6 +39,8 @@ namespace Orchard.Core.Tests.Scheduling { get { return new[] { typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(ScheduledTaskRecord), @@ -98,7 +103,7 @@ namespace Orchard.Core.Tests.Scheduling { Assert.That(_handler.TaskContext, Is.Null); _executor.Sweep(); Assert.That(_handler.TaskContext, Is.Not.Null); - + Assert.That(_handler.TaskContext.Task.TaskType, Is.EqualTo("Ignore")); Assert.That(_handler.TaskContext.Task.ContentItem, Is.Null); } diff --git a/src/Orchard.Core.Tests/Scheduling/ScheduledTaskManagerTests.cs b/src/Orchard.Core.Tests/Scheduling/ScheduledTaskManagerTests.cs index a8db8cabd..baf3063c3 100644 --- a/src/Orchard.Core.Tests/Scheduling/ScheduledTaskManagerTests.cs +++ b/src/Orchard.Core.Tests/Scheduling/ScheduledTaskManagerTests.cs @@ -5,6 +5,7 @@ using Autofac; using Moq; using NUnit.Framework; using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData.Records; using Orchard.ContentManagement.Records; using Orchard.Core.Scheduling.Models; using Orchard.Core.Scheduling.Services; @@ -26,12 +27,14 @@ namespace Orchard.Core.Tests.Scheduling { _repository = _container.Resolve>(); _scheduledTaskManager = _container.Resolve(); _contentManager = _container.Resolve(); - _mockServices.SetupGet(x=>x.ContentManager).Returns(_contentManager); + _mockServices.SetupGet(x => x.ContentManager).Returns(_contentManager); } - public override void Register(ContainerBuilder builder) { + public override void Register(ContainerBuilder builder) { builder.RegisterInstance(_mockServices.Object); builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); } @@ -39,6 +42,8 @@ namespace Orchard.Core.Tests.Scheduling { get { return new[] { typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(ScheduledTaskRecord), @@ -86,7 +91,7 @@ namespace Orchard.Core.Tests.Scheduling { public void TasksForAllVersionsOfContenItemShouldBeReturned() { var hello1 = _contentManager.New("hello"); _contentManager.Create(hello1); - + var hello2 = _contentManager.GetDraftRequired(hello1.Id); Assert.That(hello1.Version, Is.EqualTo(1)); diff --git a/src/Orchard.Profile/Orchard.Profile.csproj b/src/Orchard.Profile/Orchard.Profile.csproj index 422e4bf7a..4bf64ceba 100644 --- a/src/Orchard.Profile/Orchard.Profile.csproj +++ b/src/Orchard.Profile/Orchard.Profile.csproj @@ -40,7 +40,7 @@ DEBUG;TRACE prompt 4 - AllRules.ruleset + x86 pdbonly diff --git a/src/Orchard.Profile/Tests/HttpClient.cs b/src/Orchard.Profile/Tests/HttpClient.cs index 98e6b65f9..ec89c114d 100644 --- a/src/Orchard.Profile/Tests/HttpClient.cs +++ b/src/Orchard.Profile/Tests/HttpClient.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Text; using System.Web; +using NUnit.Framework; using TechTalk.SpecFlow; namespace Orchard.Profile.Tests { @@ -17,7 +18,18 @@ namespace Orchard.Profile.Tests { [Given(@"I am logged in")] public void GivenIAmLoggedIn() { - DoRequest("/Users/Account/LogOn", "userNameOrEmail=admin&password=profiling-secret&rememberMe=false"); + DoRequest("/Users/Account/LogOn"); + + const string requestVerificationTokenName = "__RequestVerificationToken"; + const string valueMarker = "value=\""; + + var tokenIndex = _text.IndexOf(requestVerificationTokenName); + var valueIndex = _text.IndexOf(valueMarker, tokenIndex); + var valueStart = valueIndex + valueMarker.Length; + var valueEnd = _text.IndexOf("\"", valueStart); + var requestVerificationTokenValue = _text.Substring(valueStart, valueEnd - valueStart); + + DoRequest("/Users/Account/LogOn", "userNameOrEmail=admin&password=profiling-secret&rememberMe=false&" + requestVerificationTokenName + "=" + requestVerificationTokenValue); } [When(@"I go to ""(.*)""")] diff --git a/src/Orchard.Profile/Tests/Profiling.feature b/src/Orchard.Profile/Tests/Profiling.feature index 692963c20..352c133c1 100644 --- a/src/Orchard.Profile/Tests/Profiling.feature +++ b/src/Orchard.Profile/Tests/Profiling.feature @@ -3,6 +3,24 @@ As a developer I want to generate a fixed number of repeatable requests +Scenario: Warmup + Given I am logged in + When I go to "/admin" + When I go to "/blog0" + When I go to "/" + Scenario: Dashboard Given I am logged in When I go to "/admin" 40 times + +Scenario: Hitting blogs + Given I am logged in + When I go to "/blog0" 10 times + When I go to "/blog1" 10 times + When I go to "/blog2" 10 times + When I go to "/blog3" 10 times + When I go to "/blog4" 10 times + +Scenario: Hitting home page + Given I am logged in + When I go to "/" 40 times diff --git a/src/Orchard.Profile/Tests/Profiling.feature.cs b/src/Orchard.Profile/Tests/Profiling.feature.cs index b84190450..8673be112 100644 --- a/src/Orchard.Profile/Tests/Profiling.feature.cs +++ b/src/Orchard.Profile/Tests/Profiling.feature.cs @@ -51,16 +51,73 @@ namespace Orchard.Profile.Tests } [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("Dashboard")] - public virtual void Dashboard() + [NUnit.Framework.DescriptionAttribute("Warmup")] + public virtual void Warmup() { - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Dashboard", ((string[])(null))); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Warmup", ((string[])(null))); #line 6 this.ScenarioSetup(scenarioInfo); #line 7 testRunner.Given("I am logged in"); #line 8 + testRunner.When("I go to \"/admin\""); +#line 9 + testRunner.When("I go to \"/blog0\""); +#line 10 + testRunner.When("I go to \"/\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Dashboard")] + public virtual void Dashboard() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Dashboard", ((string[])(null))); +#line 12 +this.ScenarioSetup(scenarioInfo); +#line 13 + testRunner.Given("I am logged in"); +#line 14 testRunner.When("I go to \"/admin\" 40 times"); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Hitting blogs")] + public virtual void HittingBlogs() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Hitting blogs", ((string[])(null))); +#line 16 +this.ScenarioSetup(scenarioInfo); +#line 17 + testRunner.Given("I am logged in"); +#line 18 + testRunner.When("I go to \"/blog0\" 10 times"); +#line 19 + testRunner.When("I go to \"/blog1\" 10 times"); +#line 20 + testRunner.When("I go to \"/blog2\" 10 times"); +#line 21 + testRunner.When("I go to \"/blog3\" 10 times"); +#line 22 + testRunner.When("I go to \"/blog4\" 10 times"); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Hitting home page")] + public virtual void HittingHomePage() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Hitting home page", ((string[])(null))); +#line 24 +this.ScenarioSetup(scenarioInfo); +#line 25 + testRunner.Given("I am logged in"); +#line 26 + testRunner.When("I go to \"/\" 40 times"); #line hidden testRunner.CollectScenarioErrors(); } diff --git a/src/Orchard.Profile/profiling-setup-commands.txt b/src/Orchard.Profile/profiling-setup-commands.txt index 15f7607eb..f839517bc 100644 --- a/src/Orchard.Profile/profiling-setup-commands.txt +++ b/src/Orchard.Profile/profiling-setup-commands.txt @@ -1,6 +1,3 @@ -help commands -setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SQLite /EnabledFeatures:Orchard.Framework,Common,Dashboard,Feeds,HomePage,Navigation,Scheduling,Settings,XmlRpc,Orchard.Users,Orchard.Roles,TinyMce,Orchard.Modules,Orchard.Themes,Orchard.MultiTenancy,Orchard.Pages,Orchard.Blogs,Orchard.Comments,Futures.Widgets,Orchard.Media,Orchard.Tags,Orchard.DevTools -help commands -tenant list -feature list +setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SQLite /EnabledFeatures:Orchard.Framework,Common,Dashboard,Feeds,HomePage,Navigation,Scheduling,Settings,XmlRpc,Orchard.Users,Orchard.Roles,TinyMce,Orchard.Modules,Orchard.Themes,Orchard.MultiTenancy,Orchard.Pages,Orchard.Blogs,Orchard.Comments,Futures.Widgets,Orchard.Media,Orchard.Tags,Orchard.DevTools add profiling data +feature disable Orchard.DevTools diff --git a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs index 0558fb854..2d0059b2f 100644 --- a/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs +++ b/src/Orchard.Specs/Bindings/OrchardSiteFactory.cs @@ -16,7 +16,7 @@ namespace Orchard.Specs.Bindings { var webApp = Binding(); webApp.GivenIHaveACleanSiteWith(TableData( - new { extension = "module", names = "Orchard.Setup, Orchard.Modules, Orchard.Themes, Orchard.Users, Orchard.Roles, Orchard.Pages, Orchard.Comments, TinyMce" }, + new { extension = "module", names = "Orchard.Setup, Orchard.Modules, Orchard.Themes, Orchard.Users, Orchard.Roles, Orchard.Pages, Orchard.Comments, Orchard.Tags, TinyMce" }, new { extension = "core", names = "Common, Dashboard, Feeds, HomePage, Navigation, Scheduling, Settings, XmlRpc" }, new { extension = "theme", names = "SafeMode, Classic" })); diff --git a/src/Orchard.Specs/Bindings/WebAppHosting.cs b/src/Orchard.Specs/Bindings/WebAppHosting.cs index 99e439472..66e848aea 100644 --- a/src/Orchard.Specs/Bindings/WebAppHosting.cs +++ b/src/Orchard.Specs/Bindings/WebAppHosting.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -157,22 +158,39 @@ namespace Orchard.Specs.Bindings { foreach (var row in table.Rows) { var r = row; - var input = inputs.SingleOrDefault(x => x.GetAttributeValue("name", x.GetAttributeValue("id", "")) == r["name"]); + var input = inputs.First(x => x.GetAttributeValue("name", x.GetAttributeValue("id", "")) == r["name"]); Assert.That(input, Is.Not.Null, "Unable to locate name {0} in page html:\r\n\r\n{1}", r["name"], Details.ResponseText); - input.Attributes.Add("value", row["value"]); + var inputType = input.GetAttributeValue("type", ""); + switch(inputType) { + case "radio": + var radios = inputs.Where( + x => + x.GetAttributeValue("type", "") == "radio" && + x.GetAttributeValue("name", x.GetAttributeValue("id", "")) == r["name"]); + foreach(var radio in radios) { + if (radio.GetAttributeValue("value", "") == row["value"]) + radio.Attributes.Add("checked", "checked"); + else if (radio.Attributes.Contains("checked")) + radio.Attributes.Remove("checked"); + } + break; + default: + input.Attributes.Add("value", row["value"]); + break; + } } } [When(@"I hit ""(.*)""")] public void WhenIHit(string submitText) { var submit = _doc.DocumentNode - .SelectNodes("//input[@type='submit']") - .Single(elt => elt.GetAttributeValue("value", null) == submitText); + .SelectSingleNode(string.Format("(//input[@type='submit'][@value='{0}']|//button[@type='submit'][text()='{0}'])", submitText)); var form = Form.LocateAround(submit); var urlPath = form.Start.GetAttributeValue("action", Details.UrlPath); var inputs = form.Children .SelectMany(elt => elt.DescendantsAndSelf("input")) + .Where(node => !((node.GetAttributeValue("type", "") == "radio" || node.GetAttributeValue("type", "") == "checkbox") && node.GetAttributeValue("checked", "") != "checked")) .GroupBy(elt => elt.GetAttributeValue("name", elt.GetAttributeValue("id", "")), elt => elt.GetAttributeValue("value", "")) .ToDictionary(elt => elt.Key, elt => (IEnumerable)elt); @@ -203,6 +221,11 @@ namespace Orchard.Specs.Bindings { Assert.That(Details.ResponseText, Is.StringContaining(text)); } + [Then(@"I should not see ""(.*)""")] + public void ThenIShouldNotSee(string text) { + Assert.That(Details.ResponseText, Is.Not.StringContaining(text)); + } + [Then(@"the title contains ""(.*)""")] public void ThenTheTitleContainsText(string text) { ScenarioContext.Current.Pending(); diff --git a/src/Orchard.Specs/Hosting/WebHost.cs b/src/Orchard.Specs/Hosting/WebHost.cs index d834cbe01..d19119483 100644 --- a/src/Orchard.Specs/Hosting/WebHost.cs +++ b/src/Orchard.Specs/Hosting/WebHost.cs @@ -17,7 +17,12 @@ namespace Orchard.Specs.Hosting { _tempSite = Path.Get(System.IO.Path.GetTempFileName()).Delete().CreateDirectory(); - _orchardWebPath = baseDir.Parent.Parent.Parent.Combine("Orchard.Web"); + // Trying the two known relative paths to the Orchard.Web directory. + // The second one is for the target "spec" in orchard.proj. + _orchardWebPath = baseDir.Up(3).Combine("Orchard.Web"); + if (!_orchardWebPath.Exists) { + _orchardWebPath = baseDir.Parent.Combine("stage"); + } baseDir.Combine("Hosting").Combine(templateName) .DeepCopy(_tempSite); diff --git a/src/Orchard.Specs/MultiTenancy.feature b/src/Orchard.Specs/MultiTenancy.feature index b91693135..c8602abb4 100644 --- a/src/Orchard.Specs/MultiTenancy.feature +++ b/src/Orchard.Specs/MultiTenancy.feature @@ -8,7 +8,7 @@ Scenario: Default site is listed And I have installed "Orchard.MultiTenancy" When I go to "Admin/MultiTenancy" Then I should see "List of Site's Tenants" - And I should see "Default" + And I should see "Default" And the status should be 200 OK Scenario: New tenant fields are required @@ -27,7 +27,7 @@ Scenario: A new tenant is created | Name | Scott | And I hit "Save" And I am redirected - Then I should see "Scott" + Then I should see "Scott" And the status should be 200 OK Scenario: A new tenant is created with uninitialized state @@ -39,9 +39,9 @@ Scenario: A new tenant is created with uninitialized state | Name | Scott | And I hit "Save" And I am redirected - Then I should see "Uninitialized" + Then I should see "
  • " And the status should be 200 OK - + Scenario: A new tenant goes to the setup screen Given I have installed Orchard And I have installed "Orchard.MultiTenancy" @@ -55,6 +55,23 @@ Scenario: A new tenant goes to the setup screen Then I should see "Welcome to Orchard" And I should see "Finish Setup" And the status should be 200 OK + +Scenario: A new tenant with preconfigured database goes to the setup screen + Given I have installed Orchard + And I have installed "Orchard.MultiTenancy" + When I go to "Admin/MultiTenancy/Add" + And I fill in + | name | value | + | Name | Scott | + | RequestUrlHost | scott.example.org | + | DataProvider | SQLite | + And I hit "Save" + And I am redirected + And I go to "/Setup" on host scott.example.org + Then I should see "Welcome to Orchard" + And I should see "Finish Setup" + And I should not see "SQLite" + And the status should be 200 OK Scenario: A new tenant runs the setup Given I have installed Orchard @@ -74,7 +91,75 @@ Scenario: A new tenant runs the setup And I go to "/Default.aspx" Then I should see "

    Scott Site

    " And I should see "Welcome, admin!" + +Scenario: An existing initialized tenant cannot have its database option cleared + Given I have installed Orchard + And I have installed "Orchard.MultiTenancy" + When I go to "Admin/MultiTenancy/Add" + And I fill in + | name | value | + | Name | Scott | + | RequestUrlHost | scott.example.org | + And I hit "Save" + And I go to "/Setup" on host scott.example.org + And I fill in + | name | value | + | SiteName | Scott Site | + | AdminPassword | 6655321 | + And I hit "Finish Setup" + And I go to "/Admin/MultiTenancy/Edit/Scott" on host localhost + Then I should see "

    Edit Tenant

    " + And I should see "

    Scott

    " + And I should not see "Allow the tenant to set up the database" +Scenario: Default tenant cannot be disabled + Given I have installed Orchard + And I have installed "Orchard.MultiTenancy" + When I go to "Admin/MultiTenancy" + Then I should not see "
    // This code was generated by SpecFlow (http://www.specflow.org/). -// SpecFlow Version:1.2.0.0 +// SpecFlow Version:1.3.0.0 // Runtime Version:2.0.50727.4927 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // // ------------------------------------------------------------------------------ +#region Designer generated code namespace Orchard.Specs { using TechTalk.SpecFlow; + [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "1.3.0.0")] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [NUnit.Framework.TestFixtureAttribute()] [NUnit.Framework.DescriptionAttribute("Multiple tenant management")] public partial class MultipleTenantManagementFeature @@ -58,17 +61,17 @@ namespace Orchard.Specs #line 6 this.ScenarioSetup(scenarioInfo); #line 7 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 8 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 9 - testRunner.When("I go to \"Admin/MultiTenancy\""); +testRunner.When("I go to \"Admin/MultiTenancy\""); #line 10 - testRunner.Then("I should see \"List of Site\'s Tenants\""); +testRunner.Then("I should see \"List of Site\'s Tenants\""); #line 11 - testRunner.And("I should see \"Default\""); +testRunner.And("I should see \"Default\""); #line 12 - testRunner.And("the status should be 200 OK"); +testRunner.And("the status should be 200 OK"); #line hidden testRunner.CollectScenarioErrors(); } @@ -81,15 +84,15 @@ this.ScenarioSetup(scenarioInfo); #line 14 this.ScenarioSetup(scenarioInfo); #line 15 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 16 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 17 - testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line 18 - testRunner.And("I hit \"Save\""); +testRunner.And("I hit \"Save\""); #line 19 - testRunner.Then("I should see \"is required\""); +testRunner.Then("I should see \"is required\""); #line hidden testRunner.CollectScenarioErrors(); } @@ -102,11 +105,11 @@ this.ScenarioSetup(scenarioInfo); #line 21 this.ScenarioSetup(scenarioInfo); #line 22 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 23 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 24 - testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line hidden TechTalk.SpecFlow.Table table1 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -115,15 +118,15 @@ this.ScenarioSetup(scenarioInfo); "Name", "Scott"}); #line 25 - testRunner.And("I fill in", ((string)(null)), table1); +testRunner.And("I fill in", ((string)(null)), table1); #line 28 - testRunner.And("I hit \"Save\""); +testRunner.And("I hit \"Save\""); #line 29 - testRunner.And("I am redirected"); +testRunner.And("I am redirected"); #line 30 - testRunner.Then("I should see \"Scott\""); +testRunner.Then("I should see \"Scott\""); #line 31 - testRunner.And("the status should be 200 OK"); +testRunner.And("the status should be 200 OK"); #line hidden testRunner.CollectScenarioErrors(); } @@ -136,11 +139,11 @@ this.ScenarioSetup(scenarioInfo); #line 33 this.ScenarioSetup(scenarioInfo); #line 34 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 35 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 36 - testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line hidden TechTalk.SpecFlow.Table table2 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -149,15 +152,15 @@ this.ScenarioSetup(scenarioInfo); "Name", "Scott"}); #line 37 - testRunner.And("I fill in", ((string)(null)), table2); +testRunner.And("I fill in", ((string)(null)), table2); #line 40 - testRunner.And("I hit \"Save\""); +testRunner.And("I hit \"Save\""); #line 41 - testRunner.And("I am redirected"); +testRunner.And("I am redirected"); #line 42 - testRunner.Then("I should see \"Uninitialized\""); +testRunner.Then("I should see \"
  • \""); #line 43 - testRunner.And("the status should be 200 OK"); +testRunner.And("the status should be 200 OK"); #line hidden testRunner.CollectScenarioErrors(); } @@ -170,11 +173,11 @@ this.ScenarioSetup(scenarioInfo); #line 45 this.ScenarioSetup(scenarioInfo); #line 46 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 47 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 48 - testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line hidden TechTalk.SpecFlow.Table table3 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -186,34 +189,34 @@ this.ScenarioSetup(scenarioInfo); "RequestUrlHost", "scott.example.org"}); #line 49 - testRunner.And("I fill in", ((string)(null)), table3); +testRunner.And("I fill in", ((string)(null)), table3); #line 53 - testRunner.And("I hit \"Save\""); +testRunner.And("I hit \"Save\""); #line 54 - testRunner.And("I go to \"/Setup\" on host scott.example.org"); +testRunner.And("I go to \"/Setup\" on host scott.example.org"); #line 55 - testRunner.Then("I should see \"Welcome to Orchard\""); +testRunner.Then("I should see \"Welcome to Orchard\""); #line 56 - testRunner.And("I should see \"Finish Setup\""); +testRunner.And("I should see \"Finish Setup\""); #line 57 - testRunner.And("the status should be 200 OK"); +testRunner.And("the status should be 200 OK"); #line hidden testRunner.CollectScenarioErrors(); } [NUnit.Framework.TestAttribute()] - [NUnit.Framework.DescriptionAttribute("A new tenant runs the setup")] - public virtual void ANewTenantRunsTheSetup() + [NUnit.Framework.DescriptionAttribute("A new tenant with preconfigured database goes to the setup screen")] + public virtual void ANewTenantWithPreconfiguredDatabaseGoesToTheSetupScreen() { - TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("A new tenant runs the setup", ((string[])(null))); + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("A new tenant with preconfigured database goes to the setup screen", ((string[])(null))); #line 59 this.ScenarioSetup(scenarioInfo); #line 60 - testRunner.Given("I have installed Orchard"); +testRunner.Given("I have installed Orchard"); #line 61 - testRunner.And("I have installed \"Orchard.MultiTenancy\""); +testRunner.And("I have installed \"Orchard.MultiTenancy\""); #line 62 - testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line hidden TechTalk.SpecFlow.Table table4 = new TechTalk.SpecFlow.Table(new string[] { "name", @@ -224,32 +227,266 @@ this.ScenarioSetup(scenarioInfo); table4.AddRow(new string[] { "RequestUrlHost", "scott.example.org"}); + table4.AddRow(new string[] { + "DataProvider", + "SQLite"}); #line 63 - testRunner.And("I fill in", ((string)(null)), table4); -#line 67 - testRunner.And("I hit \"Save\""); +testRunner.And("I fill in", ((string)(null)), table4); #line 68 - testRunner.And("I go to \"/Setup\" on host scott.example.org"); +testRunner.And("I hit \"Save\""); +#line 69 +testRunner.And("I am redirected"); +#line 70 +testRunner.And("I go to \"/Setup\" on host scott.example.org"); +#line 71 +testRunner.Then("I should see \"Welcome to Orchard\""); +#line 72 +testRunner.And("I should see \"Finish Setup\""); +#line 73 +testRunner.And("I should not see \"SQLite\""); +#line 74 +testRunner.And("the status should be 200 OK"); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("A new tenant runs the setup")] + public virtual void ANewTenantRunsTheSetup() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("A new tenant runs the setup", ((string[])(null))); +#line 76 +this.ScenarioSetup(scenarioInfo); +#line 77 +testRunner.Given("I have installed Orchard"); +#line 78 +testRunner.And("I have installed \"Orchard.MultiTenancy\""); +#line 79 +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); #line hidden TechTalk.SpecFlow.Table table5 = new TechTalk.SpecFlow.Table(new string[] { "name", "value"}); table5.AddRow(new string[] { + "Name", + "Scott"}); + table5.AddRow(new string[] { + "RequestUrlHost", + "scott.example.org"}); +#line 80 +testRunner.And("I fill in", ((string)(null)), table5); +#line 84 +testRunner.And("I hit \"Save\""); +#line 85 +testRunner.And("I go to \"/Setup\" on host scott.example.org"); +#line hidden + TechTalk.SpecFlow.Table table6 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table6.AddRow(new string[] { "SiteName", "Scott Site"}); - table5.AddRow(new string[] { + table6.AddRow(new string[] { "AdminPassword", "6655321"}); -#line 69 - testRunner.And("I fill in", ((string)(null)), table5); -#line 73 - testRunner.And("I hit \"Finish Setup\""); -#line 74 - testRunner.And("I go to \"/Default.aspx\""); -#line 75 - testRunner.Then("I should see \"

    Scott Site

    \""); -#line 76 - testRunner.And("I should see \"Welcome, admin!\""); +#line 86 +testRunner.And("I fill in", ((string)(null)), table6); +#line 90 +testRunner.And("I hit \"Finish Setup\""); +#line 91 +testRunner.And("I go to \"/Default.aspx\""); +#line 92 +testRunner.Then("I should see \"

    Scott Site

    \""); +#line 93 +testRunner.And("I should see \"Welcome, admin!\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("An existing initialized tenant cannot have its database option cleared")] + public virtual void AnExistingInitializedTenantCannotHaveItsDatabaseOptionCleared() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("An existing initialized tenant cannot have its database option cleared", ((string[])(null))); +#line 95 +this.ScenarioSetup(scenarioInfo); +#line 96 +testRunner.Given("I have installed Orchard"); +#line 97 +testRunner.And("I have installed \"Orchard.MultiTenancy\""); +#line 98 +testRunner.When("I go to \"Admin/MultiTenancy/Add\""); +#line hidden + TechTalk.SpecFlow.Table table7 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table7.AddRow(new string[] { + "Name", + "Scott"}); + table7.AddRow(new string[] { + "RequestUrlHost", + "scott.example.org"}); +#line 99 +testRunner.And("I fill in", ((string)(null)), table7); +#line 103 +testRunner.And("I hit \"Save\""); +#line 104 +testRunner.And("I go to \"/Setup\" on host scott.example.org"); +#line hidden + TechTalk.SpecFlow.Table table8 = new TechTalk.SpecFlow.Table(new string[] { + "name", + "value"}); + table8.AddRow(new string[] { + "SiteName", + "Scott Site"}); + table8.AddRow(new string[] { + "AdminPassword", + "6655321"}); +#line 105 +testRunner.And("I fill in", ((string)(null)), table8); +#line 109 +testRunner.And("I hit \"Finish Setup\""); +#line 110 +testRunner.And("I go to \"/Admin/MultiTenancy/Edit/Scott\" on host localhost"); +#line 111 +testRunner.Then("I should see \"

    Edit Tenant

    \""); +#line 112 +testRunner.And("I should see \"

    Scott

    \""); +#line 113 +testRunner.And("I should not see \"Allow the tenant to set up the database\""); +#line hidden + testRunner.CollectScenarioErrors(); + } + + [NUnit.Framework.TestAttribute()] + [NUnit.Framework.DescriptionAttribute("Default tenant cannot be disabled")] + public virtual void DefaultTenantCannotBeDisabled() + { + TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Default tenant cannot be disabled", ((string[])(null))); +#line 115 +this.ScenarioSetup(scenarioInfo); +#line 116 +testRunner.Given("I have installed Orchard"); +#line 117 +testRunner.And("I have installed \"Orchard.MultiTenancy\""); +#line 118 +testRunner.When("I go to \"Admin/MultiTenancy\""); +#line 119 +testRunner.Then("I should not see \"tenant list"); -#line 83 - testRunner.Then("I should see \"Name: Alpha\""); -#line 84 - testRunner.And("I should see \"Request Url Host: example.org\""); +#line 164 +testRunner.Given("I have installed Orchard"); +#line 165 +testRunner.And("I have installed \"Orchard.MultiTenancy\""); +#line 166 +testRunner.And("I have tenant \"Alpha\" on \"example.org\" as \"New-site-name\""); +#line 167 +testRunner.When("I execute >tenant list"); +#line 168 +testRunner.Then("I should see \"Name: Alpha\""); +#line 169 +testRunner.And("I should see \"Request Url Host: example.org\""); #line hidden testRunner.CollectScenarioErrors(); } } } +#endregion diff --git a/src/Orchard.Specs/Orchard.Specs.csproj b/src/Orchard.Specs/Orchard.Specs.csproj index ffedad71b..82880b9a0 100644 --- a/src/Orchard.Specs/Orchard.Specs.csproj +++ b/src/Orchard.Specs/Orchard.Specs.csproj @@ -86,8 +86,6 @@ False ..\..\lib\fluentnhibernate\log4net.dll - - False ..\..\lib\fluentnhibernate\NHibernate.dll diff --git a/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs b/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs index 17175465e..6c41371af 100644 --- a/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs +++ b/src/Orchard.Tests.Modules/Settings/Topology/ShellDescriptorManagerTests.cs @@ -15,14 +15,17 @@ namespace Orchard.Tests.Modules.Settings.Topology { public override void Register(ContainerBuilder builder) { builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); - + builder.RegisterSource(new EventsRegistrationSource()); } public class StubEventBus : IEventBus { public string LastMessageName { get; set; } - public IDictionary LastEventData { get; set; } + public IDictionary LastEventData { get; set; } - public void Notify(string messageName, IDictionary eventData) { + public void Notify_Obsolete(string messageName, IDictionary eventData) { + } + + public void Notify(string messageName, Dictionary eventData) { LastMessageName = messageName; LastEventData = eventData; } @@ -137,7 +140,7 @@ namespace Orchard.Tests.Modules.Settings.Topology { Enumerable.Empty(), Enumerable.Empty()); - Assert.That(eventBus.LastMessageName, Is.EqualTo("ShellDescriptor_Changed")); + Assert.That(eventBus.LastMessageName, Is.EqualTo("IShellDescriptorManagerEventHandler.Changed")); } } } diff --git a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs index 78ad1eaab..54dfd37f3 100644 --- a/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs +++ b/src/Orchard.Tests.Modules/Users/Controllers/AdminControllerTests.cs @@ -6,6 +6,7 @@ using System.Web.Routing; using Autofac; using Moq; using NUnit.Framework; +using Orchard.ContentManagement.MetaData.Records; using Orchard.Data; using Orchard.Environment; using Orchard.ContentManagement; @@ -30,6 +31,7 @@ namespace Orchard.Tests.Modules.Users.Controllers { public override void Register(ContainerBuilder builder) { builder.RegisterType().SingleInstance(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As().InstancePerDependency(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -43,7 +45,13 @@ namespace Orchard.Tests.Modules.Users.Controllers { protected override IEnumerable DatabaseTypes { get { - return new[] { typeof(UserRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(ContentTypeRecord) }; + return new[] { typeof(UserRecord), + typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), + typeof(ContentItemRecord), + typeof(ContentItemVersionRecord), + }; } } diff --git a/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs b/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs index 1b928a7c3..81551167c 100644 --- a/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs +++ b/src/Orchard.Tests.Modules/Users/Services/MembershipServiceTests.cs @@ -3,6 +3,7 @@ using System.Web.Security; using Autofac; using NHibernate; using NUnit.Framework; +using Orchard.ContentManagement.MetaData.Records; using Orchard.Data; using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; @@ -41,7 +42,9 @@ namespace Orchard.Tests.Modules.Users.Services { typeof(UserRecord), typeof(ContentItemVersionRecord), typeof(ContentItemRecord), - typeof(ContentTypeRecord)); + typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord)); } [TestFixtureTearDown] @@ -55,6 +58,7 @@ namespace Orchard.Tests.Modules.Users.Services { //builder.RegisterModule(new ImplicitCollectionSupportModule()); builder.RegisterType().As(); builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); _session = _sessionFactory.OpenSession(); diff --git a/src/Orchard.Tests/App.config b/src/Orchard.Tests/App.config index 033282679..9c29a24c2 100644 --- a/src/Orchard.Tests/App.config +++ b/src/Orchard.Tests/App.config @@ -9,4 +9,15 @@ - + + + + + + + + + + + + diff --git a/src/Orchard.Tests/Caching/CacheScopeTests.cs b/src/Orchard.Tests/Caching/CacheScopeTests.cs new file mode 100644 index 000000000..569bd63e6 --- /dev/null +++ b/src/Orchard.Tests/Caching/CacheScopeTests.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Orchard.Caching; +using Orchard.Environment; +using Autofac; +using Orchard.FileSystems.AppData; +using Orchard.FileSystems.WebSite; +using Orchard.Services; + +namespace Orchard.Tests.Caching { + [TestFixture] + public class CacheScopeTests { + private IContainer _hostContainer; + + [SetUp] + public void Init() { + _hostContainer = OrchardStarter.CreateHostContainer(builder => { + builder.RegisterType().InstancePerDependency(); + }); + + } + + public class Alpha { + public ICacheManager CacheManager { get; set; } + + public Alpha(ICacheManager cacheManager) { + CacheManager = cacheManager; + } + } + + [Test] + public void ComponentsAtHostLevelHaveAccessToCache() { + var alpha = _hostContainer.Resolve(); + Assert.That(alpha.CacheManager, Is.Not.Null); + } + + [Test] + public void HostLevelHasAccessToGlobalVolatileProviders() { + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + Assert.That(_hostContainer.Resolve(), Is.Not.Null); + } + + } +} diff --git a/src/Orchard.Tests/Caching/CacheTests.cs b/src/Orchard.Tests/Caching/CacheTests.cs new file mode 100644 index 000000000..09bdf353a --- /dev/null +++ b/src/Orchard.Tests/Caching/CacheTests.cs @@ -0,0 +1,97 @@ +using System; +using Autofac; +using NUnit.Framework; +using Orchard.Caching; + +namespace Orchard.Tests.Caching { + [TestFixture] + public class CacheTests { + private IContainer _container; + private ICacheManager _cacheManager; + + [SetUp] + public void Init() { + var builder = new ContainerBuilder(); + builder.RegisterModule(new CacheModule()); + builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); + _container = builder.Build(); + _cacheManager = _container.Resolve(new TypedParameter(typeof(Type), GetType())); + } + + [Test] + public void CacheManagerShouldReturnCacheItem() { + var result = _cacheManager.Get("testItem", ctx => "testResult"); + Assert.That(result, Is.EqualTo("testResult")); + } + + [Test] + public void CacheManagerShouldReturnExistingCacheItem() { + _cacheManager.Get("testItem", ctx => "testResult"); + var result = _cacheManager.Get("testItem", ctx => ""); + Assert.That(result, Is.EqualTo("testResult")); + } + + [Test] + public void CacheModuleProvidesTypeSpecificManager() { + var scope = _container.BeginLifetimeScope(builder => { + builder.RegisterModule(new CacheModule()); + builder.RegisterType(); + builder.RegisterType(); + }); + + var c1 = scope.Resolve(); + var c2 = scope.Resolve(); + var w1a = c1.CacheManager.Get("hello", ctx => "world1"); + var w1b = c1.CacheManager.Get("hello", ctx => "world2"); + var w2a = c2.CacheManager.Get("hello", ctx => "world3"); + var w2b = c2.CacheManager.Get("hello", ctx => "world4"); + + Assert.That(w1a, Is.EqualTo("world1")); + Assert.That(w1b, Is.EqualTo("world1")); + Assert.That(w2a, Is.EqualTo("world3")); + Assert.That(w2b, Is.EqualTo("world3")); + + var c3 = scope.Resolve(); + var c4 = scope.Resolve(); + var w3a = c3.CacheManager.Get("hello", ctx => "world5"); + var w3b = c3.CacheManager.Get("hello", ctx => "world6"); + var w4a = c4.CacheManager.Get("hello", ctx => "world7"); + var w4b = c4.CacheManager.Get("hello", ctx => "world8"); + + Assert.That(w3a, Is.EqualTo("world1")); + Assert.That(w3b, Is.EqualTo("world1")); + Assert.That(w4a, Is.EqualTo("world3")); + Assert.That(w4b, Is.EqualTo("world3")); + + Assert.That(c1.CacheManager, + Is.Not.SameAs(c3.CacheManager)); + + Assert.That(c1.CacheManager.GetCache(), + Is.SameAs(c3.CacheManager.GetCache())); + + Assert.That(c1.CacheManager, + Is.Not.SameAs(c2.CacheManager)); + + Assert.That(c1.CacheManager.GetCache(), + Is.Not.SameAs(c2.CacheManager.GetCache())); + } + + class ComponentOne { + public ICacheManager CacheManager { get; set; } + + public ComponentOne(ICacheManager cacheManager) { + CacheManager = cacheManager; + } + } + + class ComponentTwo { + public ICacheManager CacheManager { get; set; } + + public ComponentTwo(ICacheManager cacheManager) { + CacheManager = cacheManager; + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs b/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs index 71c22f209..55d9960e5 100644 --- a/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs +++ b/src/Orchard.Tests/ContentManagement/ContentQueryTests.cs @@ -2,6 +2,7 @@ using Autofac; using NHibernate; using NUnit.Framework; +using Orchard.ContentManagement.MetaData.Records; using Orchard.Data; using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; @@ -27,7 +28,9 @@ namespace Orchard.Tests.ContentManagement { typeof(EpsilonRecord), typeof(ContentItemVersionRecord), typeof(ContentItemRecord), - typeof(ContentTypeRecord)); + typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord)); } [TestFixtureTearDown] @@ -43,6 +46,8 @@ namespace Orchard.Tests.ContentManagement { // builder.RegisterModule(new ImplicitCollectionSupportModule()); builder.RegisterModule(new ContentModule()); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); @@ -285,7 +290,7 @@ namespace Orchard.Tests.ContentManagement { init.As().Record.Quad = "v3"; }); _session.Flush(); - _session.Clear(); + _session.Clear(); } [Test] diff --git a/src/Orchard.Tests/ContentManagement/ContentTypeMetaDataTests.cs b/src/Orchard.Tests/ContentManagement/ContentTypeMetaDataTests.cs new file mode 100644 index 000000000..94a31ebba --- /dev/null +++ b/src/Orchard.Tests/ContentManagement/ContentTypeMetaDataTests.cs @@ -0,0 +1,62 @@ +using Autofac; +using NHibernate; +using NUnit.Framework; +using Orchard.ContentManagement; +using Orchard.ContentManagement.MetaData.Records; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.ContentManagement.Records; +using Orchard.Data; + + +namespace Orchard.Tests.ContentManagement{ + [TestFixture] + public class ContentTypeMetaDataTests + { + private IContainer _container; + private ISessionFactory _sessionFactory; + private ISession _session; + + [TestFixtureSetUp] + public void InitFixture() + { + var databaseFileName = System.IO.Path.GetTempFileName(); + _sessionFactory = DataUtility.CreateSessionFactory( + databaseFileName, + typeof(ContentTypeRecord), + typeof(ContentItemRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), + typeof(ContentItemVersionRecord)); + } + + [TestFixtureTearDown] + public void TermFixture() + { + + } + + [SetUp] + public void Init() + { + var builder = new ContainerBuilder(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)); + _session = _sessionFactory.OpenSession(); + builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As(); + + _container = builder.Build(); + } + + [Test] + public void MapandUnMapContentTypeToContentPart() + { + var contentTypeService = _container.Resolve(); + contentTypeService.MapContentTypeToContentPart("foo", "bar"); + Assert.IsTrue(contentTypeService.ValidateContentTypeToContentPartMapping("foo","bar"),"Content Type not successfully mapped"); + contentTypeService.UnMapContentTypeToContentPart("foo", "bar"); + Assert.IsFalse(contentTypeService.ValidateContentTypeToContentPartMapping("foo", "bar"), "Content Type mapping not successfully deleted"); + } + } +} diff --git a/src/Orchard.Tests/ContentManagement/DefaultContentManagerTests.cs b/src/Orchard.Tests/ContentManagement/DefaultContentManagerTests.cs index dff2a8180..6341d4f4c 100644 --- a/src/Orchard.Tests/ContentManagement/DefaultContentManagerTests.cs +++ b/src/Orchard.Tests/ContentManagement/DefaultContentManagerTests.cs @@ -4,6 +4,7 @@ using System.Linq; using Autofac; using NHibernate; using NUnit.Framework; +using Orchard.ContentManagement.MetaData.Records; using Orchard.Data; using Orchard.ContentManagement; using Orchard.ContentManagement.Handlers; @@ -25,6 +26,8 @@ namespace Orchard.Tests.ContentManagement { _sessionFactory = DataUtility.CreateSessionFactory( databaseFileName, typeof(ContentTypeRecord), + typeof(ContentTypePartRecord), + typeof(ContentTypePartNameRecord), typeof(ContentItemRecord), typeof(ContentItemVersionRecord), typeof(GammaRecord), @@ -42,6 +45,8 @@ namespace Orchard.Tests.ContentManagement { var builder = new ContainerBuilder(); //builder.RegisterModule(new ImplicitCollectionSupportModule()); builder.RegisterType().As(); + builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); diff --git a/src/Orchard.Tests/Data/Builders/SessionFactoryBuilderTests.cs b/src/Orchard.Tests/Data/Builders/SessionFactoryBuilderTests.cs index e907a7e04..c19520c8e 100644 --- a/src/Orchard.Tests/Data/Builders/SessionFactoryBuilderTests.cs +++ b/src/Orchard.Tests/Data/Builders/SessionFactoryBuilderTests.cs @@ -83,7 +83,7 @@ namespace Orchard.Tests.Data.Builders { var manager = (ISessionFactoryBuilder)new SessionFactoryBuilder(); var sessionFactory = manager.BuildSessionFactory(new SessionFactoryParameters { - Provider = "SQLite", + Provider = "SqlServer", DataFolder = _tempDataFolder, ConnectionString = "Data Source=.\\SQLEXPRESS;AttachDbFileName=" + databasePath + ";Integrated Security=True;User Instance=True;", UpdateSchema = true, diff --git a/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs index 5790f5820..fe37edc8c 100644 --- a/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/AppDataFolderTests.cs @@ -1,7 +1,7 @@ using System.IO; using System.Linq; using NUnit.Framework; -using Orchard.Environment.Configuration; +using Orchard.FileSystems.AppData; namespace Orchard.Tests.Environment.Configuration { [TestFixture] diff --git a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs index d47d66904..e33c5ed42 100644 --- a/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs +++ b/src/Orchard.Tests/Environment/Configuration/DefaultTenantManagerTests.cs @@ -3,7 +3,7 @@ using System.Linq; using Moq; using NUnit.Framework; using Orchard.Environment.Configuration; -using Orchard.Events; +using Orchard.FileSystems.AppData; namespace Orchard.Tests.Environment.Configuration { [TestFixture] @@ -28,7 +28,7 @@ namespace Orchard.Tests.Environment.Configuration { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); - IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var settings = loader.LoadSettings().Single(); Assert.That(settings, Is.Not.Null); Assert.That(settings.Name, Is.EqualTo("Default")); @@ -43,7 +43,7 @@ namespace Orchard.Tests.Environment.Configuration { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); _appData.CreateFile("Sites\\Another\\Settings.txt", "Name: Another\r\nDataProvider: SQLite2\r\nDataConnectionString: something else2"); - IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var settings = loader.LoadSettings(); Assert.That(settings.Count(), Is.EqualTo(2)); @@ -62,7 +62,7 @@ namespace Orchard.Tests.Environment.Configuration { public void NewSettingsCanBeStored() { _appData.CreateFile("Sites\\Default\\Settings.txt", "Name: Default\r\nDataProvider: SQLite\r\nDataConnectionString: something else"); - IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); + IShellSettingsManager loader = new ShellSettingsManager(_appData, new Mock().Object); var foo = new ShellSettings {Name = "Foo", DataProvider = "Bar", DataConnectionString = "Quux"}; Assert.That(loader.LoadSettings().Count(), Is.EqualTo(1)); diff --git a/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs b/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs index 06b637a0f..9f1677811 100644 --- a/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs +++ b/src/Orchard.Tests/Environment/DefaultOrchardHostTests.cs @@ -18,6 +18,7 @@ using Orchard.Environment.Extensions.Models; using Orchard.Environment.ShellBuilders; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; +using Orchard.FileSystems.AppData; using Orchard.Mvc; using Orchard.Mvc.ModelBinders; using Orchard.Mvc.Routes; @@ -149,21 +150,11 @@ namespace Orchard.Tests.Environment { } - [Test] - public void DifferentShellInstanceShouldBeReturnedAfterEachCreate() { - var host = _lifetime.Resolve(); - var runtime1 = host.CreateShell_Obsolete(); - host.Reinitialize_Obsolete(); - var runtime2 = host.CreateShell_Obsolete(); - Assert.That(runtime1, Is.Not.SameAs(runtime2)); - } - - [Test] public void NormalDependenciesShouldBeUniquePerRequestContainer() { var host = _lifetime.Resolve(); var container1 = host.CreateShellContainer_Obsolete(); - host.Reinitialize_Obsolete(); + ((IShellDescriptorManagerEventHandler)host).Changed(null); var container2 = host.CreateShellContainer_Obsolete(); var requestContainer1a = container1.BeginLifetimeScope(); var requestContainer1b = container1.BeginLifetimeScope(); diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionFoldersTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionFoldersTests.cs index b023f2dd8..c80d090f6 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionFoldersTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionFoldersTests.cs @@ -1,7 +1,12 @@ -using System.IO; +using System; +using System.Collections.Generic; +using System.IO; using System.Linq; using NUnit.Framework; +using Orchard.Caching; using Orchard.Environment.Extensions.Folders; +using Orchard.FileSystems.WebSite; +using Orchard.Tests.Stubs; using Yaml.Grammar; namespace Orchard.Tests.Environment.Extensions { @@ -49,8 +54,8 @@ namespace Orchard.Tests.Environment.Extensions { [Test] public void NamesFromFoldersWithModuleTxtShouldBeListed() { - var folders = new ModuleFolders(new[] { _tempFolderName }); - var names = folders.ListNames(); + IExtensionFolders folders = new ModuleFolders(new[] { _tempFolderName }, new StubCacheManager(), new StubWebSiteFolder()); + var names = folders.AvailableExtensions().Select(d => d.Name); Assert.That(names.Count(), Is.EqualTo(2)); Assert.That(names, Has.Some.EqualTo("Sample1")); Assert.That(names, Has.Some.EqualTo("Sample3")); @@ -58,14 +63,10 @@ namespace Orchard.Tests.Environment.Extensions { [Test] public void ModuleTxtShouldBeParsedAndReturnedAsYamlDocument() { - var folders = new ModuleFolders(new[] { _tempFolderName }); - var sample1 = folders.ParseManifest("Sample1"); - var mapping = (Mapping)sample1.YamlDocument.Root; - var entities = mapping.Entities - .Where(x => x.Key is Scalar) - .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); - Assert.That(entities.Keys, Has.Some.EqualTo("name")); - Assert.That(entities.Keys, Has.Some.EqualTo("author")); + IExtensionFolders folders = new ModuleFolders(new[] { _tempFolderName }, new StubCacheManager(), new StubWebSiteFolder()); + var sample1 = folders.AvailableExtensions().Single(d => d.Name == "Sample1"); + Assert.That(sample1.Name, Is.Not.Empty); + Assert.That(sample1.Author, Is.EqualTo("Bertrand Le Roy")); } - } + } } \ No newline at end of file diff --git a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs index 0e5026b09..8e1b0aa1a 100644 --- a/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs +++ b/src/Orchard.Tests/Environment/Extensions/ExtensionManagerTests.cs @@ -34,22 +34,12 @@ namespace Orchard.Tests.Environment.Extensions { public IDictionary Manifests { get; set; } - public IEnumerable ListNames() { - return Manifests.Keys; - } - - public ParseResult ParseManifest(string name) { - var parser = new YamlParser(); - bool success; - var stream = parser.ParseYamlStream(new TextInput(Manifests[name]), out success); - if (success) { - return new ParseResult { - Location = "~/InMemory", - Name = name, - YamlDocument = stream.Documents.Single() - }; + public IEnumerable AvailableExtensions() { + foreach(var e in Manifests) { + string name = e.Key; + var parseResult = ExtensionFolders.ParseManifest(Manifests[name]); + yield return ExtensionFolders.GetDescriptorForExtension("~/", name, "Module", parseResult); } - return null; } } @@ -214,7 +204,7 @@ features: Description: Contains the Phi type. "); - ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -240,7 +230,7 @@ features: Description: Contains the Phi type. "); - ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features); @@ -297,7 +287,7 @@ features: Description: Contains the Phi type. "); - ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); var testFeature = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Name == "TestFeature"); @@ -325,7 +315,7 @@ features: Description: Contains the Phi type. "); - ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); var testModule = extensionManager.AvailableExtensions() .SelectMany(x => x.Features) .Single(x => x.Name == "TestModule"); @@ -349,7 +339,7 @@ version: 1.0.3 orchardversion: 1 "); - ExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); + IExtensionManager extensionManager = new ExtensionManager(new[] { extensionFolder }, new[] { extensionLoader }); var minimalisticModule = extensionManager.AvailableExtensions().Single(x => x.Name == "Minimalistic"); Assert.That(minimalisticModule.Features.Count(), Is.EqualTo(1)); diff --git a/src/Orchard.Tests/Environment/Extensions/FoldersData/Sample3/Module.txt b/src/Orchard.Tests/Environment/Extensions/FoldersData/Sample3/Module.txt index 44bbc569e..dab08c839 100644 --- a/src/Orchard.Tests/Environment/Extensions/FoldersData/Sample3/Module.txt +++ b/src/Orchard.Tests/Environment/Extensions/FoldersData/Sample3/Module.txt @@ -1 +1 @@ -This is another test.txt +name: This is another test.txt diff --git a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs index bbf204a10..d32d3a3cb 100644 --- a/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs +++ b/src/Orchard.Tests/Environment/ShellBuilders/DefaultShellContainerFactoryTests.cs @@ -26,6 +26,7 @@ namespace Orchard.Tests.Environment.ShellBuilders { var builder = new ContainerBuilder(); builder.RegisterType().As(); builder.RegisterType(); + builder.RegisterType().As(); _container = builder.Build(); } diff --git a/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs b/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs index 426d919a4..67ac3ffe9 100644 --- a/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs +++ b/src/Orchard.Tests/Environment/Topology/DefaultShellDescriptorCacheTests.cs @@ -3,9 +3,9 @@ using System.Runtime.Serialization; using System.Xml; using Autofac; using NUnit.Framework; -using Orchard.Environment.Configuration; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; +using Orchard.FileSystems.AppData; namespace Orchard.Tests.Environment.Topology { [TestFixture] diff --git a/src/Orchard.Tests/Events/EventTests.cs b/src/Orchard.Tests/Events/EventTests.cs index 58648fa80..1475d856a 100644 --- a/src/Orchard.Tests/Events/EventTests.cs +++ b/src/Orchard.Tests/Events/EventTests.cs @@ -2,6 +2,7 @@ using Autofac; using NUnit.Framework; using Orchard.Events; +using System; namespace Orchard.Tests.Events { [TestFixture] @@ -9,21 +10,181 @@ namespace Orchard.Tests.Events { private IContainer _container; private IEventBus _eventBus; private StubEventBusHandler _eventBusHandler; + private StubEventHandler _eventHandler; [SetUp] public void Init() { var builder = new ContainerBuilder(); _eventBusHandler = new StubEventBusHandler(); + _eventHandler = new StubEventHandler(); builder.RegisterInstance(_eventBusHandler).As(); + builder.RegisterInstance(_eventHandler).As(); builder.RegisterType().As(); _container = builder.Build(); _eventBus = _container.Resolve(); } [Test] - public void EventsAreCorrectlyDispatchedToHandlers() { + public void EventsAreCorrectlyDispatchedToEventHandlers() { + Assert.That(_eventHandler.Count, Is.EqualTo(0)); + _eventBus.Notify("ITestEventHandler.Increment", new Dictionary()); + Assert.That(_eventHandler.Count, Is.EqualTo(1)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToEventHandlers() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 5200; + arguments["b"] = 2600; + _eventBus.Notify("ITestEventHandler.Substract", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(2600)); + } + + [Test] + public void EventParametersArePassedInCorrectOrderToEventHandlers() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 2600; + arguments["b"] = 5200; + _eventBus.Notify("ITestEventHandler.Substract", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(-2600)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToMatchingMethod() { + Assert.That(_eventHandler.Summary, Is.Null); + Dictionary arguments = new Dictionary(); + arguments["a"] = "a"; + arguments["b"] = "b"; + arguments["c"] = "c"; + _eventBus.Notify("ITestEventHandler.Concat", arguments); + Assert.That(_eventHandler.Summary, Is.EqualTo("abc")); + } + + [Test] + public void EventParametersAreCorrectlyPassedToExactlyMatchingMethod() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 1000; + arguments["b"] = 100; + arguments["c"] = 10; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(1110)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToBestMatchingMethodAndExtraParametersAreIgnored() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 1000; + arguments["b"] = 100; + arguments["c"] = 10; + arguments["e"] = 1; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(1110)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToBestMatchingMethodAndExtraParametersAreIgnored2() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 1000; + arguments["e"] = 1; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(3000)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToExactlyMatchingMethodWhenThereIsOne() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 1000; + arguments["b"] = 100; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(2200)); + } + + [Test] + public void EventParametersAreCorrectlyPassedToExactlyMatchingMethodWhenThereIsOne2() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["a"] = 1000; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(3000)); + } + + [Test] + public void EventHandlerWontBeCalledWhenNoParameterMatchExists() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + arguments["e"] = 1; + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + } + + [Test] + public void EventHandlerWontBeCalledWhenNoParameterMatchExists2() { + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + Dictionary arguments = new Dictionary(); + _eventBus.Notify("ITestEventHandler.Sum", arguments); + Assert.That(_eventHandler.Result, Is.EqualTo(0)); + } + + [Test] + public void EventHandlerWontThrowIfMethodDoesNotExists() { + Dictionary arguments = new Dictionary(); + Assert.DoesNotThrow(() => _eventBus.Notify("ITestEventHandler.NotExisting", arguments)); + } + + [Test] + public void EventBusThrowsIfMessageNameIsNotCorrectlyFormatted() { + Assert.Throws(() => _eventBus.Notify("StubEventHandlerIncrement", new Dictionary())); + } + + public interface ITestEventHandler : IEventHandler { + void Increment(); + void Sum(int a); + void Sum(int a, int b); + void Sum(int a, int b, int c); + void Substract(int a, int b); + void Concat(string a, string b, string c); + } + + public class StubEventHandler : ITestEventHandler { + public int Count { get; set; } + public int Result { get; set; } + public string Summary { get; set; } + + public void Increment() { + Count++; + } + + public void Sum(int a) { + Result = 3 * a; + } + + public void Sum(int a, int b) { + Result = 2 * ( a + b ); + } + + public void Sum(int a, int b, int c) { + Result = a + b + c; + } + + public void Substract(int a, int b) { + Result = a - b; + } + + public void Concat(string a, string b, string c) { + Summary = a + b + c; + } + } + + [Test] + public void EventsAreCorrectlyDispatchedToHandlers_Obsolete() { Assert.That(_eventBusHandler.LastMessageName, Is.Null); - _eventBus.Notify("Notification", new Dictionary()); + _eventBus.Notify_Obsolete("Notification", new Dictionary()); Assert.That(_eventBusHandler.LastMessageName, Is.EqualTo("Notification")); } diff --git a/src/Orchard.Tests/Orchard.Framework.Tests.csproj b/src/Orchard.Tests/Orchard.Framework.Tests.csproj index 6d5f209c8..c624f53d3 100644 --- a/src/Orchard.Tests/Orchard.Framework.Tests.csproj +++ b/src/Orchard.Tests/Orchard.Framework.Tests.csproj @@ -78,8 +78,6 @@ False ..\..\lib\fluentnhibernate\FluentNHibernate.dll - - False ..\..\lib\moq\Moq.dll @@ -141,12 +139,15 @@ + + Code + Code @@ -193,6 +194,8 @@ + + diff --git a/src/Orchard.Tests/Storage/FileSystemStorageProviderTests.cs b/src/Orchard.Tests/Storage/FileSystemStorageProviderTests.cs index fb87bbcb2..df7e4d10a 100644 --- a/src/Orchard.Tests/Storage/FileSystemStorageProviderTests.cs +++ b/src/Orchard.Tests/Storage/FileSystemStorageProviderTests.cs @@ -4,7 +4,7 @@ using System.Linq; using NUnit.Framework; using System; using Orchard.Environment.Configuration; -using Orchard.Storage; +using Orchard.FileSystems.Media; namespace Orchard.Tests.Storage { [TestFixture] diff --git a/src/Orchard.Tests/Stubs/StubCacheManager.cs b/src/Orchard.Tests/Stubs/StubCacheManager.cs new file mode 100644 index 000000000..fd28e5083 --- /dev/null +++ b/src/Orchard.Tests/Stubs/StubCacheManager.cs @@ -0,0 +1,15 @@ +using System; +using Orchard.Caching; + +namespace Orchard.Tests.Stubs { + public class StubCacheManager : ICacheManager { + public TResult Get(TKey key, Func, TResult> acquire) { + var cache = new Cache(); + return cache.Get(key, acquire); + } + + public ICache GetCache() { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/Stubs/StubHttpContext.cs b/src/Orchard.Tests/Stubs/StubHttpContext.cs index df66a262a..0337c90fe 100644 --- a/src/Orchard.Tests/Stubs/StubHttpContext.cs +++ b/src/Orchard.Tests/Stubs/StubHttpContext.cs @@ -12,10 +12,12 @@ namespace Orchard.Tests.Stubs { public StubHttpContext() { _appRelativeCurrentExecutionFilePath = "~/yadda"; + _hostHeader = "localhost"; } public StubHttpContext(string appRelativeCurrentExecutionFilePath) { _appRelativeCurrentExecutionFilePath = appRelativeCurrentExecutionFilePath; + _hostHeader = "localhost"; } public StubHttpContext(string appRelativeCurrentExecutionFilePath, string hostHeader) { @@ -38,6 +40,7 @@ namespace Orchard.Tests.Stubs { class StubHttpRequest : HttpRequestBase { private readonly StubHttpContext _httpContext; private NameValueCollection _serverVariables; + private NameValueCollection _headers; public StubHttpRequest(StubHttpContext httpContext) { _httpContext = httpContext; @@ -54,7 +57,12 @@ namespace Orchard.Tests.Stubs { public override string PathInfo { get { return ""; } } - + public override NameValueCollection Headers { + get { + return _headers = _headers + ?? new NameValueCollection { { "Host", _httpContext._hostHeader } }; + } + } public override NameValueCollection ServerVariables { get { return _serverVariables = _serverVariables diff --git a/src/Orchard.Tests/Stubs/StubWebSiteFolder.cs b/src/Orchard.Tests/Stubs/StubWebSiteFolder.cs new file mode 100644 index 000000000..82e0446ee --- /dev/null +++ b/src/Orchard.Tests/Stubs/StubWebSiteFolder.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Orchard.Caching; +using Orchard.FileSystems.WebSite; + +namespace Orchard.Tests.Stubs { + public class StubWebSiteFolder : IWebSiteFolder { + public IEnumerable ListDirectories(string path) { + if (!Directory.Exists(path)) + return Enumerable.Empty(); + + return Directory.GetDirectories(path); + } + + public string ReadFile(string path) { + if (!File.Exists(path)) + return null; + + return File.ReadAllText(path); + } + + public IVolatileToken WhenPathChanges(string path) { + return new WebSiteFolder.Token(path); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Tests/Tasks/SweepGeneratorTests.cs b/src/Orchard.Tests/Tasks/SweepGeneratorTests.cs index 02117ed32..2c7301c9d 100644 --- a/src/Orchard.Tests/Tasks/SweepGeneratorTests.cs +++ b/src/Orchard.Tests/Tasks/SweepGeneratorTests.cs @@ -1,6 +1,5 @@ using System; using Autofac; -using Autofac.Builder; using Moq; using NUnit.Framework; using Orchard.Tasks; diff --git a/src/Orchard.Tests/Utility/Extensions/StringExtensionsTests.cs b/src/Orchard.Tests/Utility/Extensions/StringExtensionsTests.cs index 3240ab717..9266e08b6 100644 --- a/src/Orchard.Tests/Utility/Extensions/StringExtensionsTests.cs +++ b/src/Orchard.Tests/Utility/Extensions/StringExtensionsTests.cs @@ -4,6 +4,26 @@ using Orchard.Utility.Extensions; namespace Orchard.Tests.Utility.Extensions { [TestFixture] public class StringExtensionsTests { + [Test] + public void HtmlClassify_ValidSimpleClassNameReturnsSame() { + const string toClassify = "some-class"; + Assert.That(toClassify.HtmlClassify(), Is.StringMatching("some-class")); + } + [Test] + public void HtmlClassify_SimpleStringReturnsSimpleClassName() { + const string toClassify = "this is something"; + Assert.That(toClassify.HtmlClassify(), Is.StringMatching("this-is-something")); + } + [Test] + public void HtmlClassify_ValidComplexClassNameReturnsSimpleClassName() { + const string toClassify = @"some-class\&some.other.class"; + Assert.That(toClassify.HtmlClassify(), Is.StringMatching("some-class-some-other-class")); + } + [Test] + public void HtmlClassify_CompletelyInvalidClassNameReturnsEmptyString() { + const string toClassify = @"0_1234_12"; + Assert.That(toClassify.HtmlClassify(), Is.StringMatching("")); + } [Test] public void OrDefault_ReturnsDefaultForNull() { const string s = null; diff --git a/src/Orchard.Web/Core/Common/Drivers/BodyDriver.cs b/src/Orchard.Web/Core/Common/Drivers/BodyDriver.cs index 04d56dc88..f7a0a0264 100644 --- a/src/Orchard.Web/Core/Common/Drivers/BodyDriver.cs +++ b/src/Orchard.Web/Core/Common/Drivers/BodyDriver.cs @@ -104,6 +104,10 @@ namespace Orchard.Core.Common.Drivers { // Can be moved somewhere else once we have IoC enabled body text filters. private static string BbcodeReplace(string bodyText) { + + if ( string.IsNullOrEmpty(bodyText) ) + return string.Empty; + Regex urlRegex = new Regex(@"\[url\]([^\]]+)\[\/url\]"); Regex urlRegexWithLink = new Regex(@"\[url=([^\]]+)\]([^\]]+)\[\/url\]"); Regex imgRegex = new Regex(@"\[img\]([^\]]+)\[\/img\]"); diff --git a/src/Orchard.Web/Core/Common/Services/IRoutableService.cs b/src/Orchard.Web/Core/Common/Services/IRoutableService.cs index 65f0fac1a..0519f07f2 100644 --- a/src/Orchard.Web/Core/Common/Services/IRoutableService.cs +++ b/src/Orchard.Web/Core/Common/Services/IRoutableService.cs @@ -11,7 +11,7 @@ namespace Orchard.Core.Common.Services { /// /// Returns any content item of the specified content type with similar slugs /// - string[] GetSimilarSlugs(string contentType, string slug); + IEnumerable GetSimilarSlugs(string contentType, string slug); /// /// Validates the given slug diff --git a/src/Orchard.Web/Core/Common/Services/RoutableService.cs b/src/Orchard.Web/Core/Common/Services/RoutableService.cs index cfddb58b5..057ccf031 100644 --- a/src/Orchard.Web/Core/Common/Services/RoutableService.cs +++ b/src/Orchard.Web/Core/Common/Services/RoutableService.cs @@ -63,13 +63,13 @@ namespace Orchard.Core.Common.Services { : null; } - public string[] GetSimilarSlugs(string contentType, string slug) + public IEnumerable GetSimilarSlugs(string contentType, string slug) { return _contentManager.Query(contentType).Join() .List() - .Select(i => i.As().Slug) - .Where(rr => rr.StartsWith(slug, StringComparison.OrdinalIgnoreCase)) // todo: for some reason the filter doesn't work within the query, even without StringComparison or StartsWith + .Select(i => i.As()) + .Where(routable => routable.Slug.StartsWith(slug, StringComparison.OrdinalIgnoreCase)) // todo: for some reason the filter doesn't work within the query, even without StringComparison or StartsWith .ToArray(); } @@ -89,12 +89,17 @@ namespace Orchard.Core.Common.Services { var slugsLikeThis = GetSimilarSlugs(part.ContentItem.ContentType, part.Slug); + // If the part is already a valid content item, don't include it in the list + // of slug to consider for conflict detection + if (part.ContentItem.Id != 0) + slugsLikeThis = slugsLikeThis.Where(p => p.ContentItem.Id != part.ContentItem.Id); + //todo: (heskew) need better messages - if (slugsLikeThis.Length > 0) + if (slugsLikeThis.Count() > 0) { var originalSlug = part.Slug; //todo: (heskew) make auto-uniqueness optional - part.Slug = GenerateUniqueSlug(part.Slug, slugsLikeThis); + part.Slug = GenerateUniqueSlug(part.Slug, slugsLikeThis.Select(p => p.Slug)); if (originalSlug != part.Slug) { return false; diff --git a/src/Orchard.Web/Core/Common/Views/EditorTemplates/Parts/Common.Routable.ascx b/src/Orchard.Web/Core/Common/Views/EditorTemplates/Parts/Common.Routable.ascx index 3b6a81b84..b699143bb 100644 --- a/src/Orchard.Web/Core/Common/Views/EditorTemplates/Parts/Common.Routable.ascx +++ b/src/Orchard.Web/Core/Common/Views/EditorTemplates/Parts/Common.Routable.ascx @@ -10,7 +10,7 @@ <%=Html.TextBoxFor(m => m.Title, new { @class = "large text" }) %> <% using (this.Capture("end-of-page-scripts")) { %> diff --git a/src/Orchard.Web/Core/Dashboard/Module.txt b/src/Orchard.Web/Core/Dashboard/Module.txt index e646ae5c2..1fb19522a 100644 --- a/src/Orchard.Web/Core/Dashboard/Module.txt +++ b/src/Orchard.Web/Core/Dashboard/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Dashboard: Description: Standard admin dashboard. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Feeds/Controllers/FeedController.cs b/src/Orchard.Web/Core/Feeds/Controllers/FeedController.cs index 9004cb077..bde60e4a1 100644 --- a/src/Orchard.Web/Core/Feeds/Controllers/FeedController.cs +++ b/src/Orchard.Web/Core/Feeds/Controllers/FeedController.cs @@ -10,15 +10,15 @@ namespace Orchard.Core.Feeds.Controllers { public class FeedController : Controller { private readonly IEnumerable _feedFormatProviders; private readonly IEnumerable _feedQueryProviders; - private readonly IEnumerable _feedItemBuilders; + private readonly IFeedItemBuilder _feedItemBuilder; public FeedController( IEnumerable feedQueryProviders, IEnumerable feedFormatProviders, - IEnumerable feedItemBuilders) { + IFeedItemBuilder feedItemBuilder) { _feedQueryProviders = feedQueryProviders; _feedFormatProviders = feedFormatProviders; - _feedItemBuilders = feedItemBuilders; + _feedItemBuilder = feedItemBuilder; Logger = NullLogger.Instance; } @@ -49,7 +49,7 @@ namespace Orchard.Core.Feeds.Controllers { return context.Builder.Process(context, () => { bestQueryMatch.FeedQuery.Execute(context); - _feedItemBuilders.Invoke(x => x.Populate(context), Logger); + _feedItemBuilder.Populate(context); foreach (var contextualizer in context.Response.Contextualizers) { if (ControllerContext != null && ControllerContext.RequestContext != null) { diff --git a/src/Orchard.Web/Core/Feeds/IFeedItemBuilder.cs b/src/Orchard.Web/Core/Feeds/IFeedItemBuilder.cs index d0c5abed2..ea082e49b 100644 --- a/src/Orchard.Web/Core/Feeds/IFeedItemBuilder.cs +++ b/src/Orchard.Web/Core/Feeds/IFeedItemBuilder.cs @@ -1,7 +1,8 @@ using Orchard.Core.Feeds.Models; +using Orchard.Events; namespace Orchard.Core.Feeds { - public interface IFeedItemBuilder : IEvents { + public interface IFeedItemBuilder : IEventHandler { void Populate(FeedContext context); } } diff --git a/src/Orchard.Web/Core/Feeds/Module.txt b/src/Orchard.Web/Core/Feeds/Module.txt index 1b2ba39c1..eb1f5b157 100644 --- a/src/Orchard.Web/Core/Feeds/Module.txt +++ b/src/Orchard.Web/Core/Feeds/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Feeds: Description: RSS feeds for content items. - Dependencies: Common Category: Syndication \ No newline at end of file diff --git a/src/Orchard.Web/Core/HomePage/Module.txt b/src/Orchard.Web/Core/HomePage/Module.txt index b4a95af7e..63404f28d 100644 --- a/src/Orchard.Web/Core/HomePage/Module.txt +++ b/src/Orchard.Web/Core/HomePage/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: HomePage: Description: Standard site home page that allows a specified content type or container to *be* the home page. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Navigation/Module.txt b/src/Orchard.Web/Core/Navigation/Module.txt index adbfd4438..31ec9b758 100644 --- a/src/Orchard.Web/Core/Navigation/Module.txt +++ b/src/Orchard.Web/Core/Navigation/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Navigation: Description: Menu management. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Scheduling/Module.txt b/src/Orchard.Web/Core/Scheduling/Module.txt index 848b44124..e5026374d 100644 --- a/src/Orchard.Web/Core/Scheduling/Module.txt +++ b/src/Orchard.Web/Core/Scheduling/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Scheduling: Description: Scheduled background tasks. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Scheduling/Services/PublishingTaskHandler.cs b/src/Orchard.Web/Core/Scheduling/Services/PublishingTaskHandler.cs index 273f97862..2c870d405 100644 --- a/src/Orchard.Web/Core/Scheduling/Services/PublishingTaskHandler.cs +++ b/src/Orchard.Web/Core/Scheduling/Services/PublishingTaskHandler.cs @@ -1,16 +1,18 @@ using JetBrains.Annotations; +using Orchard.ContentManagement; using Orchard.Logging; using Orchard.Tasks.Scheduling; namespace Orchard.Core.Scheduling.Services { [UsedImplicitly] public class PublishingTaskHandler : IScheduledTaskHandler { - public PublishingTaskHandler(IOrchardServices services) { - Services = services; + private readonly IContentManager _contentManager; + + public PublishingTaskHandler(IContentManager contentManager) { + _contentManager = contentManager; Logger = NullLogger.Instance; } - public IOrchardServices Services { get; set; } public ILogger Logger { get; set; } public void Process(ScheduledTaskContext context) { @@ -20,7 +22,7 @@ namespace Orchard.Core.Scheduling.Services { context.Task.ContentItem.Version, context.Task.ScheduledUtc); - Services.ContentManager.Publish(context.Task.ContentItem); + _contentManager.Publish(context.Task.ContentItem); } else if (context.Task.TaskType == "Unpublish") { Logger.Information("Unpublishing item #{0} version {1} scheduled at {2} utc", @@ -28,7 +30,7 @@ namespace Orchard.Core.Scheduling.Services { context.Task.ContentItem.Version, context.Task.ScheduledUtc); - Services.ContentManager.Unpublish(context.Task.ContentItem); + _contentManager.Unpublish(context.Task.ContentItem); } } } diff --git a/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskExecutor.cs b/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskExecutor.cs index 3b7f74ed0..94e7ac005 100644 --- a/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskExecutor.cs +++ b/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskExecutor.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; +using Orchard.ContentManagement; using Orchard.Core.Scheduling.Models; using Orchard.Data; using Orchard.Logging; @@ -15,20 +16,20 @@ namespace Orchard.Core.Scheduling.Services { private readonly IClock _clock; private readonly IRepository _repository; private readonly IEnumerable _handlers; + private readonly IContentManager _contentManager; public ScheduledTaskExecutor( - IOrchardServices services, IClock clock, IRepository repository, - IEnumerable handlers) { + IEnumerable handlers, + IContentManager contentManager) { _clock = clock; _repository = repository; _handlers = handlers; - Services = services; + _contentManager = contentManager; Logger = NullLogger.Instance; } - public IOrchardServices Services { get; set; } public ILogger Logger { get; set; } public void Sweep() { @@ -52,7 +53,7 @@ namespace Orchard.Core.Scheduling.Services { _repository.Delete(taskRecord); var context = new ScheduledTaskContext { - Task = new Task(Services.ContentManager, taskRecord) + Task = new Task(_contentManager, taskRecord) }; // dispatch to standard or custom handlers diff --git a/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskManager.cs b/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskManager.cs index 90276ef52..19c73dce7 100644 --- a/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskManager.cs +++ b/src/Orchard.Web/Core/Scheduling/Services/ScheduledTaskManager.cs @@ -12,17 +12,17 @@ using Orchard.Utility.Extensions; namespace Orchard.Core.Scheduling.Services { [UsedImplicitly] public class ScheduledTaskManager : IScheduledTaskManager { + private readonly IContentManager _contentManager; private readonly IRepository _repository; public ScheduledTaskManager( - IOrchardServices services, + IContentManager contentManager, IRepository repository) { _repository = repository; - Services = services; + _contentManager = contentManager; Logger = NullLogger.Instance; } - public IOrchardServices Services { get; set; } public ILogger Logger { get; set; } public void CreateTask(string action, DateTime scheduledUtc, ContentItem contentItem) { @@ -39,7 +39,7 @@ namespace Orchard.Core.Scheduling.Services { public IEnumerable GetTasks(ContentItem contentItem) { return _repository .Fetch(x => x.ContentItemVersionRecord.ContentItemRecord == contentItem.Record) - .Select(x => new Task(Services.ContentManager, x)) + .Select(x => new Task(_contentManager, x)) .Cast() .ToReadOnlyCollection(); } @@ -50,7 +50,7 @@ namespace Orchard.Core.Scheduling.Services { .Fetch(x => x.ContentItemVersionRecord.ContentItemRecord == contentItem.Record); foreach (var task in tasks) { - if (predicate(new Task(Services.ContentManager, task))) { + if (predicate(new Task(_contentManager, task))) { _repository.Delete(task); } } diff --git a/src/Orchard.Web/Core/Settings/Module.txt b/src/Orchard.Web/Core/Settings/Module.txt index b5cd0527d..5e4f413c7 100644 --- a/src/Orchard.Web/Core/Settings/Module.txt +++ b/src/Orchard.Web/Core/Settings/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Settings: Description: Site settings. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Core/Settings/Services/SiteService.cs b/src/Orchard.Web/Core/Settings/Services/SiteService.cs index 4c95ec5eb..451bfb84b 100644 --- a/src/Orchard.Web/Core/Settings/Services/SiteService.cs +++ b/src/Orchard.Web/Core/Settings/Services/SiteService.cs @@ -1,6 +1,7 @@ using System; using System.Linq; using JetBrains.Annotations; +using Orchard.Caching; using Orchard.Core.Settings.Models; using Orchard.Data; using Orchard.Logging; @@ -10,30 +11,38 @@ using Orchard.Settings; namespace Orchard.Core.Settings.Services { [UsedImplicitly] public class SiteService : ISiteService { - private readonly IRepository _siteSettingsRepository; private readonly IContentManager _contentManager; + private readonly ICacheManager _cacheManager; - public SiteService(IRepository siteSettingsRepository, IContentManager contentManager) { - _siteSettingsRepository = siteSettingsRepository; + public SiteService( + IRepository siteSettingsRepository, + IContentManager contentManager, + ICacheManager cacheManager) { _contentManager = contentManager; + _cacheManager = cacheManager; Logger = NullLogger.Instance; } public ILogger Logger { get; set; } public ISite GetSiteSettings() { - SiteSettingsRecord record = _siteSettingsRepository.Table.FirstOrDefault(); - if (record == null) { - ISite site = _contentManager.Create("site", item => { - item.Record.SiteSalt = Guid.NewGuid().ToString("N"); - item.Record.SiteName = "My Orchard Project Application"; - item.Record.PageTitleSeparator = " - "; - }); - // ensure subsequent calls will locate this object - _contentManager.Flush(); - return site; - } - return _contentManager.Get(record.Id); + var siteId = _cacheManager.Get("SiteId", ctx => { + var site = _contentManager.Query("site") + .Slice(0, 1) + .FirstOrDefault(); + + if (site == null) { + site = _contentManager.Create("site", item => { + item.Record.SiteSalt = Guid.NewGuid().ToString("N"); + item.Record.SiteName = "My Orchard Project Application"; + item.Record.PageTitleSeparator = " - "; + }).ContentItem; + } + + return site.Id; + }); + + return _contentManager.Get(siteId); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs b/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs index 98bcddd67..5f56cf48e 100644 --- a/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs +++ b/src/Orchard.Web/Core/Settings/Topology/ShellDescriptorManager.cs @@ -11,13 +11,13 @@ using Orchard.Localization; namespace Orchard.Core.Settings.Topology { public class ShellDescriptorManager : IShellDescriptorManager { private readonly IRepository _topologyRecordRepository; - private readonly IEventBus _eventBus; + private readonly IShellDescriptorManagerEventHandler _events; public ShellDescriptorManager( IRepository repository, - IEventBus eventBus) { + IShellDescriptorManagerEventHandler events) { _topologyRecordRepository = repository; - _eventBus = eventBus; + _events = events; T = NullLocalizer.Instance; } @@ -85,9 +85,7 @@ namespace Orchard.Core.Settings.Topology { }); } - _eventBus.Notify( - "ShellDescriptor_Changed", - null); + _events.Changed(GetShellTopologyDescriptorFromRecord(topologyRecord)); } } } diff --git a/src/Orchard.Web/Core/XmlRpc/Module.txt b/src/Orchard.Web/Core/XmlRpc/Module.txt index 5db3d574c..6ab5a799d 100644 --- a/src/Orchard.Web/Core/XmlRpc/Module.txt +++ b/src/Orchard.Web/Core/XmlRpc/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: XmlRpc: Description: XML-RPC opt-in implementation. - Dependencies: Common - Category: Core \ No newline at end of file + Category: Content Publishing \ No newline at end of file diff --git a/src/Orchard.Web/Core/XmlRpc/Views/Home/Index.aspx b/src/Orchard.Web/Core/XmlRpc/Views/Home/Index.aspx index 1be46f753..56e93b956 100644 --- a/src/Orchard.Web/Core/XmlRpc/Views/Home/Index.aspx +++ b/src/Orchard.Web/Core/XmlRpc/Views/Home/Index.aspx @@ -1,9 +1,6 @@ -<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %> - - - Home Page - - - -

    Use this url for LiveWriter

    -
    +<%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage" %> + + +Use this url for LiveWriter +

    Use this url for LiveWriter

    + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Futures.Widgets/Module.txt b/src/Orchard.Web/Modules/Futures.Widgets/Module.txt index ccc415c58..827ac7017 100644 --- a/src/Orchard.Web/Modules/Futures.Widgets/Module.txt +++ b/src/Orchard.Web/Modules/Futures.Widgets/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Futures.Widgets: Description: Widgets container with simple inline content editing widget. - Dependencies: Common Category: Widget \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs index ed0a60822..c0aad6773 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Controllers/BlogController.cs @@ -98,7 +98,7 @@ namespace Orchard.Blogs.Controllers { var options = new XElement( XName.Get("service", manifestUri), - new XElement(XName.Get("engineName", manifestUri), "Orchar CMS"), + new XElement(XName.Get("engineName", manifestUri), "Orchard CMS"), new XElement(XName.Get("engineLink", manifestUri), "http://orchardproject.net"), new XElement(XName.Get("homePageLink", manifestUri), "http://orchardproject.net"), new XElement(XName.Get("apis", manifestUri), diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt b/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt index 52f596e33..f1e73406c 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Module.txt @@ -7,5 +7,8 @@ orchardversion: 0.1.2010.0312 features: Orchard.Blogs: Description: A simple web log. - Dependencies: Common, XmlRpc - Category: Content \ No newline at end of file + Category: Content + Remote Blog Publishing: + Description: Blog easier using a dedicated MetaWeblogAPI-compatible publishing tool. + Dependencies: XmlRpc + Category: Content Publishing \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs index da6094ea8..2c14b76de 100644 --- a/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Blogs/Services/XmlRpcHandler.cs @@ -8,6 +8,7 @@ using Orchard.Blogs.Models; using Orchard.ContentManagement; using Orchard.Core.XmlRpc; using Orchard.Core.XmlRpc.Models; +using Orchard.Environment.Extensions; using Orchard.Logging; using Orchard.Mvc.Extensions; using Orchard.Security; @@ -15,6 +16,7 @@ using Orchard.Blogs.Extensions; namespace Orchard.Blogs.Services { [UsedImplicitly] + [OrchardFeature("Remote Blog Publishing")] public class XmlRpcHandler : IXmlRpcHandler { private readonly IBlogService _blogService; private readonly IBlogPostService _blogPostService; diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Extensions/HtmlHelperExtensions.cs b/src/Orchard.Web/Modules/Orchard.Comments/Extensions/HtmlHelperExtensions.cs index 51c9ed94c..f519b9517 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Extensions/HtmlHelperExtensions.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Extensions/HtmlHelperExtensions.cs @@ -2,6 +2,7 @@ using System.Web.Mvc; using System.Web.Mvc.Html; using Orchard.ContentManagement; using Orchard.Localization; +using Orchard.Utility.Extensions; namespace Orchard.Comments.Extensions { public static class HtmlHelperExtensions { @@ -23,7 +24,7 @@ namespace Orchard.Comments.Extensions { Area = "Orchard.Comments", Controller = "Admin", id = item.Id, - returnUrl = html.ViewContext.HttpContext.Request.Url + returnUrl = html.ViewContext.HttpContext.Request.ToUrlString() }); } diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Handlers/HasCommentsHandler.cs b/src/Orchard.Web/Modules/Orchard.Comments/Handlers/HasCommentsHandler.cs index 6797e787b..dfa82586e 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Handlers/HasCommentsHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Handlers/HasCommentsHandler.cs @@ -14,29 +14,24 @@ namespace Orchard.Comments.Handlers { IRepository hasCommentsRepository, ICommentService commentService) { - Filters.Add(new ActivatingFilter("sandboxpage")); - Filters.Add(new ActivatingFilter("blogpost")); - Filters.Add(new ActivatingFilter("page")); Filters.Add(StorageFilter.For(hasCommentsRepository)); OnActivated((ctx, x) => { - x.CommentsActive = true; - x.CommentsShown = true; - }); + x.CommentsActive = true; + x.CommentsShown = true; + }); OnLoading((context, comments) => { - //TODO: lazy loading? - comments.Comments = contentManager - .Query() - .Where(x => x.CommentedOn == context.ContentItem.Id && x.Status == CommentStatus.Approved) - .List().ToList(); + comments._comments.Loader(list => contentManager + .Query() + .Where(x => x.CommentedOn == context.ContentItem.Id && x.Status == CommentStatus.Approved) + .List().ToList()); - //TODO: lazy loading? - comments.PendingComments = contentManager - .Query() - .Where(x => x.CommentedOn == context.ContentItem.Id && x.Status == CommentStatus.Pending) - .List().ToList(); - }); + comments._pendingComments.Loader(list => contentManager + .Query() + .Where(x => x.CommentedOn == context.ContentItem.Id && x.Status == CommentStatus.Pending) + .List().ToList()); + }); OnRemoved( (context, c) => { diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Models/HasComments.cs b/src/Orchard.Web/Modules/Orchard.Comments/Models/HasComments.cs index f5cc28197..724fc99f0 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Models/HasComments.cs +++ b/src/Orchard.Web/Modules/Orchard.Comments/Models/HasComments.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Orchard.ContentManagement; +using Orchard.ContentManagement.Utilities; namespace Orchard.Comments.Models { public class HasComments : ContentPart { @@ -8,8 +9,11 @@ namespace Orchard.Comments.Models { PendingComments = new List(); } - public IList Comments { get; set; } - public IList PendingComments { get; set; } + public readonly LazyField> _comments = new LazyField>(); + public readonly LazyField> _pendingComments = new LazyField>(); + + public IList Comments { get { return _comments.Value; } set { _comments.Value = value; } } + public IList PendingComments { get { return _pendingComments.Value; } set { _pendingComments.Value = value; } } public bool CommentsShown { get { return Record.CommentsShown; } diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Module.txt b/src/Orchard.Web/Modules/Orchard.Comments/Module.txt index 7936c3af0..d863f49bb 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Comments/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Comments: Description: Standard content item comments. - Dependencies: Common - Category: User Interaction \ No newline at end of file + Category: Social \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx b/src/Orchard.Web/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx index 49d4a3c35..79cb8e5ca 100644 --- a/src/Orchard.Web/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx +++ b/src/Orchard.Web/Modules/Orchard.Comments/Views/DisplayTemplates/Parts/Comments.HasComments.ascx @@ -2,6 +2,7 @@ <%@ Import Namespace="Orchard.Comments"%> <%@ Import Namespace="Orchard.Security" %> <%@ Import Namespace="Orchard.Comments.Models" %> +<%@ Import Namespace="Orchard.Utility.Extensions" %> <%-- todo: clean up this template - waaay too much going on in here :/ --%><% if (Model.Comments.Count > 0) { %>

    <%=_Encoded("{0} Comment{1}", Model.Comments.Count, Model.Comments.Count == 1 ? "" : "s")%>

    @@ -51,7 +52,7 @@ else { %>
    " /> <%=Html.Hidden("CommentedOn", Model.ContentItem.Id) %> - <%=Html.Hidden("ReturnUrl", Context.Request.Url) %> + <%=Html.Hidden("ReturnUrl", Context.Request.ToUrlString()) %> <%=Html.AntiForgeryTokenOrchard() %>
    <% diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Module.txt b/src/Orchard.Web/Modules/Orchard.DevTools/Module.txt index 0d8dbccc9..9d886eed2 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.DevTools: Description: An assortment of debuging tools. - Dependencies: Common Category: Developer \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj index ed107f784..b97bf93cd 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Orchard.DevTools.csproj @@ -77,7 +77,6 @@ - diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Profiler.cs b/src/Orchard.Web/Modules/Orchard.DevTools/Profiler.cs deleted file mode 100644 index 2f58cc27f..000000000 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Profiler.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Orchard.Environment; - -namespace Orchard.DevTools { - public class Profiler : IOrchardShellEvents { - public void Activated() { - HibernatingRhinos.Profiler.Appender.NHibernate.NHibernateProfiler.Initialize(); - } - - public void Terminating() { - - } - } -} diff --git a/src/Orchard.Web/Modules/Orchard.DevTools/Views/Content/Details.aspx b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Content/Details.aspx index c66c00d75..4bf063ddc 100644 --- a/src/Orchard.Web/Modules/Orchard.DevTools/Views/Content/Details.aspx +++ b/src/Orchard.Web/Modules/Orchard.DevTools/Views/Content/Details.aspx @@ -20,13 +20,13 @@
      <%foreach (var partType in Model.PartTypes.OrderBy(x => x.Name)) {%>
    • - <%if (partType.IsGenericType) {%><%=Html.Encode(partType.Name +" "+partType.GetGenericArguments().First().Name)%> + <%if (partType.IsGenericType) {%><%=Html.Encode(partType.Name +" "+partType.GetGenericArguments().First().Name)%> <%=Html.Encode(" (" + partType.GetGenericArguments().First().Namespace + ")")%><%} - else {%><%=Html.Encode(partType.Name)%> + else {%><%=Html.Encode(partType.Name)%> <%=Html.Encode( " (" + partType.Namespace + ")")%><% } - %> + %>
        <%foreach (var prop in partType.GetProperties().Where(x => x.DeclaringType == partType)) { var value = prop.GetValue(Model.Locate(partType), null);%> diff --git a/src/Orchard.Web/Modules/Orchard.Media/Module.txt b/src/Orchard.Web/Modules/Orchard.Media/Module.txt index 4f96e9b36..7a2730d9a 100644 --- a/src/Orchard.Web/Modules/Orchard.Media/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Media/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Media: Description: File system based media upload, storage and management. - Dependencies: Common Category: Media \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Media/Services/MediaService.cs b/src/Orchard.Web/Modules/Orchard.Media/Services/MediaService.cs index 7ca491cda..ebce331e2 100644 --- a/src/Orchard.Web/Modules/Orchard.Media/Services/MediaService.cs +++ b/src/Orchard.Web/Modules/Orchard.Media/Services/MediaService.cs @@ -5,8 +5,8 @@ using System.Text; using System.Web; using ICSharpCode.SharpZipLib.Zip; using JetBrains.Annotations; +using Orchard.FileSystems.Media; using Orchard.Logging; -using Orchard.Storage; using Orchard.Media.Models; namespace Orchard.Media.Services { diff --git a/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs b/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs index b5142ccd2..d6c72a73a 100644 --- a/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Media/Services/XmlRpcHandler.cs @@ -5,6 +5,7 @@ using JetBrains.Annotations; using Orchard.Core.XmlRpc; using Orchard.Core.XmlRpc.Models; using Orchard.Security; +using Orchard.Utility.Extensions; namespace Orchard.Media.Services { [UsedImplicitly] @@ -18,7 +19,7 @@ namespace Orchard.Media.Services { } public void Process(XmlRpcContext context) { - var uriBuilder = new UriBuilder(context.HttpContext.Request.Url) { + var uriBuilder = new UriBuilder(context.HttpContext.Request.ToUrlString()) { Path = context.HttpContext.Request.ApplicationPath, Query = string.Empty }; diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.MetaData/AdminMenu.cs new file mode 100644 index 000000000..ad2c436d5 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/AdminMenu.cs @@ -0,0 +1,19 @@ + +using Orchard.UI.Navigation; + +namespace Orchard.MetaData { + public class AdminMenu : INavigationProvider { + + public string MenuName { get { return "admin"; } } + + public void GetNavigation(NavigationBuilder builder) + { + builder.Add("Content Types", "5", + menu => menu + .Add("Content Types", "1.0", item => item.Action("ContentTypeList", "MetaData", new { area = "Orchard.MetaData" }).Permission(Permissions.ManageMetaData)) + ); + } + + + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Controllers/MetaDataController.cs b/src/Orchard.Web/Modules/Orchard.MetaData/Controllers/MetaDataController.cs new file mode 100644 index 000000000..dee0f6eec --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Controllers/MetaDataController.cs @@ -0,0 +1,84 @@ +using System.Web.Mvc; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.Localization; +using Orchard.MetaData.ViewModels; +using Orchard.UI.Admin; + +namespace Orchard.MetaData.Controllers +{ + [Admin] + public class MetaDataController : Controller + { + private readonly IContentTypeService _contentTypeService; + public IOrchardServices Services { get; set; } + + public MetaDataController(IOrchardServices services, IContentTypeService contentTypeService) + { + _contentTypeService = contentTypeService; + Services = services; + T = NullLocalizer.Instance; + } + + private Localizer T { get; set; } + // + // GET: /ContentTypeList/ + + public ActionResult ContentTypeList(string id) { + + if (!Services.Authorizer.Authorize(Permissions.ManageMetaData, T("Not allowed to manage MetaData"))) + return new HttpUnauthorizedResult(); + + var contentTypes = _contentTypeService.GetContentTypes(); + var contentTypePartNames = _contentTypeService.GetContentTypePartNames(); + + var model = new ContentTypesIndexViewModel(); + + foreach(var contentType in contentTypes) { + var contentTypeEntry = new ContentTypeEntry {Name = contentType.Name,DisplayName = contentType.Name}; + + if (contentType.Name==id) { + foreach(var contentTypePartNameRecord in contentTypePartNames) { + var contentTypePartEntry = new ContentTypePartEntry { Name = contentTypePartNameRecord.PartName }; + foreach(var contentTypePartEntryTest in contentType.ContentParts) { + if (contentTypePartEntryTest.PartName.PartName==contentTypePartEntry.Name) { + contentTypePartEntry.Selected = true; + } + } + model.ContentTypeParts.Add(contentTypePartEntry); + } + model.SelectedContentType = contentTypeEntry; + } + model.ContentTypes.Add(contentTypeEntry); + } + return View(model); + } + + + // + // POST: /ContentTypeList/Save + [HttpPost] + public ActionResult Save(string id, FormCollection collection) + { + if (!Services.Authorizer.Authorize(Permissions.ManageMetaData, T("Not allowed to manage MetaData"))) + return new HttpUnauthorizedResult(); + + var contentTypeRecord = _contentTypeService.GetContentTypeRecord(id); + //using a while loop because we are removing items from the collection + while (contentTypeRecord.ContentParts.Count>0) { + _contentTypeService.UnMapContentTypeToContentPart(contentTypeRecord.Name, contentTypeRecord.ContentParts[0].PartName.PartName); + } + foreach(var formKey in collection.AllKeys) { + if (formKey.Contains("part_")) { + var partName = formKey.Replace("part_", ""); + _contentTypeService.MapContentTypeToContentPart(contentTypeRecord.Name,partName); + } + } + + return RedirectToAction("ContentTypeList", new { id }); + + + } + + + } +} diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Module.txt b/src/Orchard.Web/Modules/Orchard.MetaData/Module.txt new file mode 100644 index 000000000..a1aa2af92 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Module.txt @@ -0,0 +1,7 @@ +name: MetaData +antiforgery: enabled +features: + Orchard.MetaData: + Description: Module for managing Orchard MetaData + Dependencies: Common, XmlRpc + Category: MetaData \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Orchard.MetaData.csproj b/src/Orchard.Web/Modules/Orchard.MetaData/Orchard.MetaData.csproj new file mode 100644 index 000000000..d17b873e2 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Orchard.MetaData.csproj @@ -0,0 +1,116 @@ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {23E04990-2A8D-41B8-9908-6DDB71EA3B23} + {F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Orchard.MetaData + Orchard.MetaData + v3.5 + false + + + true + full + false + bin\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\ + TRACE + prompt + 4 + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + False + ..\..\..\..\lib\aspnetmvc\System.Web.Mvc.dll + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} + Orchard.Framework + + + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} + Orchard.Core + + + + + + + + + + + + + + + + + False + True + 33002 + / + + + False + True + http://orchard.codeplex.com + False + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Permissions.cs b/src/Orchard.Web/Modules/Orchard.MetaData/Permissions.cs new file mode 100644 index 000000000..d0bbb35ed --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Permissions.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using Orchard.Security.Permissions; + +namespace Orchard.MetaData { + public class Permissions : IPermissionProvider { + public static readonly Permission ManageMetaData = new Permission { Description = "Manage MetaData", Name = "ManageMetaData" };//q: Should edit_MetaData be ManageMetaData? + + + public string ModuleName { + get { + return "MetaData"; + } + } + + public IEnumerable GetPermissions() { + return new Permission[] { + ManageMetaData, + }; + } + + public IEnumerable GetDefaultStereotypes() { + return new[] { + new PermissionStereotype { + Name = "Administrator", + Permissions = new[] {ManageMetaData} + }, + }; + } + + } +} + + diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.MetaData/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9ce2239ad --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Orchard.MetaData")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Orchard.MetaData")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9b958fbe-1d0d-4975-9a1b-7e3ff5bed510")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Styles/ContentTypes.css b/src/Orchard.Web/Modules/Orchard.MetaData/Styles/ContentTypes.css new file mode 100644 index 000000000..6c7f0ddd4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Styles/ContentTypes.css @@ -0,0 +1,35 @@ + +.ContentTypeList +{ + width: 100px; + float:left; +} +.ContentTypePartList +{ + width: 200px; + float: left; + margin-left: 20px; +} +.ContentTypePartListRow +{ + height:5px; + padding:0px; +} +.ContentTypePartListRowItem +{ + background:#EAEAEA; + + padding:0px; + vertical-align:top; + margin:1px; + padding-top: 5px; +} + +.SelectedContentPart { + background:#D1F2A5; + border-color:#BCD994; +} +.UnSelectedContentPart { + background:#EAEAEA; + border-color:#CCC; +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/ViewModels/ContentTypesViewModel.cs b/src/Orchard.Web/Modules/Orchard.MetaData/ViewModels/ContentTypesViewModel.cs new file mode 100644 index 000000000..2fd0bc744 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/ViewModels/ContentTypesViewModel.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using Orchard.Mvc.ViewModels; + +namespace Orchard.MetaData.ViewModels +{ + public class ContentTypesIndexViewModel : BaseViewModel { + public IList ContentTypes { get; set; } + public IList ContentTypeParts { get; set; } + public ContentTypesIndexViewModel() { + ContentTypes=new List(); + ContentTypeParts = new List(); + } + public ContentTypeEntry SelectedContentType { get; set; } + } + + public class ContentTypeEntry { + public string Name { get; set; } + public string DisplayName { get; set; } + public IList ContentTypeParts { get; set; } + public ContentTypeEntry(){ + ContentTypeParts = new List(); + } + } + + public class ContentTypePartEntry { + public string Name { get; set; } + public bool Selected { get; set; } + } + + +} diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Views/MetaData/ContentTypeList.ascx b/src/Orchard.Web/Modules/Orchard.MetaData/Views/MetaData/ContentTypeList.ascx new file mode 100644 index 000000000..c0b78eead --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Views/MetaData/ContentTypeList.ascx @@ -0,0 +1,79 @@ + +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.MetaData.ViewModels"%> +<% Html.RegisterStyle("ContentTypes.css"); %> + +
        + + + + + + + <% foreach (var item in Model.ContentTypes) { %> + + <% + var contentTypeClass = ""; + if (Model.SelectedContentType!=null && Model.SelectedContentType.Name == item.Name) + { + contentTypeClass = "SelectedContentPart"; + }else{ + contentTypeClass = "UnSelectedContentPart"; + } + %> + + + + + <% } %> + +
        + Content Types +
        + <%= Html.ActionLink(item.Name, "ContentTypeList", new {id=item.Name})%> +
        +
        + +<%if (Model.SelectedContentType!=null) {%> + +
        + + + + + +<% +using (Html.BeginFormAntiForgeryPost(Url.Action("Save",new {id=Model.SelectedContentType.Name}))) { %> + + <% foreach (var item in Model.ContentTypeParts) { %> + + + + + + + <% } %> + +
        + + + Included Content Part +
        + <%if (item.Selected) + {%> + " type="checkbox" checked="checked" /><%} + else {%> + " type="checkbox" /><%}%> + + <%= Html.Encode(item.Name)%> +
        +

        + " /> +

        + <% } %> +
        +<%} %> + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Views/Web.config b/src/Orchard.Web/Modules/Orchard.MetaData/Views/Web.config new file mode 100644 index 000000000..7022197d4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Views/Web.config @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MetaData/Web.config b/src/Orchard.Web/Modules/Orchard.MetaData/Web.config new file mode 100644 index 000000000..93b2ed5eb --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MetaData/Web.config @@ -0,0 +1,158 @@ + + + + + + + +
        + +
        +
        +
        +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs index 47d8fc3a0..a45979b33 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Controllers/AdminController.cs @@ -1,13 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; +using System.Linq; using System.Web.Mvc; using Orchard.Localization; using Orchard.Modules.ViewModels; -using Orchard.Mvc.AntiForgery; using Orchard.Mvc.Results; -using Orchard.UI.Notify; namespace Orchard.Modules.Controllers { public class AdminController : Controller { @@ -26,103 +21,42 @@ namespace Orchard.Modules.Controllers { if (!Services.Authorizer.Authorize(Permissions.ManageModules, T("Not allowed to manage modules"))) return new HttpUnauthorizedResult(); - var modules = _moduleService.GetInstalledModules(); + var modules = _moduleService.GetInstalledModules().ToList(); return View(new ModulesIndexViewModel {Modules = modules}); } - public ActionResult Edit(string moduleName) { - if (!Services.Authorizer.Authorize(Permissions.ManageModules, T("Not allowed to edit module"))) - return new HttpUnauthorizedResult(); - - var module = _moduleService.GetModuleByName(moduleName); - - if (module == null) - return new NotFoundResult(); - - return View(new ModuleEditViewModel { - Name = module.DisplayName - }); - } - - public ActionResult Features(FeaturesOptions options) { + public ActionResult Features() { if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) return new HttpUnauthorizedResult(); - var features = _moduleService.GetAvailableFeatures(); - return View(new FeaturesViewModel {Features = features, Options = options}); + var features = _moduleService.GetAvailableFeatures().ToList(); + return View(new FeaturesViewModel {Features = features}); } - [HttpPost, ActionName("Features")] - [FormValueRequired("submit.BulkEdit")] - public ActionResult FeaturesPOST(FeaturesOptions options, IList selection) { - if (selection != null && selection.Count > 0) - { - switch (options.BulkAction) - { - case FeaturesBulkAction.None: - break; - case FeaturesBulkAction.Enable: - if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to enable features"))) - return new HttpUnauthorizedResult(); - _moduleService.EnableFeatures(selection); - //todo: (heskew) need better messages - //todo: (heskew) hmmm...need a helper to comma-separate all but last, which would get the " and " treatment...all localized, of course - Services.Notifier.Information(T("{0} were enabled", string.Join(", ", selection.ToArray()))); - break; - case FeaturesBulkAction.Disable: - if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to disable features"))) - return new HttpUnauthorizedResult(); - _moduleService.DisableFeatures(selection); - //todo: (heskew) need better messages - Services.Notifier.Information(T("{0} were disabled", string.Join(", ", selection.ToArray()))); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } + [HttpPost] + public ActionResult Enable(string id, bool? force) { + if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) + return new HttpUnauthorizedResult(); + + if (string.IsNullOrEmpty(id)) + return new NotFoundResult(); + + _moduleService.EnableFeatures(new[] {id}, force != null && (bool) force); return RedirectToAction("Features"); } - [ValidateAntiForgeryTokenOrchard] - public ActionResult Enable(string featureName) { + [HttpPost] + public ActionResult Disable(string id, bool? force) { if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) return new HttpUnauthorizedResult(); - if (string.IsNullOrEmpty(featureName)) + if (string.IsNullOrEmpty(id)) return new NotFoundResult(); - _moduleService.EnableFeatures(new [] {featureName}); - Services.Notifier.Information(T("{0} was enabled", featureName)); + _moduleService.DisableFeatures(new[] {id}, force != null && (bool) force); return RedirectToAction("Features"); } - - [ValidateAntiForgeryTokenOrchard] - public ActionResult Disable(string featureName) { - if (!Services.Authorizer.Authorize(Permissions.ManageFeatures, T("Not allowed to manage features"))) - return new HttpUnauthorizedResult(); - - if (string.IsNullOrEmpty(featureName)) - return new NotFoundResult(); - - _moduleService.DisableFeatures(new[] { featureName }); - Services.Notifier.Information(T("{0} was disabled", featureName)); - - return RedirectToAction("Features"); - } - - private class FormValueRequiredAttribute : ActionMethodSelectorAttribute { - private readonly string _submitButtonName; - - public FormValueRequiredAttribute(string submitButtonName) { - _submitButtonName = submitButtonName; - } - - public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) { - var value = controllerContext.HttpContext.Request.Form[_submitButtonName]; - return !string.IsNullOrEmpty(value); - } - } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Extensions/StringExtensions.cs b/src/Orchard.Web/Modules/Orchard.Modules/Extensions/StringExtensions.cs new file mode 100644 index 000000000..69ce06cf9 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Modules/Extensions/StringExtensions.cs @@ -0,0 +1,13 @@ +using System; +using Orchard.Localization; +using Orchard.Utility.Extensions; + +namespace Orchard.Modules.Extensions { + public static class StringExtensions { + public static string AsFeatureId(this string text, Func localize) { + return string.IsNullOrEmpty(text) + ? "" + : string.Format(localize("{0} feature").ToString(), text).HtmlClassify(); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Module.txt b/src/Orchard.Web/Modules/Orchard.Modules/Module.txt index 8414688c6..d644877cf 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Modules/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Modules: Description: Standard module and feature management. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj b/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj index 6f46fd3df..14d3e1a81 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj +++ b/src/Orchard.Web/Modules/Orchard.Modules/Orchard.Modules.csproj @@ -68,14 +68,12 @@ + - - - @@ -94,8 +92,8 @@ + - diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs b/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs deleted file mode 100644 index aaccb5679..000000000 --- a/src/Orchard.Web/Modules/Orchard.Modules/Routes.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Collections.Generic; -using System.Web.Mvc; -using System.Web.Routing; -using Orchard.Modules.Routing; -using Orchard.Mvc.Routes; - -namespace Orchard.Modules { - public class Routes : IRouteProvider { - private readonly IModuleNameConstraint _moduleNameConstraint; - private readonly IFeatureNameConstraint _featureNameConstraint; - - public Routes(IModuleNameConstraint moduleNameConstraint, IFeatureNameConstraint featureNameConstraint) { - _moduleNameConstraint = moduleNameConstraint; - _featureNameConstraint = featureNameConstraint; - } - - public IEnumerable GetRoutes() { - return new[] { - new RouteDescriptor { - Route = new Route( - "Admin/Modules/Edit/{moduleName}", - new RouteValueDictionary { - {"area", "Orchard.Modules"}, - {"controller", "Admin"}, - {"action", "Edit"} - }, - new RouteValueDictionary { - {"moduleName", _moduleNameConstraint} - }, - new RouteValueDictionary { - {"area", "Orchard.Modules"} - }, - new MvcRouteHandler()) - }, - new RouteDescriptor { - Route = new Route( - "Admin/Modules/Enable/{featureName}", - new RouteValueDictionary { - {"area", "Orchard.Modules"}, - {"controller", "Admin"}, - {"action", "Enable"} - }, - new RouteValueDictionary { - {"featureName", _featureNameConstraint} - }, - new RouteValueDictionary { - {"area", "Orchard.Modules"} - }, - new MvcRouteHandler()) - }, - new RouteDescriptor { - Route = new Route( - "Admin/Modules/Disable/{featureName}", - new RouteValueDictionary { - {"area", "Orchard.Modules"}, - {"controller", "Admin"}, - {"action", "Disable"} - }, - new RouteValueDictionary { - {"featureName", _featureNameConstraint} - }, - new RouteValueDictionary { - {"area", "Orchard.Modules"} - }, - new MvcRouteHandler()) - } - }; - } - - public void GetRoutes(ICollection routes) { - foreach (var routeDescriptor in GetRoutes()) - routes.Add(routeDescriptor); - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs b/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs deleted file mode 100644 index bee40d3c1..000000000 --- a/src/Orchard.Web/Modules/Orchard.Modules/Routing/FeatureNameConstraint.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using System.Web; -using System.Web.Routing; - -namespace Orchard.Modules.Routing { - public interface IFeatureNameConstraint : IRouteConstraint, ISingletonDependency { - } - - public class FeatureNameConstraint : IFeatureNameConstraint { - private readonly IModuleService _moduleService; - - public FeatureNameConstraint(IModuleService moduleService) { - _moduleService = moduleService; - } - - public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { - if (routeDirection == RouteDirection.UrlGeneration) - return true; - - object value; - if (values.TryGetValue(parameterName, out value)) - return _moduleService.GetModuleByFeatureName(Convert.ToString(value)) != null; - - return false; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Routing/ModuleNameConstraint.cs b/src/Orchard.Web/Modules/Orchard.Modules/Routing/ModuleNameConstraint.cs deleted file mode 100644 index 0b537b9e1..000000000 --- a/src/Orchard.Web/Modules/Orchard.Modules/Routing/ModuleNameConstraint.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Web; -using System.Web.Routing; - -namespace Orchard.Modules.Routing { - public interface IModuleNameConstraint : IRouteConstraint, ISingletonDependency { - } - - public class ModuleNameConstraint : IModuleNameConstraint { - private readonly IModuleService _moduleService; - - public ModuleNameConstraint(IModuleService moduleService) { - _moduleService = moduleService; - } - - public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection) { - if (routeDirection == RouteDirection.UrlGeneration) - return true; - - object value; - if (values.TryGetValue(parameterName, out value)) - return _moduleService.GetModuleByName(Convert.ToString(value)) != null; - - return false; - } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs index e75b70d45..917d318ae 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/Services/ModuleService.cs @@ -6,7 +6,9 @@ using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; +using Orchard.Localization; using Orchard.Modules.Models; +using Orchard.UI.Notify; namespace Orchard.Modules.Services { public class ModuleService : IModuleService { @@ -14,21 +16,32 @@ namespace Orchard.Modules.Services { private readonly IExtensionManager _extensionManager; private readonly IShellDescriptorManager _shellDescriptorManager; - public ModuleService(IExtensionManager extensionManager, IShellDescriptorManager shellDescriptorManager) { + public ModuleService(IOrchardServices orchardServices, IExtensionManager extensionManager, + IShellDescriptorManager shellDescriptorManager) { + Services = orchardServices; _extensionManager = extensionManager; _shellDescriptorManager = shellDescriptorManager; + T = NullLocalizer.Instance; } + private Localizer T { get; set; } + public IOrchardServices Services { get; set; } + public IModule GetModuleByName(string moduleName) { - return _extensionManager.AvailableExtensions().Where(e => string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && string.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( - descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault(); + return + _extensionManager.AvailableExtensions().Where( + e => + string.Equals(e.Name, moduleName, StringComparison.OrdinalIgnoreCase) && + string.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( + descriptor => AssembleModuleFromDescriptor(descriptor)).FirstOrDefault(); } public IEnumerable GetInstalledModules() { return _extensionManager.AvailableExtensions().Where( - e => String.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select( - descriptor => AssembleModuleFromDescriptor(descriptor)); + e => String.Equals(e.ExtensionType, ModuleExtensionType, StringComparison.OrdinalIgnoreCase)).Select + ( + descriptor => AssembleModuleFromDescriptor(descriptor)); } public void InstallModule(HttpPostedFileBase file) { @@ -39,41 +52,145 @@ namespace Orchard.Modules.Services { _extensionManager.UninstallExtension(ModuleExtensionType, moduleName); } - public IModule GetModuleByFeatureName(string featureName) { - return GetInstalledModules() - .Where( - m => - m.Features.FirstOrDefault(f => string.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) != - null).FirstOrDefault(); - } - public IEnumerable GetAvailableFeatures() { var enabledFeatures = _shellDescriptorManager.GetShellDescriptor().EnabledFeatures; return GetInstalledModules() .SelectMany(m => _extensionManager.LoadFeatures(m.Features)) - .Select(f => AssembleModuleFromDescriptor(f, enabledFeatures.FirstOrDefault(sf => string.Equals(sf.Name, f.Descriptor.Name, StringComparison.OrdinalIgnoreCase)) != null)); - } - - public IEnumerable GetAvailableFeaturesByModule(string moduleName) { - throw new NotImplementedException(); + .Select( + f => + AssembleModuleFromDescriptor(f, + enabledFeatures.FirstOrDefault( + sf => + string.Equals(sf.Name, f.Descriptor.Name, + StringComparison.OrdinalIgnoreCase)) != null)); } public void EnableFeatures(IEnumerable featureNames) { + EnableFeatures(featureNames, false); + } + + public void EnableFeatures(IEnumerable features, bool force) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); + var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); - var enabledFeatures = shellDescriptor.EnabledFeatures - .Union(featureNames.Select(s => new ShellFeature {Name = s})); + var featuresToEnable = + features.Select(s => EnableFeature(s, GetAvailableFeatures(), force)). + SelectMany(ies => ies.Select(s => s)); - _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); + if (featuresToEnable.Count() == 0) + return; + + foreach (var featureToEnable in featuresToEnable) { + enabledFeatures.Add(new ShellFeature {Name = featureToEnable}); + Services.Notifier.Information(T("{0} was enabled", featureToEnable)); + } + + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, + shellDescriptor.Parameters); } public void DisableFeatures(IEnumerable featureNames) { + DisableFeatures(featureNames, false); + } + + public void DisableFeatures(IEnumerable features, bool force) { var shellDescriptor = _shellDescriptorManager.GetShellDescriptor(); - var enabledFeatures = shellDescriptor.EnabledFeatures.ToList(); - enabledFeatures.RemoveAll(f => featureNames.Contains(f.Name)); - _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, shellDescriptor.Parameters); + var featuresToDisable = + features.Select(s => DisableFeature(s, GetAvailableFeatures(), force)).SelectMany( + ies => ies.Select(s => s)); + + if (featuresToDisable.Count() == 0) + return; + + foreach (var featureToDisable in featuresToDisable) { + var feature = featureToDisable; + enabledFeatures.RemoveAll(f => f.Name == feature); + Services.Notifier.Information(T("{0} was disabled", feature)); + } + + _shellDescriptorManager.UpdateShellDescriptor(shellDescriptor.SerialNumber, enabledFeatures, + shellDescriptor.Parameters); + } + + public IModule GetModuleByFeatureName(string featureName) { + return GetInstalledModules() + .Where( + m => + m.Features.FirstOrDefault( + f => string.Equals(f.Name, featureName, StringComparison.OrdinalIgnoreCase)) != + null).FirstOrDefault(); + } + + private IEnumerable EnableFeature(string featureName, IEnumerable features, bool force) { + var featuresList = features.ToList(); + var getDisabledDependencies = + new Func, IEnumerable>( + (n, fs) => { + var feature = fs.Single(f => f.Descriptor.Name == n); + return feature.Descriptor.Dependencies != null + ? feature.Descriptor.Dependencies.Select( + fn => fs.Single(f => f.Descriptor.Name == fn)).Where(f => !f.IsEnabled) + : Enumerable.Empty(); + }); + + var featuresToEnable = GetAffectedFeatures(featureName, featuresList, getDisabledDependencies); + + if (featuresToEnable.Count() > 1 && !force) { + GenerateWarning("If you want {0} enabled, then you'll also need to enable {1}.", + featureName, + featuresToEnable.Where(fn => fn != featureName)); + return Enumerable.Empty(); + } + + return featuresToEnable; + } + + private IEnumerable DisableFeature(string featureName, IEnumerable features, bool force) { + var featuresList = features.ToList(); + var getEnabledDependants = + new Func, IEnumerable>( + (n, fs) => fs.Where(f => f.IsEnabled && f.Descriptor.Dependencies != null && f.Descriptor.Dependencies.Contains(n))); + + var featuresToDisable = GetAffectedFeatures(featureName, featuresList, getEnabledDependants); + + if (featuresToDisable.Count() > 1 && !force) { + GenerateWarning("If {0} is disabled, then you'll also lose {1}.", + featureName, + featuresToDisable.Where(fn => fn != featureName)); + return Enumerable.Empty(); + } + + return featuresToDisable; + } + + private static IEnumerable GetAffectedFeatures(string featureName, IEnumerable features, Func, IEnumerable> getAffectedDependencies) { + var dependencies = new List {featureName}; + + foreach (var dependency in getAffectedDependencies(featureName, features)) + dependencies.AddRange(GetAffectedFeatures(dependency.Descriptor.Name, features, getAffectedDependencies)); + + return dependencies; + } + + private void GenerateWarning(string messageFormat, string featureName, IEnumerable featuresInQuestion) { + if (featuresInQuestion.Count() < 1) + return; + + Services.Notifier.Warning(T( + messageFormat, + featureName, + featuresInQuestion.Count() > 1 + ? string.Join("", + featuresInQuestion.Select( + (fn, i) => + T(i == featuresInQuestion.Count() - 1 + ? "{0}" + : (i == featuresInQuestion.Count() - 2 + ? "{0} and " + : "{0}, "), fn).ToString()).ToArray()) + : featuresInQuestion.First())); } private static IModule AssembleModuleFromDescriptor(ExtensionDescriptor extensionDescriptor) { diff --git a/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs b/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs index 210ce308c..fc98504a8 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.Modules/ViewModels/FeaturesViewModel.cs @@ -4,16 +4,5 @@ using Orchard.Mvc.ViewModels; namespace Orchard.Modules.ViewModels { public class FeaturesViewModel : BaseViewModel { public IEnumerable Features { get; set; } - public FeaturesOptions Options { get; set; } - } - - public class FeaturesOptions { - public FeaturesBulkAction BulkAction { get; set; } - } - - public enum FeaturesBulkAction { - None, - Enable, - Disable } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Edit.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Edit.ascx deleted file mode 100644 index 303004ee2..000000000 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Edit.ascx +++ /dev/null @@ -1,5 +0,0 @@ -<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> -<%@ Import Namespace="Orchard.Mvc.Html"%> -<%@ Import Namespace="Orchard.Modules.ViewModels"%> -

        <%=Html.TitleForPage(T("Edit Module: {0}", Model.Name).ToString()) %>

        -

        <%=_Encoded("Edit the module. Maybe show module's features.") %>

        \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx index 73ceb2487..47dda8aae 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Features.ascx @@ -1,63 +1,74 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Modules.Extensions" %> <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Modules.ViewModels"%> +<%@ Import Namespace="Orchard.Utility.Extensions" %><% + Html.RegisterStyle("admin.css"); %>

        <%=Html.TitleForPage(T("Manage Features").ToString()) %>

        - -<% if (Model.Features.Count() > 0) { - -using (Html.BeginFormAntiForgeryPost()) { %> - <%=Html.ValidationSummary()%> -
        - - - " /> -
        -
        -
          <% - foreach (var featureGroup in Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category)) { %> - > -

          <%=Html.Encode(featureGroup.First().Descriptor.Category ?? T("Uncategorized")) %>

          -
            <% - foreach (var feature in featureGroup.OrderBy(f => f.Descriptor.Name)) {%> - id="<%=Html.Encode(feature.Descriptor.Name) %>"> -
            -
            - -

            <%=Html.Encode(feature.Descriptor.Name) %>

            -
              -
            • <% - //enabled or not - if (feature.IsEnabled) { %> - " alt="<%=_Encoded("Enabled") %>" title="<%=_Encoded("This feature is currently enabled") %>" /><%=_Encoded("Enabled") %><% - } - else { %> - " alt="<%=_Encoded("Disabled") %>" title="<%=_Encoded("This feature is currently disabled") %>" /><%=_Encoded("Disabled")%><% - } %> -
            • <% - //dependencies - if (feature.Descriptor.Dependencies != null && feature.Descriptor.Dependencies.Count() > 0) { %> -
            •  | <%=T("Depends on: {0}", string.Join(", ", feature.Descriptor.Dependencies.Select(s => Html.Link(Html.Encode(s), string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), Html.Encode(s)))).OrderBy(s => s).ToArray())) %>
            • <% - } %> -
            -
            - -
            - <% +<% if (Model.Features.Count() > 0) { %> +
              <% + var featureGroups = Model.Features.OrderBy(f => f.Descriptor.Category).GroupBy(f => f.Descriptor.Category); + foreach (var featureGroup in featureGroups) { + var categoryName = featureGroup.First().Descriptor.Category ?? T("Uncategorized"); + var categoryClassName = string.Format("category {0}", Html.Encode(categoryName.ToString().HtmlClassify())); + if (featureGroup == featureGroups.First()) + categoryClassName += " first"; + if (featureGroup == featureGroups.Last()) + categoryClassName += " last"; + + //temporarily "disable" actions on core features + var showActions = categoryName.ToString() != "Core"; %> +
            • +

              <%=Html.Encode(categoryName) %>

              +
                <% + var features = featureGroup.OrderBy(f => f.Descriptor.Name); + foreach (var feature in features) { + //hmmm...I feel like I've done this before... + var featureId = feature.Descriptor.Name.AsFeatureId(n => T(n)); + var featureState = feature.IsEnabled ? "enabled" : "disabled"; + var featureClassName = string.Format("feature {0}", featureState); + if (feature == features.First()) + featureClassName += " first"; + if (feature == features.Last()) + featureClassName += " last"; %> +
              • "> +
                +
                +

                <%=Html.Encode(feature.Descriptor.Name) %>

                <% + if (feature.Descriptor.Dependencies != null) { %> +
                +

                <%=_Encoded("Depends on:")%>

                + <%=Html.UnorderedList( + feature.Descriptor.Dependencies.OrderBy(s => s), + (s, i) => Html.Link(s, string.Format("#{0}", s.AsFeatureId(n => T(n)))), + "", + "dependency", + "") %> +
                <% + } %> +
                <% + if (showActions) { %> +
                <% + if (feature.IsEnabled) { + using (Html.BeginFormAntiForgeryPost(string.Format("{0}", Url.Action("Disable", new { area = "Orchard.Modules" })), FormMethod.Post, new {@class = "inline link"})) { %> + <%=Html.Hidden("id", feature.Descriptor.Name, new { id = "" })%> + <%=Html.Hidden("force", true)%> + <% + } + } else { + using (Html.BeginFormAntiForgeryPost(string.Format("{0}", Url.Action("Enable", new { area = "Orchard.Modules" })), FormMethod.Post, new {@class = "inline link"})) { %> + <%=Html.Hidden("id", feature.Descriptor.Name, new { id = "" })%> + <%=Html.Hidden("force", true)%> + <% + } + } %> +
                <% } %> -
              +
            • <% } %> -
            <% - } %> -
        <% +
      +
    • <% + } %> +
    <% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx index c54567b93..2cb62ae78 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.ascx @@ -1,4 +1,5 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Modules.Extensions" %> <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Modules.ViewModels"%>

    <%=Html.TitleForPage(T("Installed Modules").ToString()) %>

    @@ -12,17 +13,13 @@

    <%=Html.Encode(module.DisplayName) %>

    -
      +
      • <%=T("Version: {0}", !string.IsNullOrEmpty(module.Version) ? Html.Encode(module.Version) : T("1.0")) %>
      • -
      •  | <%=T("Features: {0}", string.Join(", ", module.Features.Select(f => Html.Link(Html.Encode(f.Name), string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), Html.Encode(f.Name)) )).OrderBy(s => s).ToArray())) %>
      • +
      •  | <%=T("Features: {0}", string.Join(", ", module.Features.Select(f => Html.Link(Html.Encode(f.Name), string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), f.Name.AsFeatureId(n => T(n))) )).OrderBy(s => s).ToArray())) %>
      •  | <%=T("Author: {0}", !string.IsNullOrEmpty(module.Author) ? Html.Encode(module.Author) : (new []{"Bradley", "Bertrand", "Renaud", "Suha", "Sebastien", "Jon", "Nathan", "Erik"})[(module.DisplayName.Length + (new Random()).Next()) % 7]) %>
      • <%-- very efficient, I know --%> -
      •  | <%=T("Website: {0}", !string.IsNullOrEmpty(module.HomePage) ? Html.Encode(module.HomePage) : T("http://orchardproject.net"))%>
      • +
      •  | <%=T("Website: {0}", !string.IsNullOrEmpty(module.HomePage) ? Html.Encode(module.HomePage) : T("http://orchardproject.net"))%>
    - <%----%>
    <% if (!string.IsNullOrEmpty(module.Description)) { %>

    <%=Html.Encode(module.Description) %>

    <% @@ -30,5 +27,7 @@
  • <% } %> + + <% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css b/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css new file mode 100644 index 000000000..3041b4688 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Modules/styles/admin.css @@ -0,0 +1,85 @@ +.orchard-modules #main h2 { + border:0; + margin-bottom:.2em; +} + +.features .category { + overflow:hidden; +} +.features .feature { + border:1px solid #EAEAEA; + display:block; + float:left; + height:5em; + margin:0 .5% 1% .5%; + position:relative; + width:32.1%; +} +.features .feature:nth-child(3n+1), +.features .feature:nth-child(3n+2), +.features .feature:nth-child(3n) { + width:32.55%; +} +.features .feature:nth-child(3n+1) { + margin-left:0; +} +.features .feature:nth-child(3n+2) { + margin-right:0; +} +.features .feature:nth-child(3n) { + float:right; + margin-left:0; + margin-right:0; +} + +.features .enabled.feature { + background:#FFF; + border-color:#CFE493;cfe493 +} +.features .disabled.feature { + background:#EAEAEA; + border-color:#CCC; +} +.features .feature .summary { + overflow:hidden; + padding:.4em .5em; +} +.features .dependencies li, +.features .actions { + font-size:1.4em; +} +.features .dependencies { + font-size:.9em; + margin:.44em 0 0; +} +.features .dependencies>* { + display:inline; +} +.features .dependencies li { + display:inline; + margin-left:.5em; +} +.features .dependencies li::after { + content:", "; +} +.features .dependencies li:last-child::after { + content:""; +} +.features .feature .actions { + position:absolute; + right:.4em; + top:.6em; +} + +.cathedral { + bottom:0; + font-size:.8em; + position:absolute; + right:3px; +} +.cathedral a, +.cathedral a:link, +.cathedral a:visited, +.cathedral form.inline.link button { + color:#aeaeae; +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/AdminMenu.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/AdminMenu.cs index a62de0bd8..aab63b491 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/AdminMenu.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/AdminMenu.cs @@ -5,7 +5,7 @@ namespace Orchard.MultiTenancy { public string MenuName { get { return "admin"; } } public void GetNavigation(NavigationBuilder builder) { - builder.Add("MultiTenancy", "2", + builder.Add("Tenants", "22", menu => menu .Add("Manage Tenants", "1.0", item => item.Action("Index", "Admin", new { area = "Orchard.MultiTenancy" }).Permission(Permissions.ManageTenants)) .Add("Add New Tenant", "1.1", item => item.Action("Add", "Admin", new { area = "Orchard.MultiTenancy" }).Permission(Permissions.ManageTenants))); diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Annotations/SqlDatabaseConnectionStringAttribute.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Annotations/SqlDatabaseConnectionStringAttribute.cs new file mode 100644 index 000000000..47112ad02 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Annotations/SqlDatabaseConnectionStringAttribute.cs @@ -0,0 +1,21 @@ +using System.ComponentModel.DataAnnotations; +using System.Data.SqlClient; + +namespace Orchard.MultiTenancy.Annotations { + public class SqlDatabaseConnectionStringAttribute : ValidationAttribute { + public override bool IsValid(object value) { + if (value is string && ((string) value).Length > 0) { + try { + var connectionStringBuilder = new SqlConnectionStringBuilder(value as string); + + //TODO: (erikpo) Should the keys be checked here to ensure that a valid combination was entered? Needs investigation. + } + catch { + return false; + } + } + + return true; + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/disabled.gif b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/disabled.gif new file mode 100644 index 000000000..42c8bde22 Binary files /dev/null and b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/disabled.gif differ diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/enabled.gif b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/enabled.gif new file mode 100644 index 000000000..f55c73a2f Binary files /dev/null and b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Content/Admin/images/enabled.gif differ diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs index 65c5a56c0..eade140b8 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Controllers/AdminController.cs @@ -1,18 +1,23 @@ using System; +using System.Linq; using System.Web.Mvc; using Orchard.Environment.Configuration; using Orchard.Localization; using Orchard.MultiTenancy.Services; using Orchard.MultiTenancy.ViewModels; +using Orchard.Mvc.Results; using Orchard.UI.Notify; namespace Orchard.MultiTenancy.Controllers { [ValidateInput(false)] public class AdminController : Controller { private readonly ITenantService _tenantService; + private readonly ShellSettings _thisShellSettings; - public AdminController(ITenantService tenantService, IOrchardServices orchardServices) { + public AdminController(ITenantService tenantService, IOrchardServices orchardServices, ShellSettings shellSettings) { _tenantService = tenantService; + _thisShellSettings = shellSettings; + Services = orchardServices; T = NullLocalizer.Instance; } @@ -25,19 +30,25 @@ namespace Orchard.MultiTenancy.Controllers { } public ActionResult Add() { - return View(new TenantsAddViewModel()); + if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Cannot create tenant"))) + return new HttpUnauthorizedResult(); + return View(new TenantAddViewModel()); } [HttpPost, ActionName("Add")] - public ActionResult AddPOST(TenantsAddViewModel viewModel) { + public ActionResult AddPOST(TenantAddViewModel viewModel) { try { if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't create tenant"))) return new HttpUnauthorizedResult(); + _tenantService.CreateTenant( new ShellSettings { Name = viewModel.Name, RequestUrlHost = viewModel.RequestUrlHost, RequestUrlPrefix = viewModel.RequestUrlPrefix, + DataProvider = viewModel.DataProvider, + DataConnectionString = viewModel.DatabaseConnectionString, + DataTablePrefix = viewModel.DatabaseTablePrefix, State = new TenantState("Uninitialized") }); @@ -48,5 +59,83 @@ namespace Orchard.MultiTenancy.Controllers { return View(viewModel); } } + + public ActionResult Edit(string name) { + if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Cannot edit tenant"))) + return new HttpUnauthorizedResult(); + + var tenant = _tenantService.GetTenants().FirstOrDefault(ss => ss.Name == name); + if (tenant == null) + return new NotFoundResult(); + + return View(new TenantEditViewModel { + Name = tenant.Name, + RequestUrlHost = tenant.RequestUrlHost, + RequestUrlPrefix = tenant.RequestUrlPrefix, + DataProvider = tenant.DataProvider, + DatabaseConnectionString = tenant.DataConnectionString, + DatabaseTablePrefix = tenant.DataTablePrefix, + State = tenant.State + }); + } + + [HttpPost, ActionName("Edit")] + public ActionResult EditPost(TenantEditViewModel viewModel) { + try { + if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't edit tenant"))) + return new HttpUnauthorizedResult(); + + var tenant = _tenantService.GetTenants().FirstOrDefault(ss => ss.Name == viewModel.Name); + if (tenant == null) + return new NotFoundResult(); + + _tenantService.UpdateTenant( + new ShellSettings { + Name = tenant.Name, + RequestUrlHost = viewModel.RequestUrlHost, + RequestUrlPrefix = viewModel.RequestUrlPrefix, + DataProvider = viewModel.DataProvider, + DataConnectionString = viewModel.DatabaseConnectionString, + DataTablePrefix = viewModel.DatabaseTablePrefix, + State = tenant.State + }); + + return RedirectToAction("Index"); + } + catch (Exception exception) { + Services.Notifier.Error(T("Failed to edit tenant: ") + exception.Message); + return View(viewModel); + } + } + + [HttpPost] + public ActionResult Disable(string name) { + if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't disable tenant"))) + return new HttpUnauthorizedResult(); + + var tenant = _tenantService.GetTenants().FirstOrDefault(ss => ss.Name == name); + + if (tenant != null && tenant.Name != _thisShellSettings.Name) { + tenant.State.CurrentState = TenantState.State.Disabled; + _tenantService.UpdateTenant(tenant); + } + + return RedirectToAction("index"); + } + + [HttpPost] + public ActionResult Enable(string name) { + if (!Services.Authorizer.Authorize(Permissions.ManageTenants, T("Couldn't enable tenant"))) + return new HttpUnauthorizedResult(); + + var tenant = _tenantService.GetTenants().FirstOrDefault(ss => ss.Name == name); + + if (tenant != null && tenant.Name != _thisShellSettings.Name) { + tenant.State.CurrentState = TenantState.State.Running; + _tenantService.UpdateTenant(tenant); + } + + return RedirectToAction("index"); + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Extensions/UrlHelperExtensions.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Extensions/UrlHelperExtensions.cs new file mode 100644 index 000000000..236d45d23 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Extensions/UrlHelperExtensions.cs @@ -0,0 +1,21 @@ +using System.Web.Mvc; +using Orchard.Environment.Configuration; + +namespace Orchard.MultiTenancy.Extensions { + public static class UrlHelperExtensions { + public static string Tenant(this UrlHelper urlHelper, ShellSettings tenantShellSettings) { + //info: (heskew) might not keep the port insertion around beyond... + var port = string.Empty; + string host = urlHelper.RequestContext.HttpContext.Request.Headers["Host"]; + + if(host.Contains(":")) + port = host.Substring(host.IndexOf(":")); + + return string.Format( + "http://{0}/{1}", + !string.IsNullOrEmpty(tenantShellSettings.RequestUrlHost) + ? tenantShellSettings.RequestUrlHost + port : host, + tenantShellSettings.RequestUrlPrefix); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt index 53c124af0..fd00b570b 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.MultiTenancy: Description: Configure multiple site tenants. - Dependencies: Common - Category: Core \ No newline at end of file + Category: Hosting \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj index d5c698117..6ab56cf17 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Orchard.MultiTenancy.csproj @@ -65,18 +65,29 @@ + + + - + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Routes.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Routes.cs new file mode 100644 index 000000000..bf389bf31 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Routes.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Web.Mvc; +using System.Web.Routing; +using Orchard.Mvc.Routes; + +namespace Orchard.MultiTenancy { + public class Routes : IRouteProvider { + public IEnumerable GetRoutes() { + return new[] { + new RouteDescriptor { + Route = new Route( + "Admin/MultiTenancy/Edit/{name}", + new RouteValueDictionary { + {"area", "Orchard.MultiTenancy"}, + {"controller", "Admin"}, + {"action", "Edit"} + }, + new RouteValueDictionary { + {"name", ".+"} + }, + new RouteValueDictionary { + {"area", "Orchard.MultiTenancy"} + }, + new MvcRouteHandler()) + } + }; + } + + public void GetRoutes(ICollection routes) { + foreach (RouteDescriptor routeDescriptor in GetRoutes()) { + routes.Add(routeDescriptor); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs index d6fbb6862..f1f5ac508 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/ITenantService.cs @@ -5,5 +5,6 @@ namespace Orchard.MultiTenancy.Services { public interface ITenantService : IDependency { IEnumerable GetTenants(); void CreateTenant(ShellSettings settings); + void UpdateTenant(ShellSettings settings); } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs index d838927e1..7d2a87eac 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Services/TenantService.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; +using System.Linq; using Orchard.Environment; using Orchard.Environment.Configuration; namespace Orchard.MultiTenancy.Services { public class TenantService : ITenantService { private readonly IShellSettingsManager _shellSettingsManager; - private readonly IOrchardHost _orchardHost; - public TenantService(IShellSettingsManager shellSettingsManager, IOrchardHost orchardHost) { + public TenantService(IShellSettingsManager shellSettingsManager) { _shellSettingsManager = shellSettingsManager; - _orchardHost = orchardHost; } public IEnumerable GetTenants() { @@ -19,5 +18,12 @@ namespace Orchard.MultiTenancy.Services { public void CreateTenant(ShellSettings settings) { _shellSettingsManager.SaveSettings(settings); } + + public void UpdateTenant(ShellSettings settings) { + var tenant = GetTenants().FirstOrDefault(ss => ss.Name == settings.Name); + if ( tenant != null ) { + _shellSettingsManager.SaveSettings(settings); + } + } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs new file mode 100644 index 000000000..72c677dc2 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantAddViewModel.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.DataAnnotations; +using Orchard.MultiTenancy.Annotations; +using Orchard.Mvc.ViewModels; + +namespace Orchard.MultiTenancy.ViewModels { + public class TenantAddViewModel : BaseViewModel { + [Required] + public string Name { get; set; } + public string RequestUrlHost { get; set; } + public string RequestUrlPrefix { get; set; } + public string DataProvider { get; set; } + [SqlDatabaseConnectionString] + public string DatabaseConnectionString { get; set; } + public string DatabaseTablePrefix { get; set; } + } +} + diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs new file mode 100644 index 000000000..b0a2edfa6 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantEditViewModel.cs @@ -0,0 +1,20 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Orchard.Environment.Configuration; +using Orchard.MultiTenancy.Annotations; +using Orchard.Mvc.ViewModels; + +namespace Orchard.MultiTenancy.ViewModels { + public class TenantEditViewModel : BaseViewModel { + [Required] + public string Name { get; set; } + public string RequestUrlHost { get; set; } + public string RequestUrlPrefix { get; set; } + public string DataProvider { get; set; } + [SqlDatabaseConnectionString] + public string DatabaseConnectionString { get; set; } + public string DatabaseTablePrefix { get; set; } + public TenantState State { get; set; } + } +} + diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantsAddViewModel.cs b/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantsAddViewModel.cs deleted file mode 100644 index 98ac2ade8..000000000 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/ViewModels/TenantsAddViewModel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.ComponentModel; -using System.ComponentModel.DataAnnotations; -using Orchard.Mvc.ViewModels; - -namespace Orchard.MultiTenancy.ViewModels { - public class TenantsAddViewModel : BaseViewModel { - [Required, DisplayName("Tenant Name:")] - public string Name { get; set; } - public string RequestUrlHost { get; set; } - public string RequestUrlPrefix { get; set; } - } -} - diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx index eb6aca4dc..08988e70f 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Add.ascx @@ -1,16 +1,43 @@ -<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.MultiTenancy.ViewModels"%> - -

    <%=Html.TitleForPage(T("Add a Tenant to your Site").ToString()) %>

    - +

    <%=Html.TitleForPage(T("Add New Tenant").ToString()) %>

    <%using (Html.BeginFormAntiForgeryPost()) { %> - <%= Html.ValidationSummary() %> + <%=Html.ValidationSummary() %>
    - -
    - -
    +
    + + +
    +
    + + + <%=_Encoded("Example: If host is \"orchardproject.net\", the tenant site URL is \"http://orchardproject.net/\"") %> +
    +
    +
    + <%=_Encoded("Database Setup") %> +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "", new { id = "tenantDatabaseOption" })%> + +
    +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "SQLite", new { id = "builtinDatabaseOption" })%> + +
    +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "SqlServer", new { id = "sqlDatabaseOption" })%> + + + + <%=Html.EditorFor(svm => svm.DatabaseConnectionString)%> + <%=_Encoded("Example:") %>
    <%=_Encoded("Data Source=sqlServerName;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password") %>
    +
    + + + <%=Html.EditorFor(svm => svm.DatabaseTablePrefix)%> + +
    " /> diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForDisabled.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForDisabled.ascx new file mode 100644 index 000000000..7e75f8f4c --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForDisabled.ascx @@ -0,0 +1,7 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Environment.Configuration" %> +<%@ Import Namespace="Orchard.Mvc.Html" %> +<% using(Html.BeginFormAntiForgeryPost(Url.Action("enable", new {area = "Orchard.MultiTenancy"}), FormMethod.Post, new {@class = "inline link"})) { %> +<%=Html.HiddenFor(ss => ss.Name) %> +<% + } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForInvalid.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForInvalid.ascx new file mode 100644 index 000000000..94d76d5bf --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForInvalid.ascx @@ -0,0 +1,3 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Environment.Configuration" %> +<%=Html.ActionLink(T("Make Valid*").ToString(), "_setup", new {tenantName = Model.Name, area = "Orchard.MultiTenancy"}) %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForRunning.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForRunning.ascx new file mode 100644 index 000000000..81d69cb91 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForRunning.ascx @@ -0,0 +1,7 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Environment.Configuration" %> +<%@ Import Namespace="Orchard.Mvc.Html" %> +<% using(Html.BeginFormAntiForgeryPost(Url.Action("disable", new {area = "Orchard.MultiTenancy"}), FormMethod.Post, new {@class = "inline link"})) { %> +<%=Html.HiddenFor(ss => ss.Name) %> +<% + } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForUninitialized.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForUninitialized.ascx new file mode 100644 index 000000000..d918cf219 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/DisplayTemplates/ActionsForUninitialized.ascx @@ -0,0 +1,5 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.MultiTenancy.Extensions"%> +<%@ Import Namespace="Orchard.Mvc.Html"%> +<%@ Import Namespace="Orchard.Environment.Configuration" %> +<%=Html.Link(T("Set Up").ToString(), Url.Tenant(Model))%> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.ascx new file mode 100644 index 000000000..44305f195 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Edit.ascx @@ -0,0 +1,49 @@ +<%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.Environment.Configuration" %> +<%@ Import Namespace="Orchard.Mvc.Html"%> +<%@ Import Namespace="Orchard.MultiTenancy.ViewModels"%> +

    <%=Html.TitleForPage(T("Edit Tenant").ToString()) %>

    +<%using (Html.BeginFormAntiForgeryPost()) { %> + <%=Html.ValidationSummary() %> +
    +
    +

    <%=Html.Encode(Model.Name) %>

    +
    +
    + + <%=Html.TextBoxFor(m => m.RequestUrlHost, new {@class = "textMedium"}) %> + <%=_Encoded("Example: If host is \"orchardproject.net\", the tenant site URL is \"http://orchardproject.net/\"") %> +
    +
    +
    + <%=_Encoded("Database Setup") %><% + if (Model.State.CurrentState != TenantState.State.Uninitialized) { %> +
    <%=_Encoded("Warning: If you don't know what you're doing you *will* (likely) send this tenant into a downward spiral of irrecoverable disrepair. Have a nice day.")%>
    <% + } else { %> +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "", new { id = "tenantDatabaseOption" })%> + +
    <% + } %> +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "SQLite", new { id = "builtinDatabaseOption" })%> + +
    +
    + <%=Html.RadioButtonFor(svm => svm.DataProvider, "SqlServer", new { id = "sqlDatabaseOption" })%> + + + + <%=Html.TextBoxFor(svm => svm.DatabaseConnectionString, new {@class = "large text"})%> + <%=_Encoded("Example:") %>
    <%=_Encoded("Data Source=sqlServerName;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password") %>
    +
    + + + <%=Html.EditorFor(svm => svm.DatabaseTablePrefix)%> + +
    +
    +
    + " /> +
    + <% } %> \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Index.ascx b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Index.ascx index 44c8a1d1a..d6853f6d5 100644 --- a/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Index.ascx +++ b/src/Orchard.Web/Modules/Orchard.MultiTenancy/Views/Admin/Index.ascx @@ -1,39 +1,32 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> +<%@ Import Namespace="Orchard.MultiTenancy.Extensions" %> <%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.MultiTenancy.ViewModels"%>

    <%=Html.TitleForPage(T("List of Site's Tenants").ToString())%>

    - - - - - - - - - - - - - - - - - - - - - - <% +
    <%=Html.ActionLink(T("Add a Tenant").ToString(), "Add", new {area = "Orchard.MultiTenancy"}, new { @class = "button primaryAction" })%>
    +
      <% foreach (var tenant in Model.TenantSettings) { %> -
    - - - - - - - - <% +
  • +
    +
    +

    <%=Html.Encode(tenant.Name) %><% + if (!string.IsNullOrEmpty(tenant.RequestUrlHost)) { + %> - <%=Html.Link(Url.Tenant(tenant), Url.Tenant(tenant))%><% + } %>

    +
    + +
    +
  • <% } %> - -
    <%=_Encoded("Name") %><%=_Encoded("Data Provider") %><%=_Encoded("ConnectionString") %><%=_Encoded("Table Prefix") %><%=_Encoded("Request Url Host") %><%=_Encoded("Request Url Prefix") %><%=_Encoded("State") %>
    <%= tenant.Name %><%= tenant.DataProvider %><%= tenant.DataConnectionString %><%= tenant.DataTablePrefix %><%= tenant.RequestUrlHost %><%= tenant.RequestUrlPrefix %><%= tenant.State %>
    + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Handlers/PageHandler.cs b/src/Orchard.Web/Modules/Orchard.Pages/Handlers/PageHandler.cs index 6b3b2e7a9..ef9d535ec 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Handlers/PageHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Handlers/PageHandler.cs @@ -28,7 +28,7 @@ namespace Orchard.Pages.Handlers { Filters.Add(new ActivatingFilter(PageDriver.ContentType.Name)); Filters.Add(new ActivatingFilter(PageDriver.ContentType.Name)); - OnLoaded((context, p) => p.ScheduledPublishUtc = _pageService.GetScheduledPublishUtc(p)); + OnLoaded((context, page) => page._scheduledPublishUtc.Loader(value => _pageService.GetScheduledPublishUtc(page))); } Localizer T { get; set; } diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Models/Page.cs b/src/Orchard.Web/Modules/Orchard.Pages/Models/Page.cs index 178bccf7c..800747dc0 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Models/Page.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Models/Page.cs @@ -1,6 +1,7 @@ using System; using System.Web.Mvc; using Orchard.ContentManagement; +using Orchard.ContentManagement.Utilities; using Orchard.Core.Common.Models; using Orchard.Security; @@ -52,7 +53,8 @@ namespace Orchard.Pages.Models { } } - public DateTime? ScheduledPublishUtc { get; set; } + public readonly LazyField _scheduledPublishUtc = new LazyField(); + public DateTime? ScheduledPublishUtc { get { return _scheduledPublishUtc.Value; } set{ _scheduledPublishUtc.Value = value;} } private string _scheduledPublishUtcDate; diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Module.txt b/src/Orchard.Web/Modules/Orchard.Pages/Module.txt index 1ade81120..f7100c9c9 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Pages/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Pages: Description: Simple pages. - Dependencies: Common Category: Content \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Orchard.Pages.csproj b/src/Orchard.Web/Modules/Orchard.Pages/Orchard.Pages.csproj index 056bde59d..301406985 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Orchard.Pages.csproj +++ b/src/Orchard.Web/Modules/Orchard.Pages/Orchard.Pages.csproj @@ -78,7 +78,7 @@ - + diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Security/Authorization.cs b/src/Orchard.Web/Modules/Orchard.Pages/Security/AuthorizationEventHandler.cs similarity index 83% rename from src/Orchard.Web/Modules/Orchard.Pages/Security/Authorization.cs rename to src/Orchard.Web/Modules/Orchard.Pages/Security/AuthorizationEventHandler.cs index 4a6254929..f86b68305 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Security/Authorization.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Security/AuthorizationEventHandler.cs @@ -7,8 +7,11 @@ using Orchard.Security.Permissions; namespace Orchard.Pages.Security { [UsedImplicitly] - public class Authorization : AuthorizationServiceEvents { - public override void Adjust(CheckAccessContext context) { + public class AuthorizationEventHandler : IAuthorizationServiceEventHandler { + public void Checking(CheckAccessContext context) { } + public void Complete(CheckAccessContext context) { } + + public void Adjust(CheckAccessContext context) { if (context.Granted == false && context.Content.Is() && OwnerVariationExists(context.Permission) && diff --git a/src/Orchard.Web/Modules/Orchard.Pages/Services/PageService.cs b/src/Orchard.Web/Modules/Orchard.Pages/Services/PageService.cs index 22160fcbf..15d09e345 100644 --- a/src/Orchard.Web/Modules/Orchard.Pages/Services/PageService.cs +++ b/src/Orchard.Web/Modules/Orchard.Pages/Services/PageService.cs @@ -35,11 +35,11 @@ namespace Orchard.Pages.Services { public IEnumerable Get(PageStatus status) { switch (status) { case PageStatus.All: - return _contentManager.Query(VersionOptions.Latest).List(); + return _contentManager.Query(PageDriver.ContentType.Name).Join().ForVersion(VersionOptions.Latest).List().AsPart(); case PageStatus.Published: - return _contentManager.Query(VersionOptions.Published).List(); + return _contentManager.Query(PageDriver.ContentType.Name).Join().ForVersion(VersionOptions.Published).List().AsPart(); case PageStatus.Offline: - return _contentManager.Query(VersionOptions.Latest).Where(ci => !ci.ContentItemVersionRecord.Published).List(); + return _contentManager.Query(PageDriver.ContentType.Name).Join().ForVersion(VersionOptions.Latest).Where(ci => !ci.ContentItemVersionRecord.Published).List().AsPart(); default: return Enumerable.Empty(); } diff --git a/src/Orchard.Web/Modules/Orchard.Roles/Module.txt b/src/Orchard.Web/Modules/Orchard.Roles/Module.txt index 32fd2408d..30653aa79 100644 --- a/src/Orchard.Web/Modules/Orchard.Roles/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Roles/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Roles: Description: Standard user roles. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Roles/Services/RolesBasedAuthorizationService.cs b/src/Orchard.Web/Modules/Orchard.Roles/Services/RolesBasedAuthorizationService.cs index c6fd794c2..a74ed21d0 100644 --- a/src/Orchard.Web/Modules/Orchard.Roles/Services/RolesBasedAuthorizationService.cs +++ b/src/Orchard.Web/Modules/Orchard.Roles/Services/RolesBasedAuthorizationService.cs @@ -13,13 +13,13 @@ namespace Orchard.Roles.Services { [UsedImplicitly] public class RolesBasedAuthorizationService : IAuthorizationService { private readonly IRoleService _roleService; - private readonly IEnumerable _events; + private readonly IAuthorizationServiceEventHandler _authorizationServiceEventHandler; private static readonly string[] AnonymousRole = new[] { "Anonymous" }; private static readonly string[] AuthenticatedRole = new[] { "Authenticated" }; - public RolesBasedAuthorizationService(IRoleService roleService, IEnumerable events) { + public RolesBasedAuthorizationService(IRoleService roleService, IAuthorizationServiceEventHandler authorizationServiceEventHandler) { _roleService = roleService; - _events = events; + _authorizationServiceEventHandler = authorizationServiceEventHandler; Logger = NullLogger.Instance; } @@ -35,9 +35,7 @@ namespace Orchard.Roles.Services { public bool TryCheckAccess(Permission permission, IUser user, IContent content) { var context = new CheckAccessContext { Permission = permission, User = user, Content = content }; - - _events.Invoke(x => x.Checking(context), Logger); - + _authorizationServiceEventHandler.Checking(context); for (var adjustmentLimiter = 0; adjustmentLimiter != 3; ++adjustmentLimiter) { if (!context.Granted && context.User != null) { @@ -67,6 +65,8 @@ namespace Orchard.Roles.Services { foreach (var role in rolesToExamine) { RoleRecord roleRecord = _roleService.GetRoleByName(role); + if ( roleRecord == null ) + continue; foreach (var permissionName in _roleService.GetPermissionsForRole(roleRecord.Id)) { string possessedName = permissionName; if (grantingNames.Any(grantingName => String.Equals(possessedName, grantingName, StringComparison.OrdinalIgnoreCase))) { @@ -83,10 +83,12 @@ namespace Orchard.Roles.Services { } context.Adjusted = false; - _events.Invoke(x => x.Adjust(context), Logger); + _authorizationServiceEventHandler.Adjust(context); + if (!context.Adjusted) + break; } - _events.Invoke(x => x.Complete(context), Logger); + _authorizationServiceEventHandler.Complete(context); return context.Granted; } diff --git a/src/Orchard.Web/Modules/Orchard.Sandbox/Module.txt b/src/Orchard.Web/Modules/Orchard.Sandbox/Module.txt index 488adb7de..81a3967b7 100644 --- a/src/Orchard.Web/Modules/Orchard.Sandbox/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Sandbox/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Sandbox: Description: A module to mess around with. Currently wiki-like. - Dependencies: Common Category: Developer \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Commands/SetupCommand.cs b/src/Orchard.Web/Modules/Orchard.Setup/Commands/SetupCommand.cs index 69943779f..8970ccb15 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Commands/SetupCommand.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Commands/SetupCommand.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System.Collections.Generic; +using System.Linq; using Orchard.Commands; using Orchard.Setup.Services; @@ -37,6 +38,14 @@ namespace Orchard.Setup.Commands { [CommandName("setup")] [OrchardSwitches("SiteName,AdminUsername,AdminPassword,DatabaseProvider,DatabaseConnectionString,DatabaseTablePrefix,EnabledFeatures")] public void Setup() { + IEnumerable enabledFeatures = null; + if (!string.IsNullOrEmpty(this.EnabledFeatures)) { + enabledFeatures = this.EnabledFeatures + .Split(',') + .Select(s => s.Trim()) + .Where(s => !string.IsNullOrEmpty(s)); + } + var setupContext = new SetupContext { SiteName = this.SiteName, AdminUsername = this.AdminUsername, @@ -44,19 +53,15 @@ namespace Orchard.Setup.Commands { DatabaseProvider = this.DatabaseProvider, DatabaseConnectionString = this.DatabaseConnectionString, DatabaseTablePrefix = this.DatabaseTablePrefix, - EnabledFeatures = this.EnabledFeatures.Split(',').Select(s => s.Trim()) + EnabledFeatures = enabledFeatures }; _setupService.Setup(setupContext); - Context.Output.WriteLine("Site \"{0}\" setup to run data provider \"{1}\" (with table prefix \"{2}\") with the following features enabled:", + Context.Output.WriteLine(T("Site \"{0}\" sucessfully setup to run data provider \"{1}\" (with table prefix \"{2}\").", setupContext.SiteName, setupContext.DatabaseProvider, - setupContext.DatabaseTablePrefix); - - foreach (var feature in setupContext.EnabledFeatures) { - this.Context.Output.WriteLine("{0}", feature); - } + setupContext.DatabaseTablePrefix)); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs index 9e7051836..1722cfa98 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Controllers/SetupController.cs @@ -8,11 +8,11 @@ using Orchard.Core.Navigation.Models; using Orchard.Core.Settings.Models; using Orchard.Data; using Orchard.Environment; -using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.ShellBuilders; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; +using Orchard.FileSystems.AppData; using Orchard.Security; using Orchard.Settings; using Orchard.Setup.Services; @@ -50,7 +50,8 @@ namespace Orchard.Setup.Controllers { } public ActionResult Index() { - return IndexViewResult(new SetupViewModel { AdminUsername = "admin" }); + var initialSettings = _setupService.Prime(); + return IndexViewResult(new SetupViewModel { AdminUsername = "admin", DatabaseIsPreconfigured = !string.IsNullOrEmpty(initialSettings.DataProvider)}); } [HttpPost, ActionName("Index")] @@ -64,26 +65,6 @@ namespace Orchard.Setup.Controllers { } try { - // The vanilla Orchard distibution has the following features enabled. - string[] hardcoded = { - "Orchard.Framework", - "Common", - "Dashboard", - "Feeds", - "HomePage", - "Navigation", - "Scheduling", - "Settings", - "XmlRpc", - "Orchard.Users", - "Orchard.Roles", - "TinyMce", - "Orchard.Modules", - "Orchard.Themes", - "Orchard.MultiTenancy", - "Orchard.Pages", - "Orchard.Blogs", - "Orchard.Comments"}; var setupContext = new SetupContext { SiteName = model.SiteName, @@ -92,7 +73,7 @@ namespace Orchard.Setup.Controllers { DatabaseProvider = model.DatabaseOptions ? "SQLite" : "SqlServer", DatabaseConnectionString = model.DatabaseConnectionString, DatabaseTablePrefix = model.DatabaseTablePrefix, - EnabledFeatures = hardcoded + EnabledFeatures = null // default list }; _setupService.Setup(setupContext); diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Module.txt b/src/Orchard.Web/Modules/Orchard.Setup/Module.txt index 1d87f1edd..069cdb7e3 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Setup/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Setup: Description: Standard site setup. - Dependencies: Common Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj b/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj index cb8375c1b..47e1b5f1d 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj +++ b/src/Orchard.Web/Modules/Orchard.Setup/Orchard.Setup.csproj @@ -101,7 +101,6 @@ - diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/ISetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/ISetupService.cs index f7a985e63..2cf512548 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/ISetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/ISetupService.cs @@ -1,5 +1,8 @@ -namespace Orchard.Setup.Services { +using Orchard.Environment.Configuration; + +namespace Orchard.Setup.Services { public interface ISetupService : IDependency { + ShellSettings Prime(); void Setup(SetupContext context); } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs index 65f9f6e9a..77007d0fa 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/Services/SetupService.cs @@ -14,6 +14,7 @@ using Orchard.Environment.ShellBuilders; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; using Orchard.Localization; +using Orchard.ContentManagement.MetaData.Services; using Orchard.Security; using Orchard.Settings; using Orchard.Themes; @@ -44,12 +45,45 @@ namespace Orchard.Setup.Services { private Localizer T { get; set; } + public ShellSettings Prime() { + return _shellSettings; + } + public void Setup(SetupContext context) { - var shellSettings = new ShellSettings(_shellSettings) { - DataProvider = context.DatabaseProvider, - DataConnectionString = context.DatabaseConnectionString, - DataTablePrefix = context.DatabaseTablePrefix, - }; + // The vanilla Orchard distibution has the following features enabled. + if (context.EnabledFeatures == null || context.EnabledFeatures.Count() == 0) { + string[] hardcoded = { + "Orchard.Framework", + "Common", + "Dashboard", + "Feeds", + "HomePage", + "Navigation", + "Scheduling", + "Settings", + "XmlRpc", + "Orchard.Users", + "Orchard.Roles", + "TinyMce", + "Orchard.Modules", + "Orchard.Themes", + "Orchard.Pages", + "Orchard.Blogs", + "Orchard.Comments", + "Orchard.Tags", + "Orchard.Media", + "Futures.Widgets"}; + + context.EnabledFeatures = hardcoded; + } + + var shellSettings = new ShellSettings(_shellSettings); + + if (string.IsNullOrEmpty(shellSettings.DataProvider)) { + shellSettings.DataProvider = context.DatabaseProvider; + shellSettings.DataConnectionString = context.DatabaseConnectionString; + shellSettings.DataTablePrefix = context.DatabaseTablePrefix; + } var shellDescriptor = new ShellDescriptor { EnabledFeatures = context.EnabledFeatures.Select(name => new ShellFeature { Name = name }) @@ -107,8 +141,10 @@ namespace Orchard.Setup.Services { page.As().Text = "

    Welcome to Orchard!

    Congratulations, you've successfully set-up your Orchard site.

    This is the home page of your new site. We've taken the liberty to write here about a few things you could look at next in order to get familiar with the application. Once you feel confident you don't need this anymore, just click Edit to go into edit mode and replace this with whatever you want on your home page to make it your own.

    One thing you could do (but you don't have to) is go into Manage Settings (follow the Admin link and then look for it under \"Settings\" in the menu on the left) and check that everything is configured the way you want.

    You probably want to make the site your own. One of the ways you can do that is by clicking Manage Themes in the admin menu. A theme is a packaged look and feel that affects the whole site.

    Next, you can start playing with the content types that we installed. For example, go ahead and click Add New Page in the admin menu and create an \"about\" page. Then, add it to the navigation menu by going to Manage Menu. You can also click Add New Blog and start posting by clicking \"Add New Post\".

    Finally, Orchard has been designed to be extended. It comes with a few built-in modules such as pages and blogs or themes. You can install new themes by going to Manage Themes and clicking Install a new Theme. Like for themes, modules are created by other users of Orchard just like you so if you feel up to it, please consider participating.

    --The Orchard Crew

    "; page.As().Slug = "home"; page.As().Title = T("Home").ToString(); - page.As().CommentsShown = false; page.As().Owner = user; + if (page.Has()) { + page.As().CommentsShown = false; + } contentManager.Publish(page); siteSettings.Record.HomePage = "PageHomePageProvider;" + page.Id; @@ -124,6 +160,24 @@ namespace Orchard.Setup.Services { var authenticationService = environment.Resolve(); authenticationService.SignIn(user, true); } + + //Add ContentType mappings + var contentTypeService = environment.Resolve(); + + //Add ContentTypePartNames to MetaData + contentTypeService.AddContentTypePartNameToMetaData("HasComments"); + contentTypeService.AddContentTypePartNameToMetaData("HasTags"); + + //Add mappings from ContentTypes to ContentParts to MetaData + contentTypeService.MapContentTypeToContentPart("blogpost","HasComments"); + contentTypeService.MapContentTypeToContentPart("page", "HasComments"); + contentTypeService.MapContentTypeToContentPart("sandboxpage", "HasComments"); + contentTypeService.MapContentTypeToContentPart("blogpost", "HasTags"); + contentTypeService.MapContentTypeToContentPart("page", "HasTags"); + contentTypeService.MapContentTypeToContentPart("sandboxpage", "HasTags"); + + + } catch { environment.Resolve().Cancel(); diff --git a/src/Orchard.Web/Modules/Orchard.Setup/ViewModels/SetupViewModel.cs b/src/Orchard.Web/Modules/Orchard.Setup/ViewModels/SetupViewModel.cs index cc48b2e49..086aebf52 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/ViewModels/SetupViewModel.cs +++ b/src/Orchard.Web/Modules/Orchard.Setup/ViewModels/SetupViewModel.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using Orchard.Setup.Annotations; using Orchard.Mvc.ViewModels; @@ -18,7 +17,7 @@ namespace Orchard.Setup.ViewModels { public bool DatabaseOptions { get; set; } [SqlDatabaseConnectionString] public string DatabaseConnectionString { get; set; } - public string DatabaseTablePrefix { get; set; } + public bool DatabaseIsPreconfigured { get; set; } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Setup/Views/Setup/Index.ascx b/src/Orchard.Web/Modules/Orchard.Setup/Views/Setup/Index.ascx index cfc7135e3..8ac805bab 100644 --- a/src/Orchard.Web/Modules/Orchard.Setup/Views/Setup/Index.ascx +++ b/src/Orchard.Web/Modules/Orchard.Setup/Views/Setup/Index.ascx @@ -1,5 +1,4 @@ <%@ Control Language="C#" Inherits="Orchard.Mvc.ViewUserControl" %> -<%@ Import Namespace="Orchard.Mvc.Html"%> <%@ Import Namespace="Orchard.Setup.ViewModels"%>

    <%=Html.TitleForPage(_Encoded("Get Started").ToHtmlString())%>

    <% @@ -9,7 +8,7 @@ using (Html.BeginFormAntiForgeryPost()) { %>
    - <%=Html.EditorFor(svm => svm.SiteName) %> + <%=Html.TextBoxFor(svm => svm.SiteName, new { autofocus = "autofocus" })%>
    @@ -19,7 +18,8 @@ using (Html.BeginFormAntiForgeryPost()) { %> <%=Html.PasswordFor(svm => svm.AdminPassword) %>
    -
    +
    <% +if (!Model.DatabaseIsPreconfigured) { %>
    <%=_Encoded("How would you like to store your data?") %> <%=Html.ValidationMessage("DatabaseOptions", "Unable to setup data storage") %> @@ -30,17 +30,18 @@ using (Html.BeginFormAntiForgeryPost()) { %>
    <%=Html.RadioButtonFor(svm => svm.DatabaseOptions, false, new { id = "sql" })%> - +
    <%=Html.EditorFor(svm => svm.DatabaseConnectionString)%> <%=_Encoded("Example:") %>
    <%=_Encoded("Data Source=sqlServerName;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password") %>
    - - +
    +
    <%=Html.EditorFor(svm => svm.DatabaseTablePrefix)%> - +
    -
    +<% +} %>
    " />
    <% diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Handlers/HasTagsHandler.cs b/src/Orchard.Web/Modules/Orchard.Tags/Handlers/HasTagsHandler.cs index 2eec0c281..c1af0e6d2 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Handlers/HasTagsHandler.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Handlers/HasTagsHandler.cs @@ -10,30 +10,34 @@ namespace Orchard.Tags.Handlers { [UsedImplicitly] public class HasTagsHandler : ContentHandler { public HasTagsHandler(IRepository tagsRepository, IRepository tagsContentItemsRepository) { - Filters.Add(new ActivatingFilter("sandboxpage")); - Filters.Add(new ActivatingFilter("blogpost")); - Filters.Add(new ActivatingFilter("page")); + + OnLoading((context, tags) => { - OnLoading((context, ht) => { - HasTags tags = context.ContentItem.As(); - tags.AllTags = tagsRepository.Table.ToList(); - IEnumerable tagsContentItems = tagsContentItemsRepository.Fetch(x => x.ContentItemId == context.ContentItem.Id); - foreach (var tagContentItem in tagsContentItems) { - Tag tag = tagsRepository.Get(tagContentItem.TagId); - tags.CurrentTags.Add(tag); - } - }); + // provide names of all tags on demand + tags._allTags.Loader(list => tagsRepository.Table.ToList()); + + // populate list of attached tags on demand + tags._currentTags.Loader(list => { + var tagsContentItems = tagsContentItemsRepository.Fetch(x => x.ContentItemId == context.ContentItem.Id); + foreach (var tagContentItem in tagsContentItems) { + var tag = tagsRepository.Get(tagContentItem.TagId); + list.Add(tag); + } + return list; + }); + + }); OnRemoved((context, ht) => { - tagsContentItemsRepository.Flush(); + tagsContentItemsRepository.Flush(); - HasTags tags = context.ContentItem.As(); - foreach (var tag in tags.CurrentTags) { - if (!tagsContentItemsRepository.Fetch(x => x.ContentItemId == context.ContentItem.Id).Any()) { - tagsRepository.Delete(tag); - } - } - }); + HasTags tags = context.ContentItem.As(); + foreach (var tag in tags.CurrentTags) { + if (!tagsContentItemsRepository.Fetch(x => x.ContentItemId == context.ContentItem.Id).Any()) { + tagsRepository.Delete(tag); + } + } + }); } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Models/HasTags.cs b/src/Orchard.Web/Modules/Orchard.Tags/Models/HasTags.cs index d73e2d518..f3fc4f296 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Models/HasTags.cs +++ b/src/Orchard.Web/Modules/Orchard.Tags/Models/HasTags.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Orchard.ContentManagement; +using Orchard.ContentManagement.Utilities; namespace Orchard.Tags.Models { public class HasTags : ContentPart { @@ -8,7 +9,10 @@ namespace Orchard.Tags.Models { CurrentTags = new List(); } - public IList AllTags { get; set; } - public IList CurrentTags { get; set; } + public readonly LazyField> _allTags = new LazyField>(); + public readonly LazyField> _currentTags = new LazyField>(); + + public IList AllTags { get { return _allTags.Value; } set { _allTags.Value = value; } } + public IList CurrentTags { get { return _currentTags.Value; } set { _currentTags.Value = value; } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Tags/Module.txt b/src/Orchard.Web/Modules/Orchard.Tags/Module.txt index 2cc734ef8..b7bf7e204 100644 --- a/src/Orchard.Web/Modules/Orchard.Tags/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Tags/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Tags: Description: Tag a content item. - Dependencies: Common - Category: Taxonomy \ No newline at end of file + Category: Navigation \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/DesignerNotes/ZoneManagerEvents.cs b/src/Orchard.Web/Modules/Orchard.Themes/DesignerNotes/ZoneManagerEvents.cs index 39b7afb04..bd2aa9f00 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/DesignerNotes/ZoneManagerEvents.cs +++ b/src/Orchard.Web/Modules/Orchard.Themes/DesignerNotes/ZoneManagerEvents.cs @@ -38,7 +38,7 @@ namespace Orchard.Themes.DesignerNotes { Controller = "Admin", context.ZoneName, theme.ThemeName, - ReturnUrl = requestContext.HttpContext.Request.Url, + ReturnUrl = requestContext.HttpContext.Request.RawUrl, })); writer.Write(""); } diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Module.txt b/src/Orchard.Web/Modules/Orchard.Themes/Module.txt index f2c729ec6..551edb361 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Module.txt +++ b/src/Orchard.Web/Modules/Orchard.Themes/Module.txt @@ -7,5 +7,4 @@ orchardversion: 0.1.2010.0312 features: Orchard.Themes: Description: Basic theming capability. - Dependencies: Common - Category: Display \ No newline at end of file + Category: Core \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Orchard.Themes.csproj b/src/Orchard.Web/Modules/Orchard.Themes/Orchard.Themes.csproj index 174c4dc67..d5cfcd5ff 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Orchard.Themes.csproj +++ b/src/Orchard.Web/Modules/Orchard.Themes/Orchard.Themes.csproj @@ -88,8 +88,6 @@ - - @@ -111,6 +109,8 @@ + + diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Scripts/base.js b/src/Orchard.Web/Modules/Orchard.Themes/Scripts/base.js index a19756dfe..a5f3d1ec7 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Scripts/base.js +++ b/src/Orchard.Web/Modules/Orchard.Themes/Scripts/base.js @@ -1,24 +1,42 @@ -jQuery.fn.extend({ +(function($){ +//todo: (heskew) make use of the autofocus attribute instead +$.fn.extend({ helpfullyFocus: function() { var _this = $(this); var firstError = _this.find(".input-validation-error").first(); - return firstError.size() === 1 - ? firstError.focus() - : _this.find("input:text").first().focus(); + // try to focus the first error on the page + if (firstError.size() === 1) { + return firstError.focus(); + } + // or, give it up to the browser to autofocus + if ('autofocus' in document.createElement('input')) { + return; + } + // otherwise, make the autofocus attribute work + var autofocus = _this.find(":input[autofocus=autofocus]").first(); + return autofocus.focus(); }, toggleWhatYouControl: function() { var _this = $(this); var _controllees = $("[data-controllerid=" + _this.attr("id") + "]"); - if (_this.is(":checked")) { - $(_controllees.slideDown(200)[0]).find("input").focus(); - } else { - _controllees.slideUp(200); + var _controlleesAreHidden = _controllees.is(":hidden"); + if (_this.is(":checked") && _controlleesAreHidden) { + _controllees.hide(); // <- unhook this when the following comment applies + $(_controllees.show()[0]).find("input").focus(); // <- aaaand a slideDown there...eventually + } else if (!(_this.is(":checked") && _controlleesAreHidden)) { + //_controllees.slideUp(200); <- hook this back up when chrome behaves, or when I care less + _controllees.hide() } } }); +// collapsable areas - anything with a data-controllerid attribute has its visibility controlled by the id-ed radio/checkbox (function() { $("[data-controllerid]").each(function() { var controller = $("#" + $(this).attr("data-controllerid")); + if (controller.data("isControlling")) { + return; + } + controller.data("isControlling", 1); if (!controller.is(":checked")) { $("[data-controllerid=" + controller.attr("id") + "]").hide(); } @@ -28,4 +46,24 @@ $("[name=" + controller.attr("name") + "]").click(function() { $("[name=" + $(this).attr("name") + "]").each($(this).toggleWhatYouControl); }); } }); -})(); \ No newline at end of file +})(); +// inline form link buttons (form.inline.link button) swapped out for a link that submits said form +(function() { + $("form.inline.link").each(function() { + var _this = $(this); + var link = $(""); + var button = _this.children("button").first(); + link.text(button.text()) + .addClass(button.attr("class")) + .click(function() { _this.submit(); return false; }) + .unload(function() { _this = 0; }); + _this.replaceWith(link); + _this.css({ "position": "absolute", "left": "-9999em" }); + $("body").append(_this); + }); +})(); +// a little better autofocus +$(function() { + $("body").helpfullyFocus(); +}); +})(jQuery); \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Scripts/jquery-1.4.1.js b/src/Orchard.Web/Modules/Orchard.Themes/Scripts/jquery-1.4.1.js deleted file mode 100644 index 109701084..000000000 --- a/src/Orchard.Web/Modules/Orchard.Themes/Scripts/jquery-1.4.1.js +++ /dev/null @@ -1,6078 +0,0 @@ -/*! - * jQuery JavaScript Library v1.4.1 - * http://jquery.com/ - * - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * http://jquery.org/license - * - * Includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * - * Date: Mon Jan 25 19:43:33 2010 -0500 - */ -(function( window, undefined ) { - -// Define a local copy of jQuery -var jQuery = function( selector, context ) { - // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context ); - }, - - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - // A central reference to the root jQuery(document) - rootjQuery, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, - - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - // Keep a UserAgent string for use with jQuery.browser - userAgent = navigator.userAgent, - - // For matching the engine and version of the browser - browserMatch, - - // Has the ready events already been bound? - readyBound = false, - - // The functions to execute on DOM ready - readyList = [], - - // The ready event handler - DOMContentLoaded, - - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwnProperty = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - indexOf = Array.prototype.indexOf; - -jQuery.fn = jQuery.prototype = { - init: function( selector, context ) { - var match, elem, ret, doc; - - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } - - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - match = quickExpr.exec( selector ); - - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - doc = (context ? context.ownerDocument || context : document); - - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); - - if ( ret ) { - if ( jQuery.isPlainObject( context ) ) { - selector = [ document.createElement( ret[1] ) ]; - jQuery.fn.attr.call( selector, context, true ); - - } else { - selector = [ doc.createElement( ret[1] ) ]; - } - - } else { - ret = buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; - } - - // HANDLE: $("#id") - } else { - elem = document.getElementById( match[2] ); - - if ( elem ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootjQuery.find( selector ); - } - - // Otherwise, we inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $("TAG") - } else if ( !context && /^\w+$/.test( selector ) ) { - this.selector = selector; - this.context = document; - selector = document.getElementsByTagName( selector ); - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return (context || rootjQuery).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return jQuery( context ).find( selector ); - } - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.isArray( selector ) ? - this.setArray( selector ) : - jQuery.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", - - // The current version of jQuery being used - jquery: "1.4.1", - - // The default length of a jQuery object is 0 - length: 0, - - // The number of elements contained in the matched element set - size: function() { - return this.length; - }, - - toArray: function() { - return slice.call( this, 0 ); - }, - - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? - - // Return a 'clean' array - this.toArray() : - - // Return just the object - ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); - }, - - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new jQuery matched element set - var ret = jQuery( elems || null ); - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; - }, - - // Force the current matched set of elements to become - // the specified array of elements (destroying the stack in the process) - // You should use pushStack() in order to do this, but maintain the stack - setArray: function( elems ) { - // Resetting the length to 0, then using the native Array push - // is a super-fast way to populate an object with array-like properties - this.length = 0; - push.apply( this, elems ); - - return this; - }, - - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return jQuery.each( this, callback, args ); - }, - - ready: function( fn ) { - // Attach the listeners - jQuery.bindReady(); - - // If the DOM is already ready - if ( jQuery.isReady ) { - // Execute the function immediately - fn.call( document, jQuery ); - - // Otherwise, remember the function for later - } else if ( readyList ) { - // Add the function to the wait list - readyList.push( fn ); - } - - return this; - }, - - eq: function( i ) { - return i === -1 ? - this.slice( i ) : - this.slice( i, +i + 1 ); - }, - - first: function() { - return this.eq( 0 ); - }, - - last: function() { - return this.eq( -1 ); - }, - - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, - - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - - end: function() { - return this.prevObject || jQuery(null); - }, - - // For internal use only. - // Behaves like an Array's method, not like a jQuery method. - push: push, - sort: [].sort, - splice: [].splice -}; - -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - -jQuery.extend = jQuery.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !jQuery.isFunction(target) ) { - target = {}; - } - - // extend jQuery itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging object literal values or arrays - if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) { - var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src - : jQuery.isArray(copy) ? [] : {}; - - // Never move original objects, clone them - target[ name ] = jQuery.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -jQuery.extend({ - noConflict: function( deep ) { - window.$ = _$; - - if ( deep ) { - window.jQuery = _jQuery; - } - - return jQuery; - }, - - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // Handle when the DOM is ready - ready: function() { - // Make sure that the DOM is not already loaded - if ( !jQuery.isReady ) { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( !document.body ) { - return setTimeout( jQuery.ready, 13 ); - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If there are functions bound, to execute - if ( readyList ) { - // Execute all of them - var fn, i = 0; - while ( (fn = readyList[ i++ ]) ) { - fn.call( document, jQuery ); - } - - // Reset the list of functions - readyList = null; - } - - // Trigger any bound ready events - if ( jQuery.fn.triggerHandler ) { - jQuery( document ).triggerHandler( "ready" ); - } - } - }, - - bindReady: function() { - if ( readyBound ) { - return; - } - - readyBound = true; - - // Catch cases where $(document).ready() is called after the - // browser event has already occurred. - if ( document.readyState === "complete" ) { - return jQuery.ready(); - } - - // Mozilla, Opera and webkit nightlies currently support this event - if ( document.addEventListener ) { - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", jQuery.ready, false ); - - // If IE event model is used - } else if ( document.attachEvent ) { - // ensure firing before onload, - // maybe late but safe also for iframes - document.attachEvent("onreadystatechange", DOMContentLoaded); - - // A fallback to window.onload, that will always work - window.attachEvent( "onload", jQuery.ready ); - - // If IE and not a frame - // continually check to see if the document is ready - var toplevel = false; - - try { - toplevel = window.frameElement == null; - } catch(e) {} - - if ( document.documentElement.doScroll && toplevel ) { - doScrollCheck(); - } - } - }, - - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { - return false; - } - - // Not own constructor property must be Object - if ( obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwnProperty.call( obj, key ); - }, - - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - error: function( msg ) { - throw msg; - }, - - parseJSON: function( data ) { - if ( typeof data !== "string" || !data ) { - return null; - } - - // Make sure the incoming data is actual JSON - // Logic borrowed from http://json.org/json2.js - if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@") - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]") - .replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) { - - // Try to use the native JSON parser first - return window.JSON && window.JSON.parse ? - window.JSON.parse( data ) : - (new Function("return " + data))(); - - } else { - jQuery.error( "Invalid JSON: " + data ); - } - }, - - noop: function() {}, - - // Evalulates a script in a global context - globalEval: function( data ) { - if ( data && rnotwhite.test(data) ) { - // Inspired by code by Andrea Giammarchi - // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html - var head = document.getElementsByTagName("head")[0] || document.documentElement, - script = document.createElement("script"); - - script.type = "text/javascript"; - - if ( jQuery.support.scriptEval ) { - script.appendChild( document.createTextNode( data ) ); - } else { - script.text = data; - } - - // Use insertBefore instead of appendChild to circumvent an IE6 bug. - // This arises when a base node is used (#2709). - head.insertBefore( script, head.firstChild ); - head.removeChild( script ); - } - }, - - nodeName: function( elem, name ) { - return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); - }, - - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || jQuery.isFunction(object); - - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } - - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} - } - } - - return object; - }, - - trim: function( text ) { - return (text || "").replace( rtrim, "" ); - }, - - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { - push.call( ret, array ); - } else { - jQuery.merge( ret, array ); - } - } - - return ret; - }, - - inArray: function( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; - }, - - merge: function( first, second ) { - var i = first.length, j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - grep: function( elems, callback, inv ) { - var ret = []; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - if ( !inv !== !callback( elems[ i ], i ) ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - - // arg is for internal usage only - map: function( elems, callback, arg ) { - var ret = [], value; - - // Go through the array, translating each of the items to their - // new value (or values). - for ( var i = 0, length = elems.length; i < length; i++ ) { - value = callback( elems[ i ], i, arg ); - - if ( value != null ) { - ret[ ret.length ] = value; - } - } - - return ret.concat.apply( [], ret ); - }, - - // A global GUID counter for objects - guid: 1, - - proxy: function( fn, proxy, thisObject ) { - if ( arguments.length === 2 ) { - if ( typeof proxy === "string" ) { - thisObject = fn; - fn = thisObject[ proxy ]; - proxy = undefined; - - } else if ( proxy && !jQuery.isFunction( proxy ) ) { - thisObject = proxy; - proxy = undefined; - } - } - - if ( !proxy && fn ) { - proxy = function() { - return fn.apply( thisObject || this, arguments ); - }; - } - - // Set the guid of unique handler to the same of original handler, so it can be removed - if ( fn ) { - proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; - } - - // So proxy can be declared as an argument - return proxy; - }, - - // Use of jQuery.browser is frowned upon. - // More details: http://docs.jquery.com/Utilities/jQuery.browser - uaMatch: function( ua ) { - ua = ua.toLowerCase(); - - var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) || - /(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) || - /(msie) ([\w.]+)/.exec( ua ) || - !/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) || - []; - - return { browser: match[1] || "", version: match[2] || "0" }; - }, - - browser: {} -}); - -browserMatch = jQuery.uaMatch( userAgent ); -if ( browserMatch.browser ) { - jQuery.browser[ browserMatch.browser ] = true; - jQuery.browser.version = browserMatch.version; -} - -// Deprecated, use jQuery.browser.webkit instead -if ( jQuery.browser.webkit ) { - jQuery.browser.safari = true; -} - -if ( indexOf ) { - jQuery.inArray = function( elem, array ) { - return indexOf.call( array, elem ); - }; -} - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); - -// Cleanup functions for the document ready method -if ( document.addEventListener ) { - DOMContentLoaded = function() { - document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); - jQuery.ready(); - }; - -} else if ( document.attachEvent ) { - DOMContentLoaded = function() { - // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). - if ( document.readyState === "complete" ) { - document.detachEvent( "onreadystatechange", DOMContentLoaded ); - jQuery.ready(); - } - }; -} - -// The DOM ready check for Internet Explorer -function doScrollCheck() { - if ( jQuery.isReady ) { - return; - } - - try { - // If IE is used, use the trick by Diego Perini - // http://javascript.nwbox.com/IEContentLoaded/ - document.documentElement.doScroll("left"); - } catch( error ) { - setTimeout( doScrollCheck, 1 ); - return; - } - - // and execute any waiting functions - jQuery.ready(); -} - -function evalScript( i, elem ) { - if ( elem.src ) { - jQuery.ajax({ - url: elem.src, - async: false, - dataType: "script" - }); - } else { - jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); - } - - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } -} - -// Mutifunctional method to get and set values to a collection -// The value/s can be optionally by executed if its a function -function access( elems, key, value, exec, fn, pass ) { - var length = elems.length; - - // Setting many attributes - if ( typeof key === "object" ) { - for ( var k in key ) { - access( elems, k, key[k], exec, fn, value ); - } - return elems; - } - - // Setting one attribute - if ( value !== undefined ) { - // Optionally, function values get executed if exec is true - exec = !pass && exec && jQuery.isFunction(value); - - for ( var i = 0; i < length; i++ ) { - fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); - } - - return elems; - } - - // Getting an attribute - return length ? fn( elems[0], key ) : null; -} - -function now() { - return (new Date).getTime(); -} -(function() { - - jQuery.support = {}; - - var root = document.documentElement, - script = document.createElement("script"), - div = document.createElement("div"), - id = "script" + now(); - - div.style.display = "none"; - div.innerHTML = "
    a"; - - var all = div.getElementsByTagName("*"), - a = div.getElementsByTagName("a")[0]; - - // Can't get basic test support - if ( !all || !all.length || !a ) { - return; - } - - jQuery.support = { - // IE strips leading whitespace when .innerHTML is used - leadingWhitespace: div.firstChild.nodeType === 3, - - // Make sure that tbody elements aren't automatically inserted - // IE will insert them into empty tables - tbody: !div.getElementsByTagName("tbody").length, - - // Make sure that link elements get serialized correctly by innerHTML - // This requires a wrapper element in IE - htmlSerialize: !!div.getElementsByTagName("link").length, - - // Get the style information from getAttribute - // (IE uses .cssText insted) - style: /red/.test( a.getAttribute("style") ), - - // Make sure that URLs aren't manipulated - // (IE normalizes it by default) - hrefNormalized: a.getAttribute("href") === "/a", - - // Make sure that element opacity exists - // (IE uses filter instead) - // Use a regex to work around a WebKit issue. See #5145 - opacity: /^0.55$/.test( a.style.opacity ), - - // Verify style float existence - // (IE uses styleFloat instead of cssFloat) - cssFloat: !!a.style.cssFloat, - - // Make sure that if no value is specified for a checkbox - // that it defaults to "on". - // (WebKit defaults to "" instead) - checkOn: div.getElementsByTagName("input")[0].value === "on", - - // Make sure that a selected-by-default option has a working selected property. - // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) - optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected, - - // Will be defined later - checkClone: false, - scriptEval: false, - noCloneEvent: true, - boxModel: null - }; - - script.type = "text/javascript"; - try { - script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); - } catch(e) {} - - root.insertBefore( script, root.firstChild ); - - // Make sure that the execution of code works by injecting a script - // tag with appendChild/createTextNode - // (IE doesn't support this, fails, and uses .text instead) - if ( window[ id ] ) { - jQuery.support.scriptEval = true; - delete window[ id ]; - } - - root.removeChild( script ); - - if ( div.attachEvent && div.fireEvent ) { - div.attachEvent("onclick", function click() { - // Cloning a node shouldn't copy over any - // bound event handlers (IE does this) - jQuery.support.noCloneEvent = false; - div.detachEvent("onclick", click); - }); - div.cloneNode(true).fireEvent("onclick"); - } - - div = document.createElement("div"); - div.innerHTML = ""; - - var fragment = document.createDocumentFragment(); - fragment.appendChild( div.firstChild ); - - // WebKit doesn't clone checked state correctly in fragments - jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked; - - // Figure out if the W3C box model works as expected - // document.body must exist before we can do this - jQuery(function() { - var div = document.createElement("div"); - div.style.width = div.style.paddingLeft = "1px"; - - document.body.appendChild( div ); - jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; - document.body.removeChild( div ).style.display = 'none'; - div = null; - }); - - // Technique from Juriy Zaytsev - // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ - var eventSupported = function( eventName ) { - var el = document.createElement("div"); - eventName = "on" + eventName; - - var isSupported = (eventName in el); - if ( !isSupported ) { - el.setAttribute(eventName, "return;"); - isSupported = typeof el[eventName] === "function"; - } - el = null; - - return isSupported; - }; - - jQuery.support.submitBubbles = eventSupported("submit"); - jQuery.support.changeBubbles = eventSupported("change"); - - // release memory in IE - root = script = div = all = a = null; -})(); - -jQuery.props = { - "for": "htmlFor", - "class": "className", - readonly: "readOnly", - maxlength: "maxLength", - cellspacing: "cellSpacing", - rowspan: "rowSpan", - colspan: "colSpan", - tabindex: "tabIndex", - usemap: "useMap", - frameborder: "frameBorder" -}; -var expando = "jQuery" + now(), uuid = 0, windowData = {}; -var emptyObject = {}; - -jQuery.extend({ - cache: {}, - - expando:expando, - - // The following elements throw uncatchable exceptions if you - // attempt to add expando properties to them. - noData: { - "embed": true, - "object": true, - "applet": true - }, - - data: function( elem, name, data ) { - if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { - return; - } - - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ], cache = jQuery.cache, thisCache; - - // Handle the case where there's no name immediately - if ( !name && !id ) { - return null; - } - - // Compute a unique ID for the element - if ( !id ) { - id = ++uuid; - } - - // Avoid generating a new cache unless none exists and we - // want to manipulate it. - if ( typeof name === "object" ) { - elem[ expando ] = id; - thisCache = cache[ id ] = jQuery.extend(true, {}, name); - } else if ( cache[ id ] ) { - thisCache = cache[ id ]; - } else if ( typeof data === "undefined" ) { - thisCache = emptyObject; - } else { - thisCache = cache[ id ] = {}; - } - - // Prevent overriding the named cache with undefined values - if ( data !== undefined ) { - elem[ expando ] = id; - thisCache[ name ] = data; - } - - return typeof name === "string" ? thisCache[ name ] : thisCache; - }, - - removeData: function( elem, name ) { - if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { - return; - } - - elem = elem == window ? - windowData : - elem; - - var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ]; - - // If we want to remove a specific section of the element's data - if ( name ) { - if ( thisCache ) { - // Remove the section of cache data - delete thisCache[ name ]; - - // If we've removed all the data, remove the element's cache - if ( jQuery.isEmptyObject(thisCache) ) { - jQuery.removeData( elem ); - } - } - - // Otherwise, we want to remove all of the element's data - } else { - // Clean up the element expando - try { - delete elem[ expando ]; - } catch( e ) { - // IE has trouble directly removing the expando - // but it's ok with using removeAttribute - if ( elem.removeAttribute ) { - elem.removeAttribute( expando ); - } - } - - // Completely remove the data cache - delete cache[ id ]; - } - } -}); - -jQuery.fn.extend({ - data: function( key, value ) { - if ( typeof key === "undefined" && this.length ) { - return jQuery.data( this[0] ); - - } else if ( typeof key === "object" ) { - return this.each(function() { - jQuery.data( this, key ); - }); - } - - var parts = key.split("."); - parts[1] = parts[1] ? "." + parts[1] : ""; - - if ( value === undefined ) { - var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); - - if ( data === undefined && this.length ) { - data = jQuery.data( this[0], key ); - } - return data === undefined && parts[1] ? - this.data( parts[0] ) : - data; - } else { - return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() { - jQuery.data( this, key, value ); - }); - } - }, - - removeData: function( key ) { - return this.each(function() { - jQuery.removeData( this, key ); - }); - } -}); -jQuery.extend({ - queue: function( elem, type, data ) { - if ( !elem ) { - return; - } - - type = (type || "fx") + "queue"; - var q = jQuery.data( elem, type ); - - // Speed up dequeue by getting out quickly if this is just a lookup - if ( !data ) { - return q || []; - } - - if ( !q || jQuery.isArray(data) ) { - q = jQuery.data( elem, type, jQuery.makeArray(data) ); - - } else { - q.push( data ); - } - - return q; - }, - - dequeue: function( elem, type ) { - type = type || "fx"; - - var queue = jQuery.queue( elem, type ), fn = queue.shift(); - - // If the fx queue is dequeued, always remove the progress sentinel - if ( fn === "inprogress" ) { - fn = queue.shift(); - } - - if ( fn ) { - // Add a progress sentinel to prevent the fx queue from being - // automatically dequeued - if ( type === "fx" ) { - queue.unshift("inprogress"); - } - - fn.call(elem, function() { - jQuery.dequeue(elem, type); - }); - } - } -}); - -jQuery.fn.extend({ - queue: function( type, data ) { - if ( typeof type !== "string" ) { - data = type; - type = "fx"; - } - - if ( data === undefined ) { - return jQuery.queue( this[0], type ); - } - return this.each(function( i, elem ) { - var queue = jQuery.queue( this, type, data ); - - if ( type === "fx" && queue[0] !== "inprogress" ) { - jQuery.dequeue( this, type ); - } - }); - }, - dequeue: function( type ) { - return this.each(function() { - jQuery.dequeue( this, type ); - }); - }, - - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[time] || time : time; - type = type || "fx"; - - return this.queue( type, function() { - var elem = this; - setTimeout(function() { - jQuery.dequeue( elem, type ); - }, time ); - }); - }, - - clearQueue: function( type ) { - return this.queue( type || "fx", [] ); - } -}); -var rclass = /[\n\t]/g, - rspace = /\s+/, - rreturn = /\r/g, - rspecialurl = /href|src|style/, - rtype = /(button|input)/i, - rfocusable = /(button|input|object|select|textarea)/i, - rclickable = /^(a|area)$/i, - rradiocheck = /radio|checkbox/; - -jQuery.fn.extend({ - attr: function( name, value ) { - return access( this, name, value, true, jQuery.attr ); - }, - - removeAttr: function( name, fn ) { - return this.each(function(){ - jQuery.attr( this, name, "" ); - if ( this.nodeType === 1 ) { - this.removeAttribute( name ); - } - }); - }, - - addClass: function( value ) { - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - self.addClass( value.call(this, i, self.attr("class")) ); - }); - } - - if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspace ); - - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - - if ( elem.nodeType === 1 ) { - if ( !elem.className ) { - elem.className = value; - - } else { - var className = " " + elem.className + " "; - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { - elem.className += " " + classNames[c]; - } - } - } - } - } - } - - return this; - }, - - removeClass: function( value ) { - if ( jQuery.isFunction(value) ) { - return this.each(function(i) { - var self = jQuery(this); - self.removeClass( value.call(this, i, self.attr("class")) ); - }); - } - - if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split(rspace); - - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - var className = (" " + elem.className + " ").replace(rclass, " "); - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[c] + " ", " "); - } - elem.className = className.substring(1, className.length - 1); - - } else { - elem.className = ""; - } - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value, isBool = typeof stateVal === "boolean"; - - if ( jQuery.isFunction( value ) ) { - return this.each(function(i) { - var self = jQuery(this); - self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, i = 0, self = jQuery(this), - state = stateVal, - classNames = value.split( rspace ); - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space seperated list - state = isBool ? state : !self.hasClass( className ); - self[ state ? "addClass" : "removeClass" ]( className ); - } - - } else if ( type === "undefined" || type === "boolean" ) { - if ( this.className ) { - // store className if set - jQuery.data( this, "__className__", this.className ); - } - - // toggle whole className - this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " "; - for ( var i = 0, l = this.length; i < l; i++ ) { - if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - if ( value === undefined ) { - var elem = this[0]; - - if ( elem ) { - if ( jQuery.nodeName( elem, "option" ) ) { - return (elem.attributes.value || {}).specified ? elem.value : elem.text; - } - - // We need to handle select boxes special - if ( jQuery.nodeName( elem, "select" ) ) { - var index = elem.selectedIndex, - values = [], - options = elem.options, - one = elem.type === "select-one"; - - // Nothing was selected - if ( index < 0 ) { - return null; - } - - // Loop through all the selected options - for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { - var option = options[ i ]; - - if ( option.selected ) { - // Get the specifc value for the option - value = jQuery(option).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - } - - // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified - if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) { - return elem.getAttribute("value") === null ? "on" : elem.value; - } - - - // Everything else, we just grab the value - return (elem.value || "").replace(rreturn, ""); - - } - - return undefined; - } - - var isFunction = jQuery.isFunction(value); - - return this.each(function(i) { - var self = jQuery(this), val = value; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call(this, i, self.val()); - } - - // Typecast each time if the value is a Function and the appended - // value is therefore different each time. - if ( typeof val === "number" ) { - val += ""; - } - - if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) { - this.checked = jQuery.inArray( self.val(), val ) >= 0; - - } else if ( jQuery.nodeName( this, "select" ) ) { - var values = jQuery.makeArray(val); - - jQuery( "option", this ).each(function() { - this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; - }); - - if ( !values.length ) { - this.selectedIndex = -1; - } - - } else { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - attrFn: { - val: true, - css: true, - html: true, - text: true, - data: true, - width: true, - height: true, - offset: true - }, - - attr: function( elem, name, value, pass ) { - // don't set attributes on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { - return undefined; - } - - if ( pass && name in jQuery.attrFn ) { - return jQuery(elem)[name](value); - } - - var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ), - // Whether we are setting (or getting) - set = value !== undefined; - - // Try to normalize/fix the name - name = notxml && jQuery.props[ name ] || name; - - // Only do all the following if this is a node (faster for style) - if ( elem.nodeType === 1 ) { - // These attributes require special treatment - var special = rspecialurl.test( name ); - - // Safari mis-reports the default selected property of an option - // Accessing the parent's selectedIndex property fixes it - if ( name === "selected" && !jQuery.support.optSelected ) { - var parent = elem.parentNode; - if ( parent ) { - parent.selectedIndex; - - // Make sure that it also works with optgroups, see #5701 - if ( parent.parentNode ) { - parent.parentNode.selectedIndex; - } - } - } - - // If applicable, access the attribute via the DOM 0 way - if ( name in elem && notxml && !special ) { - if ( set ) { - // We can't allow the type property to be changed (since it causes problems in IE) - if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) { - jQuery.error( "type property can't be changed" ); - } - - elem[ name ] = value; - } - - // browsers index elements by id/name on forms, give priority to attributes. - if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) { - return elem.getAttributeNode( name ).nodeValue; - } - - // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set - // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ - if ( name === "tabIndex" ) { - var attributeNode = elem.getAttributeNode( "tabIndex" ); - - return attributeNode && attributeNode.specified ? - attributeNode.value : - rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? - 0 : - undefined; - } - - return elem[ name ]; - } - - if ( !jQuery.support.style && notxml && name === "style" ) { - if ( set ) { - elem.style.cssText = "" + value; - } - - return elem.style.cssText; - } - - if ( set ) { - // convert the value to a string (all browsers do this but IE) see #1070 - elem.setAttribute( name, "" + value ); - } - - var attr = !jQuery.support.hrefNormalized && notxml && special ? - // Some attributes require a special call on IE - elem.getAttribute( name, 2 ) : - elem.getAttribute( name ); - - // Non-existent attributes return null, we normalize to undefined - return attr === null ? undefined : attr; - } - - // elem is actually elem.style ... set the style - // Using attr for specific style information is now deprecated. Use style insead. - return jQuery.style( elem, name, value ); - } -}); -var fcleanup = function( nm ) { - return nm.replace(/[^\w\s\.\|`]/g, function( ch ) { - return "\\" + ch; - }); -}; - -/* - * A number of helper functions used for managing events. - * Many of the ideas behind this code originated from - * Dean Edwards' addEvent library. - */ -jQuery.event = { - - // Bind an event to an element - // Original by Dean Edwards - add: function( elem, types, handler, data ) { - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - // For whatever reason, IE has trouble passing the window object - // around, causing it to be cloned in the process - if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) { - elem = window; - } - - // Make sure that the function being executed has a unique ID - if ( !handler.guid ) { - handler.guid = jQuery.guid++; - } - - // if data is passed, bind to handler - if ( data !== undefined ) { - // Create temporary function pointer to original handler - var fn = handler; - - // Create unique handler function, wrapped around original handler - handler = jQuery.proxy( fn ); - - // Store data in unique handler - handler.data = data; - } - - // Init the element's event structure - var events = jQuery.data( elem, "events" ) || jQuery.data( elem, "events", {} ), - handle = jQuery.data( elem, "handle" ), eventHandle; - - if ( !handle ) { - eventHandle = function() { - // Handle the second event of a trigger and when - // an event is called after a page has unloaded - return typeof jQuery !== "undefined" && !jQuery.event.triggered ? - jQuery.event.handle.apply( eventHandle.elem, arguments ) : - undefined; - }; - - handle = jQuery.data( elem, "handle", eventHandle ); - } - - // If no handle is found then we must be trying to bind to one of the - // banned noData elements - if ( !handle ) { - return; - } - - // Add elem as a property of the handle function - // This is to prevent a memory leak with non-native - // event in IE. - handle.elem = elem; - - // Handle multiple events separated by a space - // jQuery(...).bind("mouseover mouseout", fn); - types = types.split( /\s+/ ); - - var type, i = 0; - - while ( (type = types[ i++ ]) ) { - // Namespaced event handlers - var namespaces = type.split("."); - type = namespaces.shift(); - - if ( i > 1 ) { - handler = jQuery.proxy( handler ); - - if ( data !== undefined ) { - handler.data = data; - } - } - - handler.type = namespaces.slice(0).sort().join("."); - - // Get the current list of functions bound to this event - var handlers = events[ type ], - special = this.special[ type ] || {}; - - // Init the event handler queue - if ( !handlers ) { - handlers = events[ type ] = {}; - - // Check for a special event handler - // Only use addEventListener/attachEvent if the special - // events handler returns false - if ( !special.setup || special.setup.call( elem, data, namespaces, handler) === false ) { - // Bind the global event handler to the element - if ( elem.addEventListener ) { - elem.addEventListener( type, handle, false ); - } else if ( elem.attachEvent ) { - elem.attachEvent( "on" + type, handle ); - } - } - } - - if ( special.add ) { - var modifiedHandler = special.add.call( elem, handler, data, namespaces, handlers ); - if ( modifiedHandler && jQuery.isFunction( modifiedHandler ) ) { - modifiedHandler.guid = modifiedHandler.guid || handler.guid; - modifiedHandler.data = modifiedHandler.data || handler.data; - modifiedHandler.type = modifiedHandler.type || handler.type; - handler = modifiedHandler; - } - } - - // Add the function to the element's handler list - handlers[ handler.guid ] = handler; - - // Keep track of which events have been used, for global triggering - this.global[ type ] = true; - } - - // Nullify elem to prevent memory leaks in IE - elem = null; - }, - - global: {}, - - // Detach an event or set of events from an element - remove: function( elem, types, handler ) { - // don't do events on text and comment nodes - if ( elem.nodeType === 3 || elem.nodeType === 8 ) { - return; - } - - var events = jQuery.data( elem, "events" ), ret, type, fn; - - if ( events ) { - // Unbind all events for the element - if ( types === undefined || (typeof types === "string" && types.charAt(0) === ".") ) { - for ( type in events ) { - this.remove( elem, type + (types || "") ); - } - } else { - // types is actually an event object here - if ( types.type ) { - handler = types.handler; - types = types.type; - } - - // Handle multiple events separated by a space - // jQuery(...).unbind("mouseover mouseout", fn); - types = types.split(/\s+/); - var i = 0; - while ( (type = types[ i++ ]) ) { - // Namespaced event handlers - var namespaces = type.split("."); - type = namespaces.shift(); - var all = !namespaces.length, - cleaned = jQuery.map( namespaces.slice(0).sort(), fcleanup ), - namespace = new RegExp("(^|\\.)" + cleaned.join("\\.(?:.*\\.)?") + "(\\.|$)"), - special = this.special[ type ] || {}; - - if ( events[ type ] ) { - // remove the given handler for the given type - if ( handler ) { - fn = events[ type ][ handler.guid ]; - delete events[ type ][ handler.guid ]; - - // remove all handlers for the given type - } else { - for ( var handle in events[ type ] ) { - // Handle the removal of namespaced events - if ( all || namespace.test( events[ type ][ handle ].type ) ) { - delete events[ type ][ handle ]; - } - } - } - - if ( special.remove ) { - special.remove.call( elem, namespaces, fn); - } - - // remove generic event handler if no more handlers exist - for ( ret in events[ type ] ) { - break; - } - if ( !ret ) { - if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { - if ( elem.removeEventListener ) { - elem.removeEventListener( type, jQuery.data( elem, "handle" ), false ); - } else if ( elem.detachEvent ) { - elem.detachEvent( "on" + type, jQuery.data( elem, "handle" ) ); - } - } - ret = null; - delete events[ type ]; - } - } - } - } - - // Remove the expando if it's no longer used - for ( ret in events ) { - break; - } - if ( !ret ) { - var handle = jQuery.data( elem, "handle" ); - if ( handle ) { - handle.elem = null; - } - jQuery.removeData( elem, "events" ); - jQuery.removeData( elem, "handle" ); - } - } - }, - - // bubbling is internal - trigger: function( event, data, elem /*, bubbling */ ) { - // Event object or event type - var type = event.type || event, - bubbling = arguments[3]; - - if ( !bubbling ) { - event = typeof event === "object" ? - // jQuery.Event object - event[expando] ? event : - // Object literal - jQuery.extend( jQuery.Event(type), event ) : - // Just the event type (string) - jQuery.Event(type); - - if ( type.indexOf("!") >= 0 ) { - event.type = type = type.slice(0, -1); - event.exclusive = true; - } - - // Handle a global trigger - if ( !elem ) { - // Don't bubble custom events when global (to avoid too much overhead) - event.stopPropagation(); - - // Only trigger if we've ever bound an event for it - if ( this.global[ type ] ) { - jQuery.each( jQuery.cache, function() { - if ( this.events && this.events[type] ) { - jQuery.event.trigger( event, data, this.handle.elem ); - } - }); - } - } - - // Handle triggering a single element - - // don't do events on text and comment nodes - if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) { - return undefined; - } - - // Clean up in case it is reused - event.result = undefined; - event.target = elem; - - // Clone the incoming data, if any - data = jQuery.makeArray( data ); - data.unshift( event ); - } - - event.currentTarget = elem; - - // Trigger the event, it is assumed that "handle" is a function - var handle = jQuery.data( elem, "handle" ); - if ( handle ) { - handle.apply( elem, data ); - } - - var parent = elem.parentNode || elem.ownerDocument; - - // Trigger an inline bound script - try { - if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) { - if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) { - event.result = false; - } - } - - // prevent IE from throwing an error for some elements with some event types, see #3533 - } catch (e) {} - - if ( !event.isPropagationStopped() && parent ) { - jQuery.event.trigger( event, data, parent, true ); - - } else if ( !event.isDefaultPrevented() ) { - var target = event.target, old, - isClick = jQuery.nodeName(target, "a") && type === "click"; - - if ( !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) { - try { - if ( target[ type ] ) { - // Make sure that we don't accidentally re-trigger the onFOO events - old = target[ "on" + type ]; - - if ( old ) { - target[ "on" + type ] = null; - } - - this.triggered = true; - target[ type ](); - } - - // prevent IE from throwing an error for some elements with some event types, see #3533 - } catch (e) {} - - if ( old ) { - target[ "on" + type ] = old; - } - - this.triggered = false; - } - } - }, - - handle: function( event ) { - // returned undefined or false - var all, handlers; - - event = arguments[0] = jQuery.event.fix( event || window.event ); - event.currentTarget = this; - - // Namespaced event handlers - var namespaces = event.type.split("."); - event.type = namespaces.shift(); - - // Cache this now, all = true means, any handler - all = !namespaces.length && !event.exclusive; - - var namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)"); - - handlers = ( jQuery.data(this, "events") || {} )[ event.type ]; - - for ( var j in handlers ) { - var handler = handlers[ j ]; - - // Filter the functions by class - if ( all || namespace.test(handler.type) ) { - // Pass in a reference to the handler function itself - // So that we can later remove it - event.handler = handler; - event.data = handler.data; - - var ret = handler.apply( this, arguments ); - - if ( ret !== undefined ) { - event.result = ret; - if ( ret === false ) { - event.preventDefault(); - event.stopPropagation(); - } - } - - if ( event.isImmediatePropagationStopped() ) { - break; - } - - } - } - - return event.result; - }, - - props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), - - fix: function( event ) { - if ( event[ expando ] ) { - return event; - } - - // store a copy of the original event object - // and "clone" to set read-only properties - var originalEvent = event; - event = jQuery.Event( originalEvent ); - - for ( var i = this.props.length, prop; i; ) { - prop = this.props[ --i ]; - event[ prop ] = originalEvent[ prop ]; - } - - // Fix target property, if necessary - if ( !event.target ) { - event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either - } - - // check if target is a textnode (safari) - if ( event.target.nodeType === 3 ) { - event.target = event.target.parentNode; - } - - // Add relatedTarget, if necessary - if ( !event.relatedTarget && event.fromElement ) { - event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement; - } - - // Calculate pageX/Y if missing and clientX/Y available - if ( event.pageX == null && event.clientX != null ) { - var doc = document.documentElement, body = document.body; - event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0); - event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0); - } - - // Add which for key events - if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) { - event.which = event.charCode || event.keyCode; - } - - // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) - if ( !event.metaKey && event.ctrlKey ) { - event.metaKey = event.ctrlKey; - } - - // Add which for click: 1 === left; 2 === middle; 3 === right - // Note: button is not normalized, so don't use it - if ( !event.which && event.button !== undefined ) { - event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); - } - - return event; - }, - - // Deprecated, use jQuery.guid instead - guid: 1E8, - - // Deprecated, use jQuery.proxy instead - proxy: jQuery.proxy, - - special: { - ready: { - // Make sure the ready event is setup - setup: jQuery.bindReady, - teardown: jQuery.noop - }, - - live: { - add: function( proxy, data, namespaces, live ) { - jQuery.extend( proxy, data || {} ); - - proxy.guid += data.selector + data.live; - data.liveProxy = proxy; - - jQuery.event.add( this, data.live, liveHandler, data ); - - }, - - remove: function( namespaces ) { - if ( namespaces.length ) { - var remove = 0, name = new RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)"); - - jQuery.each( (jQuery.data(this, "events").live || {}), function() { - if ( name.test(this.type) ) { - remove++; - } - }); - - if ( remove < 1 ) { - jQuery.event.remove( this, namespaces[0], liveHandler ); - } - } - }, - special: {} - }, - beforeunload: { - setup: function( data, namespaces, fn ) { - // We only want to do this special case on windows - if ( this.setInterval ) { - this.onbeforeunload = fn; - } - - return false; - }, - teardown: function( namespaces, fn ) { - if ( this.onbeforeunload === fn ) { - this.onbeforeunload = null; - } - } - } - } -}; - -jQuery.Event = function( src ) { - // Allow instantiation without the 'new' keyword - if ( !this.preventDefault ) { - return new jQuery.Event( src ); - } - - // Event object - if ( src && src.type ) { - this.originalEvent = src; - this.type = src.type; - // Event type - } else { - this.type = src; - } - - // timeStamp is buggy for some events on Firefox(#3843) - // So we won't rely on the native value - this.timeStamp = now(); - - // Mark it as fixed - this[ expando ] = true; -}; - -function returnFalse() { - return false; -} -function returnTrue() { - return true; -} - -// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding -// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html -jQuery.Event.prototype = { - preventDefault: function() { - this.isDefaultPrevented = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - - // if preventDefault exists run it on the original event - if ( e.preventDefault ) { - e.preventDefault(); - } - // otherwise set the returnValue property of the original event to false (IE) - e.returnValue = false; - }, - stopPropagation: function() { - this.isPropagationStopped = returnTrue; - - var e = this.originalEvent; - if ( !e ) { - return; - } - // if stopPropagation exists run it on the original event - if ( e.stopPropagation ) { - e.stopPropagation(); - } - // otherwise set the cancelBubble property of the original event to true (IE) - e.cancelBubble = true; - }, - stopImmediatePropagation: function() { - this.isImmediatePropagationStopped = returnTrue; - this.stopPropagation(); - }, - isDefaultPrevented: returnFalse, - isPropagationStopped: returnFalse, - isImmediatePropagationStopped: returnFalse -}; - -// Checks if an event happened on an element within another element -// Used in jQuery.event.special.mouseenter and mouseleave handlers -var withinElement = function( event ) { - // Check if mouse(over|out) are still within the same parent element - var parent = event.relatedTarget; - - // Traverse up the tree - while ( parent && parent !== this ) { - // Firefox sometimes assigns relatedTarget a XUL element - // which we cannot access the parentNode property of - try { - parent = parent.parentNode; - - // assuming we've left the element since we most likely mousedover a xul element - } catch(e) { - break; - } - } - - if ( parent !== this ) { - // set the correct event type - event.type = event.data; - - // handle event if we actually just moused on to a non sub-element - jQuery.event.handle.apply( this, arguments ); - } - -}, - -// In case of event delegation, we only need to rename the event.type, -// liveHandler will take care of the rest. -delegate = function( event ) { - event.type = event.data; - jQuery.event.handle.apply( this, arguments ); -}; - -// Create mouseenter and mouseleave events -jQuery.each({ - mouseenter: "mouseover", - mouseleave: "mouseout" -}, function( orig, fix ) { - jQuery.event.special[ orig ] = { - setup: function( data ) { - jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig ); - }, - teardown: function( data ) { - jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement ); - } - }; -}); - -// submit delegation -if ( !jQuery.support.submitBubbles ) { - -jQuery.event.special.submit = { - setup: function( data, namespaces, fn ) { - if ( this.nodeName.toLowerCase() !== "form" ) { - jQuery.event.add(this, "click.specialSubmit." + fn.guid, function( e ) { - var elem = e.target, type = elem.type; - - if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) { - return trigger( "submit", this, arguments ); - } - }); - - jQuery.event.add(this, "keypress.specialSubmit." + fn.guid, function( e ) { - var elem = e.target, type = elem.type; - - if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) { - return trigger( "submit", this, arguments ); - } - }); - - } else { - return false; - } - }, - - remove: function( namespaces, fn ) { - jQuery.event.remove( this, "click.specialSubmit" + (fn ? "."+fn.guid : "") ); - jQuery.event.remove( this, "keypress.specialSubmit" + (fn ? "."+fn.guid : "") ); - } -}; - -} - -// change delegation, happens here so we have bind. -if ( !jQuery.support.changeBubbles ) { - -var formElems = /textarea|input|select/i; - -function getVal( elem ) { - var type = elem.type, val = elem.value; - - if ( type === "radio" || type === "checkbox" ) { - val = elem.checked; - - } else if ( type === "select-multiple" ) { - val = elem.selectedIndex > -1 ? - jQuery.map( elem.options, function( elem ) { - return elem.selected; - }).join("-") : - ""; - - } else if ( elem.nodeName.toLowerCase() === "select" ) { - val = elem.selectedIndex; - } - - return val; -} - -function testChange( e ) { - var elem = e.target, data, val; - - if ( !formElems.test( elem.nodeName ) || elem.readOnly ) { - return; - } - - data = jQuery.data( elem, "_change_data" ); - val = getVal(elem); - - // the current data will be also retrieved by beforeactivate - if ( e.type !== "focusout" || elem.type !== "radio" ) { - jQuery.data( elem, "_change_data", val ); - } - - if ( data === undefined || val === data ) { - return; - } - - if ( data != null || val ) { - e.type = "change"; - return jQuery.event.trigger( e, arguments[1], elem ); - } -} - -jQuery.event.special.change = { - filters: { - focusout: testChange, - - click: function( e ) { - var elem = e.target, type = elem.type; - - if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) { - return testChange.call( this, e ); - } - }, - - // Change has to be called before submit - // Keydown will be called before keypress, which is used in submit-event delegation - keydown: function( e ) { - var elem = e.target, type = elem.type; - - if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") || - (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || - type === "select-multiple" ) { - return testChange.call( this, e ); - } - }, - - // Beforeactivate happens also before the previous element is blurred - // with this event you can't trigger a change event, but you can store - // information/focus[in] is not needed anymore - beforeactivate: function( e ) { - var elem = e.target; - - if ( elem.nodeName.toLowerCase() === "input" && elem.type === "radio" ) { - jQuery.data( elem, "_change_data", getVal(elem) ); - } - } - }, - setup: function( data, namespaces, fn ) { - for ( var type in changeFilters ) { - jQuery.event.add( this, type + ".specialChange." + fn.guid, changeFilters[type] ); - } - - return formElems.test( this.nodeName ); - }, - remove: function( namespaces, fn ) { - for ( var type in changeFilters ) { - jQuery.event.remove( this, type + ".specialChange" + (fn ? "."+fn.guid : ""), changeFilters[type] ); - } - - return formElems.test( this.nodeName ); - } -}; - -var changeFilters = jQuery.event.special.change.filters; - -} - -function trigger( type, elem, args ) { - args[0].type = type; - return jQuery.event.handle.apply( elem, args ); -} - -// Create "bubbling" focus and blur events -if ( document.addEventListener ) { - jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - jQuery.event.special[ fix ] = { - setup: function() { - this.addEventListener( orig, handler, true ); - }, - teardown: function() { - this.removeEventListener( orig, handler, true ); - } - }; - - function handler( e ) { - e = jQuery.event.fix( e ); - e.type = fix; - return jQuery.event.handle.call( this, e ); - } - }); -} - -jQuery.each(["bind", "one"], function( i, name ) { - jQuery.fn[ name ] = function( type, data, fn ) { - // Handle object literals - if ( typeof type === "object" ) { - for ( var key in type ) { - this[ name ](key, data, type[key], fn); - } - return this; - } - - if ( jQuery.isFunction( data ) ) { - fn = data; - data = undefined; - } - - var handler = name === "one" ? jQuery.proxy( fn, function( event ) { - jQuery( this ).unbind( event, handler ); - return fn.apply( this, arguments ); - }) : fn; - - return type === "unload" && name !== "one" ? - this.one( type, data, fn ) : - this.each(function() { - jQuery.event.add( this, type, handler, data ); - }); - }; -}); - -jQuery.fn.extend({ - unbind: function( type, fn ) { - // Handle object literals - if ( typeof type === "object" && !type.preventDefault ) { - for ( var key in type ) { - this.unbind(key, type[key]); - } - return this; - } - - return this.each(function() { - jQuery.event.remove( this, type, fn ); - }); - }, - trigger: function( type, data ) { - return this.each(function() { - jQuery.event.trigger( type, data, this ); - }); - }, - - triggerHandler: function( type, data ) { - if ( this[0] ) { - var event = jQuery.Event( type ); - event.preventDefault(); - event.stopPropagation(); - jQuery.event.trigger( event, data, this[0] ); - return event.result; - } - }, - - toggle: function( fn ) { - // Save reference to arguments for access in closure - var args = arguments, i = 1; - - // link all the functions, so any of them can unbind this click handler - while ( i < args.length ) { - jQuery.proxy( fn, args[ i++ ] ); - } - - return this.click( jQuery.proxy( fn, function( event ) { - // Figure out which function to execute - var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i; - jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 ); - - // Make sure that clicks stop - event.preventDefault(); - - // and execute the function - return args[ lastToggle ].apply( this, arguments ) || false; - })); - }, - - hover: function( fnOver, fnOut ) { - return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); - } -}); - -jQuery.each(["live", "die"], function( i, name ) { - jQuery.fn[ name ] = function( types, data, fn ) { - var type, i = 0; - - if ( jQuery.isFunction( data ) ) { - fn = data; - data = undefined; - } - - types = (types || "").split( /\s+/ ); - - while ( (type = types[ i++ ]) != null ) { - type = type === "focus" ? "focusin" : // focus --> focusin - type === "blur" ? "focusout" : // blur --> focusout - type === "hover" ? types.push("mouseleave") && "mouseenter" : // hover support - type; - - if ( name === "live" ) { - // bind live handler - jQuery( this.context ).bind( liveConvert( type, this.selector ), { - data: data, selector: this.selector, live: type - }, fn ); - - } else { - // unbind live handler - jQuery( this.context ).unbind( liveConvert( type, this.selector ), fn ? { guid: fn.guid + this.selector + type } : null ); - } - } - - return this; - } -}); - -function liveHandler( event ) { - var stop, elems = [], selectors = [], args = arguments, - related, match, fn, elem, j, i, l, data, - live = jQuery.extend({}, jQuery.data( this, "events" ).live); - - // Make sure we avoid non-left-click bubbling in Firefox (#3861) - if ( event.button && event.type === "click" ) { - return; - } - - for ( j in live ) { - fn = live[j]; - if ( fn.live === event.type || - fn.altLive && jQuery.inArray(event.type, fn.altLive) > -1 ) { - - data = fn.data; - if ( !(data.beforeFilter && data.beforeFilter[event.type] && - !data.beforeFilter[event.type](event)) ) { - selectors.push( fn.selector ); - } - } else { - delete live[j]; - } - } - - match = jQuery( event.target ).closest( selectors, event.currentTarget ); - - for ( i = 0, l = match.length; i < l; i++ ) { - for ( j in live ) { - fn = live[j]; - elem = match[i].elem; - related = null; - - if ( match[i].selector === fn.selector ) { - // Those two events require additional checking - if ( fn.live === "mouseenter" || fn.live === "mouseleave" ) { - related = jQuery( event.relatedTarget ).closest( fn.selector )[0]; - } - - if ( !related || related !== elem ) { - elems.push({ elem: elem, fn: fn }); - } - } - } - } - - for ( i = 0, l = elems.length; i < l; i++ ) { - match = elems[i]; - event.currentTarget = match.elem; - event.data = match.fn.data; - if ( match.fn.apply( match.elem, args ) === false ) { - stop = false; - break; - } - } - - return stop; -} - -function liveConvert( type, selector ) { - return "live." + (type ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&"); -} - -jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + - "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + - "change select submit keydown keypress keyup error").split(" "), function( i, name ) { - - // Handle event binding - jQuery.fn[ name ] = function( fn ) { - return fn ? this.bind( name, fn ) : this.trigger( name ); - }; - - if ( jQuery.attrFn ) { - jQuery.attrFn[ name ] = true; - } -}); - -// Prevent memory leaks in IE -// Window isn't included so as not to unbind existing unload events -// More info: -// - http://isaacschlueter.com/2006/10/msie-memory-leaks/ -if ( window.attachEvent && !window.addEventListener ) { - window.attachEvent("onunload", function() { - for ( var id in jQuery.cache ) { - if ( jQuery.cache[ id ].handle ) { - // Try/Catch is to handle iframes being unloaded, see #4280 - try { - jQuery.event.remove( jQuery.cache[ id ].handle.elem ); - } catch(e) {} - } - } - }); -} -/*! - * Sizzle CSS Selector Engine - v1.0 - * Copyright 2009, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * More information: http://sizzlejs.com/ - */ -(function(){ - -var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, - done = 0, - toString = Object.prototype.toString, - hasDuplicate = false, - baseHasDuplicate = true; - -// Here we check if the JavaScript engine is using some sort of -// optimization where it does not always call our comparision -// function. If that is the case, discard the hasDuplicate value. -// Thus far that includes Google Chrome. -[0, 0].sort(function(){ - baseHasDuplicate = false; - return 0; -}); - -var Sizzle = function(selector, context, results, seed) { - results = results || []; - var origContext = context = context || document; - - if ( context.nodeType !== 1 && context.nodeType !== 9 ) { - return []; - } - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context), - soFar = selector; - - // Reset the position of the chunker regexp (start from head) - while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { - soFar = m[3]; - - parts.push( m[1] ); - - if ( m[2] ) { - extra = m[3]; - break; - } - } - - if ( parts.length > 1 && origPOS.exec( selector ) ) { - if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { - set = posProcess( parts[0] + parts[1], context ); - } else { - set = Expr.relative[ parts[0] ] ? - [ context ] : - Sizzle( parts.shift(), context ); - - while ( parts.length ) { - selector = parts.shift(); - - if ( Expr.relative[ selector ] ) { - selector += parts.shift(); - } - - set = posProcess( selector, set ); - } - } - } else { - // Take a shortcut and set the context if the root selector is an ID - // (but not if it'll be faster if the inner selector is an ID) - if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && - Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { - var ret = Sizzle.find( parts.shift(), context, contextXML ); - context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; - } - - if ( context ) { - var ret = seed ? - { expr: parts.pop(), set: makeArray(seed) } : - Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); - set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; - - if ( parts.length > 0 ) { - checkSet = makeArray(set); - } else { - prune = false; - } - - while ( parts.length ) { - var cur = parts.pop(), pop = cur; - - if ( !Expr.relative[ cur ] ) { - cur = ""; - } else { - pop = parts.pop(); - } - - if ( pop == null ) { - pop = context; - } - - Expr.relative[ cur ]( checkSet, pop, contextXML ); - } - } else { - checkSet = parts = []; - } - } - - if ( !checkSet ) { - checkSet = set; - } - - if ( !checkSet ) { - Sizzle.error( cur || selector ); - } - - if ( toString.call(checkSet) === "[object Array]" ) { - if ( !prune ) { - results.push.apply( results, checkSet ); - } else if ( context && context.nodeType === 1 ) { - for ( var i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { - results.push( set[i] ); - } - } - } else { - for ( var i = 0; checkSet[i] != null; i++ ) { - if ( checkSet[i] && checkSet[i].nodeType === 1 ) { - results.push( set[i] ); - } - } - } - } else { - makeArray( checkSet, results ); - } - - if ( extra ) { - Sizzle( extra, origContext, results, seed ); - Sizzle.uniqueSort( results ); - } - - return results; -}; - -Sizzle.uniqueSort = function(results){ - if ( sortOrder ) { - hasDuplicate = baseHasDuplicate; - results.sort(sortOrder); - - if ( hasDuplicate ) { - for ( var i = 1; i < results.length; i++ ) { - if ( results[i] === results[i-1] ) { - results.splice(i--, 1); - } - } - } - } - - return results; -}; - -Sizzle.matches = function(expr, set){ - return Sizzle(expr, null, null, set); -}; - -Sizzle.find = function(expr, context, isXML){ - var set, match; - - if ( !expr ) { - return []; - } - - for ( var i = 0, l = Expr.order.length; i < l; i++ ) { - var type = Expr.order[i], match; - - if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { - var left = match[1]; - match.splice(1,1); - - if ( left.substr( left.length - 1 ) !== "\\" ) { - match[1] = (match[1] || "").replace(/\\/g, ""); - set = Expr.find[ type ]( match, context, isXML ); - if ( set != null ) { - expr = expr.replace( Expr.match[ type ], "" ); - break; - } - } - } - } - - if ( !set ) { - set = context.getElementsByTagName("*"); - } - - return {set: set, expr: expr}; -}; - -Sizzle.filter = function(expr, set, inplace, not){ - var old = expr, result = [], curLoop = set, match, anyFound, - isXMLFilter = set && set[0] && isXML(set[0]); - - while ( expr && set.length ) { - for ( var type in Expr.filter ) { - if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { - var filter = Expr.filter[ type ], found, item, left = match[1]; - anyFound = false; - - match.splice(1,1); - - if ( left.substr( left.length - 1 ) === "\\" ) { - continue; - } - - if ( curLoop === result ) { - result = []; - } - - if ( Expr.preFilter[ type ] ) { - match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); - - if ( !match ) { - anyFound = found = true; - } else if ( match === true ) { - continue; - } - } - - if ( match ) { - for ( var i = 0; (item = curLoop[i]) != null; i++ ) { - if ( item ) { - found = filter( item, match, i, curLoop ); - var pass = not ^ !!found; - - if ( inplace && found != null ) { - if ( pass ) { - anyFound = true; - } else { - curLoop[i] = false; - } - } else if ( pass ) { - result.push( item ); - anyFound = true; - } - } - } - } - - if ( found !== undefined ) { - if ( !inplace ) { - curLoop = result; - } - - expr = expr.replace( Expr.match[ type ], "" ); - - if ( !anyFound ) { - return []; - } - - break; - } - } - } - - // Improper expression - if ( expr === old ) { - if ( anyFound == null ) { - Sizzle.error( expr ); - } else { - break; - } - } - - old = expr; - } - - return curLoop; -}; - -Sizzle.error = function( msg ) { - throw "Syntax error, unrecognized expression: " + msg; -}; - -var Expr = Sizzle.selectors = { - order: [ "ID", "NAME", "TAG" ], - match: { - ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, - CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, - NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, - ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, - TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, - CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, - POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, - PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ - }, - leftMatch: {}, - attrMap: { - "class": "className", - "for": "htmlFor" - }, - attrHandle: { - href: function(elem){ - return elem.getAttribute("href"); - } - }, - relative: { - "+": function(checkSet, part){ - var isPartStr = typeof part === "string", - isTag = isPartStr && !/\W/.test(part), - isPartStrNotTag = isPartStr && !isTag; - - if ( isTag ) { - part = part.toLowerCase(); - } - - for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { - if ( (elem = checkSet[i]) ) { - while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} - - checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? - elem || false : - elem === part; - } - } - - if ( isPartStrNotTag ) { - Sizzle.filter( part, checkSet, true ); - } - }, - ">": function(checkSet, part){ - var isPartStr = typeof part === "string"; - - if ( isPartStr && !/\W/.test(part) ) { - part = part.toLowerCase(); - - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - var parent = elem.parentNode; - checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; - } - } - } else { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - checkSet[i] = isPartStr ? - elem.parentNode : - elem.parentNode === part; - } - } - - if ( isPartStr ) { - Sizzle.filter( part, checkSet, true ); - } - } - }, - "": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; - - if ( typeof part === "string" && !/\W/.test(part) ) { - var nodeCheck = part = part.toLowerCase(); - checkFn = dirNodeCheck; - } - - checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); - }, - "~": function(checkSet, part, isXML){ - var doneName = done++, checkFn = dirCheck; - - if ( typeof part === "string" && !/\W/.test(part) ) { - var nodeCheck = part = part.toLowerCase(); - checkFn = dirNodeCheck; - } - - checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); - } - }, - find: { - ID: function(match, context, isXML){ - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - return m ? [m] : []; - } - }, - NAME: function(match, context){ - if ( typeof context.getElementsByName !== "undefined" ) { - var ret = [], results = context.getElementsByName(match[1]); - - for ( var i = 0, l = results.length; i < l; i++ ) { - if ( results[i].getAttribute("name") === match[1] ) { - ret.push( results[i] ); - } - } - - return ret.length === 0 ? null : ret; - } - }, - TAG: function(match, context){ - return context.getElementsByTagName(match[1]); - } - }, - preFilter: { - CLASS: function(match, curLoop, inplace, result, not, isXML){ - match = " " + match[1].replace(/\\/g, "") + " "; - - if ( isXML ) { - return match; - } - - for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { - if ( elem ) { - if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) { - if ( !inplace ) { - result.push( elem ); - } - } else if ( inplace ) { - curLoop[i] = false; - } - } - } - - return false; - }, - ID: function(match){ - return match[1].replace(/\\/g, ""); - }, - TAG: function(match, curLoop){ - return match[1].toLowerCase(); - }, - CHILD: function(match){ - if ( match[1] === "nth" ) { - // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' - var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( - match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || - !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); - - // calculate the numbers (first)n+(last) including if they are negative - match[2] = (test[1] + (test[2] || 1)) - 0; - match[3] = test[3] - 0; - } - - // TODO: Move to normal caching system - match[0] = done++; - - return match; - }, - ATTR: function(match, curLoop, inplace, result, not, isXML){ - var name = match[1].replace(/\\/g, ""); - - if ( !isXML && Expr.attrMap[name] ) { - match[1] = Expr.attrMap[name]; - } - - if ( match[2] === "~=" ) { - match[4] = " " + match[4] + " "; - } - - return match; - }, - PSEUDO: function(match, curLoop, inplace, result, not){ - if ( match[1] === "not" ) { - // If we're dealing with a complex expression, or a simple one - if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { - match[3] = Sizzle(match[3], null, null, curLoop); - } else { - var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); - if ( !inplace ) { - result.push.apply( result, ret ); - } - return false; - } - } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { - return true; - } - - return match; - }, - POS: function(match){ - match.unshift( true ); - return match; - } - }, - filters: { - enabled: function(elem){ - return elem.disabled === false && elem.type !== "hidden"; - }, - disabled: function(elem){ - return elem.disabled === true; - }, - checked: function(elem){ - return elem.checked === true; - }, - selected: function(elem){ - // Accessing this property makes selected-by-default - // options in Safari work properly - elem.parentNode.selectedIndex; - return elem.selected === true; - }, - parent: function(elem){ - return !!elem.firstChild; - }, - empty: function(elem){ - return !elem.firstChild; - }, - has: function(elem, i, match){ - return !!Sizzle( match[3], elem ).length; - }, - header: function(elem){ - return /h\d/i.test( elem.nodeName ); - }, - text: function(elem){ - return "text" === elem.type; - }, - radio: function(elem){ - return "radio" === elem.type; - }, - checkbox: function(elem){ - return "checkbox" === elem.type; - }, - file: function(elem){ - return "file" === elem.type; - }, - password: function(elem){ - return "password" === elem.type; - }, - submit: function(elem){ - return "submit" === elem.type; - }, - image: function(elem){ - return "image" === elem.type; - }, - reset: function(elem){ - return "reset" === elem.type; - }, - button: function(elem){ - return "button" === elem.type || elem.nodeName.toLowerCase() === "button"; - }, - input: function(elem){ - return /input|select|textarea|button/i.test(elem.nodeName); - } - }, - setFilters: { - first: function(elem, i){ - return i === 0; - }, - last: function(elem, i, match, array){ - return i === array.length - 1; - }, - even: function(elem, i){ - return i % 2 === 0; - }, - odd: function(elem, i){ - return i % 2 === 1; - }, - lt: function(elem, i, match){ - return i < match[3] - 0; - }, - gt: function(elem, i, match){ - return i > match[3] - 0; - }, - nth: function(elem, i, match){ - return match[3] - 0 === i; - }, - eq: function(elem, i, match){ - return match[3] - 0 === i; - } - }, - filter: { - PSEUDO: function(elem, match, i, array){ - var name = match[1], filter = Expr.filters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } else if ( name === "contains" ) { - return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; - } else if ( name === "not" ) { - var not = match[3]; - - for ( var i = 0, l = not.length; i < l; i++ ) { - if ( not[i] === elem ) { - return false; - } - } - - return true; - } else { - Sizzle.error( "Syntax error, unrecognized expression: " + name ); - } - }, - CHILD: function(elem, match){ - var type = match[1], node = elem; - switch (type) { - case 'only': - case 'first': - while ( (node = node.previousSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - if ( type === "first" ) { - return true; - } - node = elem; - case 'last': - while ( (node = node.nextSibling) ) { - if ( node.nodeType === 1 ) { - return false; - } - } - return true; - case 'nth': - var first = match[2], last = match[3]; - - if ( first === 1 && last === 0 ) { - return true; - } - - var doneName = match[0], - parent = elem.parentNode; - - if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { - var count = 0; - for ( node = parent.firstChild; node; node = node.nextSibling ) { - if ( node.nodeType === 1 ) { - node.nodeIndex = ++count; - } - } - parent.sizcache = doneName; - } - - var diff = elem.nodeIndex - last; - if ( first === 0 ) { - return diff === 0; - } else { - return ( diff % first === 0 && diff / first >= 0 ); - } - } - }, - ID: function(elem, match){ - return elem.nodeType === 1 && elem.getAttribute("id") === match; - }, - TAG: function(elem, match){ - return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match; - }, - CLASS: function(elem, match){ - return (" " + (elem.className || elem.getAttribute("class")) + " ") - .indexOf( match ) > -1; - }, - ATTR: function(elem, match){ - var name = match[1], - result = Expr.attrHandle[ name ] ? - Expr.attrHandle[ name ]( elem ) : - elem[ name ] != null ? - elem[ name ] : - elem.getAttribute( name ), - value = result + "", - type = match[2], - check = match[4]; - - return result == null ? - type === "!=" : - type === "=" ? - value === check : - type === "*=" ? - value.indexOf(check) >= 0 : - type === "~=" ? - (" " + value + " ").indexOf(check) >= 0 : - !check ? - value && result !== false : - type === "!=" ? - value !== check : - type === "^=" ? - value.indexOf(check) === 0 : - type === "$=" ? - value.substr(value.length - check.length) === check : - type === "|=" ? - value === check || value.substr(0, check.length + 1) === check + "-" : - false; - }, - POS: function(elem, match, i, array){ - var name = match[2], filter = Expr.setFilters[ name ]; - - if ( filter ) { - return filter( elem, i, match, array ); - } - } - } -}; - -var origPOS = Expr.match.POS; - -for ( var type in Expr.match ) { - Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); - Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){ - return "\\" + (num - 0 + 1); - })); -} - -var makeArray = function(array, results) { - array = Array.prototype.slice.call( array, 0 ); - - if ( results ) { - results.push.apply( results, array ); - return results; - } - - return array; -}; - -// Perform a simple check to determine if the browser is capable of -// converting a NodeList to an array using builtin methods. -try { - Array.prototype.slice.call( document.documentElement.childNodes, 0 ); - -// Provide a fallback method if it does not work -} catch(e){ - makeArray = function(array, results) { - var ret = results || []; - - if ( toString.call(array) === "[object Array]" ) { - Array.prototype.push.apply( ret, array ); - } else { - if ( typeof array.length === "number" ) { - for ( var i = 0, l = array.length; i < l; i++ ) { - ret.push( array[i] ); - } - } else { - for ( var i = 0; array[i]; i++ ) { - ret.push( array[i] ); - } - } - } - - return ret; - }; -} - -var sortOrder; - -if ( document.documentElement.compareDocumentPosition ) { - sortOrder = function( a, b ) { - if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.compareDocumentPosition ? -1 : 1; - } - - var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} else if ( "sourceIndex" in document.documentElement ) { - sortOrder = function( a, b ) { - if ( !a.sourceIndex || !b.sourceIndex ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.sourceIndex ? -1 : 1; - } - - var ret = a.sourceIndex - b.sourceIndex; - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} else if ( document.createRange ) { - sortOrder = function( a, b ) { - if ( !a.ownerDocument || !b.ownerDocument ) { - if ( a == b ) { - hasDuplicate = true; - } - return a.ownerDocument ? -1 : 1; - } - - var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); - aRange.setStart(a, 0); - aRange.setEnd(a, 0); - bRange.setStart(b, 0); - bRange.setEnd(b, 0); - var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); - if ( ret === 0 ) { - hasDuplicate = true; - } - return ret; - }; -} - -// Utility function for retreiving the text value of an array of DOM nodes -function getText( elems ) { - var ret = "", elem; - - for ( var i = 0; elems[i]; i++ ) { - elem = elems[i]; - - // Get the text from text nodes and CDATA nodes - if ( elem.nodeType === 3 || elem.nodeType === 4 ) { - ret += elem.nodeValue; - - // Traverse everything else, except comment nodes - } else if ( elem.nodeType !== 8 ) { - ret += getText( elem.childNodes ); - } - } - - return ret; -} - -// Check to see if the browser returns elements by name when -// querying by getElementById (and provide a workaround) -(function(){ - // We're going to inject a fake input element with a specified name - var form = document.createElement("div"), - id = "script" + (new Date).getTime(); - form.innerHTML = ""; - - // Inject it into the root element, check its status, and remove it quickly - var root = document.documentElement; - root.insertBefore( form, root.firstChild ); - - // The workaround has to do additional checks after a getElementById - // Which slows things down for other browsers (hence the branching) - if ( document.getElementById( id ) ) { - Expr.find.ID = function(match, context, isXML){ - if ( typeof context.getElementById !== "undefined" && !isXML ) { - var m = context.getElementById(match[1]); - return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; - } - }; - - Expr.filter.ID = function(elem, match){ - var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); - return elem.nodeType === 1 && node && node.nodeValue === match; - }; - } - - root.removeChild( form ); - root = form = null; // release memory in IE -})(); - -(function(){ - // Check to see if the browser returns only elements - // when doing getElementsByTagName("*") - - // Create a fake element - var div = document.createElement("div"); - div.appendChild( document.createComment("") ); - - // Make sure no comments are found - if ( div.getElementsByTagName("*").length > 0 ) { - Expr.find.TAG = function(match, context){ - var results = context.getElementsByTagName(match[1]); - - // Filter out possible comments - if ( match[1] === "*" ) { - var tmp = []; - - for ( var i = 0; results[i]; i++ ) { - if ( results[i].nodeType === 1 ) { - tmp.push( results[i] ); - } - } - - results = tmp; - } - - return results; - }; - } - - // Check to see if an attribute returns normalized href attributes - div.innerHTML = ""; - if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && - div.firstChild.getAttribute("href") !== "#" ) { - Expr.attrHandle.href = function(elem){ - return elem.getAttribute("href", 2); - }; - } - - div = null; // release memory in IE -})(); - -if ( document.querySelectorAll ) { - (function(){ - var oldSizzle = Sizzle, div = document.createElement("div"); - div.innerHTML = "

    "; - - // Safari can't handle uppercase or unicode characters when - // in quirks mode. - if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { - return; - } - - Sizzle = function(query, context, extra, seed){ - context = context || document; - - // Only use querySelectorAll on non-XML documents - // (ID selectors don't work in non-HTML documents) - if ( !seed && context.nodeType === 9 && !isXML(context) ) { - try { - return makeArray( context.querySelectorAll(query), extra ); - } catch(e){} - } - - return oldSizzle(query, context, extra, seed); - }; - - for ( var prop in oldSizzle ) { - Sizzle[ prop ] = oldSizzle[ prop ]; - } - - div = null; // release memory in IE - })(); -} - -(function(){ - var div = document.createElement("div"); - - div.innerHTML = "
    "; - - // Opera can't find a second classname (in 9.6) - // Also, make sure that getElementsByClassName actually exists - if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { - return; - } - - // Safari caches class attributes, doesn't catch changes (in 3.2) - div.lastChild.className = "e"; - - if ( div.getElementsByClassName("e").length === 1 ) { - return; - } - - Expr.order.splice(1, 0, "CLASS"); - Expr.find.CLASS = function(match, context, isXML) { - if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { - return context.getElementsByClassName(match[1]); - } - }; - - div = null; // release memory in IE -})(); - -function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - elem = elem[dir]; - var match = false; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 && !isXML ){ - elem.sizcache = doneName; - elem.sizset = i; - } - - if ( elem.nodeName.toLowerCase() === cur ) { - match = elem; - break; - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { - for ( var i = 0, l = checkSet.length; i < l; i++ ) { - var elem = checkSet[i]; - if ( elem ) { - elem = elem[dir]; - var match = false; - - while ( elem ) { - if ( elem.sizcache === doneName ) { - match = checkSet[elem.sizset]; - break; - } - - if ( elem.nodeType === 1 ) { - if ( !isXML ) { - elem.sizcache = doneName; - elem.sizset = i; - } - if ( typeof cur !== "string" ) { - if ( elem === cur ) { - match = true; - break; - } - - } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { - match = elem; - break; - } - } - - elem = elem[dir]; - } - - checkSet[i] = match; - } - } -} - -var contains = document.compareDocumentPosition ? function(a, b){ - return a.compareDocumentPosition(b) & 16; -} : function(a, b){ - return a !== b && (a.contains ? a.contains(b) : true); -}; - -var isXML = function(elem){ - // documentElement is verified for cases where it doesn't yet exist - // (such as loading iframes in IE - #4833) - var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -var posProcess = function(selector, context){ - var tmpSet = [], later = "", match, - root = context.nodeType ? [context] : context; - - // Position selectors must be done after the filter - // And so must :not(positional) so we move all PSEUDOs to the end - while ( (match = Expr.match.PSEUDO.exec( selector )) ) { - later += match[0]; - selector = selector.replace( Expr.match.PSEUDO, "" ); - } - - selector = Expr.relative[selector] ? selector + "*" : selector; - - for ( var i = 0, l = root.length; i < l; i++ ) { - Sizzle( selector, root[i], tmpSet ); - } - - return Sizzle.filter( later, tmpSet ); -}; - -// EXPOSE -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; -jQuery.expr[":"] = jQuery.expr.filters; -jQuery.unique = Sizzle.uniqueSort; -jQuery.getText = getText; -jQuery.isXMLDoc = isXML; -jQuery.contains = contains; - -return; - -window.Sizzle = Sizzle; - -})(); -var runtil = /Until$/, - rparentsprev = /^(?:parents|prevUntil|prevAll)/, - // Note: This RegExp should be improved, or likely pulled from Sizzle - rmultiselector = /,/, - slice = Array.prototype.slice; - -// Implement the identical functionality for filter and not -var winnow = function( elements, qualifier, keep ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep(elements, function( elem, i ) { - return !!qualifier.call( elem, i, elem ) === keep; - }); - - } else if ( qualifier.nodeType ) { - return jQuery.grep(elements, function( elem, i ) { - return (elem === qualifier) === keep; - }); - - } else if ( typeof qualifier === "string" ) { - var filtered = jQuery.grep(elements, function( elem ) { - return elem.nodeType === 1; - }); - - if ( isSimple.test( qualifier ) ) { - return jQuery.filter(qualifier, filtered, !keep); - } else { - qualifier = jQuery.filter( qualifier, filtered ); - } - } - - return jQuery.grep(elements, function( elem, i ) { - return (jQuery.inArray( elem, qualifier ) >= 0) === keep; - }); -}; - -jQuery.fn.extend({ - find: function( selector ) { - var ret = this.pushStack( "", "find", selector ), length = 0; - - for ( var i = 0, l = this.length; i < l; i++ ) { - length = ret.length; - jQuery.find( selector, this[i], ret ); - - if ( i > 0 ) { - // Make sure that the results are unique - for ( var n = length; n < ret.length; n++ ) { - for ( var r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } - - return ret; - }, - - has: function( target ) { - var targets = jQuery( target ); - return this.filter(function() { - for ( var i = 0, l = targets.length; i < l; i++ ) { - if ( jQuery.contains( this, targets[i] ) ) { - return true; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector, false), "not", selector); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector, true), "filter", selector ); - }, - - is: function( selector ) { - return !!selector && jQuery.filter( selector, this ).length > 0; - }, - - closest: function( selectors, context ) { - if ( jQuery.isArray( selectors ) ) { - var ret = [], cur = this[0], match, matches = {}, selector; - - if ( cur && selectors.length ) { - for ( var i = 0, l = selectors.length; i < l; i++ ) { - selector = selectors[i]; - - if ( !matches[selector] ) { - matches[selector] = jQuery.expr.match.POS.test( selector ) ? - jQuery( selector, context || this.context ) : - selector; - } - } - - while ( cur && cur.ownerDocument && cur !== context ) { - for ( selector in matches ) { - match = matches[selector]; - - if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) { - ret.push({ selector: selector, elem: cur }); - delete matches[selector]; - } - } - cur = cur.parentNode; - } - } - - return ret; - } - - var pos = jQuery.expr.match.POS.test( selectors ) ? - jQuery( selectors, context || this.context ) : null; - - return this.map(function( i, cur ) { - while ( cur && cur.ownerDocument && cur !== context ) { - if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) { - return cur; - } - cur = cur.parentNode; - } - return null; - }); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - if ( !elem || typeof elem === "string" ) { - return jQuery.inArray( this[0], - // If it receives a string, the selector is used - // If it receives nothing, the siblings are used - elem ? jQuery( elem ) : this.parent().children() ); - } - // Locate the position of the desired element - return jQuery.inArray( - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[0] : elem, this ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context || this.context ) : - jQuery.makeArray( selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? - all : - jQuery.unique( all ) ); - }, - - andSelf: function() { - return this.add( this.prevObject ); - } -}); - -// A painfully simple check to see if an element is disconnected -// from a document (should be improved, where feasible). -function isDisconnected( node ) { - return !node || !node.parentNode || node.parentNode.nodeType === 11; -} - -jQuery.each({ - parent: function( elem ) { - var parent = elem.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - parents: function( elem ) { - return jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return jQuery.nth( elem, 2, "nextSibling" ); - }, - prev: function( elem ) { - return jQuery.nth( elem, 2, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( elem.parentNode.firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return jQuery.nodeName( elem, "iframe" ) ? - elem.contentDocument || elem.contentWindow.document : - jQuery.makeArray( elem.childNodes ); - } -}, function( name, fn ) { - jQuery.fn[ name ] = function( until, selector ) { - var ret = jQuery.map( this, fn, until ); - - if ( !runtil.test( name ) ) { - selector = until; - } - - if ( selector && typeof selector === "string" ) { - ret = jQuery.filter( selector, ret ); - } - - ret = this.length > 1 ? jQuery.unique( ret ) : ret; - - if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { - ret = ret.reverse(); - } - - return this.pushStack( ret, name, slice.call(arguments).join(",") ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return jQuery.find.matches(expr, elems); - }, - - dir: function( elem, dir, until ) { - var matched = [], cur = elem[dir]; - while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { - if ( cur.nodeType === 1 ) { - matched.push( cur ); - } - cur = cur[dir]; - } - return matched; - }, - - nth: function( cur, result, dir, elem ) { - result = result || 1; - var num = 0; - - for ( ; cur; cur = cur[dir] ) { - if ( cur.nodeType === 1 && ++num === result ) { - break; - } - } - - return cur; - }, - - sibling: function( n, elem ) { - var r = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - r.push( n ); - } - } - - return r; - } -}); -var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, - rleadingWhitespace = /^\s+/, - rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g, - rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i, - rtagName = /<([\w:]+)/, - rtbody = /"; - }, - wrapMap = { - option: [ 1, "" ], - legend: [ 1, "
    ", "
    " ], - thead: [ 1, "", "
    " ], - tr: [ 2, "", "
    " ], - td: [ 3, "", "
    " ], - col: [ 2, "", "
    " ], - area: [ 1, "", "" ], - _default: [ 0, "", "" ] - }; - -wrapMap.optgroup = wrapMap.option; -wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; -wrapMap.th = wrapMap.td; - -// IE can't serialize and <% } %> \ No newline at end of file +} %> \ No newline at end of file diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index 6e47c2ba3..5e9820a4f 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -134,6 +134,10 @@ {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} Orchard.Media + + {23E04990-2A8D-41B8-9908-6DDB71EA3B23} + Orchard.MetaData + {17F86780-9A1F-4AA1-86F1-875EEC2730C7} Orchard.Modules diff --git a/src/Orchard.Web/Themes/Classic/Styles/blog.css b/src/Orchard.Web/Themes/Classic/Styles/blog.css index 062e7a068..ad41558af 100644 --- a/src/Orchard.Web/Themes/Classic/Styles/blog.css +++ b/src/Orchard.Web/Themes/Classic/Styles/blog.css @@ -15,17 +15,19 @@ ul.blogs p { } .postsummary { - border-bottom:1px dotted #f1f1f1; + padding: 0 0 4px 0; + border-bottom:1px dotted #666; } .meta { + font-size:1.5em; font-style:italic; color:#666666; } .tagCloud { font-style:italic; - font-size:120%; + font-size:1.8em; } .tagCloud li { @@ -39,5 +41,14 @@ ul.blogs p { } ul.archiveMonthList li { + font-size:1.5em; padding:8px; +} + +.comment span { + font-size:1.4em; +} + +.text p { + margin:0 0 1.4em 0; } \ No newline at end of file diff --git a/src/Orchard.Web/Themes/Classic/Styles/site.css b/src/Orchard.Web/Themes/Classic/Styles/site.css index 6a421733f..1f1ade327 100644 --- a/src/Orchard.Web/Themes/Classic/Styles/site.css +++ b/src/Orchard.Web/Themes/Classic/Styles/site.css @@ -19,30 +19,28 @@ abbr,acronym { border:0;} html {height: 100%;} body { - font: normal 90% "Georgia", Times New Roman, Times, serif; + font: normal 62.5% "Georgia", Times New Roman, Times, serif; /* 62.5% sets font to 10px */ height: 100%; text-align:left; color:#373737; - background:#fdfdfd; background:#f7f7f7; - /*background:#E5E5E5 url(../Content/Images/bodyBackgroundgrey.gif) repeat-x scroll left top;*/ } /* ---------- Headings and defaults ---------- */ -h1,h2,h3,h4,h5,h6,legend {font-weight:normal; font-style: normal; margin:12px 0 4px 0;} +h1,h2,h3,h4,h5,h6,legend {font-weight:normal; font-style: normal; margin:.2em 0;} -h1 {font-size: 180%;} -h2 {font-size: 160%;} -h3 {font-size: 145%;} -h4 {font-size: 130%;} -h5 {font-size: 120%;} -h6 {font-size: 105%;} +h1 { font-size:2.6em; } /* 26px */ +h2 { font-size:2.4em; } /* 24px */ +h3 { font-size:2.2em; } /* 22px */ +h4 { font-size:2.0em; } /* 20px */ +h5 { font-size:1.8em; } /* 18px */ +h6 { font-size:1.6em; } /* 16px */ h1.sitename {border-bottom:none;} -p {line-height:24px; margin:12px 0 32px 0;} -p.small {line-height:24px; font-size:85%;} +p {font-size:1.4em; line-height:2em; margin:1.2em 0 1.2em 0;} +p.small {font-size:1.2em;} a {color:#004386; text-decoration:none;} /* unvisited link */ a:active {color: #004386;} /* selected link */ @@ -51,8 +49,8 @@ a:hover {color: #087df3;} /* mouse over link */ #content a:hover, #sidebar a:hover {text-decoration:underline;} -ul.square {list-style:square; list-style-position:inside; line-height:24px; margin:0 0 0 12px;} -ol.decimal {list-style:decimal; list-style-position:inside; line-height:24px; margin:0 0 0 12px;} +ul.square {font-size:1.4em; list-style:square; list-style-position:inside; line-height:2em; margin:0 0 0 1.2em;} +ol.decimal {font-size:1.4em; list-style:decimal; list-style-position:inside; line-height:2em; margin:0 0 0 1.2em;} /* Forms @@ -61,37 +59,38 @@ ol.decimal {list-style:decimal; list-style-position:inside; line-height:24px; ma input[type="text"], #CommentText, #password, #confirmPassword { border:1px solid #cacec6; display: block; - padding:2px; + padding:3px; width:90%; } -fieldset div {margin:16px 0 0 0} +fieldset div {margin:1.6em 0 0 0} legend { - font-size: 110%; + font-size: 1.8em; border:none; } label { + font-size: 1.4em; display: block; - margin:0 0 2px 0; + margin:0 0 .1em 0; } input[type="checkbox"] { - margin:2px 0 20px 10px; + margin:.2em 0 2em 1em; } input[type="submit"], input[type="button"], .button { - font: normal 90% "Georgia", Times New Roman, Times, serif; + font: normal 1.4em "Georgia", Times New Roman, Times, serif; color:#333; padding:2px 14px; display: block; - margin:2px 0 20px 0; + margin:.2em 0 2em 0; font-weight:600; } .forcheckbox { - margin:0 0 0 4px; + margin:0 0 0 .4em; display:inline; } @@ -99,6 +98,7 @@ input[type="submit"], input[type="button"], .button { /* Tables ----------------------------------------------------------*/ table { + font:1.2em Verdana, Geneva, Tahoma, sans-serif; background:#fff; border:1px solid #f1f1f1; border-collapse:collapse; @@ -111,7 +111,6 @@ table thead, table th { } table th { - font:70% Verdana, Geneva, Tahoma, sans-serif; background-color:#69625e; color:#f1f1f1; } @@ -135,15 +134,16 @@ table tbody { th, td { - font:70% Verdana, Geneva, Tahoma, sans-serif; - padding:6px + font:1em Verdana, Geneva, Tahoma, sans-serif; + padding:6px; } td.even { background-color:#f1f1f1; } -/* ---------- Layout ---------- */ +/* Layout +----------------------------------------------------------*/ #headercontainer { border:2px solid #69625e; @@ -157,21 +157,22 @@ td.even { border:1px solid #69625e; border-left:0; border-right:0; - margin:3px 0 3px 0; + margin:.3em 0 .3em 0; } #header h1 { - font-size:240%; + font-size:3.2em; color:#333; border:0; - margin:12px 18px; + margin:0 0 0 .8em; + padding:10px 0; float:left; } #logindisplay { - font:80%; - margin:12px auto; + font-size:1.4em; + margin:1em auto; text-align:right; width: 930px; } @@ -185,7 +186,7 @@ td.even { } #main { - margin:0 0 0 27px; + margin:0 0 0 2.7em; width:930px; background:url(../Content/Images/sidebarBackground.gif) no-repeat right top #fff; } @@ -193,45 +194,45 @@ td.even { #content { float:left; - margin:26px 0 0 27px; + margin:2.6em 0 0 2.7em; width:520px; min-height:555px; } #sidebar { float:right; - margin:28px 0 0 0; + margin:2.8em 0 0 0; width:325px; } #sidebar h3 { - border-bottom:1px dotted #f1f1f1; - font-size: 130%; + border-bottom:1px dotted #666; + font-size: 2.0em; } #footercontainer { clear:both; border-top:2px solid #69625e; width:930px; - margin:18px 0 6px 0; + margin:1.8em 0 .6em 0; } #footer { border-top:1px solid #69625e; - margin-top:3px; + margin-top:.3em; } #footer a{ text-transform:uppercase; } - -/* ---------- Navigation ---------- */ +/* Navigation +----------------------------------------------------------*/ .menucontainer { display:block; float:right; - margin:18px 0; + margin-top:1.3em; } .menucontainer ul, #footer ul { @@ -239,12 +240,13 @@ td.even { } .menucontainer ul li { - margin:8px 0 0 0; + margin:.8em 0 0 0; float:left; } .menucontainer ul li a { - margin:0 18px 0 18px; + font-size:1.4em; + margin:0 1.8em 0 1.8em; display:block; text-decoration:none; text-transform:uppercase; @@ -252,23 +254,23 @@ td.even { } #footer .menucontainer { - font-size:80%; margin:0px; } #footer .menucontainer ul li { - margin:12px 0; + margin:1.2em 0; } /* Confirmations, Messages and the like ----------------------------------------------------------*/ .message, .validation-summary-errors { - margin:10px 0 4px 0; + font-size:1.4em; + margin:1em 0 .4em 0; padding:4px; } span.message { display:block; - margin:4px 0 4px 4px; + margin:.4em 0 .4em .4em; } .message a { font-weight:bold; @@ -299,24 +301,25 @@ span.message { background:#eee; border:1px dashed #D2D6C6; color:#7a7a7a; - margin:20px 0 14px 0; + margin:2em 0 1.4em 0; } .debug.message:before { content:"DEBUG » "; } + +/* Misc classes +-------------------------------------------------------------- */ +.clearBoth { + clear:both; +} + /* ---------- Tags and Comments ---------- */ .tags, .comment { font-style:italic; color:#666666; } - -/* ---------- Generic ---------- */ -.clearBoth { - clear:both; -} - /* ---------- Hide the edit link for blogs hack ---------- */ .manage { display:none; diff --git a/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Items/Pages.Page.ascx b/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Items/Pages.Page.ascx index c38404c4e..92d580be8 100644 --- a/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Items/Pages.Page.ascx +++ b/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Items/Pages.Page.ascx @@ -5,12 +5,12 @@
    <%=Html.TitleForPage(Model.Item.Title)%>
    -<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> -<%} %> +<%--<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> +<%} %>--%> <% Html.Zone("primary"); Html.ZonesAny(); %> -<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> +<%--<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> -<%} %> \ No newline at end of file +<%} %>--%> \ No newline at end of file diff --git a/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx b/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx index b6f3d75c3..60253b6d4 100644 --- a/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx +++ b/src/Orchard.Web/Themes/Contoso/Views/DisplayTemplates/Parts/Comments.HasComments.ascx @@ -51,7 +51,7 @@ else { %>
    " /> <%=Html.Hidden("CommentedOn", Model.ContentItem.Id) %> - <%=Html.Hidden("ReturnUrl", Context.Request.Url) %> + <%=Html.Hidden("ReturnUrl", Context.Request.RawUrl) %> <%=Html.AntiForgeryTokenOrchard() %>
    <% diff --git a/src/Orchard.Web/Themes/Contoso/Views/LogOn.ascx b/src/Orchard.Web/Themes/Contoso/Views/LogOn.ascx index 7b9d29760..a54cc96d2 100644 --- a/src/Orchard.Web/Themes/Contoso/Views/LogOn.ascx +++ b/src/Orchard.Web/Themes/Contoso/Views/LogOn.ascx @@ -10,8 +10,8 @@ using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new {ReturnUrl = Reques <%=_Encoded("Account Information")%>
    - <%= Html.TextBox("username")%> - <%= Html.ValidationMessage("username")%> + <%= Html.TextBox("userNameOrEmail", "", new { autofocus = "autofocus" })%> + <%= Html.ValidationMessage("userNameOrEmail")%>
    @@ -23,5 +23,4 @@ using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new {ReturnUrl = Reques
    " /> <% -} %><% -using (this.Capture("end-of-page-scripts")) { %><% } %> \ No newline at end of file +} %> \ No newline at end of file diff --git a/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Items/Pages.Page.ascx b/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Items/Pages.Page.ascx index c38404c4e..92d580be8 100644 --- a/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Items/Pages.Page.ascx +++ b/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Items/Pages.Page.ascx @@ -5,12 +5,12 @@
    <%=Html.TitleForPage(Model.Item.Title)%>
    -<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> -<%} %> +<%--<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> +<%} %>--%> <% Html.Zone("primary"); Html.ZonesAny(); %> -<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> +<%--<% if (Html.Resolve().GetAuthenticatedUser() != null){ %> -<%} %> \ No newline at end of file +<%} %>--%> \ No newline at end of file diff --git a/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx b/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx index b6f3d75c3..60253b6d4 100644 --- a/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx +++ b/src/Orchard.Web/Themes/Corporate/Views/DisplayTemplates/Parts/Comments.HasComments.ascx @@ -51,7 +51,7 @@ else { %>
    " /> <%=Html.Hidden("CommentedOn", Model.ContentItem.Id) %> - <%=Html.Hidden("ReturnUrl", Context.Request.Url) %> + <%=Html.Hidden("ReturnUrl", Context.Request.RawUrl) %> <%=Html.AntiForgeryTokenOrchard() %>
    <% diff --git a/src/Orchard.Web/Themes/Corporate/Views/LogOn.ascx b/src/Orchard.Web/Themes/Corporate/Views/LogOn.ascx index 7b9d29760..a54cc96d2 100644 --- a/src/Orchard.Web/Themes/Corporate/Views/LogOn.ascx +++ b/src/Orchard.Web/Themes/Corporate/Views/LogOn.ascx @@ -10,8 +10,8 @@ using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new {ReturnUrl = Reques <%=_Encoded("Account Information")%>
    - <%= Html.TextBox("username")%> - <%= Html.ValidationMessage("username")%> + <%= Html.TextBox("userNameOrEmail", "", new { autofocus = "autofocus" })%> + <%= Html.ValidationMessage("userNameOrEmail")%>
    @@ -23,5 +23,4 @@ using (Html.BeginFormAntiForgeryPost(Url.Action("LogOn", new {ReturnUrl = Reques
    " /> <% -} %><% -using (this.Capture("end-of-page-scripts")) { %><% } %> \ No newline at end of file +} %> \ No newline at end of file diff --git a/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx b/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx index 0cabeb730..f7ee8c90c 100644 --- a/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx +++ b/src/Orchard.Web/Themes/SafeMode/Views/Layout.ascx @@ -5,7 +5,7 @@ <%@ Import Namespace="Orchard.Mvc.ViewModels" %> <%@ Import Namespace="Orchard.Mvc.Html" %> <% //todo: (heskew) this should really be using the IResourceManager if it's to be a theme especially for the jquery dep (w/out needing to copy into this theme...) - var jquery = ResolveUrl("~/Modules/Orchard.Themes/Scripts/jquery-1.4.1.js"); + var jquery = ResolveUrl("~/Modules/Orchard.Themes/Scripts/jquery-1.4.2.js"); Model.Zones.AddAction("head:scripts", html => html.ViewContext.Writer.Write(@"")); var basejs = ResolveUrl("~/Modules/Orchard.Themes/Scripts/base.js"); diff --git a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css index 768ef4a9f..96a7c5011 100644 --- a/src/Orchard.Web/Themes/TheAdmin/Styles/site.css +++ b/src/Orchard.Web/Themes/TheAdmin/Styles/site.css @@ -56,6 +56,11 @@ body { min-width:94.6em; /* 946px */ padding:0; } +/*todo: (heskew) find out why I need this...*/ +button { + font-family:Segoe UI,Trebuchet,Arial,Sans-Serif; + font-size:1.01em; +} body#preview { min-width:0; } @@ -158,15 +163,21 @@ p .button { font-size:inherit; } /* Links ----------------------------------------------------------*/ -a, a:link, a:visited { +a, a:link, a:visited, +form.link button { color:#1e5d7d; text-decoration:none; } +form.link button { + height:1.3em; +} a:hover, a:active, a:focus { color:#1e5d7d; text-decoration:underline; } - +form.link button:hover { + border-bottom:1px solid #1e5d7d; +} /* Header - Branding and Login ----------------------------------------------------------*/ @@ -309,8 +320,8 @@ span.message { } .confirmation.message { - background:#e6f1c9; /* green */ - border:1px solid #cfe493; + background:#D1F2A5; /* green */ + border:1px solid #BCD994; } .warning.message { background:#fdf5bc; /* yellow */ @@ -326,8 +337,8 @@ span.message { color:#fff; } .info.message { - background:#e6f1c9; /* green*/ - border:1px solid #d4deb9; + background:#e6f1c9; /* green */ + border:1px solid #cfe493; color:#062232; } .debug.message { @@ -467,7 +478,18 @@ button, .button, .button:link, .button:visited { color:#2d2f25; cursor:pointer; text-align:center; - padding:0 .8em .1em .8em; + padding:0 .8em .1em; +} +button { + padding-top:.08em; +} +form.link button { + background:inherit; + border:0; + padding:0; + width:auto; + margin:-2px -3px 0; + } .primaryAction, .primaryAction:link, .primaryAction:visited { background:#4687ad; @@ -709,8 +731,11 @@ todo: (heskew) pull out into relevant modules where appropriate zoom:1; *display: inline; } -.templates fieldset { - margin:0 0 .933%; +.templates .inline button { + font-size:1.2em; +} +.templates .wasFormInlineLink { + font-size:1.4em; } .templates p { overflow:hidden; @@ -767,6 +792,7 @@ table.items, textarea, input.text, input.text-box, text-align:right; } .contentItems { + clear:both; padding:0; } .properties { @@ -785,6 +811,11 @@ table.items, textarea, input.text, input.text-box, float:right; text-align:right; } +/*todo: (heskew) cleanup */ +.related .button { + font-size:1em; +} +/*end todo*/ .commentcount { line-height:2em; } @@ -798,6 +829,9 @@ table.items, textarea, input.text, input.text-box, .icon { margin:0 .2em -.2em .2em; } +h3 .icon { + margin-bottom:-.05em; +} .linkButton { border:none; padding:0; diff --git a/src/Orchard.sln b/src/Orchard.sln index 316a45174..7337aa9e1 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -63,6 +63,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Modules", "Orchard. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Profile", "Orchard.Profile\Orchard.Profile.csproj", "{94E694A2-D140-468D-A277-C5FCE1D13E9B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.MetaData", "Orchard.Web\Modules\Orchard.MetaData\Orchard.MetaData.csproj", "{23E04990-2A8D-41B8-9908-6DDB71EA3B23}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -177,6 +179,10 @@ Global {94E694A2-D140-468D-A277-C5FCE1D13E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU {94E694A2-D140-468D-A277-C5FCE1D13E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU {94E694A2-D140-468D-A277-C5FCE1D13E9B}.Release|Any CPU.Build.0 = Release|Any CPU + {23E04990-2A8D-41B8-9908-6DDB71EA3B23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23E04990-2A8D-41B8-9908-6DDB71EA3B23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23E04990-2A8D-41B8-9908-6DDB71EA3B23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23E04990-2A8D-41B8-9908-6DDB71EA3B23}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -197,6 +203,7 @@ Global {72457126-E118-4171-A08F-9A709EE4B7FC} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {CDE24A24-01D3-403C-84B9-37722E18DFB7} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {17F86780-9A1F-4AA1-86F1-875EEC2730C7} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} + {23E04990-2A8D-41B8-9908-6DDB71EA3B23} = {E9C9F120-07BA-4DFB-B9C3-3AFB9D44C9D5} {ABC826D4-2FA1-4F2F-87DE-E6095F653810} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {F112851D-B023-4746-B6B1-8D2E5AD8F7AA} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} {6CB3EB30-F725-45C0-9742-42599BA8E8D2} = {74E681ED-FECC-4034-B9BD-01B0BB1BDECA} diff --git a/src/Orchard/Caching/AcquireContext.cs b/src/Orchard/Caching/AcquireContext.cs new file mode 100644 index 000000000..a2d54e7de --- /dev/null +++ b/src/Orchard/Caching/AcquireContext.cs @@ -0,0 +1,13 @@ +using System; + +namespace Orchard.Caching { + public class AcquireContext { + public AcquireContext(TKey key, Action monitor) { + Key = key; + Monitor = monitor; + } + + public TKey Key { get; private set; } + public Action Monitor { get; private set; } + } +} diff --git a/src/Orchard/Caching/Cache.cs b/src/Orchard/Caching/Cache.cs new file mode 100644 index 000000000..4f0bb402a --- /dev/null +++ b/src/Orchard/Caching/Cache.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Orchard.Caching { + public class Cache : ICache { + private readonly Dictionary _entries; + + public Cache() { + _entries = new Dictionary(); + } + + public TResult Get(TKey key, Func, TResult> acquire) { + CacheEntry entry; + if (!_entries.TryGetValue(key, out entry) || entry.Tokens.Any(t => !t.IsCurrent)) { + entry = new CacheEntry { Tokens = new List() }; + + var context = new AcquireContext(key, volatileItem => entry.Tokens.Add(volatileItem)); + entry.Result = acquire(context); + _entries[key] = entry; + } + return entry.Result; + } + + private class CacheEntry { + public TResult Result { get; set; } + public IList Tokens { get; set; } + } + } + +} diff --git a/src/Orchard/Caching/CacheModule.cs b/src/Orchard/Caching/CacheModule.cs new file mode 100644 index 000000000..bba898374 --- /dev/null +++ b/src/Orchard/Caching/CacheModule.cs @@ -0,0 +1,29 @@ +using System; +using System.Linq; +using Autofac; + +namespace Orchard.Caching { + public class CacheModule : Module { + protected override void Load(ContainerBuilder builder) { + builder.RegisterType() + .As() + .InstancePerDependency(); + } + + protected override void AttachToComponentRegistration(Autofac.Core.IComponentRegistry componentRegistry, Autofac.Core.IComponentRegistration registration) { + var needsCacheManager = registration.Activator.LimitType + .GetConstructors() + .Any(x => x.GetParameters() + .Any(xx => xx.ParameterType == typeof(ICacheManager))); + + if (needsCacheManager) { + registration.Preparing += (sender, e) => { + var parameter = new TypedParameter( + typeof(ICacheManager), + e.Context.Resolve(new TypedParameter(typeof(Type), registration.Activator.LimitType))); + e.Parameters = e.Parameters.Concat(new[] { parameter }); + }; + } + } + } +} diff --git a/src/Orchard/Caching/DefaultCacheHolder.cs b/src/Orchard/Caching/DefaultCacheHolder.cs new file mode 100644 index 000000000..57b0ec140 --- /dev/null +++ b/src/Orchard/Caching/DefaultCacheHolder.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using Castle.Core; + +namespace Orchard.Caching { + public class DefaultCacheHolder : ICacheHolder { + private readonly IDictionary _caches = new Dictionary(); + + class CacheKey : Pair> { + public CacheKey(Type component, Type key, Type result) + : base(component, new Pair(key, result)) { + } + } + + public ICache GetCache(Type component) { + var cacheKey = new CacheKey(component, typeof(TKey), typeof(TResult)); + lock (_caches) { + object value; + if (!_caches.TryGetValue(cacheKey, out value)) { + value = new Cache(); + _caches[cacheKey] = value; + } + return (ICache)value; + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/DefaultCacheManager.cs b/src/Orchard/Caching/DefaultCacheManager.cs new file mode 100644 index 000000000..8fdb2eebc --- /dev/null +++ b/src/Orchard/Caching/DefaultCacheManager.cs @@ -0,0 +1,21 @@ +using System; + +namespace Orchard.Caching { + public class DefaultCacheManager : ICacheManager { + private readonly Type _component; + private readonly ICacheHolder _cacheHolder; + + public DefaultCacheManager(Type component, ICacheHolder cacheHolder) { + _component = component; + _cacheHolder = cacheHolder; + } + + public ICache GetCache() { + return _cacheHolder.GetCache(_component); + } + + public TResult Get(TKey key, Func, TResult> acquire) { + return GetCache().Get(key, acquire); + } + } +} diff --git a/src/Orchard/Caching/ICache.cs b/src/Orchard/Caching/ICache.cs new file mode 100644 index 000000000..691484d95 --- /dev/null +++ b/src/Orchard/Caching/ICache.cs @@ -0,0 +1,7 @@ +using System; + +namespace Orchard.Caching { + public interface ICache { + TResult Get(TKey key, Func, TResult> acquire); + } +} diff --git a/src/Orchard/Caching/ICacheHolder.cs b/src/Orchard/Caching/ICacheHolder.cs new file mode 100644 index 000000000..730bd1888 --- /dev/null +++ b/src/Orchard/Caching/ICacheHolder.cs @@ -0,0 +1,7 @@ +using System; + +namespace Orchard.Caching { + public interface ICacheHolder : ISingletonDependency { + ICache GetCache(Type component); + } +} diff --git a/src/Orchard/Caching/ICacheManager.cs b/src/Orchard/Caching/ICacheManager.cs new file mode 100644 index 000000000..c54902eb9 --- /dev/null +++ b/src/Orchard/Caching/ICacheManager.cs @@ -0,0 +1,8 @@ +using System; + +namespace Orchard.Caching { + public interface ICacheManager { + TResult Get(TKey key, Func, TResult> acquire); + ICache GetCache(); + } +} diff --git a/src/Orchard/Caching/IVolatileProvider.cs b/src/Orchard/Caching/IVolatileProvider.cs new file mode 100644 index 000000000..8f6fffc45 --- /dev/null +++ b/src/Orchard/Caching/IVolatileProvider.cs @@ -0,0 +1,4 @@ +namespace Orchard.Caching { + public interface IVolatileProvider : ISingletonDependency { + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/IVolatileToken.cs b/src/Orchard/Caching/IVolatileToken.cs new file mode 100644 index 000000000..7c0e17c60 --- /dev/null +++ b/src/Orchard/Caching/IVolatileToken.cs @@ -0,0 +1,5 @@ +namespace Orchard.Caching { + public interface IVolatileToken { + bool IsCurrent { get; } + } +} \ No newline at end of file diff --git a/src/Orchard/Caching/Weak.cs b/src/Orchard/Caching/Weak.cs new file mode 100644 index 000000000..ce8ba0c61 --- /dev/null +++ b/src/Orchard/Caching/Weak.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.Serialization; + +namespace Orchard.Caching { + public class Weak : WeakReference { + public Weak(T target) + : base(target) { + } + + public Weak(T target, bool trackResurrection) + : base(target, trackResurrection) { + } + + protected Weak(SerializationInfo info, StreamingContext context) + : base(info, context) { + } + + public new T Target { + get { return (T)base.Target; } + set { base.Target = value; } + } + } +} diff --git a/src/Orchard/ContentManagement/ContentPart.cs b/src/Orchard/ContentManagement/ContentPart.cs index 1b56b8fc1..180ea362f 100644 --- a/src/Orchard/ContentManagement/ContentPart.cs +++ b/src/Orchard/ContentManagement/ContentPart.cs @@ -1,10 +1,13 @@ +using Orchard.ContentManagement.Utilities; + namespace Orchard.ContentManagement { public abstract class ContentPart : IContent { public virtual ContentItem ContentItem { get; set; } } public class ContentPart : ContentPart { - public TRecord Record { get; set; } + public readonly LazyField _record = new LazyField(); + public TRecord Record { get { return _record.Value; } set { _record.Value = value; } } } } diff --git a/src/Orchard/ContentManagement/DefaultContentManager.cs b/src/Orchard/ContentManagement/DefaultContentManager.cs index eeb389ba0..ee99a3716 100644 --- a/src/Orchard/ContentManagement/DefaultContentManager.cs +++ b/src/Orchard/ContentManagement/DefaultContentManager.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Autofac; using Orchard.ContentManagement.Handlers; @@ -12,16 +13,19 @@ namespace Orchard.ContentManagement { private readonly IRepository _contentTypeRepository; private readonly IRepository _contentItemRepository; private readonly IRepository _contentItemVersionRepository; + private readonly Func _contentManagerSession; public DefaultContentManager( IComponentContext context, IRepository contentTypeRepository, IRepository contentItemRepository, - IRepository contentItemVersionRepository) { + IRepository contentItemVersionRepository, + Func contentManagerSession) { _context = context; _contentTypeRepository = contentTypeRepository; _contentItemRepository = contentItemRepository; _contentItemVersionRepository = contentItemVersionRepository; + _contentManagerSession = contentManagerSession; } private IEnumerable _handlers; @@ -72,45 +76,24 @@ namespace Orchard.ContentManagement { } public virtual ContentItem Get(int id, VersionOptions options) { + var session = _contentManagerSession(); + ContentItem contentItem; + ContentItemVersionRecord versionRecord = null; - var appendLatestVersion = false; // obtain the root records based on version options if (options.VersionRecordId != 0) { - // explicit version record known + // short-circuit if item held in session + if (session.RecallVersionRecordId(options.VersionRecordId, out contentItem)) + return contentItem; + + // locate explicit version record versionRecord = _contentItemVersionRepository.Get(options.VersionRecordId); } else { - // FIX: rework this so it falls back to an in-memory scan when the results don't fit the criteria var record = _contentItemRepository.Get(id); - if (options.IsPublished) { - versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Published); - } - else if (options.IsLatest) { - versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest); - } - else if (options.IsDraft || options.IsDraftRequired) { - versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest && !x.Published); - if (versionRecord == null && options.IsDraftRequired) { - versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Latest); - appendLatestVersion = true; - } - } - else if (options.VersionNumber != 0) { - versionRecord = _contentItemVersionRepository.Get(x => x.ContentItemRecord == record && x.Number == options.VersionNumber); - } - - //TEMP: this is to transition people with old databases - if (versionRecord == null && record != null && !record.Versions.Any() && options.IsPublished) { - versionRecord = new ContentItemVersionRecord { - ContentItemRecord = record, - Latest = true, - Published = true, - Number = 1 - }; - record.Versions.Add(versionRecord); - _contentItemVersionRepository.Create(versionRecord); - } + if (record != null) + versionRecord = GetVersionRecord(options, record); } // no record means content item doesn't exist @@ -118,10 +101,19 @@ namespace Orchard.ContentManagement { return null; } + // return item if obtained earlier in session + if (session.RecallVersionRecordId(versionRecord.Id, out contentItem)) { + return contentItem; + } + + // allocate instance and set record property - var contentItem = New(versionRecord.ContentItemRecord.ContentType.Name); + contentItem = New(versionRecord.ContentItemRecord.ContentType.Name); contentItem.VersionRecord = versionRecord; + // store in session prior to loading to avoid some problems with simple circular dependencies + session.Store(contentItem); + // create a context with a new instance to load var context = new LoadContentContext(contentItem); @@ -133,14 +125,42 @@ namespace Orchard.ContentManagement { handler.Loaded(context); } - // when draft is required and not currently available a new version is appended - if (appendLatestVersion) { + // when draft is required and latest is published a new version is appended + if (options.IsDraftRequired && versionRecord.Published) { return BuildNewVersion(context.ContentItem); } return context.ContentItem; } + private ContentItemVersionRecord GetVersionRecord(VersionOptions options, ContentItemRecord itemRecord) { + if (options.IsPublished) { + return itemRecord.Versions.FirstOrDefault( + x => x.Published) ?? + _contentItemVersionRepository.Get( + x => x.ContentItemRecord == itemRecord && x.Published); + } + if (options.IsLatest || options.IsDraftRequired) { + return itemRecord.Versions.FirstOrDefault( + x => x.Latest) ?? + _contentItemVersionRepository.Get( + x => x.ContentItemRecord == itemRecord && x.Latest); + } + if (options.IsDraft) { + return itemRecord.Versions.FirstOrDefault( + x => x.Latest && !x.Published) ?? + _contentItemVersionRepository.Get( + x => x.ContentItemRecord == itemRecord && x.Latest && !x.Published); + } + if (options.VersionNumber != 0) { + return itemRecord.Versions.FirstOrDefault( + x => x.Number == options.VersionNumber) ?? + _contentItemVersionRepository.Get( + x => x.ContentItemRecord == itemRecord && x.Number == options.VersionNumber); + } + return null; + } + public virtual IEnumerable GetAllVersions(int id) { return _contentItemVersionRepository .Fetch(x => x.ContentItemRecord.Id == id) diff --git a/src/Orchard/ContentManagement/DefaultContentManagerSession.cs b/src/Orchard/ContentManagement/DefaultContentManagerSession.cs new file mode 100644 index 000000000..ad6b78735 --- /dev/null +++ b/src/Orchard/ContentManagement/DefaultContentManagerSession.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; + +namespace Orchard.ContentManagement { + public class DefaultContentManagerSession : IContentManagerSession { + private readonly IDictionary _itemByVersionRecordId = new Dictionary(); + + public void Store(ContentItem item) { + _itemByVersionRecordId.Add(item.VersionRecord.Id, item); + } + + public bool RecallVersionRecordId(int id, out ContentItem item) { + return _itemByVersionRecordId.TryGetValue(id, out item); + } + } +} diff --git a/src/Orchard/ContentManagement/Drivers/AutomaticContentPartDriver.cs b/src/Orchard/ContentManagement/Drivers/AutomaticContentPartDriver.cs index 2d927b5af..e66c8e276 100644 --- a/src/Orchard/ContentManagement/Drivers/AutomaticContentPartDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/AutomaticContentPartDriver.cs @@ -1,5 +1,5 @@ namespace Orchard.ContentManagement.Drivers { - public abstract class AutomaticContentPartDriver : ContentPartDriver where TPart : class, IContent { + public abstract class AutomaticContentPartDriver : ContentPartDriver where TPart : ContentPart, new() { protected override string Prefix { get { return (typeof (TPart).Name); diff --git a/src/Orchard/ContentManagement/Drivers/ContentItemDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentItemDriver.cs index 90ba0f121..d1f71ddb0 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentItemDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentItemDriver.cs @@ -5,7 +5,7 @@ using Orchard.ContentManagement.Handlers; using Orchard.Mvc.ViewModels; namespace Orchard.ContentManagement.Drivers { - public abstract class ContentItemDriver : ContentPartDriver, IContentItemDriver where TContent : class, IContent { + public abstract class ContentItemDriver : ContentPartDriver, IContentItemDriver where TContent : ContentPart, new() { private readonly ContentType _contentType; protected virtual bool UseDefaultTemplate { get { return false; } } diff --git a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs index 53c466aa8..91b12f342 100644 --- a/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs +++ b/src/Orchard/ContentManagement/Drivers/ContentPartDriver.cs @@ -1,13 +1,18 @@ -using Orchard.ContentManagement.Handlers; +using System.Collections.Generic; +using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.MetaData; namespace Orchard.ContentManagement.Drivers { + public interface IContentPartDriver : IEvents { DriverResult BuildDisplayModel(BuildDisplayModelContext context); DriverResult BuildEditorModel(BuildEditorModelContext context); DriverResult UpdateEditorModel(UpdateEditorModelContext context); + + IEnumerable GetPartInfo(); } - public abstract class ContentPartDriver : IContentPartDriver where TContent : class, IContent { + public abstract class ContentPartDriver : IContentPartDriver where TContent : ContentPart, new() { protected virtual string Prefix { get { return ""; } } protected virtual string Zone { get { return "body"; } } @@ -46,5 +51,16 @@ namespace Orchard.ContentManagement.Drivers { public CombinedResult Combined(params DriverResult[] results) { return new CombinedResult(results); } + + public IEnumerable GetPartInfo() + { + var contentPartInfo = new List() { + new ContentPartInfo() + {PartName = typeof(TContent).Name,Factory = () => new TContent()} + }; + + return contentPartInfo; + } + } } \ No newline at end of file diff --git a/src/Orchard/ContentManagement/Handlers/ContentItemBuilder.cs b/src/Orchard/ContentManagement/Handlers/ContentItemBuilder.cs index 4c6960eed..fc8449d6c 100644 --- a/src/Orchard/ContentManagement/Handlers/ContentItemBuilder.cs +++ b/src/Orchard/ContentManagement/Handlers/ContentItemBuilder.cs @@ -15,5 +15,11 @@ _item.Weld(part); return this; } + + public ContentItemBuilder Weld(ContentPart contentPart) { + _item.Weld(contentPart); + return this; + } + } } diff --git a/src/Orchard/ContentManagement/Handlers/IContentHandler.cs b/src/Orchard/ContentManagement/Handlers/IContentHandler.cs index 46a53bac2..057165c3c 100644 --- a/src/Orchard/ContentManagement/Handlers/IContentHandler.cs +++ b/src/Orchard/ContentManagement/Handlers/IContentHandler.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Orchard.Events; namespace Orchard.ContentManagement.Handlers { public interface IContentHandler : IEvents { diff --git a/src/Orchard/ContentManagement/Handlers/StorageFilter.cs b/src/Orchard/ContentManagement/Handlers/StorageFilter.cs index 2abdc5efe..b6411f730 100644 --- a/src/Orchard/ContentManagement/Handlers/StorageFilter.cs +++ b/src/Orchard/ContentManagement/Handlers/StorageFilter.cs @@ -26,6 +26,16 @@ namespace Orchard.ContentManagement.Handlers { _repository = repository; } + protected virtual TRecord GetRecordCore(ContentItemVersionRecord versionRecord) { + return _repository.Get(versionRecord.ContentItemRecord.Id); + } + + protected virtual TRecord CreateRecordCore(ContentItemVersionRecord versionRecord, TRecord record) { + record.ContentItemRecord = versionRecord.ContentItemRecord; + _repository.Create(record); + return record; + } + protected override void Activated(ActivatedContentContext context, ContentPart instance) { if (instance.Record != null) { throw new InvalidOperationException(string.Format( @@ -36,23 +46,12 @@ namespace Orchard.ContentManagement.Handlers { } protected override void Creating(CreateContentContext context, ContentPart instance) { - instance.Record.ContentItemRecord = context.ContentItemRecord; - _repository.Create(instance.Record); - } - - protected virtual TRecord GetRecord(LoadContentContext context) { - return _repository.Get(context.Id); + CreateRecordCore(context.ContentItemVersionRecord, instance.Record); } protected override void Loading(LoadContentContext context, ContentPart instance) { - var record = GetRecord(context); - if (record != null) { - instance.Record = record; - } - else { - var createContext = new CreateContentContext(context.ContentItem); - Creating(createContext, instance); - } + var versionRecord = context.ContentItemVersionRecord; + instance._record.Loader(prior => GetRecordCore(versionRecord) ?? CreateRecordCore(versionRecord, prior)); } protected override void Versioning(VersionContentContext context, ContentPart existing, ContentPart building) { diff --git a/src/Orchard/ContentManagement/Handlers/StorageVersionFilter.cs b/src/Orchard/ContentManagement/Handlers/StorageVersionFilter.cs index e7247fd4a..2ac987351 100644 --- a/src/Orchard/ContentManagement/Handlers/StorageVersionFilter.cs +++ b/src/Orchard/ContentManagement/Handlers/StorageVersionFilter.cs @@ -7,14 +7,15 @@ namespace Orchard.ContentManagement.Handlers { : base(repository) { } - protected override TRecord GetRecord(LoadContentContext context) { - return _repository.Get(context.ContentItemVersionRecord.Id); + protected override TRecord GetRecordCore(ContentItemVersionRecord versionRecord) { + return _repository.Get(versionRecord.Id); } - protected override void Creating(CreateContentContext context, ContentPart instance) { - instance.Record.ContentItemRecord = context.ContentItemRecord; - instance.Record.ContentItemVersionRecord = context.ContentItemVersionRecord; - _repository.Create(instance.Record); + protected override TRecord CreateRecordCore(ContentItemVersionRecord versionRecord, TRecord record) { + record.ContentItemRecord = versionRecord.ContentItemRecord; + record.ContentItemVersionRecord = versionRecord; + _repository.Create(record); + return record; } protected override void Versioning(VersionContentContext context, ContentPart existing, ContentPart building) { diff --git a/src/Orchard/ContentManagement/IContentManagerSession.cs b/src/Orchard/ContentManagement/IContentManagerSession.cs new file mode 100644 index 000000000..5664a62d9 --- /dev/null +++ b/src/Orchard/ContentManagement/IContentManagerSession.cs @@ -0,0 +1,6 @@ +namespace Orchard.ContentManagement { + public interface IContentManagerSession : IDependency { + void Store(ContentItem item); + bool RecallVersionRecordId(int id, out ContentItem item); + } +} diff --git a/src/Orchard/ContentManagement/MetaData/ContentPartHandler.cs b/src/Orchard/ContentManagement/MetaData/ContentPartHandler.cs new file mode 100644 index 000000000..350e27371 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/ContentPartHandler.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using System.Linq; +using Orchard.ContentManagement.Handlers; +using Orchard.ContentManagement.MetaData.Services; +using Orchard.ContentManagement.Drivers; + +namespace Orchard.ContentManagement.MetaData { + public class ContentPartHandler : ContentHandler { + private readonly IEnumerable _contentPartDrivers; + private readonly IContentTypeService _contentTypeService; + + public ContentPartHandler(IEnumerable contentPartDrivers, IContentTypeService contentTypeService) { + _contentPartDrivers = contentPartDrivers; + _contentTypeService = contentTypeService; + } + + protected override void Activating(ActivatingContentContext context) { + var contentTypeRecord = _contentTypeService.GetContentTypeRecord(context.ContentType); + if (contentTypeRecord == null) + return; + + var contentPartInfos = _contentPartDrivers.SelectMany(cpp => cpp.GetPartInfo()).ToList(); + + foreach (var contentTypePartRecord in contentTypeRecord.ContentParts) { + // We might have a part in the database, but the corresponding feature might not + // be enabled anymore, so we need to be resilient to that situation. + var contentPartInfo = contentPartInfos.SingleOrDefault(x => x.PartName == contentTypePartRecord.PartName.PartName); + if (contentPartInfo != null) { + context.Builder.Weld(contentPartInfo.Factory()); + } + } + } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/ContentPartInfo.cs b/src/Orchard/ContentManagement/MetaData/ContentPartInfo.cs new file mode 100644 index 000000000..23a731cbf --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/ContentPartInfo.cs @@ -0,0 +1,10 @@ +using System; + +namespace Orchard.ContentManagement.MetaData +{ + public class ContentPartInfo + { + public string PartName { get; set; } + public Func Factory { get; set; } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartNameRecord.cs b/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartNameRecord.cs new file mode 100644 index 000000000..7f0caddb2 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartNameRecord.cs @@ -0,0 +1,6 @@ +namespace Orchard.ContentManagement.MetaData.Records { + public class ContentTypePartNameRecord { + public virtual int Id { get; set; } + public virtual string PartName { get; set; } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartRecord.cs b/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartRecord.cs new file mode 100644 index 000000000..e723d18e7 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Records/ContentTypePartRecord.cs @@ -0,0 +1,6 @@ +namespace Orchard.ContentManagement.MetaData.Records { + public class ContentTypePartRecord { + public virtual int Id { get; set; } + public virtual ContentTypePartNameRecord PartName { get; set; } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Services/ContentTypeService.cs b/src/Orchard/ContentManagement/MetaData/Services/ContentTypeService.cs new file mode 100644 index 000000000..a0b71bb20 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Services/ContentTypeService.cs @@ -0,0 +1,81 @@ +using System.Collections.Generic; +using System.Linq; +using JetBrains.Annotations; +using Orchard.ContentManagement.MetaData.Records; +using Orchard.ContentManagement.Records; +using Orchard.Data; + +namespace Orchard.ContentManagement.MetaData.Services { + [UsedImplicitly] + public class ContentTypeService : IContentTypeService { + private readonly IRepository _contentTypeRepository; + private readonly IRepository _contentTypePartNameRepository; + private readonly IRepository _contentTypePartRepository; + + public ContentTypeService(IRepository contentTypePartRepository, IRepository contentTypeRepository, IRepository contentTypePartNameRepository) { + _contentTypeRepository = contentTypeRepository; + _contentTypePartNameRepository = contentTypePartNameRepository; + _contentTypePartRepository = contentTypePartRepository; + } + + public ContentTypeRecord GetContentTypeRecord(string contentTypeName) { + return _contentTypeRepository.Fetch(x => x.Name == contentTypeName).SingleOrDefault(); + } + + public ContentTypePartNameRecord GetContentPartNameRecord(string name) { + return _contentTypePartNameRepository.Fetch(x => x.PartName == name).SingleOrDefault(); + } + + public IEnumerable GetContentTypes() { + return _contentTypeRepository.Table.ToList(); + } + + public IEnumerable GetContentTypePartNames() { + return _contentTypePartNameRepository.Table.ToList(); + } + + public void MapContentTypeToContentPart(string contentType, string contentPart) { + // Create content type if needed + var contentTypeRecord = GetContentTypeRecord(contentType); + if (contentTypeRecord == null) { + contentTypeRecord = new ContentTypeRecord { Name = contentType }; + _contentTypeRepository.Create(contentTypeRecord); + } + + // Create part name if needed + var contentTypePartNameRecord = GetContentPartNameRecord(contentPart); + if (contentTypePartNameRecord == null) { + contentTypePartNameRecord = new ContentTypePartNameRecord { PartName = contentPart }; + _contentTypePartNameRepository.Create(contentTypePartNameRecord); + } + + // Add part name to content type + var contentTypePartRecord = new ContentTypePartRecord { PartName = contentTypePartNameRecord }; + contentTypeRecord.ContentParts.Add(contentTypePartRecord); + } + + public void UnMapContentTypeToContentPart(string contentType, string contentPart) { + var contentTypeRecord = GetContentTypeRecord(contentType); + var contentTypePartNameRecord = _contentTypePartNameRepository.Fetch(x => x.PartName == contentPart).Single(); + var contentTypePartRecord = contentTypeRecord.ContentParts.Single(x => x.PartName == contentTypePartNameRecord); + contentTypeRecord.ContentParts.Remove(contentTypePartRecord); + } + + public bool ValidateContentTypeToContentPartMapping(string contentType, string contentPart) { + var contentTypeRecord = GetContentTypeRecord(contentType) ?? new ContentTypeRecord(); + if (contentTypeRecord.ContentParts.Count == 0) + return false; + var contentTypePart = contentTypeRecord.ContentParts.Single(x => x.PartName.PartName == contentPart); + return contentTypePart != null; + } + + + public void AddContentTypePartNameToMetaData(string contentTypePartName) { + var contentTypePartNameRecord = new ContentTypePartNameRecord() { + PartName = contentTypePartName + }; + + _contentTypePartNameRepository.Update(contentTypePartNameRecord); + } + } +} diff --git a/src/Orchard/ContentManagement/MetaData/Services/IContentTypeService.cs b/src/Orchard/ContentManagement/MetaData/Services/IContentTypeService.cs new file mode 100644 index 000000000..2ab533896 --- /dev/null +++ b/src/Orchard/ContentManagement/MetaData/Services/IContentTypeService.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Orchard.ContentManagement.MetaData.Records; +using Orchard.ContentManagement.Records; + +namespace Orchard.ContentManagement.MetaData.Services +{ + public interface IContentTypeService : IDependency { + void MapContentTypeToContentPart(string contentType, string contentPart); + void UnMapContentTypeToContentPart(string contentType, string contentPart); + void AddContentTypePartNameToMetaData(string contentTypePartName); + ContentTypeRecord GetContentTypeRecord(string contentTypeName); + bool ValidateContentTypeToContentPartMapping(string contentType, string contentPart); + IEnumerable GetContentTypes(); + IEnumerable GetContentTypePartNames(); + ContentTypePartNameRecord GetContentPartNameRecord(string name); + } +} diff --git a/src/Orchard/ContentManagement/Records/ContentPartRecord.cs b/src/Orchard/ContentManagement/Records/ContentPartRecord.cs index b51db5f85..df2c6f019 100644 --- a/src/Orchard/ContentManagement/Records/ContentPartRecord.cs +++ b/src/Orchard/ContentManagement/Records/ContentPartRecord.cs @@ -1,6 +1,9 @@ +using Orchard.Data.Conventions; + namespace Orchard.ContentManagement.Records { public abstract class ContentPartRecord { public virtual int Id { get; set; } + [CascadeAllDeleteOrphan] public virtual ContentItemRecord ContentItemRecord { get; set; } } } diff --git a/src/Orchard/ContentManagement/Records/ContentTypeRecord.cs b/src/Orchard/ContentManagement/Records/ContentTypeRecord.cs index f908ceff3..b21774f3f 100644 --- a/src/Orchard/ContentManagement/Records/ContentTypeRecord.cs +++ b/src/Orchard/ContentManagement/Records/ContentTypeRecord.cs @@ -1,6 +1,15 @@ -namespace Orchard.ContentManagement.Records { +using System.Collections.Generic; +using Orchard.ContentManagement.MetaData.Records; +using Orchard.Data.Conventions; + +namespace Orchard.ContentManagement.Records { public class ContentTypeRecord { + public ContentTypeRecord() { + ContentParts = new List(); + } public virtual int Id { get; set; } public virtual string Name { get; set; } + [CascadeAllDeleteOrphan] + public virtual IList ContentParts { get; set; } } } diff --git a/src/Orchard/ContentManagement/Utilities/LazyField.cs b/src/Orchard/ContentManagement/Utilities/LazyField.cs new file mode 100644 index 000000000..b03c59779 --- /dev/null +++ b/src/Orchard/ContentManagement/Utilities/LazyField.cs @@ -0,0 +1,40 @@ +using System; + +namespace Orchard.ContentManagement.Utilities { + public class LazyField { + private T _value; + private Func _loader; + private Func _setter; + + public T Value { + get { return GetValue(); } + set { SetValue(value); } + } + + public void Loader(Func loader) { + _loader = loader; + } + + public void Setter(Func setter) { + _setter = setter; + } + + private T GetValue() { + if (_loader != null) { + _value = _loader(_value); + _loader = null; + } + return _value; + } + + private void SetValue(T value) { + _loader = null; + if (_setter != null) { + _value = _setter(value); + } + else { + _value = value; + } + } + } +} diff --git a/src/Orchard/Data/SessionFactoryHolder.cs b/src/Orchard/Data/SessionFactoryHolder.cs index e072559c2..8c37274b7 100644 --- a/src/Orchard/Data/SessionFactoryHolder.cs +++ b/src/Orchard/Data/SessionFactoryHolder.cs @@ -5,6 +5,7 @@ using Orchard.Data.Builders; using Orchard.Environment.Configuration; using Orchard.Environment.Topology; using Orchard.Environment.Topology.Models; +using Orchard.FileSystems.AppData; using Orchard.Logging; namespace Orchard.Data { diff --git a/src/Orchard/Environment/Configuration/IShellSettingsManager.cs b/src/Orchard/Environment/Configuration/IShellSettingsManager.cs new file mode 100644 index 000000000..d58eedade --- /dev/null +++ b/src/Orchard/Environment/Configuration/IShellSettingsManager.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace Orchard.Environment.Configuration { + public interface IShellSettingsManager { + IEnumerable LoadSettings(); + void SaveSettings(ShellSettings settings); + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/Configuration/IShellSettingsManagerEventHandler.cs b/src/Orchard/Environment/Configuration/IShellSettingsManagerEventHandler.cs new file mode 100644 index 000000000..f0aff295d --- /dev/null +++ b/src/Orchard/Environment/Configuration/IShellSettingsManagerEventHandler.cs @@ -0,0 +1,7 @@ +using Orchard.Events; + +namespace Orchard.Environment.Configuration { + public interface IShellSettingsManagerEventHandler : IEventHandler { + void Saved(ShellSettings settings); + } +} \ No newline at end of file diff --git a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs index 0267bc955..85927d370 100644 --- a/src/Orchard/Environment/Configuration/ShellSettingsManager.cs +++ b/src/Orchard/Environment/Configuration/ShellSettingsManager.cs @@ -3,23 +3,22 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Yaml.Serialization; -using Orchard.Events; +using Orchard.FileSystems.AppData; using Orchard.Localization; namespace Orchard.Environment.Configuration { - public interface IShellSettingsManager { - IEnumerable LoadSettings(); - void SaveSettings(ShellSettings settings); - } - public class ShellSettingsManager : IShellSettingsManager { private readonly IAppDataFolder _appDataFolder; - private readonly IEventBus _eventBus; - Localizer T { get; set; } + private readonly IShellSettingsManagerEventHandler _events; - public ShellSettingsManager(IAppDataFolder appDataFolder, IEventBus eventBus) { + Localizer T { get; set; } + + public ShellSettingsManager( + IAppDataFolder appDataFolder, + IShellSettingsManagerEventHandler events) { _appDataFolder = appDataFolder; - _eventBus = eventBus; + _events = events; + T = NullLocalizer.Instance; } @@ -35,8 +34,7 @@ namespace Orchard.Environment.Configuration { var filePath = Path.Combine(Path.Combine("Sites", settings.Name), "Settings.txt"); _appDataFolder.CreateFile(filePath, ComposeSettings(settings)); - - _eventBus.Notify("ShellSettings_Saved", null); + _events.Saved(settings); } IEnumerable LoadSettings() { diff --git a/src/Orchard/Environment/DefaultOrchardHost.cs b/src/Orchard/Environment/DefaultOrchardHost.cs index 97d62fa8c..1ba354df6 100644 --- a/src/Orchard/Environment/DefaultOrchardHost.cs +++ b/src/Orchard/Environment/DefaultOrchardHost.cs @@ -6,13 +6,15 @@ using System.Collections.Generic; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; using Orchard.Environment.ShellBuilders; +using Orchard.Environment.Topology; +using Orchard.Environment.Topology.Models; using Orchard.Logging; using Orchard.Mvc; using Orchard.Mvc.ViewEngines; using Orchard.Utility.Extensions; namespace Orchard.Environment { - public class DefaultOrchardHost : IOrchardHost { + public class DefaultOrchardHost : IOrchardHost, IShellSettingsManagerEventHandler, IShellDescriptorManagerEventHandler { private readonly ControllerBuilder _controllerBuilder; private readonly IShellSettingsManager _shellSettingsManager; @@ -49,10 +51,6 @@ namespace Orchard.Environment { BuildCurrent(); } - void IOrchardHost.Reinitialize_Obsolete() { - _current = null; - } - void IOrchardHost.BeginRequest() { Logger.Debug("BeginRequest"); @@ -135,13 +133,13 @@ namespace Orchard.Environment { } } - public void Process(string messageName, IDictionary eventData) { - if (messageName == "ShellSettings_Saved") { - _current = null; - } - else if (messageName == "ShellDescriptor_Changed") { - _current = null; - } + + void IShellSettingsManagerEventHandler.Saved(ShellSettings settings) { + _current = null; + } + + void IShellDescriptorManagerEventHandler.Changed(ShellDescriptor descriptor) { + _current = null; } } } diff --git a/src/Orchard/Environment/DefaultOrchardHostEventSink.cs b/src/Orchard/Environment/DefaultOrchardHostEventSink.cs deleted file mode 100644 index 9d0542e21..000000000 --- a/src/Orchard/Environment/DefaultOrchardHostEventSink.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; -using Orchard.Events; - -namespace Orchard.Environment { - /// - /// This handler forwards calls to the IOrchardHost when it is an instance of DefaultOrchardHost. - /// The reason for this is to avoid adding IEventBusHandler, because DefaultOrchardHost is a component - /// that should not be detected and registererd automatically as an IDependency. - /// - public class DefaultOrchardHostEventSink : IEventBusHandler { - private readonly DefaultOrchardHost _host; - - public DefaultOrchardHostEventSink(IOrchardHost host) { - _host = host as DefaultOrchardHost; - } - - public void Process(string messageName, IDictionary eventData) { - if (_host != null) { - _host.Process(messageName, eventData); - } - } - } -} diff --git a/src/Orchard/Environment/Extensions/ExtensionManager.cs b/src/Orchard/Environment/Extensions/ExtensionManager.cs index 99821ae0a..8f8737b09 100644 --- a/src/Orchard/Environment/Extensions/ExtensionManager.cs +++ b/src/Orchard/Environment/Extensions/ExtensionManager.cs @@ -34,13 +34,7 @@ namespace Orchard.Environment.Extensions { // This method does not load extension types, simply parses extension manifests from // the filesystem. public IEnumerable AvailableExtensions() { - var availableExtensions = new List(); - foreach (var folder in _folders) { - foreach (var name in folder.ListNames()) { - availableExtensions.Add(GetDescriptorForExtension(name, folder)); - } - } - return availableExtensions; + return _folders.SelectMany(folder=>folder.AvailableExtensions()); } public IEnumerable LoadFeatures(IEnumerable featureDescriptors) { @@ -57,65 +51,6 @@ namespace Orchard.Environment.Extensions { return _activeExtensions; } - - private static ExtensionDescriptor GetDescriptorForExtension(string name, IExtensionFolders folder) { - string extensionType = folder is ThemeFolders ? "Theme" : "Module"; - var parseResult = folder.ParseManifest(name); - var mapping = (Mapping)parseResult.YamlDocument.Root; - var fields = mapping.Entities - .Where(x => x.Key is Scalar) - .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); - - var extensionDescriptor = new ExtensionDescriptor { - Location = parseResult.Location, - Name = name, - ExtensionType = extensionType, - DisplayName = GetValue(fields, "name") ?? name, - Description = GetValue(fields, "description"), - Version = GetValue(fields, "version"), - OrchardVersion = GetValue(fields, "orchardversion"), - Author = GetValue(fields, "author"), - WebSite = GetValue(fields, "website"), - Tags = GetValue(fields, "tags"), - AntiForgery = GetValue(fields, "antiforgery"), - }; - extensionDescriptor.Features = GetFeaturesForExtension(GetMapping(fields, "features"), extensionDescriptor); - return extensionDescriptor; - } - - private static IEnumerable GetFeaturesForExtension(Mapping features, ExtensionDescriptor extensionDescriptor) { - List featureDescriptors = new List(); - if (features != null) { - foreach (var entity in features.Entities) { - FeatureDescriptor featureDescriptor = new FeatureDescriptor { - Extension = extensionDescriptor, - Name = entity.Key.ToString(), - }; - Mapping featureMapping = (Mapping)entity.Value; - foreach (var featureEntity in featureMapping.Entities) { - if (String.Equals(featureEntity.Key.ToString(), "description", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Description = featureEntity.Value.ToString(); - } - else if (String.Equals(featureEntity.Key.ToString(), "category", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Category = featureEntity.Value.ToString(); - } - else if (String.Equals(featureEntity.Key.ToString(), "dependencies", StringComparison.OrdinalIgnoreCase)) { - featureDescriptor.Dependencies = ParseFeatureDependenciesEntry(featureEntity.Value.ToString()); - } - } - featureDescriptors.Add(featureDescriptor); - } - } - if (!featureDescriptors.Any(fd => fd.Name == extensionDescriptor.Name)) { - featureDescriptors.Add(new FeatureDescriptor { - Name = extensionDescriptor.Name, - Dependencies = new string[0], - Extension = extensionDescriptor, - }); - } - return featureDescriptors; - } - private Feature LoadFeature(FeatureDescriptor featureDescriptor) { var featureName = featureDescriptor.Name; string extensionName = GetExtensionForFeature(featureName); @@ -237,28 +172,5 @@ namespace Orchard.Environment.Extensions { return null; } - private static string[] ParseFeatureDependenciesEntry(string dependenciesEntry) { - List dependencies = new List(); - foreach (var s in dependenciesEntry.Split(',')) { - dependencies.Add(s.Trim()); - } - return dependencies.ToArray(); - } - - private static Mapping GetMapping( - IDictionary fields, - string key) { - - DataItem value; - return fields.TryGetValue(key, out value) ? (Mapping)value : null; - } - - private static string GetValue( - IDictionary fields, - string key) { - - DataItem value; - return fields.TryGetValue(key, out value) ? value.ToString() : null; - } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Folders/AreaFolders.cs b/src/Orchard/Environment/Extensions/Folders/AreaFolders.cs index eb9a17fc0..7571343b1 100644 --- a/src/Orchard/Environment/Extensions/Folders/AreaFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/AreaFolders.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using Orchard.Caching; +using Orchard.FileSystems.WebSite; namespace Orchard.Environment.Extensions.Folders { public class AreaFolders : ExtensionFolders { - public AreaFolders(IEnumerable paths) : - base(paths, "Module.txt", true/*isManifestOptional*/) { + public AreaFolders(IEnumerable paths, ICacheManager cacheManager, IWebSiteFolder webSiteFolder) : + base(paths, "Module.txt", true/*isManifestOptional*/, cacheManager, webSiteFolder) { } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs index 86ed67d61..c8dd79a7e 100644 --- a/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/ExtensionFolders.cs @@ -1,67 +1,166 @@ +using System; using System.Collections.Generic; using System.IO; using System.Linq; -using Orchard.Environment.Extensions.Helpers; +using Orchard.Caching; +using Orchard.Environment.Extensions.Models; +using Orchard.FileSystems.WebSite; using Yaml.Grammar; namespace Orchard.Environment.Extensions.Folders { + public class ParseResult { + public string Location { get; set; } + public string Name { get; set; } + public YamlDocument YamlDocument { get; set; } + } + public class ExtensionFolders : IExtensionFolders { private readonly IEnumerable _paths; private readonly string _manifestName; + private readonly string _extensionType; private readonly bool _manifestIsOptional; + private readonly ICacheManager _cacheManager; + private readonly IWebSiteFolder _webSiteFolder; - public ExtensionFolders(IEnumerable paths, string manifestName, bool manifestIsOptional) { + protected ExtensionFolders( + IEnumerable paths, + string manifestName, + bool manifestIsOptional, + ICacheManager cacheManager, + IWebSiteFolder webSiteFolder) { _paths = paths; _manifestName = manifestName; + _extensionType = manifestName == "Theme.txt" ? "Theme" : "Module"; _manifestIsOptional = manifestIsOptional; + _cacheManager = cacheManager; + _webSiteFolder = webSiteFolder; } - public IEnumerable ListNames() { - foreach (var path in _paths) { - if (!Directory.Exists(PathHelpers.GetPhysicalPath(path))) - continue; + public IEnumerable AvailableExtensions() { + var list = new List(); + foreach (var locationPath in _paths) { + var subfolderPaths = _cacheManager.Get(locationPath, ctx => { + ctx.Monitor(_webSiteFolder.WhenPathChanges(ctx.Key)); + return _webSiteFolder.ListDirectories(ctx.Key); + }); + foreach (var subfolderPath in subfolderPaths) { + var extensionName = Path.GetFileName(subfolderPath.TrimEnd('/', '\\')); + var manifestPath = Path.Combine(subfolderPath, _manifestName); + var descriptor = GetExtensionDescriptor(locationPath, extensionName, manifestPath); + if (descriptor != null) + list.Add(descriptor); + } + } + return list; + } - foreach (var directoryName in Directory.GetDirectories(PathHelpers.GetPhysicalPath(path))) { - if (_manifestIsOptional || File.Exists(Path.Combine(directoryName, _manifestName))) { - yield return Path.GetFileName(directoryName); + private ExtensionDescriptor GetExtensionDescriptor(string locationPath, string extensionName, string manifestPath) { + return _cacheManager.Get(manifestPath, context => { + + context.Monitor(_webSiteFolder.WhenPathChanges(manifestPath)); + + var manifestText = _webSiteFolder.ReadFile(manifestPath); + if (manifestText == null) { + if (_manifestIsOptional) { + manifestText = string.Format("name: {0}", extensionName); + } + else { + return null; } } - } + + return GetDescriptorForExtension(locationPath, extensionName, ParseManifest(manifestText)); + }); } - public ParseResult ParseManifest(string name) { - foreach (var path in _paths) { - if (!Directory.Exists(PathHelpers.GetPhysicalPath(path))) - continue; + private ExtensionDescriptor GetDescriptorForExtension(string locationPath, string extensionName, ParseResult parseResult) { + return GetDescriptorForExtension(locationPath, extensionName, _extensionType, parseResult); + } - var extensionDirectoryPath = Path.Combine(PathHelpers.GetPhysicalPath(path), name); - if (!Directory.Exists(PathHelpers.GetPhysicalPath(extensionDirectoryPath))) - continue; + public static ParseResult ParseManifest(string manifestText) { + bool success; + var yamlStream = new YamlParser().ParseYamlStream(new TextInput(manifestText), out success); + if (yamlStream == null || !success) { + return null; + } + return new ParseResult { + Name = manifestText, + YamlDocument = yamlStream.Documents.Single() + }; + } - var extensionManifestPath = Path.Combine(extensionDirectoryPath, _manifestName); + public static ExtensionDescriptor GetDescriptorForExtension(string locationPath, string extensionName, string extensionType, ParseResult parseResult) { + var mapping = (Mapping)parseResult.YamlDocument.Root; + var fields = mapping.Entities + .Where(x => x.Key is Scalar) + .ToDictionary(x => ((Scalar)x.Key).Text, x => x.Value); - if (File.Exists(extensionManifestPath)) { - var yamlStream = YamlParser.Load(extensionManifestPath); - return new ParseResult { - Location = path, - Name = name, - YamlDocument = yamlStream.Documents.Single() - }; - } + var extensionDescriptor = new ExtensionDescriptor { + Location = locationPath, + Name = extensionName, + ExtensionType = extensionType, + DisplayName = GetValue(fields, "name") ?? extensionName, + Description = GetValue(fields, "description"), + Version = GetValue(fields, "version"), + OrchardVersion = GetValue(fields, "orchardversion"), + Author = GetValue(fields, "author"), + WebSite = GetValue(fields, "website"), + Tags = GetValue(fields, "tags"), + AntiForgery = GetValue(fields, "antiforgery"), + }; + extensionDescriptor.Features = GetFeaturesForExtension(GetMapping(fields, "features"), extensionDescriptor); + return extensionDescriptor; + } - if (_manifestIsOptional) { - var yamlInput = new TextInput(string.Format("name: {0}", name)); - var parser = new YamlParser(); - bool success; - var yamlStream = parser.ParseYamlStream(yamlInput, out success); - return new ParseResult { - Location = path, - Name = name, - YamlDocument = yamlStream.Documents.Single() - }; + private static IEnumerable GetFeaturesForExtension(Mapping features, ExtensionDescriptor extensionDescriptor) { + var featureDescriptors = new List(); + if (features != null) { + foreach (var entity in features.Entities) { + var featureDescriptor = new FeatureDescriptor { + Extension = extensionDescriptor, + Name = entity.Key.ToString(), + }; + var featureMapping = (Mapping)entity.Value; + foreach (var featureEntity in featureMapping.Entities) { + if (String.Equals(featureEntity.Key.ToString(), "description", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Description = featureEntity.Value.ToString(); + } + else if (String.Equals(featureEntity.Key.ToString(), "category", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Category = featureEntity.Value.ToString(); + } + else if (String.Equals(featureEntity.Key.ToString(), "dependencies", StringComparison.OrdinalIgnoreCase)) { + featureDescriptor.Dependencies = ParseFeatureDependenciesEntry(featureEntity.Value.ToString()); + } + } + featureDescriptors.Add(featureDescriptor); } } - return null; + if (!featureDescriptors.Any(fd => fd.Name == extensionDescriptor.Name)) { + featureDescriptors.Add(new FeatureDescriptor { + Name = extensionDescriptor.Name, + Dependencies = new string[0], + Extension = extensionDescriptor, + }); + } + return featureDescriptors; + } + + private static string[] ParseFeatureDependenciesEntry(string dependenciesEntry) { + var dependencies = new List(); + foreach (var s in dependenciesEntry.Split(',')) { + dependencies.Add(s.Trim()); + } + return dependencies.ToArray(); + } + + private static Mapping GetMapping(IDictionary fields, string key) { + DataItem value; + return fields.TryGetValue(key, out value) ? (Mapping)value : null; + } + + private static string GetValue(IDictionary fields, string key) { + DataItem value; + return fields.TryGetValue(key, out value) ? value.ToString() : null; } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Folders/IExtensionFolders.cs b/src/Orchard/Environment/Extensions/Folders/IExtensionFolders.cs index eb57ccf24..94612540a 100644 --- a/src/Orchard/Environment/Extensions/Folders/IExtensionFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/IExtensionFolders.cs @@ -1,15 +1,9 @@ using System.Collections.Generic; +using Orchard.Environment.Extensions.Models; using Yaml.Grammar; namespace Orchard.Environment.Extensions.Folders { public interface IExtensionFolders { - IEnumerable ListNames(); - ParseResult ParseManifest(string name); - } - - public class ParseResult { - public string Location { get; set; } - public string Name { get; set; } - public YamlDocument YamlDocument { get; set; } + IEnumerable AvailableExtensions(); } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Folders/ModuleFolders.cs b/src/Orchard/Environment/Extensions/Folders/ModuleFolders.cs index 1e256a4e0..dd8569f41 100644 --- a/src/Orchard/Environment/Extensions/Folders/ModuleFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/ModuleFolders.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using Orchard.Caching; +using Orchard.FileSystems.WebSite; namespace Orchard.Environment.Extensions.Folders { public class ModuleFolders : ExtensionFolders { - public ModuleFolders(IEnumerable paths) : - base(paths, "Module.txt", false/*isManifestOptional*/) { + public ModuleFolders(IEnumerable paths, ICacheManager cacheManager, IWebSiteFolder webSiteFolder) : + base(paths, "Module.txt", false/*isManifestOptional*/, cacheManager, webSiteFolder) { } } } \ No newline at end of file diff --git a/src/Orchard/Environment/Extensions/Folders/ThemeFolders.cs b/src/Orchard/Environment/Extensions/Folders/ThemeFolders.cs index 08253d384..b832514ad 100644 --- a/src/Orchard/Environment/Extensions/Folders/ThemeFolders.cs +++ b/src/Orchard/Environment/Extensions/Folders/ThemeFolders.cs @@ -1,9 +1,11 @@ using System.Collections.Generic; +using Orchard.Caching; +using Orchard.FileSystems.WebSite; namespace Orchard.Environment.Extensions.Folders { public class ThemeFolders : ExtensionFolders { - public ThemeFolders(IEnumerable paths) : - base(paths, "Theme.txt", false/*manifestIsOptional*/) { + public ThemeFolders(IEnumerable paths, ICacheManager cacheManager, IWebSiteFolder webSiteFolder) : + base(paths, "Theme.txt", false/*manifestIsOptional*/, cacheManager, webSiteFolder) { } } } \ No newline at end of file diff --git a/src/Orchard/Environment/IOrchardHost.cs b/src/Orchard/Environment/IOrchardHost.cs index 754ca84a0..0a88e51bb 100644 --- a/src/Orchard/Environment/IOrchardHost.cs +++ b/src/Orchard/Environment/IOrchardHost.cs @@ -7,11 +7,6 @@ namespace Orchard.Environment { /// void Initialize(); - /// - /// Called when configuration changes requires the shell topology to be reloaded and applied - /// - void Reinitialize_Obsolete(); - /// /// Called each time a request begins to offer a just-in-time reinitialization point /// diff --git a/src/Orchard/Environment/OrchardStarter.cs b/src/Orchard/Environment/OrchardStarter.cs index b2ca7ad74..3d43bd9ad 100644 --- a/src/Orchard/Environment/OrchardStarter.cs +++ b/src/Orchard/Environment/OrchardStarter.cs @@ -5,6 +5,7 @@ using System.Web.Hosting; using Autofac; using Autofac.Configuration; using Autofac.Integration.Web; +using Orchard.Caching; using Orchard.Environment.AutofacUtil; using Orchard.Environment.Configuration; using Orchard.Environment.Extensions; @@ -13,19 +14,28 @@ using Orchard.Environment.Extensions.Loaders; using Orchard.Environment.ShellBuilders; using Orchard.Environment.Topology; using Orchard.Events; +using Orchard.FileSystems.AppData; +using Orchard.FileSystems.WebSite; using Orchard.Logging; +using Orchard.Services; namespace Orchard.Environment { public static class OrchardStarter { public static IContainer CreateHostContainer(Action registrations) { var builder = new ContainerBuilder(); builder.RegisterModule(new LoggingModule()); + builder.RegisterModule(new EventsModule()); + builder.RegisterModule(new CacheModule()); // a single default host implementation is needed for bootstrapping a web app domain builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); + RegisterVolatileProvider(builder); + + builder.RegisterType().As().As().SingleInstance(); { builder.RegisterType().As().SingleInstance(); @@ -90,6 +100,13 @@ namespace Orchard.Environment { return builder.Build(); } + private static void RegisterVolatileProvider(ContainerBuilder builder) where TService : IVolatileProvider { + builder.RegisterType() + .As() + .As() + .SingleInstance(); + } + public static IOrchardHost CreateHost(Action registrations) { var container = CreateHostContainer(registrations); return container.Resolve(); diff --git a/src/Orchard/Environment/RunningShellTable.cs b/src/Orchard/Environment/RunningShellTable.cs index ca96ecadc..5816c5125 100644 --- a/src/Orchard/Environment/RunningShellTable.cs +++ b/src/Orchard/Environment/RunningShellTable.cs @@ -8,6 +8,8 @@ using Orchard.Environment.Configuration; namespace Orchard.Environment { public interface IRunningShellTable { void Add(ShellSettings settings); + void Remove(ShellSettings settings); + void Update(ShellSettings settings); ShellSettings Match(HttpContextBase httpContext); } @@ -22,6 +24,30 @@ namespace Orchard.Environment { .Concat(new[] { settings }) .ToArray(); + Organize(); + } + + public void Remove(ShellSettings settings) { + _shells = _shells + .Where(s => s.Name != settings.Name) + .ToArray(); + + Organize(); + } + + public void Update(ShellSettings settings) { + _shells = _shells + .Where(s => s.Name != settings.Name) + .ToArray(); + + _shells = _shells + .Concat(new[] { settings }) + .ToArray(); + + Organize(); + } + + private void Organize() { var qualified = _shells.Where(x => !string.IsNullOrEmpty(x.RequestUrlHost) || !string.IsNullOrEmpty(x.RequestUrlPrefix)); @@ -50,7 +76,8 @@ namespace Orchard.Environment { } public ShellSettings Match(HttpContextBase httpContext) { - var host = httpContext.Request.ServerVariables.Get("HTTP_HOST") ?? ""; + // use Host header to prevent proxy alteration of the orignal request + var host = httpContext.Request.Headers["Host"]; var hostLength = host.IndexOf(':'); if (hostLength != -1) diff --git a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs index 2db70da88..bf20ac080 100644 --- a/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs +++ b/src/Orchard/Environment/ShellBuilders/ShellContainerFactory.cs @@ -12,6 +12,7 @@ using Autofac.Integration.Web.Mvc; using Orchard.Environment.AutofacUtil.DynamicProxy2; using Orchard.Environment.Configuration; using Orchard.Environment.Topology.Models; +using Orchard.Events; namespace Orchard.Environment.ShellBuilders { @@ -61,7 +62,9 @@ namespace Orchard.Environment.ShellBuilders { .EnableDynamicProxy(dynamicProxyContext) .InstancePerLifetimeScope(); - foreach (var interfaceType in item.Type.GetInterfaces().Where(itf => typeof(IDependency).IsAssignableFrom(itf))) { + foreach (var interfaceType in item.Type.GetInterfaces() + .Where(itf => typeof(IDependency).IsAssignableFrom(itf) + && !typeof(IEventHandler).IsAssignableFrom(itf))) { registration = registration.As(interfaceType); if (typeof(ISingletonDependency).IsAssignableFrom(interfaceType)) { registration = registration.InstancePerMatchingLifetimeScope("shell"); @@ -71,6 +74,10 @@ namespace Orchard.Environment.ShellBuilders { } } + if (typeof(IEventHandler).IsAssignableFrom(item.Type)) { + registration = registration.As(typeof(IEventHandler)); + } + foreach (var parameter in item.Parameters) { registration = registration .WithParameter(parameter.Name, parameter.Value) @@ -99,7 +106,8 @@ namespace Orchard.Environment.ShellBuilders { private IRegistrationBuilder RegisterType(ContainerBuilder builder, ShellTopologyItem item) { return builder.RegisterType(item.Type) - .WithProperty("Feature", item.Feature); + .WithProperty("Feature", item.Feature) + .WithMetadata("Feature", item.Feature); } } } diff --git a/src/Orchard/Environment/Topology/CompositionStrategy.cs b/src/Orchard/Environment/Topology/CompositionStrategy.cs index d50313f7f..f6dcd70fc 100644 --- a/src/Orchard/Environment/Topology/CompositionStrategy.cs +++ b/src/Orchard/Environment/Topology/CompositionStrategy.cs @@ -67,6 +67,7 @@ namespace Orchard.Environment.Topology { ExportedTypes = typeof(OrchardStarter).Assembly.GetExportedTypes() .Where(t => t.IsClass && !t.IsAbstract) + .Except(new[] { typeof(DefaultOrchardHost) }) .ToArray() }; } diff --git a/src/Orchard/Environment/Topology/IShellDescriptorManager.cs b/src/Orchard/Environment/Topology/IShellDescriptorManager.cs index 1a539c8c9..34b6c53e2 100644 --- a/src/Orchard/Environment/Topology/IShellDescriptorManager.cs +++ b/src/Orchard/Environment/Topology/IShellDescriptorManager.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Orchard.Environment.Topology.Models; +using Orchard.Events; namespace Orchard.Environment.Topology { /// @@ -24,5 +25,8 @@ namespace Orchard.Environment.Topology { IEnumerable parameters); } + public interface IShellDescriptorManagerEventHandler : IEventHandler { + void Changed(ShellDescriptor descriptor); + } } diff --git a/src/Orchard/Environment/Topology/ShellDescriptorCache.cs b/src/Orchard/Environment/Topology/ShellDescriptorCache.cs index 8d2fa07b4..5b9dda423 100644 --- a/src/Orchard/Environment/Topology/ShellDescriptorCache.cs +++ b/src/Orchard/Environment/Topology/ShellDescriptorCache.cs @@ -2,8 +2,8 @@ using System.IO; using System.Runtime.Serialization; using System.Xml; -using Orchard.Environment.Configuration; using Orchard.Environment.Topology.Models; +using Orchard.FileSystems.AppData; using Orchard.Localization; using Orchard.Logging; diff --git a/src/Orchard/Events/DefaultOrchardEventBus.cs b/src/Orchard/Events/DefaultOrchardEventBus.cs index 820b1fa55..d8eebfac4 100644 --- a/src/Orchard/Events/DefaultOrchardEventBus.cs +++ b/src/Orchard/Events/DefaultOrchardEventBus.cs @@ -1,24 +1,100 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Orchard.Localization; using Orchard.Logging; namespace Orchard.Events { public class DefaultOrchardEventBus : IEventBus { private readonly Func> _handlers; + private readonly Func> _eventHandlers; - public DefaultOrchardEventBus(Func> handlers) { + public DefaultOrchardEventBus(Func> handlers, Func> eventHandlers) { _handlers = handlers; + _eventHandlers = eventHandlers; Logger = NullLogger.Instance; + T = NullLocalizer.Instance; } public ILogger Logger { get; set; } + public Localizer T { get; set; } #region Implementation of IEventBus - public void Notify(string messageName, IDictionary eventData) { + public void Notify_Obsolete(string messageName, IDictionary eventData) { _handlers().Invoke(handler => handler.Process(messageName, eventData), Logger); } + public void Notify(string messageName, Dictionary eventData) { + string[] parameters = messageName.Split('.'); + if (parameters.Length != 2) { + throw new ArgumentException(messageName + T(" is not formatted correctly")); + } + string interfaceName = parameters[0]; + string methodName = parameters[1]; + + var eventHandlers = _eventHandlers(); + foreach (var eventHandler in eventHandlers) { + try { + TryInvoke(eventHandler, interfaceName, methodName, eventData); + } + catch(Exception ex) { + Logger.Error(ex, "{2} thrown from {0} by {1}", + interfaceName + "." +methodName, + eventHandler.GetType().FullName, + ex.GetType().Name); + } + } + } + + private static void TryInvoke(IEventHandler eventHandler, string interfaceName, string methodName, IDictionary arguments) { + Type type = eventHandler.GetType(); + foreach (var interfaceType in type.GetInterfaces()) { + if (String.Equals(interfaceType.Name, interfaceName, StringComparison.OrdinalIgnoreCase)) { + TryInvokeMethod(eventHandler, interfaceType, methodName, arguments); + break; + } + } + } + + private static void TryInvokeMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary arguments) { + MethodInfo method = GetMatchingMethod(eventHandler, interfaceType, methodName, arguments); + if (method != null) { + List parameters = new List(); + foreach (var methodParameter in method.GetParameters()) { + parameters.Add(arguments[methodParameter.Name]); + } + method.Invoke(eventHandler, parameters.ToArray()); + } + } + + private static MethodInfo GetMatchingMethod(IEventHandler eventHandler, Type interfaceType, string methodName, IDictionary arguments) { + List allMethods = new List(interfaceType.GetMethods()); + List candidates = new List(allMethods); + + foreach (var method in allMethods) { + if (String.Equals(method.Name, methodName, StringComparison.OrdinalIgnoreCase)) { + ParameterInfo[] parameterInfos = method.GetParameters(); + foreach (var parameter in parameterInfos) { + if (!arguments.ContainsKey(parameter.Name)) { + candidates.Remove(method); + break; + } + } + } + else { + candidates.Remove(method); + } + } + + if (candidates.Count != 0) { + return candidates.OrderBy(x => x.GetParameters().Length).Last(); + } + + return null; + } + #endregion } } diff --git a/src/Orchard/Events/EventsInterceptor.cs b/src/Orchard/Events/EventsInterceptor.cs new file mode 100644 index 000000000..f40fbdbbb --- /dev/null +++ b/src/Orchard/Events/EventsInterceptor.cs @@ -0,0 +1,23 @@ +using System.Linq; +using Castle.Core.Interceptor; + +namespace Orchard.Events { + public class EventsInterceptor : IInterceptor { + private readonly IEventBus _eventBus; + + public EventsInterceptor(IEventBus eventBus) { + _eventBus = eventBus; + } + + public void Intercept(IInvocation invocation) { + var interfaceName = invocation.Method.DeclaringType.Name; + var methodName = invocation.Method.Name; + + var data = invocation.Method.GetParameters() + .Select((parameter, index) => new { parameter.Name, Value = invocation.Arguments[index] }) + .ToDictionary(kv => kv.Name, kv => kv.Value); + + _eventBus.Notify(interfaceName + "." + methodName, data); + } + } +} diff --git a/src/Orchard/Events/EventsModule.cs b/src/Orchard/Events/EventsModule.cs new file mode 100644 index 000000000..3cbbfa388 --- /dev/null +++ b/src/Orchard/Events/EventsModule.cs @@ -0,0 +1,10 @@ +using Autofac; + +namespace Orchard.Events { + internal class EventsModule : Module { + protected override void Load(ContainerBuilder builder) { + builder.RegisterSource(new EventsRegistrationSource()); + base.Load(builder); + } + } +} diff --git a/src/Orchard/Events/EventsRegistrationSource.cs b/src/Orchard/Events/EventsRegistrationSource.cs new file mode 100644 index 000000000..3a09cc77b --- /dev/null +++ b/src/Orchard/Events/EventsRegistrationSource.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Autofac; +using Autofac.Builder; +using Autofac.Core; +using Castle.Core.Interceptor; +using Castle.DynamicProxy; + +namespace Orchard.Events { + public class EventsRegistrationSource : IRegistrationSource { + private readonly DefaultProxyBuilder _proxyBuilder; + + public EventsRegistrationSource() { + _proxyBuilder = new DefaultProxyBuilder(); + } + + public IEnumerable RegistrationsFor(Service service, Func> registrationAccessor) { + var serviceWithType = service as IServiceWithType; + if (serviceWithType == null) + yield break; + + var serviceType = serviceWithType.ServiceType; + if (!serviceType.IsInterface || !typeof(IEventHandler).IsAssignableFrom(serviceType) || serviceType == typeof(IEventHandler)) + yield break; + + var interfaceProxyType = _proxyBuilder.CreateInterfaceProxyTypeWithoutTarget( + serviceType, + new Type[0], + ProxyGenerationOptions.Default); + + + var rb = RegistrationBuilder + .ForDelegate((ctx, parameters) => { + var interceptors = new IInterceptor[] { new EventsInterceptor(ctx.Resolve()) }; + var args = new object[] { interceptors, null }; + return Activator.CreateInstance(interfaceProxyType, args); + }) + .As(service); + + yield return rb.CreateRegistration(); + } + } +} \ No newline at end of file diff --git a/src/Orchard/Events/IEventBus.cs b/src/Orchard/Events/IEventBus.cs index 56dafe0eb..46cb7f214 100644 --- a/src/Orchard/Events/IEventBus.cs +++ b/src/Orchard/Events/IEventBus.cs @@ -2,6 +2,7 @@ namespace Orchard.Events { public interface IEventBus : IDependency { - void Notify(string messageName, IDictionary eventData); + void Notify_Obsolete(string messageName, IDictionary eventData); + void Notify(string messageName, Dictionary eventData); } } diff --git a/src/Orchard/Events/IEventHandler.cs b/src/Orchard/Events/IEventHandler.cs new file mode 100644 index 000000000..2dbd17c04 --- /dev/null +++ b/src/Orchard/Events/IEventHandler.cs @@ -0,0 +1,4 @@ +namespace Orchard.Events { + public interface IEventHandler : IDependency { + } +} diff --git a/src/Orchard/Environment/Configuration/AppDataFolder.cs b/src/Orchard/FileSystems/AppData/AppDataFolder.cs similarity index 63% rename from src/Orchard/Environment/Configuration/AppDataFolder.cs rename to src/Orchard/FileSystems/AppData/AppDataFolder.cs index 050ef0b71..a0537ad76 100644 --- a/src/Orchard/Environment/Configuration/AppDataFolder.cs +++ b/src/Orchard/FileSystems/AppData/AppDataFolder.cs @@ -1,35 +1,9 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Web.Hosting; -namespace Orchard.Environment.Configuration { - /// - /// Abstraction of App_Data folder - /// Expected to work on physical filesystem, but decouples core - /// system from web hosting apis - /// - public interface IAppDataFolder { - IEnumerable ListFiles(string path); - IEnumerable ListDirectories(string path); - - void CreateFile(string path, string content); - string ReadFile(string path); - void DeleteFile(string path); - bool FileExists(string path); - - string CreateDirectory(string path); - - - /// - /// May be called to initialize component when not in a hosting environment - /// app domain - /// - void SetBasePath(string basePath); - string MapPath(string path); - } - +namespace Orchard.FileSystems.AppData { public class AppDataFolder : IAppDataFolder { protected string _basePath; @@ -66,9 +40,9 @@ namespace Orchard.Environment.Configuration { var files = Directory.GetFiles(directoryPath); return files.Select(file => { - var fileName = Path.GetFileName(file); - return Path.Combine(path, fileName); - }); + var fileName = Path.GetFileName(file); + return Path.Combine(path, fileName); + }); } public IEnumerable ListDirectories(string path) { @@ -79,9 +53,9 @@ namespace Orchard.Environment.Configuration { var files = Directory.GetDirectories(directoryPath); return files.Select(file => { - var fileName = Path.GetFileName(file); - return Path.Combine(path, fileName); - }); + var fileName = Path.GetFileName(file); + return Path.Combine(path, fileName); + }); } public string CreateDirectory(string path) { @@ -98,5 +72,6 @@ namespace Orchard.Environment.Configuration { public string MapPath(string path) { return Path.Combine(_basePath, path); } + } -} +} \ No newline at end of file diff --git a/src/Orchard/FileSystems/AppData/IAppDataFolder.cs b/src/Orchard/FileSystems/AppData/IAppDataFolder.cs new file mode 100644 index 000000000..3dde70238 --- /dev/null +++ b/src/Orchard/FileSystems/AppData/IAppDataFolder.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using Orchard.Caching; + +namespace Orchard.FileSystems.AppData { + /// + /// Abstraction of App_Data folder + /// Expected to work on physical filesystem, but decouples core + /// system from web hosting apis + /// + public interface IAppDataFolder : IVolatileProvider { + IEnumerable ListFiles(string path); + IEnumerable ListDirectories(string path); + + void CreateFile(string path, string content); + string ReadFile(string path); + void DeleteFile(string path); + bool FileExists(string path); + + string CreateDirectory(string path); + + + /// + /// May be called to initialize component when not in a hosting environment + /// app domain + /// + void SetBasePath(string basePath); + string MapPath(string path); + } +} \ No newline at end of file diff --git a/src/Orchard/Storage/FileSystemStorageProvider.cs b/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs similarity index 92% rename from src/Orchard/Storage/FileSystemStorageProvider.cs rename to src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs index 32522a8a9..7f0817619 100644 --- a/src/Orchard/Storage/FileSystemStorageProvider.cs +++ b/src/Orchard/FileSystems/Media/FileSystemStorageProvider.cs @@ -5,15 +5,15 @@ using System.Linq; using System.Web.Hosting; using Orchard.Environment.Configuration; -namespace Orchard.Storage { +namespace Orchard.FileSystems.Media { public class FileSystemStorageProvider : IStorageProvider { private readonly string _storagePath; private readonly string _publicPath; public FileSystemStorageProvider(ShellSettings settings) { var mediaPath = HostingEnvironment.IsHosted - ? HostingEnvironment.MapPath("~/Media/") ?? "" - : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media"); + ? HostingEnvironment.MapPath("~/Media/") ?? "" + : Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Media"); _storagePath = Path.Combine(mediaPath, settings.Name); @@ -35,10 +35,10 @@ namespace Orchard.Storage { static string Fix(string path) { return string.IsNullOrEmpty(path) - ? "" - : Path.DirectorySeparatorChar != '/' - ? path.Replace('/', Path.DirectorySeparatorChar) - : path; + ? "" + : Path.DirectorySeparatorChar != '/' + ? path.Replace('/', Path.DirectorySeparatorChar) + : path; } #region Implementation of IStorageProvider @@ -248,4 +248,4 @@ namespace Orchard.Storage { } } -} +} \ No newline at end of file diff --git a/src/Orchard/Storage/IStorageFile.cs b/src/Orchard/FileSystems/Media/IStorageFile.cs similarity index 88% rename from src/Orchard/Storage/IStorageFile.cs rename to src/Orchard/FileSystems/Media/IStorageFile.cs index bd7177bd4..1f33ed907 100644 --- a/src/Orchard/Storage/IStorageFile.cs +++ b/src/Orchard/FileSystems/Media/IStorageFile.cs @@ -1,7 +1,7 @@ using System; using System.IO; -namespace Orchard.Storage { +namespace Orchard.FileSystems.Media { public interface IStorageFile { string GetPath(); string GetName(); @@ -19,4 +19,4 @@ namespace Orchard.Storage { /// Stream OpenWrite(); } -} +} \ No newline at end of file diff --git a/src/Orchard/Storage/IStorageFolder.cs b/src/Orchard/FileSystems/Media/IStorageFolder.cs similarity index 81% rename from src/Orchard/Storage/IStorageFolder.cs rename to src/Orchard/FileSystems/Media/IStorageFolder.cs index a31b0a0e9..ea416ff32 100644 --- a/src/Orchard/Storage/IStorageFolder.cs +++ b/src/Orchard/FileSystems/Media/IStorageFolder.cs @@ -1,6 +1,6 @@ using System; -namespace Orchard.Storage { +namespace Orchard.FileSystems.Media { public interface IStorageFolder { string GetPath(); string GetName(); @@ -8,4 +8,4 @@ namespace Orchard.Storage { DateTime GetLastUpdated(); IStorageFolder GetParent(); } -} +} \ No newline at end of file diff --git a/src/Orchard/Storage/IStorageProvider.cs b/src/Orchard/FileSystems/Media/IStorageProvider.cs similarity index 91% rename from src/Orchard/Storage/IStorageProvider.cs rename to src/Orchard/FileSystems/Media/IStorageProvider.cs index da5b7bbce..b2e47c034 100644 --- a/src/Orchard/Storage/IStorageProvider.cs +++ b/src/Orchard/FileSystems/Media/IStorageProvider.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace Orchard.Storage { +namespace Orchard.FileSystems.Media { public interface IStorageProvider : IDependency { string GetPublicUrl(string path); IStorageFile GetFile(string path); @@ -13,4 +13,4 @@ namespace Orchard.Storage { void RenameFile(string path, string newPath); IStorageFile CreateFile(string path); } -} +} \ No newline at end of file diff --git a/src/Orchard/FileSystems/WebSite/IWebSiteFolder.cs b/src/Orchard/FileSystems/WebSite/IWebSiteFolder.cs new file mode 100644 index 000000000..a7dc73925 --- /dev/null +++ b/src/Orchard/FileSystems/WebSite/IWebSiteFolder.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using Orchard.Caching; + +namespace Orchard.FileSystems.WebSite { + public interface IWebSiteFolder : IVolatileProvider { + IEnumerable ListDirectories(string path); + string ReadFile(string path); + + IVolatileToken WhenPathChanges(string path); + } +} \ No newline at end of file diff --git a/src/Orchard/FileSystems/WebSite/WebSiteFolder.cs b/src/Orchard/FileSystems/WebSite/WebSiteFolder.cs new file mode 100644 index 000000000..14543853f --- /dev/null +++ b/src/Orchard/FileSystems/WebSite/WebSiteFolder.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Web.Caching; +using System.Web.Hosting; +using Orchard.Caching; +using Orchard.Services; + +namespace Orchard.FileSystems.WebSite { + public class WebSiteFolder : IWebSiteFolder { + private readonly IClock _clock; + private readonly Thunk _thunk; + private readonly string _prefix = Guid.NewGuid().ToString("n"); + private readonly IDictionary> _tokens = new Dictionary>(); + + + public WebSiteFolder(IClock clock) { + _clock = clock; + _thunk = new Thunk(this); + } + + public IEnumerable ListDirectories(string virtualPath) { + if (!HostingEnvironment.VirtualPathProvider.DirectoryExists(virtualPath)) + return Enumerable.Empty(); + + return HostingEnvironment.VirtualPathProvider + .GetDirectory(virtualPath) + .Directories.OfType() + .Select(d => d.VirtualPath) + .ToArray(); + } + + public string ReadFile(string virtualPath) { + if (!HostingEnvironment.VirtualPathProvider.FileExists(virtualPath)) + return null; + + using (var stream = VirtualPathProvider.OpenFile(virtualPath)) { + using (var reader = new StreamReader(stream)) { + return reader.ReadToEnd(); + } + } + } + + public IVolatileToken WhenPathChanges(string virtualPath) { + var token = BindToken(virtualPath); + BindSignal(virtualPath); + return token; + } + + private Token BindToken(string virtualPath) { + lock (_tokens) { + Weak weak; + if (!_tokens.TryGetValue(virtualPath, out weak)) { + weak = new Weak(new Token(virtualPath)); + _tokens[virtualPath] = weak; + } + + var token = weak.Target; + if (token == null) { + token = new Token(virtualPath); + weak.Target = token; + } + + return token; + } + } + + private Token DetachToken(string virtualPath) { + lock (_tokens) { + Weak weak; + if (!_tokens.TryGetValue(virtualPath, out weak)) { + return null; + } + var token = weak.Target; + weak.Target = null; + return token; + } + } + + private void BindSignal(string virtualPath) { + var cacheDependency = HostingEnvironment.VirtualPathProvider.GetCacheDependency( + virtualPath, + new[] { virtualPath }, + _clock.UtcNow); + + HostingEnvironment.Cache.Add( + _prefix + virtualPath, + virtualPath, + cacheDependency, + Cache.NoAbsoluteExpiration, + Cache.NoSlidingExpiration, + CacheItemPriority.NotRemovable, + _thunk.Signal); + } + + public void Signal(string key, object value, CacheItemRemovedReason reason) { + var virtualPath = Convert.ToString(value); + var token = DetachToken(virtualPath); + if (token != null) + token.IsCurrent = false; + } + + public class Token : IVolatileToken { + public Token(string virtualPath) { + IsCurrent = true; + VirtualPath = virtualPath; + } + public bool IsCurrent { get; set; } + public string VirtualPath { get; private set; } + } + + class Thunk { + private readonly Weak _weak; + + public Thunk(WebSiteFolder provider) { + _weak = new Weak(provider); + } + + public void Signal(string key, object value, CacheItemRemovedReason reason) { + var provider = _weak.Target; + if (provider != null) + provider.Signal(key, value, reason); + } + } + } +} \ No newline at end of file diff --git a/src/Orchard/IDependency.cs b/src/Orchard/IDependency.cs index eb81e0a5c..ab8d283dd 100644 --- a/src/Orchard/IDependency.cs +++ b/src/Orchard/IDependency.cs @@ -5,6 +5,7 @@ public interface ISingletonDependency : IDependency { } + public interface ITransientDependency : IDependency { } diff --git a/src/Orchard/Modules/IModuleService.cs b/src/Orchard/Modules/IModuleService.cs index d2792315e..a4033bb84 100644 --- a/src/Orchard/Modules/IModuleService.cs +++ b/src/Orchard/Modules/IModuleService.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Web; -using Orchard.Environment.Extensions.Models; namespace Orchard.Modules { public interface IModuleService : IDependency { @@ -8,9 +7,10 @@ namespace Orchard.Modules { IEnumerable GetInstalledModules(); void InstallModule(HttpPostedFileBase file); void UninstallModule(string moduleName); - IModule GetModuleByFeatureName(string featureName); IEnumerable GetAvailableFeatures(); void EnableFeatures(IEnumerable featureNames); + void EnableFeatures(IEnumerable featureNames, bool force); void DisableFeatures(IEnumerable featureNames); + void DisableFeatures(IEnumerable featureNames, bool force); } } \ No newline at end of file diff --git a/src/Orchard/Mvc/Extensions/ControllerExtensions.cs b/src/Orchard/Mvc/Extensions/ControllerExtensions.cs deleted file mode 100644 index db4e2c201..000000000 --- a/src/Orchard/Mvc/Extensions/ControllerExtensions.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Web.Mvc; - -namespace Orchard.Mvc.Extensions { - public static class ControllerExtensions { - public static RedirectResult ReturnUrlRedirect(this Controller controller) { - var request = controller.HttpContext.Request; - Uri returnUrl = null; - try { - returnUrl = new Uri(request.QueryString["ReturnUrl"]); - } - catch { - try { - returnUrl = - new Uri(string.Format("{0}://{1}{2}{3}", request.Url.Scheme, request.Url.Host, - request.Url.Port != 80 ? ":" + request.Url.Port : "", - request.QueryString["ReturnUrl"])); - } - catch { } - } - - if (returnUrl != null && - returnUrl.Scheme == request.Url.Scheme && - returnUrl.Port == request.Url.Port && - returnUrl.Host == request.Url.Host) { - return new RedirectResult(returnUrl.ToString()); - } - return new RedirectResult("~/"); - } - } -} diff --git a/src/Orchard/Mvc/Extensions/UrlHelperExtensions.cs b/src/Orchard/Mvc/Extensions/UrlHelperExtensions.cs index 8425d36cd..bee1ddc6b 100644 --- a/src/Orchard/Mvc/Extensions/UrlHelperExtensions.cs +++ b/src/Orchard/Mvc/Extensions/UrlHelperExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Web.Mvc; +using Orchard.Utility.Extensions; namespace Orchard.Mvc.Extensions { public static class UrlHelperExtensions { @@ -8,7 +9,7 @@ namespace Orchard.Mvc.Extensions { } private static string MakeAbsolute(this UrlHelper urlHelper, string url) { - var siteUrl = urlHelper.RequestContext.HttpContext.Request.Url.GetLeftPart(UriPartial.Authority); + var siteUrl = urlHelper.RequestContext.HttpContext.Request.ToRootUrlString(); return siteUrl + url; } } diff --git a/src/Orchard/Mvc/ViewEngines/ThemeViewLocationCache.cs b/src/Orchard/Mvc/ViewEngines/ThemeViewLocationCache.cs new file mode 100644 index 000000000..a5a39f1eb --- /dev/null +++ b/src/Orchard/Mvc/ViewEngines/ThemeViewLocationCache.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Web.Mvc; +using System.Web.Caching; +using System.Web; +using System.Web.Hosting; + +namespace Orchard.Mvc.ViewEngines { + public class ThemeViewLocationCache : IViewLocationCache { + private readonly string _requestTheme; + + public ThemeViewLocationCache(string requestTheme) { + _requestTheme = requestTheme; + } + + private string AlterKey(string key) { + return key + ":" + _requestTheme; + } + + public string GetViewLocation(HttpContextBase httpContext, string key) { + if (httpContext == null) { + throw new ArgumentNullException("httpContext"); + } + + return (string)httpContext.Cache[AlterKey(key)]; + } + + public void InsertViewLocation(HttpContextBase httpContext, string key, string virtualPath) { + if (httpContext == null) { + throw new ArgumentNullException("httpContext"); + } + + httpContext.Cache.Insert(AlterKey(key), virtualPath, new CacheDependency(HostingEnvironment.MapPath("~/Themes"))); + } + } +} diff --git a/src/Orchard/Mvc/ViewEngines/WebFormsViewEngineProvider.cs b/src/Orchard/Mvc/ViewEngines/WebFormsViewEngineProvider.cs index 24a425b44..6192160fb 100644 --- a/src/Orchard/Mvc/ViewEngines/WebFormsViewEngineProvider.cs +++ b/src/Orchard/Mvc/ViewEngines/WebFormsViewEngineProvider.cs @@ -31,6 +31,8 @@ namespace Orchard.Mvc.ViewEngines { AreaPartialViewLocationFormats = DisabledFormats, }; + viewEngine.ViewLocationCache = new ThemeViewLocationCache(parameters.VirtualPath); + // enable /Views/{partialName} // enable /Views/"DisplayTemplates/"+{templateName} // enable /Views/"EditorTemplates/+{templateName} diff --git a/src/Orchard/Orchard.Framework.csproj b/src/Orchard/Orchard.Framework.csproj index ce9644cb0..59da30171 100644 --- a/src/Orchard/Orchard.Framework.csproj +++ b/src/Orchard/Orchard.Framework.csproj @@ -84,6 +84,18 @@ False ..\..\lib\sharpziplib\ICSharpCode.SharpZipLib.dll + + False + ..\..\lib\machine.migrations\Machine.Core.dll + + + False + ..\..\lib\machine.migrations\Machine.Migrations.dll + + + False + ..\..\lib\machine.migrations\Machine.Migrations.NHibernate.dll + False ..\..\lib\fluentnhibernate\NHibernate.dll @@ -152,6 +164,24 @@ + + + + + + + + + + + + + + + + + + @@ -174,6 +204,11 @@ + + + + + @@ -183,13 +218,13 @@ + - - + @@ -202,6 +237,10 @@ + + + + @@ -211,11 +250,11 @@ + - @@ -223,6 +262,7 @@ + ASPXCodeBehind @@ -231,7 +271,7 @@ - + @@ -427,15 +467,15 @@ - - - - + + + + - + @@ -447,6 +487,7 @@ + False .NET Framework 3.5 SP1 Client Profile diff --git a/src/Orchard/Security/IAuthorizationServiceEvents.cs b/src/Orchard/Security/IAuthorizationServiceEventHandler.cs similarity index 56% rename from src/Orchard/Security/IAuthorizationServiceEvents.cs rename to src/Orchard/Security/IAuthorizationServiceEventHandler.cs index def7998ae..84acaec5d 100644 --- a/src/Orchard/Security/IAuthorizationServiceEvents.cs +++ b/src/Orchard/Security/IAuthorizationServiceEventHandler.cs @@ -1,8 +1,9 @@ using Orchard.ContentManagement; +using Orchard.Events; using Orchard.Security.Permissions; namespace Orchard.Security { - public interface IAuthorizationServiceEvents : IEvents { + public interface IAuthorizationServiceEventHandler : IEventHandler { void Checking(CheckAccessContext context); void Adjust(CheckAccessContext context); void Complete(CheckAccessContext context); @@ -12,13 +13,12 @@ namespace Orchard.Security { public Permission Permission { get; set; } public IUser User { get; set; } public IContent Content { get; set; } + + // true if the permission has been granted to the user. public bool Granted { get; set; } + + // if context.Permission was modified during an Adjust(context) in an event handler, Adjusted should be set to true. + // It means that the permission check will be done again by the framework. public bool Adjusted { get; set; } } - - public abstract class AuthorizationServiceEvents : IAuthorizationServiceEvents { - public virtual void Checking(CheckAccessContext context) { } - public virtual void Adjust(CheckAccessContext context) { } - public virtual void Complete(CheckAccessContext context) { } - } } diff --git a/src/Orchard/Services/Clock.cs b/src/Orchard/Services/Clock.cs index 038865060..6e1b5a38b 100644 --- a/src/Orchard/Services/Clock.cs +++ b/src/Orchard/Services/Clock.cs @@ -1,13 +1,15 @@ using System; +using Orchard.Caching; namespace Orchard.Services { - public interface IClock : IDependency { + public interface IClock : IVolatileProvider { DateTime UtcNow { get; } } public class Clock : IClock { public DateTime UtcNow { - get { return DateTime.Now; } + get { return DateTime.UtcNow; } } + } } diff --git a/src/Orchard/Tasks/BackgroundService.cs b/src/Orchard/Tasks/BackgroundService.cs index 0267c44f2..ee76fbca7 100644 --- a/src/Orchard/Tasks/BackgroundService.cs +++ b/src/Orchard/Tasks/BackgroundService.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using JetBrains.Annotations; using Orchard.Logging; @@ -10,9 +9,9 @@ namespace Orchard.Tasks { [UsedImplicitly] public class BackgroundService : IBackgroundService { - private readonly IEnumerable _tasks; + private readonly IBackgroundTask _tasks; - public BackgroundService(IEnumerable tasks) { + public BackgroundService(IBackgroundTask tasks) { _tasks = tasks; Logger = NullLogger.Instance; } @@ -20,7 +19,7 @@ namespace Orchard.Tasks { public ILogger Logger { get; set; } public void Sweep() { - _tasks.Invoke(task => task.Sweep(), Logger); + _tasks.Sweep(); } } } diff --git a/src/Orchard/Tasks/IBackgroundTask.cs b/src/Orchard/Tasks/IBackgroundTask.cs index 22c3e70d9..175efd8e2 100644 --- a/src/Orchard/Tasks/IBackgroundTask.cs +++ b/src/Orchard/Tasks/IBackgroundTask.cs @@ -1,5 +1,7 @@ -namespace Orchard.Tasks { - public interface IBackgroundTask : IEvents { +using Orchard.Events; + +namespace Orchard.Tasks { + public interface IBackgroundTask : IEventHandler { void Sweep(); } } diff --git a/src/Orchard/UI/Zones/IZoneManagerEvents.cs b/src/Orchard/UI/Zones/IZoneManagerEvents.cs index 4eb7273f6..eb7e5c5ef 100644 --- a/src/Orchard/UI/Zones/IZoneManagerEvents.cs +++ b/src/Orchard/UI/Zones/IZoneManagerEvents.cs @@ -1,8 +1,9 @@ using System.Collections.Generic; using System.Web.Mvc; +using Orchard.Events; namespace Orchard.UI.Zones { - public interface IZoneManagerEvents : IEvents { + public interface IZoneManagerEvents : IEventHandler { void ZoneRendering(ZoneRenderContext context); void ZoneItemRendering(ZoneRenderContext context, ZoneItem item); void ZoneItemRendered(ZoneRenderContext context, ZoneItem item); diff --git a/src/Orchard/UI/Zones/ZoneManager.cs b/src/Orchard/UI/Zones/ZoneManager.cs index 932154039..a7794399f 100644 --- a/src/Orchard/UI/Zones/ZoneManager.cs +++ b/src/Orchard/UI/Zones/ZoneManager.cs @@ -8,10 +8,10 @@ using Orchard.UI.Navigation; namespace Orchard.UI.Zones { public class ZoneManager : IZoneManager { - private readonly IEnumerable _events; + private readonly IEnumerable _zoneManagerEventHandler; - public ZoneManager(IEnumerable events) { - _events = events; + public ZoneManager(IEnumerable eventHandler) { + _zoneManagerEventHandler = eventHandler; Logger = NullLogger.Instance; } @@ -38,16 +38,23 @@ namespace Orchard.UI.Zones { RenderingItems = groups.SelectMany(x => x.Items).ToList() }; - _events.Invoke(x => x.ZoneRendering(context), Logger); + foreach (var zoneManagerEventHandler in _zoneManagerEventHandler) { + zoneManagerEventHandler.ZoneRendering(context); + } foreach (var item in context.RenderingItems) { var zoneItem = item; - _events.Invoke(x => x.ZoneItemRendering(context, zoneItem), Logger); + foreach (var zoneManagerEventHandler in _zoneManagerEventHandler) { + zoneManagerEventHandler.ZoneItemRendering(context, zoneItem); + } zoneItem.WasExecuted = true; zoneItem.Execute(html); - _events.Invoke(x => x.ZoneItemRendered(context, zoneItem), Logger); + foreach (var zoneManagerEventHandler in _zoneManagerEventHandler) { + zoneManagerEventHandler.ZoneItemRendered(context, zoneItem); + } + } + foreach (var zoneManagerEventHandler in _zoneManagerEventHandler) { + zoneManagerEventHandler.ZoneRendered(context); } - _events.Invoke(x => x.ZoneRendered(context), Logger); - } protected ILogger Logger { get; set; } diff --git a/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs b/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs new file mode 100644 index 000000000..b6821d6a6 --- /dev/null +++ b/src/Orchard/Utility/Extensions/HttpRequestExtensions.cs @@ -0,0 +1,38 @@ +using System.Web; + +namespace Orchard.Utility.Extensions { + public static class HttpRequestExtensions { + /// + /// Returns the root part of a request. + /// + /// Prevents port number issues by using the client requested host + public static string ToRootUrlString(this HttpRequestBase request) { + return string.Format("{0}://{1}", request.Url.Scheme, request.Headers["Host"]); + } + + /// + /// Returns the root part of a request. + /// + /// Prevents port number issues by using the client requested host + public static string ToRootUrlString(this HttpRequest request) { + return string.Format("{0}://{1}", request.Url.Scheme, request.Headers["Host"]); + } + + /// + /// Returns the client requested url. + /// + /// Prevents port number issues by using the client requested host + public static string ToUrlString(this HttpRequestBase request) { + return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.RawUrl); + } + + /// + /// Returns the client requested url. + /// + /// Prevents port number issues by using the client requested host + public static string ToUrlString(this HttpRequest request) { + return string.Format("{0}://{1}{2}", request.Url.Scheme, request.Headers["Host"], request.RawUrl); + } + + } +} \ No newline at end of file diff --git a/src/Orchard/Utility/Extensions/StringExtensions.cs b/src/Orchard/Utility/Extensions/StringExtensions.cs index 4e7e233f8..bbd10a13c 100644 --- a/src/Orchard/Utility/Extensions/StringExtensions.cs +++ b/src/Orchard/Utility/Extensions/StringExtensions.cs @@ -14,16 +14,20 @@ namespace Orchard.Utility.Extensions { return cleanTailRegex.Replace(text.Substring(0, characterCount + 1), "") + ellipsis; } + + public static string HtmlClassify(this string text) { + return Regex.Replace(text, @"[^a-zA-Z]+", m => m.Index == 0 ? "" : "-").ToLowerInvariant(); + } + public static bool IsNullOrEmptyTrimmed(this string text) { - if (text == null) return true; - return string.IsNullOrEmpty(text.Trim()); + return text == null + || string.IsNullOrEmpty(text.Trim()); } public static string OrDefault(this string text, string defaultValue) { - if (string.IsNullOrEmpty(text)) - return defaultValue; - else - return text; + return string.IsNullOrEmpty(text) + ? defaultValue + : text; } } } \ No newline at end of file diff --git a/src/Orchard/Utility/Extensions/UriExtensions.cs b/src/Orchard/Utility/Extensions/UriExtensions.cs deleted file mode 100644 index d0e5acff5..000000000 --- a/src/Orchard/Utility/Extensions/UriExtensions.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Orchard.Utility.Extensions { - public static class UriExtensions { - public static string ToRootString(this Uri uri) { - return string.Format("{0}://{1}{2}", uri.Scheme, uri.Host, uri.Port != 80 ? ":" + uri.Port : ""); - } - } -} \ No newline at end of file diff --git a/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj b/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj index d2c1fea68..bd8b30733 100644 --- a/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj +++ b/src/Tools/MSBuild.Orchard.Tasks.Tests/MSBuild.Orchard.Tasks.Tests.csproj @@ -40,7 +40,7 @@ DEBUG;TRACE prompt 4 - AllRules.ruleset + x86 pdbonly