diff --git a/.hgignore b/.hgignore
index 349eab9bb..9f8e4dd39 100644
--- a/.hgignore
+++ b/.hgignore
@@ -7,6 +7,7 @@ glob:*.user
glob:*.patch
glob:*.hg
glob:build
+glob:artifacts
glob:*.sln.cache
glob:src/Orchard.Web/Media/*
glob:desktop.ini
diff --git a/AzurePackage.proj b/AzurePackage.proj
new file mode 100644
index 000000000..ad02d137d
--- /dev/null
+++ b/AzurePackage.proj
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+ $(MSBuildProjectDirectory)\lib
+ $(MSBuildProjectDirectory)\src
+ $(MSBuildProjectDirectory)\build
+ $(MSBuildProjectDirectory)\artifacts\Azure
+
+ $(BuildFolder)\Compile
+ $(CompileFolder)\Orchard.Azure.CloudService.csx
+ $(ServiceFolder)\roles\Orchard.Azure.Web\approot
+ $(CompileFolder)\_PublishedWebsites
+ $(BuildFolder)\Stage
+
+ x64
+ x86
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.cmd b/build.cmd
index 8e320d6af..1e1be68bc 100644
--- a/build.cmd
+++ b/build.cmd
@@ -1,2 +1,3 @@
if "%~1"=="" build Build
-msbuild /t:%~1
+msbuild /t:%~1 Orchard.proj
+msbuild /t:%~1 AzurePackage.proj
diff --git a/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs b/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs
index 405569136..ccd2a82cb 100644
--- a/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs
+++ b/src/Orchard.Azure.Tests/Environment/Configuration/AzureShellSettingsManagerTests.cs
@@ -24,6 +24,12 @@ namespace Orchard.Azure.Tests.Environment.Configuration {
DeleteAllBlobs( ((AzureShellSettingsManager)Loader).Container);
}
+ [TearDown]
+ public void TearDown() {
+ // ensure default container is empty after running tests
+ DeleteAllBlobs(( (AzureShellSettingsManager)Loader ).Container);
+ }
+
[Test]
public void SingleSettingsFileShouldComeBackAsExpected() {
diff --git a/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs b/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs
index 5be4b3e8b..8c43b9a15 100644
--- a/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs
+++ b/src/Orchard.Azure.Tests/Storage/AzureBlobStorageProviderTests.cs
@@ -1,13 +1,9 @@
using System;
-using System.Configuration;
using System.IO;
using NUnit.Framework;
-using System.Diagnostics;
using Orchard.Azure.Storage;
using Microsoft.WindowsAzure;
using System.Linq;
-using Microsoft.WindowsAzure.StorageClient;
-using System.Text;
namespace Orchard.Azure.Tests.Storage {
[TestFixture]
diff --git a/src/Orchard.Azure/AzureFileSystem.cs b/src/Orchard.Azure/AzureFileSystem.cs
new file mode 100644
index 000000000..4f66b1bc6
--- /dev/null
+++ b/src/Orchard.Azure/AzureFileSystem.cs
@@ -0,0 +1,271 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.WindowsAzure;
+using Microsoft.WindowsAzure.StorageClient;
+using System.IO;
+using Orchard.Storage;
+
+namespace Orchard.Azure {
+ public class AzureFileSystem {
+ public string ContainerName { get; protected set; }
+
+ private readonly CloudStorageAccount _storageAccount;
+ private readonly string _shellName;
+ 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 shellName, bool isPrivate, CloudStorageAccount storageAccount) {
+ // Setup the connection to custom storage accountm, e.g. Development Storage
+ _storageAccount = storageAccount;
+ ContainerName = containerName;
+ _shellName = shellName;
+
+ BlobClient = _storageAccount.CreateCloudBlobClient();
+ // Get and create the container if it does not exist
+ // The container is named with DNS naming restrictions (i.e. all lower case)
+ Container = BlobClient.GetContainerReference(ContainerName);
+ Container.CreateIfNotExist();
+
+ if ( isPrivate ) {
+ Container.SetPermissions(new BlobContainerPermissions
+ {PublicAccess = BlobContainerPublicAccessType.Off});
+ }
+ else {
+ Container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container });
+ }
+ }
+
+ private static void EnsurePathIsRelative(string path) {
+ if ( path.StartsWith("/") || path.StartsWith("http://"))
+ throw new ArgumentException("Path must be relative");
+ }
+
+ public IStorageFile GetFile(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+ Container.EnsureBlobExists(path);
+ return new AzureBlobFileStorage(Container.GetBlockBlobReference(path));
+ }
+
+ public bool FileExists(string path) {
+ path = String.Concat(_shellName, "/", path);
+ return Container.BlobExists(path);
+ }
+
+ public IEnumerable ListFiles(string path) {
+ EnsurePathIsRelative(path);
+
+ string prefix = String.Concat(Container.Name, "/", _shellName, "/", path);
+ if ( !prefix.EndsWith("/") )
+ prefix += "/";
+
+ foreach ( var blobItem in BlobClient.ListBlobsWithPrefix(prefix).OfType() ) {
+ yield return new AzureBlobFileStorage(blobItem);
+ }
+ }
+
+ public IEnumerable ListFolders(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ if ( !Container.DirectoryExists(path) ) {
+ try {
+ CreateFolder(path);
+ }
+ catch ( Exception ex ) {
+ throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex));
+ }
+ }
+
+ return Container.GetDirectoryReference(path)
+ .ListBlobs()
+ .OfType()
+ .Select(d => new AzureBlobFolderStorage(d))
+ .ToList();
+ }
+
+ public void CreateFolder(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ Container.EnsureDirectoryDoesNotExist(path);
+ Container.GetDirectoryReference(path);
+ }
+
+ public void DeleteFolder(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ Container.EnsureDirectoryExists(path);
+ foreach ( var blob in Container.GetDirectoryReference(path).ListBlobs() ) {
+ if ( blob is CloudBlob )
+ ( (CloudBlob)blob ).Delete();
+
+ if ( blob is CloudBlobDirectory )
+ DeleteFolder(blob.Uri.ToString().Substring(Container.Uri.ToString().Length + 2 + _shellName.Length));
+ }
+ }
+
+ public void RenameFolder(string path, string newPath) {
+ EnsurePathIsRelative(path);
+
+ EnsurePathIsRelative(newPath);
+
+ if ( !path.EndsWith("/") )
+ path += "/";
+
+ if ( !newPath.EndsWith("/") )
+ newPath += "/";
+
+ foreach ( var blob in Container.GetDirectoryReference(_shellName + "/" + path).ListBlobs() ) {
+ if ( blob is CloudBlob ) {
+ string filename = Path.GetFileName(blob.Uri.ToString());
+ string source = String.Concat(path, filename);
+ string destination = String.Concat(newPath, filename);
+ RenameFile(source, destination);
+ }
+
+ if ( blob is CloudBlobDirectory ) {
+ string foldername = blob.Uri.Segments.Last();
+ string source = String.Concat(path, foldername);
+ string destination = String.Concat(newPath, foldername);
+ RenameFolder(source, destination);
+ }
+ }
+
+ }
+
+ public void DeleteFile(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ Container.EnsureBlobExists(path);
+ var blob = Container.GetBlockBlobReference(path);
+ blob.Delete();
+ }
+
+ public void RenameFile(string path, string newPath) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ EnsurePathIsRelative(newPath);
+ newPath = String.Concat(_shellName, "/", newPath);
+
+ Container.EnsureBlobExists(path);
+ Container.EnsureBlobDoesNotExist(newPath);
+
+ var blob = Container.GetBlockBlobReference(path);
+ var newBlob = Container.GetBlockBlobReference(newPath);
+ newBlob.CopyFromBlob(blob);
+ blob.Delete();
+ }
+
+ public IStorageFile CreateFile(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+
+ if ( Container.BlobExists(path) ) {
+ throw new ArgumentException("File " + path + " already exists");
+ }
+
+ var blob = Container.GetBlockBlobReference(path);
+ blob.OpenWrite().Dispose(); // force file creation
+ return new AzureBlobFileStorage(blob);
+ }
+
+ public string GetPublicUrl(string path) {
+ EnsurePathIsRelative(path);
+ path = String.Concat(_shellName, "/", path);
+ Container.EnsureBlobExists(path);
+ return Container.GetBlockBlobReference(path).Uri.ToString();
+ }
+
+ private class AzureBlobFileStorage : IStorageFile {
+ private readonly CloudBlockBlob _blob;
+
+ public AzureBlobFileStorage(CloudBlockBlob blob) {
+ _blob = blob;
+ }
+
+ public string GetPath() {
+ return _blob.Uri.ToString();
+ }
+
+ public string GetName() {
+ return Path.GetFileName(GetPath());
+ }
+
+ public long GetSize() {
+ return _blob.Properties.Length;
+ }
+
+ public DateTime GetLastUpdated() {
+ return _blob.Properties.LastModifiedUtc;
+ }
+
+ public string GetFileType() {
+ return Path.GetExtension(GetPath());
+ }
+
+ public Stream OpenRead() {
+ return _blob.OpenRead();
+ }
+
+ public Stream OpenWrite() {
+ return _blob.OpenWrite();
+ }
+
+ }
+
+ private class AzureBlobFolderStorage : IStorageFolder {
+ private readonly CloudBlobDirectory _blob;
+
+ public AzureBlobFolderStorage(CloudBlobDirectory blob) {
+ _blob = blob;
+ }
+
+ public string GetName() {
+ return Path.GetDirectoryName(_blob.Uri.ToString());
+ }
+
+ public string GetPath() {
+ return _blob.Uri.ToString();
+ }
+
+ public long GetSize() {
+ return GetDirectorySize(_blob);
+ }
+
+ public DateTime GetLastUpdated() {
+ return DateTime.MinValue;
+ }
+
+ public IStorageFolder GetParent() {
+ if ( _blob.Parent != null ) {
+ return new AzureBlobFolderStorage(_blob.Parent);
+ }
+ throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory");
+ }
+
+ private static long GetDirectorySize(CloudBlobDirectory directoryBlob) {
+ long size = 0;
+
+ foreach ( var blobItem in directoryBlob.ListBlobs() ) {
+ if ( blobItem is CloudBlob )
+ size += ( (CloudBlob)blobItem ).Properties.Length;
+
+ if ( blobItem is CloudBlobDirectory )
+ size += GetDirectorySize((CloudBlobDirectory)blobItem);
+ }
+
+ return size;
+ }
+ }
+
+ }
+}
diff --git a/src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs b/src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs
new file mode 100644
index 000000000..e108a5f05
--- /dev/null
+++ b/src/Orchard.Azure/Environment/Configuration/AzureAppDataFolder.cs
@@ -0,0 +1,65 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Orchard.Environment.Configuration;
+
+namespace Orchard.Azure.Environment.Configuration {
+ public class AzureAppDataFolder : IAppDataFolder {
+
+ private readonly AzureFileSystem _fs;
+
+ public AzureAppDataFolder(string shellName) {
+ _fs = new AzureFileSystem("appdata", shellName, true);
+ }
+
+ public void CreateFile(string path, string content) {
+ if(_fs.FileExists(path)) {
+ DeleteFile(path);
+ }
+
+ using (var stream = _fs.CreateFile(path).OpenWrite()) {
+ using(var writer = new StreamWriter(stream)) {
+ writer.Write(content);
+ }
+ }
+ }
+
+ public string ReadFile(string path) {
+ using ( var stream = _fs.GetFile(path).OpenRead() ) {
+ using ( var reader = new StreamReader(stream) ) {
+ return reader.ReadToEnd();
+ }
+ }
+ }
+
+ public void DeleteFile(string path) {
+ _fs.DeleteFile(path);
+ }
+
+ public bool FileExists(string path) {
+ return _fs.FileExists(path);
+ }
+
+ public IEnumerable ListFiles(string path) {
+ return _fs.ListFiles(path).Select(sf => sf.GetPath());
+ }
+
+ public IEnumerable ListDirectories(string path) {
+ return _fs.ListFolders(path).Select(sf => sf.GetName());
+ }
+
+ public string CreateDirectory(string path) {
+ _fs.CreateFolder(path);
+ return Path.GetDirectoryName(path);
+ }
+
+ public void SetBasePath(string basePath) {
+ throw new NotImplementedException();
+ }
+
+ public string MapPath(string path) {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService.sln b/src/Orchard.Azure/Orchard.Azure.CloudService.sln
new file mode 100644
index 000000000..8f2b2423a
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.CloudService.sln
@@ -0,0 +1,38 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "Orchard.Azure.CloudService", "Orchard.Azure.CloudService\Orchard.Azure.CloudService.ccproj", "{03C5327D-4E8E-45A7-ACD1-E18E7CAA3C4A}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure.Web", "Orchard.Azure.Web\Orchard.Azure.Web.csproj", "{0DF8F426-9F30-4918-8F64-A5B40BA12D10}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Azure.csproj", "{2505AA84-65A6-43D0-9C27-4F44FD576284}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Framework", "..\Orchard\Orchard.Framework.csproj", "{2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {03C5327D-4E8E-45A7-ACD1-E18E7CAA3C4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {03C5327D-4E8E-45A7-ACD1-E18E7CAA3C4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {03C5327D-4E8E-45A7-ACD1-E18E7CAA3C4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {03C5327D-4E8E-45A7-ACD1-E18E7CAA3C4A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0DF8F426-9F30-4918-8F64-A5B40BA12D10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0DF8F426-9F30-4918-8F64-A5B40BA12D10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0DF8F426-9F30-4918-8F64-A5B40BA12D10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0DF8F426-9F30-4918-8F64-A5B40BA12D10}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2505AA84-65A6-43D0-9C27-4F44FD576284}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2505AA84-65A6-43D0-9C27-4F44FD576284}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2505AA84-65A6-43D0-9C27-4F44FD576284}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2505AA84-65A6-43D0-9C27-4F44FD576284}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService/Orchard.Azure.CloudService.ccproj b/src/Orchard.Azure/Orchard.Azure.CloudService/Orchard.Azure.CloudService.ccproj
new file mode 100644
index 000000000..22ee25d64
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.CloudService/Orchard.Azure.CloudService.ccproj
@@ -0,0 +1,51 @@
+
+
+
+ Debug
+ AnyCPU
+ 1.0.0
+ {03c5327d-4e8e-45a7-acd1-e18e7caa3c4a}
+ Library
+ Properties
+ Orchard.Azure.CloudService
+ Orchard.Azure.CloudService
+ True
+ OrchardCloudService
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+ Orchard.Azure.Web
+ {0df8f426-9f30-4918-8f64-a5b40ba12d10}
+ True
+ Web
+ Orchard.Azure.Web
+
+
+
+
+ $(MSBuildExtensionsPath)\Microsoft\Cloud Service\v1.0\
+
+
+
\ No newline at end of file
diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg
new file mode 100644
index 000000000..7e0691f74
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef
new file mode 100644
index 000000000..c1f66c33a
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ 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
new file mode 100644
index 000000000..124536a7d
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/Diagnostics.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config
new file mode 100644
index 000000000..b56b3523b
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Global.asax b/src/Orchard.Azure/Orchard.Azure.Web/Global.asax
new file mode 100644
index 000000000..8d7ecf448
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Global.asax
@@ -0,0 +1 @@
+<%@ Application Codebehind="Global.asax.cs" Inherits="Orchard.Azure.Web.MvcApplication" Language="C#" %>
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Global.asax.cs b/src/Orchard.Azure/Orchard.Azure.Web/Global.asax.cs
new file mode 100644
index 000000000..d7ee97dbf
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Global.asax.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Web;
+using System.Web.Mvc;
+using System.Web.Routing;
+using Autofac;
+using Orchard.Environment;
+
+namespace Orchard.Azure.Web {
+ // Note: For instructions on enabling IIS6 or IIS7 classic mode,
+ // visit http://go.microsoft.com/?LinkId=9394801
+
+ public class MvcApplication : HttpApplication {
+ private static IOrchardHost _host;
+
+ public static void RegisterRoutes(RouteCollection routes) {
+ routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
+ }
+
+ protected void Application_Start() {
+ // This is temporary until MVC2 is officially released.
+ // We want to avoid running against an outdated preview installed in the GAC
+ CheckMvcVersion(
+ new Version("2.0.50217.0")/*MVC2 RTM file version #*/,
+ new Version("2.0.50129.0")/*MVC2 RC2 file version #*/,
+ new Version("2.0.41211.0")/*MVC2 RC file version #*/);
+
+ RegisterRoutes(RouteTable.Routes);
+
+ _host = OrchardStarter.CreateHost(MvcSingletons);
+ _host.Initialize();
+
+ //TODO: what's the failed initialization story - IoC failure in app start can leave you with a zombie appdomain
+ }
+
+ protected void Application_BeginRequest() {
+ Context.Items["originalHttpContext"] = Context;
+
+ _host.BeginRequest();
+ }
+
+ protected void Application_EndRequest() {
+ _host.EndRequest();
+ }
+
+ private void CheckMvcVersion(params Version[] allowedVersions) {
+ Assembly loadedMvcAssembly = typeof(System.Web.Mvc.Controller).Assembly;
+ Version loadedMvcVersion = ReadAssemblyFileVersion(loadedMvcAssembly);
+
+ if ( allowedVersions.All(allowed => loadedMvcVersion != allowed) ) {
+ string message;
+ if ( loadedMvcAssembly.GlobalAssemblyCache ) {
+ message = string.Format(
+ "Orchard has been deployed with a version of {0} that has a different file version ({1}) " +
+ "than the version installed in the GAC ({2}).\r\n" +
+ "This implies that Orchard will not be able to run properly in this machine configuration.\r\n" +
+ "Please un-install MVC from the GAC or install a more recent version.",
+ loadedMvcAssembly.GetName().Name,
+ allowedVersions.First(),
+ loadedMvcVersion);
+ }
+ else {
+ message = string.Format(
+ "Orchard has been configured to run with a file version {1} of \"{0}\" " +
+ "but the version deployed with the application is {2}.\r\n" +
+ "This probably implies that Orchard is deployed with a newer version " +
+ "and the source code hasn't been updated accordingly.\r\n" +
+ "Update the Orchard.Web application source code (look for \"CheckMvcVersion\") to " +
+ "specify the correct file version number.\r\n",
+ loadedMvcAssembly.GetName().Name,
+ allowedVersions.First(),
+ loadedMvcVersion);
+ }
+
+ throw new HttpException(500, message);
+ }
+ }
+
+ private Version ReadAssemblyFileVersion(Assembly assembly) {
+ object[] attributes = assembly.GetCustomAttributes(typeof(AssemblyFileVersionAttribute), true);
+ if ( attributes == null || attributes.Length != 1 ) {
+ string message = string.Format("Assembly \"{0}\" doesn't have a \"{1}\" attribute",
+ assembly.GetName().Name, typeof(AssemblyFileVersionAttribute).FullName);
+ throw new FileLoadException(message);
+ }
+
+ var attribute = (AssemblyFileVersionAttribute)attributes[0];
+ return new Version(attribute.Version);
+ }
+
+
+ protected void MvcSingletons(ContainerBuilder builder) {
+ builder.RegisterInstance(ControllerBuilder.Current);
+ builder.RegisterInstance(RouteTable.Routes);
+ builder.RegisterInstance(ModelBinders.Binders);
+ builder.RegisterInstance(ModelMetadataProviders.Current);
+ builder.RegisterInstance(ViewEngines.Engines);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj b/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj
new file mode 100644
index 000000000..0f11fc754
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj
@@ -0,0 +1,157 @@
+
+
+
+ Debug
+ AnyCPU
+ 9.0.30729
+ 2.0
+ {0DF8F426-9F30-4918-8F64-A5B40BA12D10}
+ {F85E285D-A4E0-4152-9332-AB1D724D3325};{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}
+ Library
+ Properties
+ Orchard.Azure.Web
+ Orchard.Azure.Web
+ v3.5
+ false
+
+
+ true
+ full
+ false
+ bin\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\
+ TRACE
+ prompt
+ 4
+
+
+
+ False
+ ..\..\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
+
+
+ False
+ ..\..\..\lib\Castle Windsor 2.0\bin\Castle.DynamicProxy2.dll
+
+
+
+
+
+ False
+ ..\..\lib\fluentnhibernate\NHibernate.ByteCode.Castle.dll
+
+
+
+
+ 3.5
+
+
+ 3.5
+
+
+ 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
+ True
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ bin\Orchard.Framework.dll
+
+
+ False
+ bin\Orchard.Azure.dll
+
+
+
+
+ Global.asax
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ True
+ 60453
+ /
+
+
+ False
+ False
+
+
+ False
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Properties/AssemblyInfo.cs b/src/Orchard.Azure/Orchard.Azure.Web/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..c02dd9f71
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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.Azure.Web")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Orchard.Azure.Web")]
+[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("76077e05-d95f-49c7-b11a-2b0fc9176e7c")]
+
+// 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.Azure/Orchard.Azure.Web/Web.config b/src/Orchard.Azure/Orchard.Azure.Web/Web.config
new file mode 100644
index 000000000..b1de2f004
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/Web.config
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Orchard.Azure/Orchard.Azure.Web/WebRole.cs b/src/Orchard.Azure/Orchard.Azure.Web/WebRole.cs
new file mode 100644
index 000000000..e5b12b130
--- /dev/null
+++ b/src/Orchard.Azure/Orchard.Azure.Web/WebRole.cs
@@ -0,0 +1,52 @@
+using System.Linq;
+using Microsoft.WindowsAzure;
+using Microsoft.WindowsAzure.Diagnostics;
+using Microsoft.WindowsAzure.ServiceRuntime;
+
+namespace Orchard.Azure.Web {
+ public class WebRole : RoleEntryPoint {
+ public override bool OnStart() {
+ DiagnosticMonitor.Start("DiagnosticsConnectionString");
+
+ #region Setup CloudStorageAccount Configuration Setting Publisher
+
+ // This code sets up a handler to update CloudStorageAccount instances when their corresponding
+ // configuration settings change in the service configuration file.
+ CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => {
+ // Provide the configSetter with the initial value
+ configSetter(RoleEnvironment.GetConfigurationSettingValue(configName));
+
+ RoleEnvironment.Changed += (sender, arg) => {
+ if ( arg.Changes.OfType()
+ .Any(change => ( change.ConfigurationSettingName == configName )) ) {
+ // The corresponding configuration setting has changed, propagate the value
+ if ( !configSetter(RoleEnvironment.GetConfigurationSettingValue(configName)) ) {
+ // In this case, the change to the storage account credentials in the
+ // service configuration is significant enough that the role needs to be
+ // recycled in order to use the latest settings. (for example, the
+ // endpoint has changed)
+ RoleEnvironment.RequestRecycle();
+ }
+ }
+ };
+ });
+ #endregion
+
+
+ // For information on handling configuration changes
+ // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
+ RoleEnvironment.Changing += (sender, e) => {
+ // If a configuration setting is changing
+ if (
+ e.Changes.Any(
+ change => change is RoleEnvironmentConfigurationSettingChange) ) {
+ // Set e.Cancel to true to restart this role instance
+ e.Cancel = true;
+ }
+ };
+
+ return base.OnStart();
+ }
+
+ }
+}
diff --git a/src/Orchard.Azure/Orchard.Azure.csproj b/src/Orchard.Azure/Orchard.Azure.csproj
index f23dc2e67..21becee28 100644
--- a/src/Orchard.Azure/Orchard.Azure.csproj
+++ b/src/Orchard.Azure/Orchard.Azure.csproj
@@ -51,9 +51,11 @@
+
+
diff --git a/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs b/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs
index 2181919c8..938a16602 100644
--- a/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs
+++ b/src/Orchard.Azure/Storage/AzureBlobStorageProvider.cs
@@ -1,250 +1,16 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.WindowsAzure;
-using Microsoft.WindowsAzure.StorageClient;
-using System.IO;
+using Microsoft.WindowsAzure;
using Orchard.Storage;
namespace Orchard.Azure.Storage {
- public interface IDependency { }
- public class AzureBlobStorageProvider : IStorageProvider {
- public const string ContainerName = "media"; // container names must be lower cased
-
- private readonly CloudStorageAccount _storageAccount;
- private string _shellName;
- public CloudBlobClient BlobClient { get; private set; }
- public CloudBlobContainer Container { get; private set; }
+ public class AzureBlobStorageProvider : AzureFileSystem, IStorageProvider {
public AzureBlobStorageProvider(string shellName)
: this(shellName, CloudStorageAccount.FromConfigurationSetting("DataConnectionString")) {
}
- public AzureBlobStorageProvider(string shellName, CloudStorageAccount storageAccount) {
- // Setup the connection to custom storage accountm, e.g. Development Storage
- _storageAccount = storageAccount;
-
- _shellName = shellName;
-
- BlobClient = _storageAccount.CreateCloudBlobClient();
- // Get and create the container if it does not exist
- // The container is named with DNS naming restrictions (i.e. all lower case)
- Container = BlobClient.GetContainerReference(ContainerName);
- Container.CreateIfNotExist();
-
- Container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container });
- }
-
- private static void EnsurePathIsRelative(string path) {
- if ( path.StartsWith("/") || path.StartsWith("http://"))
- throw new ArgumentException("Path must be relative");
- }
-
- public IStorageFile GetFile(string path) {
- EnsurePathIsRelative(path);
- Container.EnsureBlobExists(path);
- return new AzureBlobFileStorage(Container.GetBlockBlobReference(path));
- }
-
- public IEnumerable ListFiles(string path) {
- EnsurePathIsRelative(path);
-
- string prefix = String.Concat(Container.Name, "/", _shellName, "/", path);
- if ( !prefix.EndsWith("/") )
- prefix += "/";
-
- foreach ( var blobItem in BlobClient.ListBlobsWithPrefix(prefix).OfType() ) {
- yield return new AzureBlobFileStorage(blobItem);
- }
- }
-
- public IEnumerable ListFolders(string path) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- if ( !Container.DirectoryExists(path) ) {
- try {
- CreateFolder(path);
- }
- catch ( Exception ex ) {
- throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", path, ex));
- }
- }
-
- return Container.GetDirectoryReference(path)
- .ListBlobs()
- .OfType()
- .Select(d => new AzureBlobFolderStorage(d))
- .ToList();
- }
-
- public void CreateFolder(string path) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- Container.EnsureDirectoryDoesNotExist(path);
- Container.GetDirectoryReference(path);
- }
-
- public void DeleteFolder(string path) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- Container.EnsureDirectoryExists(path);
- foreach ( var blob in Container.GetDirectoryReference(path).ListBlobs() ) {
- if ( blob is CloudBlob )
- ( (CloudBlob)blob ).Delete();
-
- if ( blob is CloudBlobDirectory )
- DeleteFolder(blob.Uri.ToString().Substring(Container.Uri.ToString().Length + 2 + _shellName.Length));
- }
- }
-
- public void RenameFolder(string path, string newPath) {
- EnsurePathIsRelative(path);
-
- EnsurePathIsRelative(newPath);
-
- if ( !path.EndsWith("/") )
- path += "/";
-
- if ( !newPath.EndsWith("/") )
- newPath += "/";
-
- foreach ( var blob in Container.GetDirectoryReference(_shellName + "/" + path).ListBlobs() ) {
- if ( blob is CloudBlob ) {
- string filename = Path.GetFileName(blob.Uri.ToString());
- string source = String.Concat(path, filename);
- string destination = String.Concat(newPath, filename);
- RenameFile(source, destination);
- }
-
- if ( blob is CloudBlobDirectory ) {
- string foldername = blob.Uri.Segments.Last();
- string source = String.Concat(path, foldername);
- string destination = String.Concat(newPath, foldername);
- RenameFolder(source, destination);
- }
- }
-
- }
-
- public void DeleteFile(string path) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- Container.EnsureBlobExists(path);
- var blob = Container.GetBlockBlobReference(path);
- blob.Delete();
- }
-
- public void RenameFile(string path, string newPath) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- EnsurePathIsRelative(newPath);
- newPath = String.Concat(_shellName, "/", newPath);
-
- Container.EnsureBlobExists(path);
- Container.EnsureBlobDoesNotExist(newPath);
-
- var blob = Container.GetBlockBlobReference(path);
- var newBlob = Container.GetBlockBlobReference(newPath);
- newBlob.CopyFromBlob(blob);
- blob.Delete();
- }
-
- public IStorageFile CreateFile(string path) {
- EnsurePathIsRelative(path);
- path = String.Concat(_shellName, "/", path);
-
- if ( Container.BlobExists(path) ) {
- throw new ArgumentException("File " + path + " already exists");
- }
-
- var blob = Container.GetBlockBlobReference(path);
- blob.OpenWrite().Dispose(); // force file creation
- return new AzureBlobFileStorage(blob);
- }
-
- private class AzureBlobFileStorage : IStorageFile {
- private readonly CloudBlockBlob _blob;
-
- public AzureBlobFileStorage(CloudBlockBlob blob) {
- _blob = blob;
- }
-
- public string GetPath() {
- return _blob.Uri.ToString();
- }
-
- public string GetName() {
- return Path.GetFileName(GetPath());
- }
-
- public long GetSize() {
- return _blob.Properties.Length;
- }
-
- public DateTime GetLastUpdated() {
- return _blob.Properties.LastModifiedUtc;
- }
-
- public string GetFileType() {
- return Path.GetExtension(GetPath());
- }
-
- public Stream OpenRead() {
- return _blob.OpenRead();
- }
-
- public Stream OpenWrite() {
- return _blob.OpenWrite();
- }
-
- }
-
- private class AzureBlobFolderStorage : IStorageFolder {
- private readonly CloudBlobDirectory _blob;
-
- public AzureBlobFolderStorage(CloudBlobDirectory blob) {
- _blob = blob;
- }
-
- public string GetName() {
- return _blob.Uri.ToString();
- }
-
- public long GetSize() {
- return GetDirectorySize(_blob);
- }
-
- public DateTime GetLastUpdated() {
- return DateTime.MinValue;
- }
-
- public IStorageFolder GetParent() {
- if ( _blob.Parent != null ) {
- return new AzureBlobFolderStorage(_blob.Parent);
- }
- throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory");
- }
-
- private static long GetDirectorySize(CloudBlobDirectory directoryBlob) {
- long size = 0;
-
- foreach ( var blobItem in directoryBlob.ListBlobs() ) {
- if ( blobItem is CloudBlob )
- size += ( (CloudBlob)blobItem ).Properties.Length;
-
- if ( blobItem is CloudBlobDirectory )
- size += GetDirectorySize((CloudBlobDirectory)blobItem);
- }
-
- return size;
- }
- }
-
+ public AzureBlobStorageProvider(string shellName, CloudStorageAccount storageAccount) : base("media", shellName, false, storageAccount) { }
}
}
+
+