diff --git a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs b/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs deleted file mode 100644 index c5e4ea1c3..000000000 --- a/src/Orchard.Azure/Environment/Configuration/AzureShellSettingsManager.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Microsoft.WindowsAzure; -using Microsoft.WindowsAzure.ServiceRuntime; -using Orchard.Environment.Configuration; -using Orchard.Azure.FileSystems; -using Microsoft.WindowsAzure.Storage; -using Orchard.FileSystems.Media; - -namespace Orchard.Azure.Environment.Configuration { - - public class AzureShellSettingsManager : IShellSettingsManager { - public const string ContainerName = "sites"; // container names must be lower cased - public const string SettingsFilename = "Settings.txt"; - public const char Separator = ':'; - public const string EmptyValue = "null"; - - private readonly IShellSettingsManagerEventHandler _events; - private readonly AzureFileSystem _fileSystem; - - public AzureShellSettingsManager(IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) - : this(CloudStorageAccount.Parse(RoleEnvironment.GetConfigurationSettingValue("DataConnectionString")), events, mimeTypeProvider) {} - - public AzureShellSettingsManager(CloudStorageAccount storageAccount, IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) - { - _events = events; - _fileSystem = new AzureFileSystem(ContainerName, String.Empty, true, mimeTypeProvider); - } - - IEnumerable IShellSettingsManager.LoadSettings() { - var settings = LoadSettings().ToArray(); - return settings; - } - - void IShellSettingsManager.SaveSettings(ShellSettings settings) { - var content = ShellSettingsSerializer.ComposeSettings(settings); - var filePath = _fileSystem.Combine(settings.Name, SettingsFilename); - - var file = _fileSystem.FileExists(filePath) - ? _fileSystem.GetFile(filePath) - : _fileSystem.CreateFile(filePath); - - using (var stream = file.OpenWrite()) { - using (var writer = new StreamWriter(stream)) { - writer.Write(content); - } - } - - _events.Saved(settings); - } - - IEnumerable LoadSettings() { - foreach (var folder in _fileSystem.ListFolders(null)) - foreach (var file in _fileSystem.ListFiles(folder.GetPath())) { - if (!String.Equals(file.GetName(), SettingsFilename)) - continue; - - using (var stream = file.OpenRead()) - using (var reader = new StreamReader(stream)) - yield return ShellSettingsSerializer.ParseSettings(reader.ReadToEnd()); - } - } - } -} diff --git a/src/Orchard.Azure/Logging/AzureAppender.cs b/src/Orchard.Azure/Logging/AzureAppender.cs deleted file mode 100644 index a7eccdbd5..000000000 --- a/src/Orchard.Azure/Logging/AzureAppender.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using Microsoft.WindowsAzure.Diagnostics; -using log4net.Appender; -using log4net.Core; - -namespace Orchard.Azure.Logging { - - /// - /// Azure specific implementation of a log4net appender. - /// Uses DataConnectionString to define the location of the log. - /// - public class AzureAppender : AppenderSkeleton { - private const string WadConnectionString = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"; - public AzureAppender() { - - var defaultDiagnostics = DiagnosticMonitor.GetDefaultInitialConfiguration(); - var period = TimeSpan.FromMinutes(1d); - - defaultDiagnostics.Directories.ScheduledTransferPeriod = period; - defaultDiagnostics.Logs.ScheduledTransferPeriod = period; - defaultDiagnostics.WindowsEventLog.ScheduledTransferPeriod = period; - - DiagnosticMonitor.Start(WadConnectionString, defaultDiagnostics); - } - - protected override void Append(LoggingEvent loggingEvent) { - var formattedLog = RenderLoggingEvent(loggingEvent); - - // this is the way to logging into Azure - System.Diagnostics.Trace.WriteLine(formattedLog); - } - } -} diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg index aec004893..23fe3b2af 100644 --- a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg +++ b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceConfiguration.cscfg @@ -3,7 +3,7 @@ - + diff --git a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef index a0185fefe..1f8e20000 100644 --- a/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef +++ b/src/Orchard.Azure/Orchard.Azure.CloudService/ServiceDefinition.csdef @@ -12,7 +12,7 @@ - + diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config index 8b0a12608..f0854a40b 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/Host.config @@ -1,19 +1,15 @@  - - -
- - - - - - - - - - - + + +
+ + + + + + + + + diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/HostComponents.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/HostComponents.config index 4a0d2b08b..91d330492 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/HostComponents.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/HostComponents.config @@ -1,85 +1,85 @@  - - - - - - - - + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - - + + + + + + - - - - - - + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + + + + + + + + diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/Sites.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/Sites.config deleted file mode 100644 index bbbaba9be..000000000 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/Sites.config +++ /dev/null @@ -1,20 +0,0 @@ - - - - -
- - - - - - - - - - - - - diff --git a/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config b/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config index 9ab248527..c9dfbe0f0 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config +++ b/src/Orchard.Azure/Orchard.Azure.Web/Config/log4net.config @@ -7,7 +7,7 @@ - + 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 d2ad69d4d..72f3d0a00 100644 --- a/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj +++ b/src/Orchard.Azure/Orchard.Azure.Web/Orchard.Azure.Web.csproj @@ -1,533 +1,523 @@  - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {0DF8F426-9F30-4918-8F64-A5B40BA12D10} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - Orchard.Azure.Web - Orchard.Azure.Web - v4.0 - false - false - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\ - TRACE - prompt - 4 - AllRules.ruleset - AnyCPU - - - - False - ..\..\..\lib\autofac\Autofac.dll - True - - - ..\..\..\lib\autofac\Autofac.Integration.Web.dll - True - - - False - ..\..\..\lib\aspnetmvc\Microsoft.Web.Infrastructure.dll - True - - - False - C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-10\ref\Microsoft.WindowsAzure.Configuration.dll - - - C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-10\ref\Microsoft.WindowsAzure.Diagnostics.dll - True - - - False - - - - ..\..\..\lib\newtonsoft.json\Newtonsoft.Json.dll - True - - - ..\..\..\lib\nhibernate.sqlazure\NHibernate.SqlAzure.dll - - - False - - - False - - - 3.5 - False - - - False - - - - False - ..\..\..\lib\sqlce\System.Data.SqlServerCe.dll - True - - - False - - - False - - - False - - - False - - - ..\..\..\lib\aspnetmvc\System.Web.Helpers.dll - True - - - False - ..\..\..\lib\aspnetmvc\System.Web.Mvc.dll - True - - - False - - - False - - - ..\..\..\lib\aspnetmvc\System.Web.Razor.dll - True - - - False - - - ..\..\..\lib\aspnetmvc\System.Web.WebPages.dll - True - - - False - ..\..\..\lib\aspnetmvc\System.Web.WebPages.Deployment.dll - True - - - ..\..\..\lib\aspnetmvc\System.Web.WebPages.Razor.dll - True - - - False - - - False - - - False - - - False - - - False - - - ..\..\..\lib\aspnetmvc\WebMatrix.Data.dll - True - - - - - Global.asax - - - - - - - - - Designer - - - - - Web.config - - - Web.config - - - - - {9916839C-39FC-4CEB-A5AF-89CA7E87119F} - Orchard.Core - True - - - {D5D447D7-EF8E-43A6-B9A4-3B025DD9F45D} - Lucene - True - - - {3158C928-888C-4A84-8BC1-4A8257489538} - Markdown - - - {475B6C45-B27C-438B-8966-908B9D6D1077} - Orchard.Alias - - - {91bc2e7f-da04-421c-98ef-76d37cec130c} - Orchard.AntiSpam - - - {1C981BB3-26F7-494C-9005-CC27A5144233} - Orchard.ArchiveLater - True - - - {66FCCD76-2761-47E3-8D11-B45D0001DDAA} - Orchard.Autoroute - - - {cbc7993c-57d8-4a6c-992c-19e849dfe71d} - Orchard.AzureBlobStorage - - - {63FBD4D9-E1DA-4A7B-AA6A-D6074FE50867} - Orchard.Blogs - True - - - {C0C45321-B51D-4D8D-9B7B-AA4C2E0B2962} - Orchard.CodeGeneration - True - - - {14C049FD-B35B-415A-A824-87F26B26E7FD} - Orchard.Comments - True - - - {e826f796-8ce3-4b5b-8423-5aa5f81d2fc3} - Orchard.ContentPermissions - - - {f301ef7d-f19c-4d83-aa94-cb64f29c037d} - Orchard.ContentPicker - - - {0E7646E8-FE8F-43C1-8799-D97860925EC4} - Orchard.ContentTypes - True - - - {2cf067ca-064b-43c6-8b88-5e3b99a65f1d} - Orchard.CustomForms - - - {4A4595EF-6C37-4F99-96ED-4AE0B9E438D3} - Orchard.DesignerTools - True - - - {05660F47-D649-48BD-9DED-DF4E01E7CFF9} - Orchard.Email - True - - - {3787DDE5-E5C8-4841-BDA7-DCB325388064} - Orchard.Fields - - - {642A49D7-8752-4177-80D6-BFBBCFAD3DE0} - Orchard.Forms - - - {1f0b6b85-8b0b-47ca-899d-f25b4f1b52c3} - Orchard.ImageEditor - - - {fe5c5947-d2d5-42c5-992a-13d672946135} - Orchard.ImportExport - - - {EA2B9121-EF54-40A6-A53E-6593C86EE696} - Orchard.Indexing - True - - - {8F116B06-1C0E-4E4C-9A0A-D2FAB851E768} - Orchard.jQuery - True - - - {137906EA-15FE-4AD8-A6A0-27528F0477D6} - Orchard.Lists - True - - - {FBC8B571-ED50-49D8-8D9D-64AB7454A0D6} - Orchard.Localization - True - - - {73a7688a-5bd3-4f7e-adfa-ce36c5a10e3b} - Orchard.MediaLibrary - - - {43D0EC0B-1955-4566-8D31-7B9102DA1703} - Orchard.MediaPicker - True - - - {08191fcd-7258-4f19-95fb-aec3de77b2eb} - Orchard.MediaProcessing - - - {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} - Orchard.Media - True - - - {085948FF-0E9B-4A9A-B564-F8B8B4BDDDBC} - Orchard.Messaging - True - - - {EA4F1DA7-F2AB-4384-9AA4-9B756E2026B1} - Orchard.Migrations - True - - - {17F86780-9A1F-4AA1-86F1-875EEC2730C7} - Orchard.Modules - True - - - {72457126-E118-4171-A08F-9A709EE4B7FC} - Orchard.MultiTenancy - True - - - {6e444ff1-a47c-4cf6-bb3f-507c8ebd776d} - Orchard.OutputCache - - - {DFD137A2-DDB5-4D22-BE0D-FA9AD4C8B059} - Orchard.Packaging - True - - - {3420C92A-747F-4990-BA08-F2C9531E44AD} - Orchard.Pages - True - - - {5531E894-D259-45A3-AA61-26DBE720C1CE} - Orchard.Projections - - - {C889167C-E52C-4A65-A419-224B3D1B957D} - Orchard.PublishLater - True - - - {FC1D74E8-7A4D-48F4-83DE-95C6173780C4} - Orchard.Recipes - True - - - {D10AD48F-407D-4DB5-A328-173EC7CB010F} - Orchard.Roles - True - - - {966EC390-3C7F-4D98-92A6-F0F30D02E9B1} - Orchard.Rules - - - {5d13ef34-8b39-4ec5-847f-e12892acf841} - Orchard.Scripting.CSharp - - - {2AD6973D-C7BB-416E-89FE-EEE34664E05F} - Orchard.Scripting.Dlr - True - - - {2AD6973D-C7BB-416E-89FE-EEE34664E05F} - Orchard.Scripting.Dlr - True - - - {4BE4EB01-AC56-4048-924E-2CA77F509ABA} - Orchard.Search - True - - - {8C7FCBC2-E6E1-405E-BFB5-D8D9E67A09C4} - Orchard.Setup - True - - - {5D0F00F0-26C9-4785-AD61-B85710C60EB0} - Orchard.Tags - True - - - {3F72A4E9-7B72-4260-B010-C16EC54F9BAF} - Orchard.TaskLease - - - {e649ea64-d213-461b-87f7-d67035801443} - Orchard.Taxonomies - - - {CDE24A24-01D3-403C-84B9-37722E18DFB7} - Orchard.Themes - True - - - {6F759635-13D7-4E94-BCC9-80445D63F117} - Orchard.Tokens - - - {79AED36E-ABD0-4747-93D3-8722B042454B} - Orchard.Users - True - - - {9cd5c81f-5828-4384-8474-2e2be71d5edd} - Orchard.Warmup - - - {194D3CCC-1153-474D-8176-FDE8D7D0D0BD} - Orchard.Widgets - True - - - {7059493c-8251-4764-9c1e-2368b8b485bc} - Orchard.Workflows - - - {3bd22132-d538-48c6-8854-f71333c798eb} - SysCache - - - {954CA994-D204-468B-9D69-51F6AD3E1C29} - TinyMce - True - - - {8a9fdb57-342d-49c2-bafc-d885aae5cc7c} - Upgrade - - - {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} - Orchard.Framework - False - True - - - {33B1BC8D-E292-4972-A363-22056B207156} - Orchard - True - - - {2505AA84-65A6-43D0-9C27-4F44FD576284} - Orchard.Azure - True - - - - - - Designer - - - - - - - - Designer - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - Config\log4net.config - False - $(TransformWebConfigIntermediateLocation)\original - Config\log4net.$(Configuration).config - $(TransformWebConfigIntermediateLocation)\original - $(TransformWebConfigIntermediateLocation)\original\%(DestinationRelativePath) - $(TransformWebConfigIntermediateLocation)\transformed\%(DestinationRelativePath) - $(_PackageTempDir)\%(DestinationRelativePath) - Designer - - - log4net.config - - - log4net.config - - - - - - - - - - False - True - 60453 - / - - - False - False - - - False - - - - - - - - - -
-
- -
- + + + + +
+
+ +
+ - - - - + + + + - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + + + - - - - + - - - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - diff --git a/src/Orchard.Azure/Orchard.Azure.csproj b/src/Orchard.Azure/Orchard.Azure.csproj deleted file mode 100644 index bfb25270b..000000000 --- a/src/Orchard.Azure/Orchard.Azure.csproj +++ /dev/null @@ -1,127 +0,0 @@ - - - - Debug - AnyCPU - 9.0.30729 - 2.0 - {2505AA84-65A6-43D0-9C27-4F44FD576284} - Library - Properties - Orchard.Azure - Orchard.Azure - v4.0 - 512 - - - 3.5 - - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - AllRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - AllRules.ruleset - - - - ..\..\lib\log4net\log4net.dll - - - - False - C:\Program Files\Microsoft SDKs\Windows Azure\.NET SDK\2012-10\ref\Microsoft.WindowsAzure.Diagnostics.dll - - - False - True - - - - - - 3.5 - - - - 3.5 - - - 3.5 - - - - - - - - - - - - - - - - - - {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} - Orchard.Framework - True - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - \ No newline at end of file diff --git a/src/Orchard.Azure/Orchard.Azure.sln b/src/Orchard.Azure/Orchard.Azure.sln index 9bdc6c000..51f70678a 100644 --- a/src/Orchard.Azure/Orchard.Azure.sln +++ b/src/Orchard.Azure/Orchard.Azure.sln @@ -11,8 +11,6 @@ Project("{CC5FD16D-436D-48AD-A40C-5A424C6E3E79}") = "Orchard.Azure.CloudService" 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 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Core", "..\Orchard.Web\Core\Orchard.Core.csproj", "{9916839C-39FC-4CEB-A5AF-89CA7E87119F}" @@ -133,10 +131,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Scripting.CSharp", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Taxonomies", "..\Orchard.Web\Modules\Orchard.Taxonomies\Orchard.Taxonomies.csproj", "{E649EA64-D213-461B-87F7-D67035801443}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.AzureBlobStorage", "..\Orchard.Web\Modules\Orchard.AzureBlobStorage\Orchard.AzureBlobStorage.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "..\Orchard.Web\Modules\Orchard.Azure\Orchard.Azure.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules.Deprecated", "Modules.Deprecated", "{B6092A92-1071-4C30-AD55-8E8D46BC2F14}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Orchard", "Orchard", "{F2AB7512-139A-420F-AE3A-9ED22CA52CE1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -151,10 +151,6 @@ Global {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 @@ -460,8 +456,10 @@ Global {33B1BC8D-E292-4972-A363-22056B207156} = {75E7476C-C05B-4C41-8E38-081D3EB55659} {CB70A642-8CEC-4DDE-8C9F-AD08900EC98D} = {84650275-884D-4CBB-9CC0-67553996E211} {137906EA-15FE-4AD8-A6A0-27528F0477D6} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14} - {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14} {43D0EC0B-1955-4566-8D31-7B9102DA1703} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14} {966EC390-3C7F-4D98-92A6-F0F30D02E9B1} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14} + {D9A7B330-CD22-4DA1-A95A-8DE1982AD8EB} = {B6092A92-1071-4C30-AD55-8E8D46BC2F14} + {9916839C-39FC-4CEB-A5AF-89CA7E87119F} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1} + {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} = {F2AB7512-139A-420F-AE3A-9ED22CA52CE1} EndGlobalSection EndGlobal diff --git a/src/Orchard.Azure/Properties/AssemblyInfo.cs b/src/Orchard.Azure/Properties/AssemblyInfo.cs deleted file mode 100644 index bae2c6fee..000000000 --- a/src/Orchard.Azure/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -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")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Microsoft")] -[assembly: AssemblyProduct("Orchard.Azure")] -[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2009")] -[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("02bb8033-01ce-4c2d-a3f7-2a03641f4640")] - -// 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 Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.2.41")] -[assembly: AssemblyFileVersion("1.2.41")] diff --git a/src/Orchard.Web/Config/Host.config b/src/Orchard.Web/Config/Host.config index 7b19c77c8..813b53f8e 100644 --- a/src/Orchard.Web/Config/Host.config +++ b/src/Orchard.Web/Config/Host.config @@ -1,27 +1,17 @@  - -
- + +
+ - - - - - - - - - + + + + + + + \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Module.txt b/src/Orchard.Web/Modules/Orchard.Azure/Module.txt new file mode 100644 index 000000000..bb99dbe61 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Module.txt @@ -0,0 +1,21 @@ +Name: Orchard.Azure +AntiForgery: enabled +Author: The Orchard Team +Website: http://orchardproject.net +Version: 1.0 +OrchardVersion: 1.7 +Description: Provides a set of Orchard service implementations targeting Windows Azure services. +Features: + Orchard.Azure.OutputCache: + Name: Windows Azure Output Cache + Description: Activates an Orchard output cache provider that targets Windows Azure Cache. + Dependencies: Orchard.OutputCache + Category: Hosting + Orchard.Azure.DatabaseCache: + Name: Windows Azure Database Cache + Description: Activates an NHibernate second-level cache provider that targets Windows Azure Cache. + Category: Hosting + Orchard.Azure.MediaStorage: + Name: Windows Azure Media Storage + Description: Activates an Orchard media storage provider that targets Windows Azure Blob Storage. + Category: Hosting \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Orchard.AzureBlobStorage.csproj b/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj similarity index 73% rename from src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Orchard.AzureBlobStorage.csproj rename to src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj index 4b0e18004..d208b03ad 100644 --- a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Orchard.AzureBlobStorage.csproj +++ b/src/Orchard.Web/Modules/Orchard.Azure/Orchard.Azure.csproj @@ -10,8 +10,8 @@ {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} Library Properties - Orchard.AzureBlobStorage - Orchard.AzureBlobStorage + Orchard.Azure + Orchard.Azure v4.0 false @@ -45,14 +45,20 @@ AllRules.ruleset - - + False - ..\..\..\..\lib\windowsazure\Microsoft.WindowsAzure.Configuration.dll + ..\..\..\..\lib\log4net\log4net.dll - + + + + + + + + False - ..\..\..\..\lib\windowsazure\Microsoft.WindowsAzure.Storage.dll + ..\..\..\..\lib\nhibernate\NHibernate.dll @@ -77,10 +83,6 @@ - - {2505aa84-65a6-43d0-9c27-4f44fd576284} - Orchard.Azure - {2D1D92BB-4555-4CBE-8D0E-63563D6CE4C6} Orchard.Framework @@ -89,9 +91,22 @@ {9916839C-39FC-4CEB-A5AF-89CA7E87119F} Orchard.Core + + {6e444ff1-a47c-4cf6-bb3f-507c8ebd776d} + Orchard.OutputCache + - + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Properties/AssemblyInfo.cs b/src/Orchard.Web/Modules/Orchard.Azure/Properties/AssemblyInfo.cs similarity index 97% rename from src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Properties/AssemblyInfo.cs rename to src/Orchard.Web/Modules/Orchard.Azure/Properties/AssemblyInfo.cs index 760d681e8..029e245ff 100644 --- a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Properties/AssemblyInfo.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Security; - -// 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.AzureBlobStorage")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyProduct("Orchard")] -[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2013")] -[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("bcd6a602-4041-4a40-af4f-82f3dc0ab136")] - -// 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")] -[assembly: AssemblyFileVersion("1.0")] - +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// 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.AzureBlobStorage")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Orchard")] +[assembly: AssemblyCopyright("Copyright © Outercurve Foundation 2013")] +[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("bcd6a602-4041-4a40-af4f-82f3dc0ab136")] + +// 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")] +[assembly: AssemblyFileVersion("1.0")] + diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheClient.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheClient.cs new file mode 100644 index 000000000..da491c64a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheClient.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.ApplicationServer.Caching; +using NHibernate; +using NHibernate.Cache; + +namespace Orchard.Azure.Services.Caching.Database { + + public class AzureCacheClient : ICache { + + public AzureCacheClient(string cacheHostIdentifier, string cacheName, string region) { + + var dataCacheFactoryConfiguration = new DataCacheFactoryConfiguration() { + AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, cacheHostIdentifier), + MaxConnectionsToServer = 32, + UseLegacyProtocol = false + }; + + var dataCacheFactory = new DataCacheFactory(dataCacheFactoryConfiguration); + + _logger = LoggerProvider.LoggerFor(typeof(AzureCacheClient)); + _region = region ?? _defaultRegion; + + // Azure Cache supports only alphanumeric strings for regions and + // Orchard can get a lot more creative than that. Remove all non + // alphanumering characters from the region, and append the hash code + // of the original string to mitigate the risk of two distinct original + // region strings yielding the same transformed region string. + _regionAlphaNumeric = new String(Array.FindAll(_region.ToCharArray(), c => Char.IsLetterOrDigit(c))) + _region.GetHashCode().ToString(); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Creating instance with CacheName='{0}' and Region='{1}' (original Region='{2}').", cacheName, _regionAlphaNumeric, _region); + + if (!String.IsNullOrEmpty(cacheName)) + _cache = dataCacheFactory.GetCache(cacheName); + else + _cache = dataCacheFactory.GetDefaultCache(); + + _cache.CreateRegion(_regionAlphaNumeric); + + _lockHandleDictionary = new ConcurrentDictionary(); + _lockTimeout = TimeSpan.FromSeconds(30); + } + + private const string _defaultRegion = "NHibernate"; + private readonly IInternalLogger _logger; + private readonly string _region; + private readonly string _regionAlphaNumeric; + private readonly DataCache _cache; + private readonly ConcurrentDictionary _lockHandleDictionary; + private readonly TimeSpan _lockTimeout; + + #region ICache Members + + public object Get(object key) { + if (key == null) + throw new ArgumentNullException("key", "The parameter 'key' must not be null."); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Get() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); + + return _cache.Get(key.ToString(), _regionAlphaNumeric); + } + + public void Put(object key, object value) { + if (key == null) + throw new ArgumentNullException("key", "The parameter 'key' must not be null."); + if (value == null) + throw new ArgumentNullException("value", "The parameter 'value' must not be null."); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Put() invoked with key='{0}' and value='{1}' in region '{2}'.", key, value, _regionAlphaNumeric); + + _cache.Put(key.ToString(), value, _regionAlphaNumeric); + } + + public void Remove(object key) { + if (key == null) + throw new ArgumentNullException("key", "The parameter 'key' must not be null."); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Remove() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); + + _cache.Remove(key.ToString(), _regionAlphaNumeric); + } + + public void Clear() { + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Clear() invoked in region '{0}'.", _regionAlphaNumeric); + + _cache.ClearRegion(_regionAlphaNumeric); + } + + public void Destroy() { + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Destroy() invoked in region '{0}'.", _regionAlphaNumeric); + + Clear(); + } + + // The NHibernate locking mechanism and the Azure Cache pessimistic concurrency + // model are not a perfect fit. For example, Azure Cache has atomic "get-and-lock" + // and "put-and-unlock" semantics but there is no corresponding atomic operations + // defined on the ICache interface of NHibernate. Also, Azure Cache does not + // strictly enforce the pessimistic concurrency model - clients are responsible + // for following the locking protocol and therefore the implementation assumes that + // NHibernate will always call ICache.Lock() before calling ICache.Put() for data + // with concurrency management requirements. The implementations of ICache.Lock() + // and ICache.Unlock() below are therefore not as elegant as they would otherwise + // be (if not downright hackish). + + // TODO: Try to understand how it's used, and make locking more robust. + public void Lock(object key) { + if (key == null) + throw new ArgumentNullException("key", "The parameter 'key' must not be null."); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Lock() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); + + try { + DataCacheLockHandle lockHandle = null; + _cache.GetAndLock(key.ToString(), _lockTimeout, out lockHandle, _regionAlphaNumeric); + _lockHandleDictionary.TryAdd(key, lockHandle); + } + catch (Exception ex) { + if (_logger.IsErrorEnabled) + _logger.Error("Exception thrown while trying to lock object in cache.", ex); + + throw; + } + } + + // TODO: Try to understand how it's used, and make locking more robust. + public void Unlock(object key) { + if (key == null) + throw new ArgumentNullException("key", "The parameter 'key' must not be null."); + + if (_logger.IsDebugEnabled) + _logger.DebugFormat("Unlock() invoked with key='{0}' in region '{1}'.", key, _regionAlphaNumeric); + + try { + DataCacheLockHandle lockHandle = null; + if (_lockHandleDictionary.TryRemove(key, out lockHandle)) + _cache.Unlock(key.ToString(), lockHandle, _regionAlphaNumeric); + } + catch (Exception ex) { + if (_logger.IsErrorEnabled) + _logger.Error("Exception thrown while trying to unlock object in cache.", ex); + + throw; + } + } + + // TODO: Try to understand what this is for and how it's used. + public long NextTimestamp() { + if (_logger.IsDebugEnabled) + _logger.DebugFormat("NextTimestamp() invoked in region '{0}'.", _regionAlphaNumeric); + + return Timestamper.Next(); + } + + // TODO: Try to understand what this is for and how it's used. + public int Timeout { + get { + return Timestamper.OneMs * (int)_lockTimeout.TotalMilliseconds; + } + } + + public string RegionName { + get { + // Return original region here (which may be non-alphanumeric) so NHibernate + // will recognize it as the same region supplied to the constructor. + return _region; + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheConfiguration.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheConfiguration.cs new file mode 100644 index 000000000..9399036d8 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheConfiguration.cs @@ -0,0 +1,36 @@ +using NHibernate.Cfg.Loquacious; +using Orchard; +using Orchard.Data; +using Orchard.Environment.Configuration; +using Orchard.Environment.Extensions; +using Orchard.Logging; + +namespace Orchard.Azure.Services.Caching.Database { + + [OrchardFeature("Orchard.Azure.DatabaseCache")] + [OrchardSuppressDependency("Orchard.Data.DefaultDatabaseCacheConfiguration")] + public class AzureCacheConfiguration : Component, IDatabaseCacheConfiguration { + + public static string CacheHostIdentifier; + public static string CacheName; + + public AzureCacheConfiguration(ShellSettings shellSettings) + : base() { + _shellSettings = shellSettings; + + CacheHostIdentifier = shellSettings["Azure.DatabaseCache.HostIdentifier"]; + CacheName = shellSettings["Azure.DatabaseCache.CacheName"]; + } + + private readonly ShellSettings _shellSettings; + + public void Configure(ICacheConfigurationProperties cache) { + cache.Provider(); + cache.UseQueryCache = true; + cache.RegionsPrefix = _shellSettings.Name; + //cache.RegionsPrefix = "Orchard"; + + Logger.Information("Configured NHibernate cache provider '{0}' with regions prefix '{1}'.", typeof(AzureCacheProvider).Name, _shellSettings.Name); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheProvider.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheProvider.cs new file mode 100644 index 000000000..6c393db3b --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Database/AzureCacheProvider.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using Microsoft.WindowsAzure.ServiceRuntime; +using NHibernate.Cache; + +namespace Orchard.Azure.Services.Caching.Database { + + public class AzureCacheProvider : ICacheProvider { + + #region ICacheProvider Members + + public ICache BuildCache(string regionName, IDictionary properties) { + return new AzureCacheClient(AzureCacheConfiguration.CacheHostIdentifier, AzureCacheConfiguration.CacheName, regionName); + } + + public long NextTimestamp() { + return Timestamper.Next(); + } + + public void Start(IDictionary properties) { + } + + public void Stop() { + } + + #endregion + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs new file mode 100644 index 000000000..19d3336b0 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Caching/Output/AzureOutputCacheStorageProvider.cs @@ -0,0 +1,102 @@ +using Microsoft.ApplicationServer.Caching; +using Orchard.Environment.Configuration; +using Orchard.Environment.Extensions; +using Orchard.OutputCache.Models; +using Orchard.OutputCache.Services; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Web; + +namespace Orchard.Azure.Services.Caching.Output { + + [OrchardFeature("Orchard.Azure.OutputCache")] + [OrchardSuppressDependency("Orchard.OutputCache.Services.DefaultCacheStorageProvider")] + public class AzureOutputCacheStorageProvider : Component, IOutputCacheStorageProvider { + + public AzureOutputCacheStorageProvider(ShellSettings shellSettings) + : base() { + + var cacheHostIdentifier = shellSettings["Azure.OutputCache.HostIdentifier"]; + var cacheName = shellSettings["Azure.OutputCache.CacheName"]; + + var dataCacheFactoryConfiguration = new DataCacheFactoryConfiguration() { + AutoDiscoverProperty = new DataCacheAutoDiscoverProperty(true, cacheHostIdentifier), + MaxConnectionsToServer = 32, + UseLegacyProtocol = false + }; + + var dataCacheFactory = new DataCacheFactory(dataCacheFactoryConfiguration); + + if (!String.IsNullOrEmpty(cacheName)) + _cache = dataCacheFactory.GetCache(cacheName); + else + _cache = dataCacheFactory.GetDefaultCache(); + + _usingSharedCaching = Boolean.Parse(shellSettings["Azure.OutputCache.IsSharedCaching"]); + + if (!_usingSharedCaching) + { + // If not using Windows Azure Shared Caching we can enable additional features by + // storing all cache items in a region. This enables enumerating and counting all + // items currently in the cache. + _region = shellSettings.Name; + _cache.CreateRegion(_region); + } + } + + private readonly DataCache _cache; + private readonly bool _usingSharedCaching; // True if using Windows Azure Shared Caching rather than role-based caching. + private readonly string _region; + + public void Set(string key, CacheItem cacheItem) { + if (_usingSharedCaching) + _cache.Put(key, cacheItem); + else + _cache.Put(key, cacheItem, _region); + } + + public void Remove(string key) { + if (_usingSharedCaching) + _cache.Remove(key); + else + _cache.Remove(key, _region); + } + + public void RemoveAll() { + if (_usingSharedCaching) + _cache.Clear(); + else + _cache.ClearRegion(_region); + } + + public CacheItem GetCacheItem(string key) { + if (_usingSharedCaching) + return _cache.Get(key) as CacheItem; + else + return _cache.Get(key, _region) as CacheItem; + } + + public IEnumerable GetCacheItems(int skip, int count) { + if (_usingSharedCaching) + throw new NotSupportedException("The GetCacheItems() method is not supported with Windows Azure Shared Caching."); + + return _cache.GetObjectsInRegion(_region).AsParallel() + .Select(x => x.Value) + .OfType() + .Skip(skip) + .Take(count) + .ToArray(); + } + + public int GetCacheItemsCount() { + if (_usingSharedCaching) + throw new NotSupportedException("The GetCacheItemsCount() method is not supported with Windows Azure Shared Caching."); + + return _cache.GetObjectsInRegion(_region).AsParallel() + .Select(x => x.Value) + .OfType() + .Count(); + } + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureBlobShellSettingsManager.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureBlobShellSettingsManager.cs new file mode 100644 index 000000000..3767a65f4 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureBlobShellSettingsManager.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.WindowsAzure; +using Microsoft.WindowsAzure.Storage; +using Orchard.Azure.Services.FileSystems; +using Orchard.Environment.Configuration; +using Orchard.FileSystems.Media; + +namespace Orchard.Azure.Services.Environment.Configuration { + + public class AzureBlobShellSettingsManager : Component, IShellSettingsManager { + + public const string ConnectionStringSettingName = "Orchard.StorageConnectionString"; + public const string ContainerName = "sites"; // Container names must be lower cased. + public const string SettingsFilename = "Settings.txt"; + public const char Separator = ':'; + public const string EmptyValue = "null"; + + protected readonly IShellSettingsManagerEventHandler Events; + protected readonly AzureFileSystem FileSystem; + + public AzureBlobShellSettingsManager(IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) + : this(CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting(ConnectionStringSettingName)), events, mimeTypeProvider) { + } + + public AzureBlobShellSettingsManager(CloudStorageAccount storageAccount, IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) { + Events = events; + FileSystem = new AzureFileSystem(ContainerName, String.Empty, true, mimeTypeProvider); + } + + public virtual IEnumerable LoadSettings() { + var settings = LoadSettingsInternal().ToArray(); + return settings; + } + + public virtual void SaveSettings(ShellSettings settings) { + var content = ShellSettingsSerializer.ComposeSettings(settings); + var filePath = FileSystem.Combine(settings.Name, SettingsFilename); + + var file = FileSystem.FileExists(filePath) + ? FileSystem.GetFile(filePath) + : FileSystem.CreateFile(filePath); + + using (var stream = file.OpenWrite()) { + using (var writer = new StreamWriter(stream)) { + writer.Write(content); + } + } + + Events.Saved(settings); + } + + private IEnumerable LoadSettingsInternal() { + foreach (var folder in FileSystem.ListFolders(null)) { + foreach (var file in FileSystem.ListFiles(folder.GetPath())) { + if (!String.Equals(file.GetName(), SettingsFilename)) + continue; + using (var stream = file.OpenRead()) { + using (var reader = new StreamReader(stream)) + yield return ShellSettingsSerializer.ParseSettings(reader.ReadToEnd()); + } + } + } + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureConfigShellSettingsManager.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureConfigShellSettingsManager.cs new file mode 100644 index 000000000..dc96b6f0a --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Environment/Configuration/AzureConfigShellSettingsManager.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.WindowsAzure; +using Microsoft.WindowsAzure.ServiceRuntime; +using Microsoft.WindowsAzure.Storage; +using Orchard.Environment.Configuration; +using Orchard.FileSystems.Media; +using Orchard.Logging; + +namespace Orchard.Azure.Services.Environment.Configuration { + + /// + /// Provides a ShellSettingsManager implementation that reads each value first + /// from platform configuration and second from the Settings.txt file stored in + /// Azure Blob Storage (using AzureBlobShellSettingsManager). + /// + /// + /// This provider can be activated by configuration in Orchard.Web or Orchard.Azure.Web. + /// + /// For each setting {SettingName} found in the Settings.txt file, this provider also looks + /// for a corresponding platform setting Orchard.{TenantName}.{SettingName}. The + /// class is used to search + /// for platform configuration settings in Azure Cloud Service configuration settings, Azure + /// Web Sites configuration settings and finally the Web.config appSettings element. + /// + /// If a settings value is found, that value takes precedence over the Settings.txt + /// file when settings are read. When settings are written, they are always only written + /// to the backing Settings.txt file since cloud configuration settings are read-only. + /// + /// This can be very useful in scenarios when you deploy your site to a number of different + /// environments and want to use cloud configuration profiles to maintain separate + /// settings for each of them that are automatically used when publishing. The fallback + /// mechanism enables you to keep environment-specific read-only settings (such as + /// SqlServerConnectionString) in cloud configuration profiles while leaving the + /// remaining and writeable settings (such as State) in the Settings.txt file. + /// + /// When distributing your settings across cloud configuration and Settings.txt, you + /// must ensure that any settings that Orchard considers writeable (such as State) are + /// not placed in cloud configuration. + /// + /// This provider also handles role configuration change events when running in an + /// Azure Cloud Service to ensure all Orchard tenents are notified whenever a role configuration + /// settings is changed though the management portal or API. + /// + public class AzureConfigShellSettingsManager : AzureBlobShellSettingsManager { + + private const string _prefix = "Orchard"; + + public AzureConfigShellSettingsManager(IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) + : this(CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting(ConnectionStringSettingName)), events, mimeTypeProvider) { + } + + public AzureConfigShellSettingsManager(CloudStorageAccount storageAccount, IShellSettingsManagerEventHandler events, IMimeTypeProvider mimeTypeProvider) + : base(storageAccount, events, mimeTypeProvider) { + if (RoleEnvironment.IsAvailable) { + RoleEnvironment.Changing += RoleEnvironment_Changing; + RoleEnvironment.Changed += RoleEnvironment_Changed; + } + } + + public override IEnumerable LoadSettings() { + var settingsList = base.LoadSettings(); + + foreach (var settings in settingsList) { + foreach (var key in settings.Keys) { + var cloudConfigurationKey = String.Format("{0}.{1}.{2}", _prefix, settings.Name, key); + var cloudConfigurationValue = ParseValue(CloudConfigurationManager.GetSetting(cloudConfigurationKey)); + if (cloudConfigurationValue != null) + settings[key] = cloudConfigurationValue; + } + } + + return settingsList; + } + + void RoleEnvironment_Changing(object sender, RoleEnvironmentChangingEventArgs e) { + // Indicate to the fabric controller that we can handle any changes. + e.Cancel = false; + } + + private void RoleEnvironment_Changed(object sender, RoleEnvironmentChangedEventArgs e) { + Logger.Debug("Handling RoleEnvironmentChanged event."); + + var configurationChangeQuery = + from c in e.Changes + where c is RoleEnvironmentConfigurationSettingChange + select c; + + // If there's a configuration settings change, inform all Orchard tenants. + if (configurationChangeQuery.Any()) { + Logger.Information("Role configuration settings changed; refreshing Orchard shell settings."); + var settingsList = LoadSettings(); + foreach (var settings in settingsList) + Events.Saved(settings); + } + } + + private string ParseValue(string value) { + if (value == EmptyValue || String.IsNullOrWhiteSpace(value)) + return null; + return value; + } + } +} diff --git a/src/Orchard.Azure/FileSystems/AzureFileSystem.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs similarity index 89% rename from src/Orchard.Azure/FileSystems/AzureFileSystem.cs rename to src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs index 22919cf5c..5271c3da8 100644 --- a/src/Orchard.Azure/FileSystems/AzureFileSystem.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/AzureFileSystem.cs @@ -7,13 +7,15 @@ using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Blob; using Orchard.FileSystems.Media; -namespace Orchard.Azure.FileSystems { +namespace Orchard.Azure.Services.FileSystems { + public class AzureFileSystem { + public const string FolderEntry = "$$$ORCHARD$$$.$$$"; - + private readonly bool _isPrivate; private readonly IMimeTypeProvider _mimeTypeProvider; - + protected string _root; protected string _absoluteRoot; @@ -21,7 +23,10 @@ namespace Orchard.Azure.FileSystems { private CloudBlobClient _blobClient; private CloudBlobContainer _container; - public string ContainerName { get; protected set; } + public string ContainerName { + get; + protected set; + } public CloudBlobClient BlobClient { get { @@ -32,7 +37,7 @@ namespace Orchard.Azure.FileSystems { public CloudBlobContainer Container { get { - EnsureInitialized(); + EnsureInitialized(); return _container; } } @@ -49,8 +54,7 @@ namespace Orchard.Azure.FileSystems { return; } - - _storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString")); + _storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("Orchard.StorageConnectionString")); _absoluteRoot = Combine(Combine(_storageAccount.BlobEndpoint.AbsoluteUri, ContainerName), _root); _blobClient = _storageAccount.CreateCloudBlobClient(); @@ -61,9 +65,12 @@ namespace Orchard.Azure.FileSystems { _container.CreateIfNotExists(); _container.SetPermissions(_isPrivate - ? new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Off } - : new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Container }); - + ? new BlobContainerPermissions { + PublicAccess = BlobContainerPublicAccessType.Off + } + : new BlobContainerPermissions { + PublicAccess = BlobContainerPublicAccessType.Container + }); } private static string ConvertToRelativeUriPath(string path) { @@ -109,16 +116,16 @@ namespace Orchard.Azure.FileSystems { path = ConvertToRelativeUriPath(path); - Container.EnsureBlobExists(String.Concat(_root, path)); - return new AzureBlobFileStorage(Container.GetBlockBlobReference(String.Concat(_root, path)), _absoluteRoot); + Container.EnsureBlobExists(String.Concat(_root, path)); + return new AzureBlobFileStorage(Container.GetBlockBlobReference(String.Concat(_root, path)), _absoluteRoot); } public bool FileExists(string path) { - return Container.BlobExists(String.Concat(_root, path)); + return Container.BlobExists(String.Concat(_root, path)); } public bool FolderExists(string path) { - return Container.DirectoryExists(String.Concat(_root, path)); + return Container.DirectoryExists(String.Concat(_root, path)); } public IEnumerable ListFiles(string path) { @@ -252,7 +259,7 @@ namespace Orchard.Azure.FileSystems { Container.EnsureBlobExists(String.Concat(_root, path)); Container.EnsureBlobDoesNotExist(String.Concat(_root, newPath)); - + var blob = Container.GetBlockBlobReference(String.Concat(_root, path)); var newBlob = Container.GetBlockBlobReference(String.Concat(_root, newPath)); newBlob.StartCopyFromBlob(blob); @@ -287,8 +294,8 @@ namespace Orchard.Azure.FileSystems { public string GetPublicUrl(string path) { path = ConvertToRelativeUriPath(path); - Container.EnsureBlobExists(String.Concat(_root, path)); - return Container.GetBlockBlobReference(String.Concat(_root, path)).Uri.ToString(); + Container.EnsureBlobExists(String.Concat(_root, path)); + return Container.GetBlockBlobReference(String.Concat(_root, path)).Uri.ToString(); } private class AzureBlobFileStorage : IStorageFile { @@ -369,7 +376,7 @@ namespace Orchard.Azure.FileSystems { if (_blob.Parent != null) { return new AzureBlobFolderStorage(_blob.Parent, _rootPath); } - throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory"); + throw new ArgumentException("Directory " + _blob.Uri + " does not have a parent directory"); } private static long GetDirectorySize(CloudBlobDirectory directoryBlob) { @@ -386,6 +393,5 @@ namespace Orchard.Azure.FileSystems { return size; } } - } } diff --git a/src/Orchard.Azure/FileSystems/CloudBlobContainerExtensions.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/CloudBlobContainerExtensions.cs similarity index 95% rename from src/Orchard.Azure/FileSystems/CloudBlobContainerExtensions.cs rename to src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/CloudBlobContainerExtensions.cs index 6adc192d7..76e54b82c 100644 --- a/src/Orchard.Azure/FileSystems/CloudBlobContainerExtensions.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/CloudBlobContainerExtensions.cs @@ -3,7 +3,8 @@ using System.Linq; using Microsoft.WindowsAzure.Storage; using Microsoft.WindowsAzure.Storage.Blob; -namespace Orchard.Azure.FileSystems { +namespace Orchard.Azure.Services.FileSystems { + public static class CloudBlobContainerExtensions { public static bool BlobExists(this CloudBlobContainer container, string path) { diff --git a/src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/Media/AzureBlobStorageProvider.cs similarity index 67% rename from src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs rename to src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/Media/AzureBlobStorageProvider.cs index be5de8973..093dfbded 100644 --- a/src/Orchard.Azure/FileSystems/Media/AzureBlobStorageProvider.cs +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/FileSystems/Media/AzureBlobStorageProvider.cs @@ -1,13 +1,16 @@ using System.IO; using Orchard.Environment.Configuration; -using Orchard.Environment.Extensions; using Orchard.FileSystems.Media; +using Orchard.Environment.Extensions; -namespace Orchard.Azure.FileSystems.Media { - +namespace Orchard.Azure.Services.FileSystems.Media { + + [OrchardFeature("Orchard.Azure.MediaStorage")] + [OrchardSuppressDependency("Orchard.FileSystems.Media.FileSystemStorageProvider")] public class AzureBlobStorageProvider : AzureFileSystem, IStorageProvider { - public AzureBlobStorageProvider(ShellSettings shellSettings, IMimeTypeProvider mimeTypeProvider) : base("media", shellSettings.Name, false, mimeTypeProvider) { } + public AzureBlobStorageProvider(ShellSettings shellSettings, IMimeTypeProvider mimeTypeProvider) : base("media", shellSettings.Name, false, mimeTypeProvider) + { } public bool TrySaveStream(string path, Stream inputStream) { try { @@ -21,13 +24,12 @@ namespace Orchard.Azure.FileSystems.Media { } public void SaveStream(string path, Stream inputStream) { - // Create the file. - // The CreateFile method will map the still relative path + // Create the file. The CreateFile() method will map the still relative path. var file = CreateFile(path); - using(var outputStream = file.OpenWrite()) { + using (var outputStream = file.OpenWrite()) { var buffer = new byte[8192]; - for (;;) { + while (true) { var length = inputStream.Read(buffer, 0, buffer.Length); if (length <= 0) break; @@ -37,10 +39,10 @@ namespace Orchard.Azure.FileSystems.Media { } /// - /// Retrieves the local path for a given url within the storage provider. + /// Retrieves the local path for a given URL within the storage provider. /// - /// The public url of the media. - /// The local path. + /// The public URL of the media. + /// The corresponding local path. public string GetStoragePath(string url) { if (url.StartsWith(_absoluteRoot)) { return url.Substring(Combine(_absoluteRoot, "/").Length); @@ -52,6 +54,5 @@ namespace Orchard.Azure.FileSystems.Media { public string GetRelativePath(string path) { return GetPublicUrl(path); } - } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Azure/Services/Logging/AzureDiagnosticsAppender.cs b/src/Orchard.Web/Modules/Orchard.Azure/Services/Logging/AzureDiagnosticsAppender.cs new file mode 100644 index 000000000..654225bd0 --- /dev/null +++ b/src/Orchard.Web/Modules/Orchard.Azure/Services/Logging/AzureDiagnosticsAppender.cs @@ -0,0 +1,31 @@ +using System; +using Microsoft.WindowsAzure.Diagnostics; +using log4net.Appender; +using log4net.Core; + +namespace Orchard.Azure.Services.Logging { + + /// + /// Provides a Log4net appender implementation that sends log messages to Windows Azure Diagnostics. + /// + public class AzureDiagnosticsAppender : AppenderSkeleton { + + private const string _wadStorageAccountConnectionStringSettingName = "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString"; + + public AzureDiagnosticsAppender() { + var defaultDiagnostics = DiagnosticMonitor.GetDefaultInitialConfiguration(); + var period = TimeSpan.FromMinutes(1d); + + defaultDiagnostics.Directories.ScheduledTransferPeriod = period; + defaultDiagnostics.Logs.ScheduledTransferPeriod = period; + defaultDiagnostics.WindowsEventLog.ScheduledTransferPeriod = period; + + DiagnosticMonitor.Start(_wadStorageAccountConnectionStringSettingName, defaultDiagnostics); + } + + protected override void Append(LoggingEvent loggingEvent) { + var formattedMessage = RenderLoggingEvent(loggingEvent); + System.Diagnostics.Trace.WriteLine(formattedMessage); + } + } +} diff --git a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Web.config b/src/Orchard.Web/Modules/Orchard.Azure/Web.config similarity index 98% rename from src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Web.config rename to src/Orchard.Web/Modules/Orchard.Azure/Web.config index 88ddc0ef0..88b84a792 100644 --- a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Web.config +++ b/src/Orchard.Web/Modules/Orchard.Azure/Web.config @@ -1,41 +1,41 @@ - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Module.txt b/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Module.txt deleted file mode 100644 index ce5feb438..000000000 --- a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Module.txt +++ /dev/null @@ -1,12 +0,0 @@ -Name: Orchard.AzureBlobStorage -AntiForgery: enabled -Author: The Orchard Team -Website: http://orchardproject.net -Version: 1.0 -OrchardVersion: 1.7 -Description: Description for the module -Features: - Orchard.AzureBlobStorage: - Description: Configures Windows Azure Blob Storage to be used as the underlying storage for Orchard - Name: Azure Blob Storage - Category: Hosting \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Services/AzureBlobStorageProviderDependency.cs b/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Services/AzureBlobStorageProviderDependency.cs deleted file mode 100644 index d8e5d5fd2..000000000 --- a/src/Orchard.Web/Modules/Orchard.AzureBlobStorage/Services/AzureBlobStorageProviderDependency.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.IO; -using Orchard.Environment.Configuration; -using Orchard.Environment.Extensions; -using Orchard.FileSystems.Media; -using Orchard.Azure.FileSystems.Media; - -namespace Orchard.AzureBlobStorage.Services { - [OrchardSuppressDependency("Orchard.FileSystems.Media.FileSystemStorageProvider")] - public class AzureBlobStorageProviderDependency : AzureBlobStorageProvider { - public AzureBlobStorageProviderDependency(ShellSettings shellSettings, IMimeTypeProvider mimeTypeProvider) : base(shellSettings, mimeTypeProvider) { } - } -} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj b/src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj index a58ddba13..e143b4f3b 100644 --- a/src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj +++ b/src/Orchard.Web/Modules/Orchard.Recipes/Orchard.Recipes.csproj @@ -88,7 +88,7 @@ {33B1BC8D-E292-4972-A363-22056B207156} - Orchard + Orchard %28Tools\Orchard%29 {DFD137A2-DDB5-4D22-BE0D-FA9AD4C8B059} diff --git a/src/Orchard.Web/Orchard.Web.csproj b/src/Orchard.Web/Orchard.Web.csproj index ff9df31fd..33450c964 100644 --- a/src/Orchard.Web/Orchard.Web.csproj +++ b/src/Orchard.Web/Orchard.Web.csproj @@ -150,6 +150,10 @@ {9916839C-39FC-4CEB-A5AF-89CA7E87119F} Orchard.Core + + {cbc7993c-57d8-4a6c-992c-19e849dfe71d} + Orchard.Azure + diff --git a/src/Orchard.Web/Web.config b/src/Orchard.Web/Web.config index 2c4e8e8c2..2f82e2f7b 100644 --- a/src/Orchard.Web/Web.config +++ b/src/Orchard.Web/Web.config @@ -1,13 +1,6 @@ - + @@ -15,12 +8,14 @@
-
+
+ + @@ -36,22 +31,31 @@ + + + + + Set default transaction timeout to 30 minutes so that interactive debugging + is easier (default timeout is less than one minute). + --> + - + + - + @@ -83,22 +87,20 @@ - + - + + + + + @@ -110,19 +112,17 @@ + - + - + @@ -130,9 +130,9 @@ - + - + @@ -142,15 +142,16 @@ - + - + + @@ -193,4 +194,5 @@ + \ No newline at end of file diff --git a/src/Orchard.sln b/src/Orchard.sln index 304ec0d7d..7a7e9c4d3 100644 --- a/src/Orchard.sln +++ b/src/Orchard.sln @@ -160,9 +160,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Upgrade", "Orchard.Web\Modu EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Modules.Deprecated", "Modules.Deprecated", "{902528F6-1444-42A3-8B75-A54B775B539C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.AzureBlobStorage", "Orchard.Web\Modules\Orchard.AzureBlobStorage\Orchard.AzureBlobStorage.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Azure\Orchard.Azure.csproj", "{2505AA84-65A6-43D0-9C27-4F44FD576284}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Orchard.Azure", "Orchard.Web\Modules\Orchard.Azure\Orchard.Azure.csproj", "{CBC7993C-57D8-4A6C-992C-19E849DFE71D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -902,16 +900,6 @@ Global {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.FxCop|Any CPU.Build.0 = Release|Any CPU {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CBC7993C-57D8-4A6C-992C-19E849DFE71D}.Release|Any CPU.Build.0 = Release|Any CPU - {2505AA84-65A6-43D0-9C27-4F44FD576284}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU - {2505AA84-65A6-43D0-9C27-4F44FD576284}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU - {2505AA84-65A6-43D0-9C27-4F44FD576284}.Coverage|Any CPU.ActiveCfg = Release|Any CPU - {2505AA84-65A6-43D0-9C27-4F44FD576284}.Coverage|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}.FxCop|Any CPU.ActiveCfg = Release|Any CPU - {2505AA84-65A6-43D0-9C27-4F44FD576284}.FxCop|Any CPU.Build.0 = Release|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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Orchard/FileSystems/Media/IStorageProvider.cs b/src/Orchard/FileSystems/Media/IStorageProvider.cs index 1b6bfecb5..63702fc34 100644 --- a/src/Orchard/FileSystems/Media/IStorageProvider.cs +++ b/src/Orchard/FileSystems/Media/IStorageProvider.cs @@ -25,7 +25,6 @@ namespace Orchard.FileSystems.Media { /// The storage path or null if the media is not in a correct format. string GetStoragePath(string url); - /// /// Retrieves a file within the storage provider. ///