--HG--
branch : dev
This commit is contained in:
Dave Reed
2010-11-04 12:05:56 -07:00
27 changed files with 295 additions and 195 deletions

View File

@@ -48,7 +48,8 @@
<MSBuild <MSBuild
Projects="$(SrcFolder)\Orchard.Azure\Orchard.Azure.CloudService.sln" Projects="$(SrcFolder)\Orchard.Azure\Orchard.Azure.CloudService.sln"
Targets="Build" Targets="Build"
Properties="Configuration=Release;OutputPath=$(CompileFolder);PlatformTarget=x64" /> Properties="Configuration=Release;OutputPath=$(CompileFolder);PlatformTarget=x64;DefineConstants=AZURE"
/>
<MSBuild <MSBuild
Projects="$(SrcFolder)\Orchard.Azure.sln" Projects="$(SrcFolder)\Orchard.Azure.sln"

View File

@@ -50,7 +50,7 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
Assert.AreEqual(".txt", storageFile.GetFileType()); Assert.AreEqual(".txt", storageFile.GetFileType());
Assert.AreEqual("foo.txt", storageFile.GetName()); Assert.AreEqual("foo.txt", storageFile.GetName());
Assert.That(storageFile.GetPath().EndsWith("/default/foo.txt"), Is.True); Assert.AreEqual("foo.txt", storageFile.GetPath());
Assert.AreEqual(0, storageFile.GetSize()); Assert.AreEqual(0, storageFile.GetSize());
} }
@@ -71,7 +71,8 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
var files = _azureBlobStorageProvider.ListFiles(""); var files = _azureBlobStorageProvider.ListFiles("");
Assert.AreEqual(1, files.Count()); Assert.AreEqual(1, files.Count());
Assert.That(files.First().GetPath().EndsWith("foo2.txt"), Is.True); Assert.That(files.First().GetPath().Equals("foo2.txt"), Is.True);
Assert.That(files.First().GetName().Equals("foo2.txt"), Is.True);
} }
[Test] [Test]
@@ -82,17 +83,28 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("").Count()); Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("").Count());
Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("folder").Count()); Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("folder").Count());
Assert.AreEqual("folder/foo.txt", _azureBlobStorageProvider.ListFiles("folder").First().GetPath());
Assert.AreEqual("foo.txt", _azureBlobStorageProvider.ListFiles("folder").First().GetName());
Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("folder/folder").Count()); Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("folder/folder").Count());
Assert.AreEqual("folder/folder/foo.txt", _azureBlobStorageProvider.ListFiles("folder/folder").First().GetPath());
Assert.AreEqual("foo.txt", _azureBlobStorageProvider.ListFiles("folder/folder").First().GetName());
} }
[Test] [Test]
[ExpectedException(typeof(ArgumentException))] [ExpectedException(typeof(ArgumentException))]
public void CreateFolderThatExistsShouldThrow() { public void CreateFolderThatExistsShouldThrow() {
// sebros: In Azure, the folder concept is just about checking files prefix. So until a file exists, a folder is nothing
_azureBlobStorageProvider.CreateFile("folder/foo.txt"); _azureBlobStorageProvider.CreateFile("folder/foo.txt");
_azureBlobStorageProvider.CreateFolder("folder"); _azureBlobStorageProvider.CreateFolder("folder");
} }
[Test]
public void ListFolderShouldAcceptNullPath() {
_azureBlobStorageProvider.CreateFolder("folder");
Assert.AreEqual(1, _azureBlobStorageProvider.ListFolders(null).Count());
Assert.AreEqual("folder", _azureBlobStorageProvider.ListFolders(null).First().GetName());
Assert.AreEqual("folder", _azureBlobStorageProvider.ListFolders(null).First().GetPath());
}
[Test] [Test]
public void DeleteFolderShouldDeleteFilesAlso() { public void DeleteFolderShouldDeleteFilesAlso() {
_azureBlobStorageProvider.CreateFile("folder/foo1.txt"); _azureBlobStorageProvider.CreateFile("folder/foo1.txt");

View File

@@ -8,11 +8,15 @@ using Microsoft.WindowsAzure.StorageClient;
using Orchard.FileSystems.Media; using Orchard.FileSystems.Media;
namespace Orchard.Azure { namespace Orchard.Azure {
public class AzureFileSystem { public class AzureFileSystem
{
private const string FolderEntry = "$$$ORCHARD$$$.$$$";
public string ContainerName { get; protected set; } public string ContainerName { get; protected set; }
private readonly CloudStorageAccount _storageAccount; private readonly CloudStorageAccount _storageAccount;
private readonly string _root; private readonly string _root;
private readonly string _absoluteRoot;
public CloudBlobClient BlobClient { get; private set; } public CloudBlobClient BlobClient { get; private set; }
public CloudBlobContainer Container { get; private set; } public CloudBlobContainer Container { get; private set; }
@@ -25,6 +29,7 @@ namespace Orchard.Azure {
_storageAccount = storageAccount; _storageAccount = storageAccount;
ContainerName = containerName; ContainerName = containerName;
_root = String.IsNullOrEmpty(root) || root == "/" ? String.Empty : root + "/"; _root = String.IsNullOrEmpty(root) || root == "/" ? String.Empty : root + "/";
_absoluteRoot = _storageAccount.BlobEndpoint.AbsoluteUri + containerName + "/" + root + "/";
using ( new HttpContextWeaver() ) { using ( new HttpContextWeaver() ) {
@@ -46,66 +51,65 @@ namespace Orchard.Azure {
} }
private static void EnsurePathIsRelative(string path) { private static void EnsurePathIsRelative(string path) {
if ( path.StartsWith("/") || path.StartsWith("http://")) if (path.StartsWith("/") || path.StartsWith("http://"))
throw new ArgumentException("Path must be relative"); throw new ArgumentException("Path must be relative");
} }
public IStorageFile GetFile(string path) { public IStorageFile GetFile(string path) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
using ( new HttpContextWeaver() ) using ( new HttpContextWeaver() ) {
{ Container.EnsureBlobExists(String.Concat(_root, path));
Container.EnsureBlobExists(path); return new AzureBlobFileStorage(Container.GetBlockBlobReference(path), _absoluteRoot);
return new AzureBlobFileStorage(Container.GetBlockBlobReference(path));
} }
} }
public bool FileExists(string path) { public bool FileExists(string path) {
using ( new HttpContextWeaver() ) using ( new HttpContextWeaver() ) {
{ return Container.BlobExists(String.Concat(_root, path));
path = String.Concat(_root, path);
return Container.BlobExists(path);
} }
} }
public IEnumerable<IStorageFile> ListFiles(string path) { public IEnumerable<IStorageFile> ListFiles(string path) {
path = path ?? String.Empty;
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
string prefix = String.Concat(Container.Name, "/", _root, path); string prefix = String.Concat(Container.Name, "/", _root, path);
if ( !prefix.EndsWith("/") ) if ( !prefix.EndsWith("/") )
prefix += "/"; prefix += "/";
using ( new HttpContextWeaver() )
{ using ( new HttpContextWeaver() ) {
foreach (var blobItem in BlobClient.ListBlobsWithPrefix(prefix).OfType<CloudBlockBlob>()) foreach (var blobItem in BlobClient.ListBlobsWithPrefix(prefix).OfType<CloudBlockBlob>()) {
{ // ignore directory entries
yield return new AzureBlobFileStorage(blobItem); if(blobItem.Uri.AbsoluteUri.EndsWith(FolderEntry))
continue;
yield return new AzureBlobFileStorage(blobItem, _absoluteRoot);
} }
} }
} }
public IEnumerable<IStorageFolder> ListFolders(string path) { public IEnumerable<IStorageFolder> ListFolders(string path) {
path = path ?? String.Empty;
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path); using ( new HttpContextWeaver() ) {
using ( new HttpContextWeaver() ) if ( !Container.DirectoryExists(String.Concat(_root, path)) ) {
{ try {
if (!Container.DirectoryExists(path)) CreateFolder(String.Concat(_root, path));
{
try
{
CreateFolder(path);
} }
catch (Exception ex) catch ( Exception ex ) {
{
throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}", throw new ArgumentException(string.Format("The folder could not be created at path: {0}. {1}",
path, ex)); path, ex));
} }
} }
return Container.GetDirectoryReference(path) return Container.GetDirectoryReference(String.Concat(_root, path))
.ListBlobs() .ListBlobs()
.OfType<CloudBlobDirectory>() .OfType<CloudBlobDirectory>()
.Select<CloudBlobDirectory, IStorageFolder>(d => new AzureBlobFolderStorage(d)) .Select<CloudBlobDirectory, IStorageFolder>(d => new AzureBlobFolderStorage(d, _absoluteRoot))
.ToList(); .ToList();
} }
} }
@@ -113,23 +117,20 @@ namespace Orchard.Azure {
public void CreateFolder(string path) public void CreateFolder(string path)
{ {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path); using (new HttpContextWeaver()) {
using (new HttpContextWeaver()) Container.EnsureDirectoryDoesNotExist(String.Concat(_root, path));
{
Container.EnsureDirectoryDoesNotExist(path); // Creating a virtually hidden file to make the directory an existing concept
Container.GetDirectoryReference(path); CreateFile(path + "/" + FolderEntry);
} }
} }
public void DeleteFolder(string path) { public void DeleteFolder(string path) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
using ( new HttpContextWeaver() ) using ( new HttpContextWeaver() ) {
{ Container.EnsureDirectoryExists(String.Concat(_root, path));
Container.EnsureDirectoryExists(path); foreach ( var blob in Container.GetDirectoryReference(String.Concat(_root, path)).ListBlobs() ) {
foreach (var blob in Container.GetDirectoryReference(path).ListBlobs())
{
if (blob is CloudBlob) if (blob is CloudBlob)
((CloudBlob) blob).Delete(); ((CloudBlob) blob).Delete();
@@ -141,7 +142,6 @@ namespace Orchard.Azure {
public void RenameFolder(string path, string newPath) { public void RenameFolder(string path, string newPath) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
EnsurePathIsRelative(newPath); EnsurePathIsRelative(newPath);
if ( !path.EndsWith("/") ) if ( !path.EndsWith("/") )
@@ -149,20 +149,16 @@ namespace Orchard.Azure {
if ( !newPath.EndsWith("/") ) if ( !newPath.EndsWith("/") )
newPath += "/"; newPath += "/";
using ( new HttpContextWeaver() ) using ( new HttpContextWeaver() ) {
{ foreach (var blob in Container.GetDirectoryReference(_root + path).ListBlobs()) {
foreach (var blob in Container.GetDirectoryReference(_root + path).ListBlobs()) if (blob is CloudBlob) {
{
if (blob is CloudBlob)
{
string filename = Path.GetFileName(blob.Uri.ToString()); string filename = Path.GetFileName(blob.Uri.ToString());
string source = String.Concat(path, filename); string source = String.Concat(path, filename);
string destination = String.Concat(newPath, filename); string destination = String.Concat(newPath, filename);
RenameFile(source, destination); RenameFile(source, destination);
} }
if (blob is CloudBlobDirectory) if (blob is CloudBlobDirectory) {
{
string foldername = blob.Uri.Segments.Last(); string foldername = blob.Uri.Segments.Last();
string source = String.Concat(path, foldername); string source = String.Concat(path, foldername);
string destination = String.Concat(newPath, foldername); string destination = String.Concat(newPath, foldername);
@@ -174,29 +170,24 @@ namespace Orchard.Azure {
public void DeleteFile(string path) { public void DeleteFile(string path) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
using ( new HttpContextWeaver() ) {
using ( new HttpContextWeaver() )
{
Container.EnsureBlobExists(path); Container.EnsureBlobExists(path);
var blob = Container.GetBlockBlobReference(path); var blob = Container.GetBlockBlobReference(String.Concat(_root, path));
blob.Delete(); blob.Delete();
} }
} }
public void RenameFile(string path, string newPath) { public void RenameFile(string path, string newPath) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
EnsurePathIsRelative(newPath); EnsurePathIsRelative(newPath);
newPath = String.Concat(_root, newPath);
using ( new HttpContextWeaver() )
{
Container.EnsureBlobExists(path);
Container.EnsureBlobDoesNotExist(newPath);
var blob = Container.GetBlockBlobReference(path); using ( new HttpContextWeaver() ) {
var newBlob = Container.GetBlockBlobReference(newPath); 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.CopyFromBlob(blob); newBlob.CopyFromBlob(blob);
blob.Delete(); blob.Delete();
} }
@@ -204,36 +195,36 @@ namespace Orchard.Azure {
public IStorageFile CreateFile(string path) { public IStorageFile CreateFile(string path) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
if ( Container.BlobExists(path) ) { if ( Container.BlobExists(String.Concat(_root, path)) ) {
throw new ArgumentException("File " + path + " already exists"); throw new ArgumentException("File " + path + " already exists");
} }
var blob = Container.GetBlockBlobReference(path); var blob = Container.GetBlockBlobReference(String.Concat(_root, path));
blob.OpenWrite().Dispose(); // force file creation blob.OpenWrite().Dispose(); // force file creation
return new AzureBlobFileStorage(blob); return new AzureBlobFileStorage(blob, _absoluteRoot);
} }
public string GetPublicUrl(string path) { public string GetPublicUrl(string path) {
EnsurePathIsRelative(path); EnsurePathIsRelative(path);
path = String.Concat(_root, path);
using ( new HttpContextWeaver() ) using ( new HttpContextWeaver() ) {
{ Container.EnsureBlobExists(String.Concat(_root, path));
Container.EnsureBlobExists(path); return Container.GetBlockBlobReference(String.Concat(_root, path)).Uri.ToString();
return Container.GetBlockBlobReference(path).Uri.ToString();
} }
} }
private class AzureBlobFileStorage : IStorageFile { private class AzureBlobFileStorage : IStorageFile {
private readonly CloudBlockBlob _blob; private readonly CloudBlockBlob _blob;
private readonly string _rootPath;
public AzureBlobFileStorage(CloudBlockBlob blob) { public AzureBlobFileStorage(CloudBlockBlob blob, string rootPath) {
_blob = blob; _blob = blob;
_rootPath = rootPath;
} }
public string GetPath() { public string GetPath() {
return _blob.Uri.ToString(); return _blob.Uri.ToString().Substring(_rootPath.Length+1);
} }
public string GetName() { public string GetName() {
@@ -264,17 +255,19 @@ namespace Orchard.Azure {
private class AzureBlobFolderStorage : IStorageFolder { private class AzureBlobFolderStorage : IStorageFolder {
private readonly CloudBlobDirectory _blob; private readonly CloudBlobDirectory _blob;
private readonly string _rootPath;
public AzureBlobFolderStorage(CloudBlobDirectory blob) { public AzureBlobFolderStorage(CloudBlobDirectory blob, string rootPath) {
_blob = blob; _blob = blob;
_rootPath = rootPath;
} }
public string GetName() { public string GetName() {
return Path.GetDirectoryName(_blob.Uri.ToString()); return Path.GetDirectoryName(GetPath() + "/");
} }
public string GetPath() { public string GetPath() {
return _blob.Uri.ToString(); return _blob.Uri.ToString().Substring(_rootPath.Length + 1).TrimEnd('/');
} }
public long GetSize() { public long GetSize() {
@@ -287,7 +280,7 @@ namespace Orchard.Azure {
public IStorageFolder GetParent() { public IStorageFolder GetParent() {
if ( _blob.Parent != null ) { if ( _blob.Parent != null ) {
return new AzureBlobFolderStorage(_blob.Parent); 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");
} }

View File

@@ -8,6 +8,11 @@ namespace Orchard.Azure.Web {
public override bool OnStart() { public override bool OnStart() {
DiagnosticMonitor.Start("DiagnosticsConnectionString"); DiagnosticMonitor.Start("DiagnosticsConnectionString");
CloudStorageAccount.SetConfigurationSettingPublisher(
(configName, configSetter) =>
configSetter(RoleEnvironment.GetConfigurationSettingValue(configName))
);
// For information on handling configuration changes // For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357. // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging; RoleEnvironment.Changing += RoleEnvironmentChanging;

View File

@@ -26,6 +26,7 @@ namespace Orchard.Tests.DataMigration {
private string _tempFolder; private string _tempFolder;
private SchemaBuilder _schemaBuilder; private SchemaBuilder _schemaBuilder;
private DefaultDataMigrationInterpreter _interpreter; private DefaultDataMigrationInterpreter _interpreter;
private ISession _session;
[SetUp] [SetUp]
public void Setup() { public void Setup() {
@@ -38,7 +39,7 @@ namespace Orchard.Tests.DataMigration {
var builder = new ContainerBuilder(); var builder = new ContainerBuilder();
var session = _sessionFactory.OpenSession(); _session = _sessionFactory.OpenSession();
builder.RegisterInstance(appDataFolder).As<IAppDataFolder>(); builder.RegisterInstance(appDataFolder).As<IAppDataFolder>();
builder.RegisterType<SqlCeDataServicesProvider>().As<IDataServicesProvider>(); builder.RegisterType<SqlCeDataServicesProvider>().As<IDataServicesProvider>();
builder.RegisterType<DataServicesProviderFactory>().As<IDataServicesProviderFactory>(); builder.RegisterType<DataServicesProviderFactory>().As<IDataServicesProviderFactory>();
@@ -46,7 +47,7 @@ namespace Orchard.Tests.DataMigration {
builder.RegisterType<DefaultDataMigrationInterpreter>().As<IDataMigrationInterpreter>(); builder.RegisterType<DefaultDataMigrationInterpreter>().As<IDataMigrationInterpreter>();
builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>(); builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>();
builder.RegisterType<SessionFactoryHolder>().As<ISessionFactoryHolder>(); builder.RegisterType<SessionFactoryHolder>().As<ISessionFactoryHolder>();
builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(session)).As<ISessionLocator>(); builder.RegisterInstance(new DefaultContentManagerTests.TestSessionLocator(_session)).As<ISessionLocator>();
builder.RegisterInstance(new ShellBlueprint { Records = Enumerable.Empty<RecordBlueprint>() }).As<ShellBlueprint>(); builder.RegisterInstance(new ShellBlueprint { Records = Enumerable.Empty<RecordBlueprint>() }).As<ShellBlueprint>();
builder.RegisterInstance(new ShellSettings { Name = "temp", DataProvider = "SqlCe", DataTablePrefix = "TEST" }).As<ShellSettings>(); builder.RegisterInstance(new ShellSettings { Name = "temp", DataProvider = "SqlCe", DataTablePrefix = "TEST" }).As<ShellSettings>();
builder.RegisterModule(new DataModule()); builder.RegisterModule(new DataModule());
@@ -133,10 +134,24 @@ namespace Orchard.Tests.DataMigration {
.AlterTable("User", table => table .AlterTable("User", table => table
.AddColumn("Age", DbType.Int32)) .AddColumn("Age", DbType.Int32))
.AlterTable("User", table => table .AlterTable("User", table => table
.AlterColumn("Lastname", column => column.WithDefault("John"))) .AlterColumn("Lastname", column => column.WithDefault("Doe")))
.AlterTable("User", table => table .AlterTable("User", table => table
.DropColumn("Firstname") .DropColumn("Firstname")
); );
// creating a new row should assign a default value to Firstname and Age
_schemaBuilder
.ExecuteSql("insert into TEST_User VALUES (DEFAULT, DEFAULT)");
// ensure wehave one record woth the default value
var command = _session.Connection.CreateCommand();
command.CommandText = "SELECT count(*) FROM TEST_User WHERE Lastname = 'Doe'";
Assert.That(command.ExecuteScalar(), Is.EqualTo(1));
// ensure this is not a false positive
command = _session.Connection.CreateCommand();
command.CommandText = "SELECT count(*) FROM TEST_User WHERE Lastname = 'Foo'";
Assert.That(command.ExecuteScalar(), Is.EqualTo(0));
} }
[Test] [Test]

View File

@@ -6,7 +6,7 @@
Parts_Localization_ContentTranslations_SummaryAdmin Parts_Localization_ContentTranslations_SummaryAdmin
--> -->
<!-- edit shape just gets default placement --> <!-- edit shape just gets default placement -->
<Place Parts_Localization_ContentTranslations_Edit="Content:before.1"/> <Place Parts_Localization_ContentTranslations_Edit="Content:0"/>
<Match DisplayType="Detail"> <Match DisplayType="Detail">
<Place Parts_Localization_ContentTranslations="Content:2"/> <Place Parts_Localization_ContentTranslations="Content:2"/>
</Match> </Match>

View File

@@ -298,7 +298,6 @@
<Content Include="Shapes\Views\HeadPreload.cshtml" /> <Content Include="Shapes\Views\HeadPreload.cshtml" />
<Content Include="Shapes\Views\Message.cshtml" /> <Content Include="Shapes\Views\Message.cshtml" />
<Content Include="Shapes\Views\NotFound.cshtml" /> <Content Include="Shapes\Views\NotFound.cshtml" />
<Content Include="Shapes\Views\UI\Switchable.cshtml" />
<Content Include="Web.config" /> <Content Include="Web.config" />
<Content Include="XmlRpc\Module.txt" /> <Content Include="XmlRpc\Module.txt" />
<Content Include="Settings\Views\EditorTemplates\Parts\Settings.SiteSettingsPart.cshtml" /> <Content Include="Settings\Views\EditorTemplates\Parts\Settings.SiteSettingsPart.cshtml" />

View File

@@ -1,7 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Orchard.UI.Resources; using Orchard.UI.Resources;
namespace Orchard.Core.Shapes { namespace Orchard.Core.Shapes {
@@ -11,6 +7,11 @@ namespace Orchard.Core.Shapes {
manifest.DefineScript("ShapesBase").SetUrl("base.js").SetDependencies("jQuery"); manifest.DefineScript("ShapesBase").SetUrl("base.js").SetDependencies("jQuery");
manifest.DefineStyle("Shapes").SetUrl("site.css"); // todo: missing manifest.DefineStyle("Shapes").SetUrl("site.css"); // todo: missing
manifest.DefineStyle("ShapesSpecial").SetUrl("special.css"); manifest.DefineStyle("ShapesSpecial").SetUrl("special.css");
manifest.DefineScript("Switchable").SetUrl("jquery.switchable.js")
.SetDependencies("jQuery")
.SetDependencies("ShapesBase");
manifest.DefineStyle("Switchable").SetUrl("jquery.switchable.css");
} }
} }
} }

View File

@@ -1,5 +0,0 @@
@{
Style.Require("Switchable");
Script.Require("Switchable");
}
@string.Format("{0} switchable", Model)

View File

@@ -3,10 +3,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Web; using System.Web;
using System.Web.Mvc; using System.Web.Mvc;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Localization; using Orchard.Localization;
using Orchard.Media.Models; using Orchard.Media.Models;
using Orchard.Media.Services; using Orchard.Media.Services;
using Orchard.Media.ViewModels; using Orchard.Media.ViewModels;
using Orchard.Settings;
using Orchard.UI.Notify; using Orchard.UI.Notify;
using Orchard.Utility.Extensions; using Orchard.Utility.Extensions;
@@ -153,6 +156,15 @@ namespace Orchard.Media.Controllers {
if (!ModelState.IsValid) if (!ModelState.IsValid)
return View(viewModel); return View(viewModel);
// first validate them all
foreach (string fileName in Request.Files) {
HttpPostedFileBase file = Request.Files[fileName];
if (!_mediaService.FileAllowed(file)) {
ModelState.AddModelError("File", T("That file type is not allowed.").ToString());
return View(viewModel);
}
}
// then save them
foreach (string fileName in Request.Files) { foreach (string fileName in Request.Files) {
HttpPostedFileBase file = Request.Files[fileName]; HttpPostedFileBase file = Request.Files[fileName];
_mediaService.UploadMediaFile(viewModel.MediaPath, file); _mediaService.UploadMediaFile(viewModel.MediaPath, file);
@@ -195,10 +207,11 @@ namespace Orchard.Media.Controllers {
} }
} }
public ActionResult EditMedia(string name, string caption, DateTime lastUpdated, long size, string folderName, string mediaPath) { public ActionResult EditMedia(string name, DateTime lastUpdated, long size, string folderName, string mediaPath) {
var model = new MediaItemEditViewModel(); var model = new MediaItemEditViewModel();
model.Name = name; model.Name = name;
model.Caption = caption ?? String.Empty; // todo: reimplement
//model.Caption = caption ?? String.Empty;
model.LastUpdated = lastUpdated; model.LastUpdated = lastUpdated;
model.Size = size; model.Size = size;
model.FolderName = folderName; model.FolderName = folderName;

View File

@@ -0,0 +1,15 @@
using JetBrains.Annotations;
using Orchard.Data;
using Orchard.ContentManagement.Handlers;
using Orchard.Media.Models;
namespace Orchard.Media.Handlers {
[UsedImplicitly]
public class MediaSettingsPartHandler : ContentHandler {
public MediaSettingsPartHandler(IRepository<MediaSettingsPartRecord> repository) {
Filters.Add(new ActivatingFilter<MediaSettingsPart>("Site"));
Filters.Add(StorageFilter.For(repository));
Filters.Add(new TemplateFilterForRecord<MediaSettingsPartRecord>("MediaSettings", "Parts/Media.MediaSettings"));
}
}
}

View File

@@ -0,0 +1,16 @@
using Orchard.Data.Migration;
using Orchard.Media.Models;
namespace Orchard.Media {
public class MediaDataMigration : DataMigrationImpl {
public int Create() {
SchemaBuilder.CreateTable("MediaSettingsPartRecord",
table => table
.ContentPartRecord()
.Column<string>("UploadAllowedFileTypeWhitelist", c => c.WithDefault(MediaSettingsPartRecord.DefaultWhitelist).WithLength(255))
);
return 1;
}
}
}

View File

@@ -0,0 +1,11 @@
using Orchard.ContentManagement;
using System;
namespace Orchard.Media.Models {
public class MediaSettingsPart : ContentPart<MediaSettingsPartRecord> {
public string UploadAllowedFileTypeWhitelist {
get { return Record.UploadAllowedFileTypeWhitelist; }
set { Record.UploadAllowedFileTypeWhitelist = value; }
}
}
}

View File

@@ -0,0 +1,15 @@
using System.Net.Mail;
using Orchard.ContentManagement.Records;
using System.ComponentModel.DataAnnotations;
namespace Orchard.Media.Models {
public class MediaSettingsPartRecord : ContentPartRecord {
internal const string DefaultWhitelist = "jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp";
private string _whitelist = DefaultWhitelist;
public virtual string UploadAllowedFileTypeWhitelist {
get { return _whitelist; }
set { _whitelist = value; }
}
}
}

View File

@@ -71,6 +71,10 @@
<ItemGroup> <ItemGroup>
<Compile Include="AdminMenu.cs" /> <Compile Include="AdminMenu.cs" />
<Compile Include="Controllers\AdminController.cs" /> <Compile Include="Controllers\AdminController.cs" />
<Compile Include="Handlers\MediaSettingsPartHandler.cs" />
<Compile Include="Migrations.cs" />
<Compile Include="Models\MediaSettingsPart.cs" />
<Compile Include="Models\MediaSettingsPartRecord.cs" />
<Compile Include="ResourceManifest.cs" /> <Compile Include="ResourceManifest.cs" />
<Compile Include="Helpers\MediaHelpers.cs" /> <Compile Include="Helpers\MediaHelpers.cs" />
<Compile Include="Permissions.cs" /> <Compile Include="Permissions.cs" />
@@ -114,6 +118,7 @@
<Content Include="Views\Admin\Index.cshtml" /> <Content Include="Views\Admin\Index.cshtml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Include="Views\EditorTemplates\Parts\Media.MediaSettings.cshtml" />
<Content Include="Content\Web.config"> <Content Include="Content\Web.config">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</Content> </Content>

View File

@@ -13,5 +13,6 @@ namespace Orchard.Media.Services {
void DeleteFile(string name, string folderName); void DeleteFile(string name, string folderName);
void RenameFile(string name, string newName, string folderName); void RenameFile(string name, string newName, string folderName);
string UploadMediaFile(string folderName, HttpPostedFileBase postedFile); string UploadMediaFile(string folderName, HttpPostedFileBase postedFile);
bool FileAllowed(HttpPostedFileBase postedFile);
} }
} }

View File

@@ -5,9 +5,12 @@ using System.Text;
using System.Web; using System.Web;
using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip;
using JetBrains.Annotations; using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.FileSystems.Media; using Orchard.FileSystems.Media;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Media.Models; using Orchard.Media.Models;
using Orchard.Security;
using Orchard.Settings;
namespace Orchard.Media.Services { namespace Orchard.Media.Services {
[UsedImplicitly] [UsedImplicitly]
@@ -21,6 +24,9 @@ namespace Orchard.Media.Services {
} }
public ILogger Logger { get; set; } public ILogger Logger { get; set; }
protected virtual ISite CurrentSite { get; [UsedImplicitly] private set; }
protected virtual IUser CurrentUser { get; [UsedImplicitly] private set; }
public string GetPublicUrl(string path) { public string GetPublicUrl(string path) {
return _storageProvider.GetPublicUrl(path); return _storageProvider.GetPublicUrl(path);
@@ -82,18 +88,18 @@ namespace Orchard.Media.Services {
} }
public void RenameFile(string name, string newName, string folderName) { public void RenameFile(string name, string newName, string folderName) {
_storageProvider.RenameFile(folderName + "\\" + name, folderName + "\\" + newName); if (FileAllowed(newName, false)) {
_storageProvider.RenameFile(folderName + "\\" + name, folderName + "\\" + newName);
}
} }
public string UploadMediaFile(string folderName, HttpPostedFileBase postedFile) { public string UploadMediaFile(string folderName, HttpPostedFileBase postedFile) {
if (postedFile.FileName.EndsWith(".zip")) { if (postedFile.FileName.EndsWith(".zip")) {
UnzipMediaFileArchive(folderName, postedFile); UnzipMediaFileArchive(folderName, postedFile);
// Don't save the zip file. // Don't save the zip file.
return _storageProvider.GetPublicUrl(folderName); return _storageProvider.GetPublicUrl(folderName);
} }
if (FileAllowed(postedFile) && postedFile.ContentLength > 0) {
if (postedFile.ContentLength > 0) {
var filePath = Path.Combine(folderName, Path.GetFileName(postedFile.FileName)); var filePath = Path.Combine(folderName, Path.GetFileName(postedFile.FileName));
var inputStream = postedFile.InputStream; var inputStream = postedFile.InputStream;
@@ -104,6 +110,41 @@ namespace Orchard.Media.Services {
return null; return null;
} }
private bool FileAllowed(string name, bool allowZip) {
if (string.IsNullOrWhiteSpace(name)) {
return false;
}
var mediaSettings = CurrentSite.As<MediaSettingsPart>();
var allowedExtensions = mediaSettings.UploadAllowedFileTypeWhitelist.ToLowerInvariant().Split(' ');
var ext = (Path.GetExtension(name) ?? "").TrimStart('.').ToLowerInvariant();
if (string.IsNullOrWhiteSpace(ext)) {
return false;
}
// whitelist does not apply to the superuser
if (CurrentUser == null || !CurrentSite.SuperUser.Equals(CurrentUser.UserName, StringComparison.Ordinal)) {
// zip files at the top level are allowed since this is how you upload multiple files at once.
if (allowZip && ext.Equals("zip", StringComparison.OrdinalIgnoreCase)) {
return true;
}
// must be in the whitelist
if (Array.IndexOf(allowedExtensions, ext) == -1) {
return false;
}
}
// blacklist always applies
if (string.Equals(name.Trim(), "web.config", StringComparison.OrdinalIgnoreCase)) {
return false;
}
return true;
}
public bool FileAllowed(HttpPostedFileBase postedFile) {
if (postedFile == null) {
return false;
}
return FileAllowed(postedFile.FileName, true);
}
private void SaveStream(string filePath, Stream inputStream) { private void SaveStream(string filePath, Stream inputStream) {
var file = _storageProvider.CreateFile(filePath); var file = _storageProvider.CreateFile(filePath);
var outputStream = file.OpenWrite(); var outputStream = file.OpenWrite();
@@ -139,12 +180,17 @@ namespace Orchard.Media.Services {
var entryName = Path.Combine(targetFolder, entry.Name); var entryName = Path.Combine(targetFolder, entry.Name);
var directoryName = Path.GetDirectoryName(entryName); var directoryName = Path.GetDirectoryName(entryName);
try { _storageProvider.CreateFolder(directoryName); } // skip disallowed files
catch { if (FileAllowed(entry.Name, false)) {
// no handling needed - this is to force the folder to exist if it doesn't try {
} _storageProvider.CreateFolder(directoryName);
}
catch {
// no handling needed - this is to force the folder to exist if it doesn't
}
SaveStream(entryName, fileInflater); SaveStream(entryName, fileInflater);
}
} }
} }
} }

View File

@@ -1,4 +1,6 @@
namespace Orchard.Media.ViewModels { using Orchard.Media.Models;
namespace Orchard.Media.ViewModels {
public class MediaItemAddViewModel { public class MediaItemAddViewModel {
public string FolderName { get; set; } public string FolderName { get; set; }
public string MediaPath { get; set; } public string MediaPath { get; set; }

View File

@@ -19,7 +19,7 @@
@Html.ValidationSummary() @Html.ValidationSummary()
<div class="primary"> <div class="primary">
<div> <div>
<img src="@Model.PublicUrl" class="previewImage" alt="@Model.Caption" /> <img src="@Model.PublicUrl" class="previewImage"/>
</div> </div>
<fieldset> <fieldset>
@* todo: make these real (including markup) *@ @* todo: make these real (including markup) *@
@@ -32,7 +32,7 @@
</div> </div>
<div> <div>
<label for="embedPath">@T("Embed:")</label> <label for="embedPath">@T("Embed:")</label>
<input id="embedPath" class="textMedium" name="embedPath" type="text" readonly="readonly" value="&lt;img src=&quot;@Href("~/Media/" + Model.RelativePath + "/" + Model.Name)&quot; @* width=&quot;500&quot; height=&quot;375&quot; *@ alt=&quot;@Model.Caption&quot; /&gt;" /> <input id="embedPath" class="textMedium" name="embedPath" type="text" readonly="readonly" value="&lt;img src=&quot;@Model.PublicUrl&quot; @* width=&quot;500&quot; height=&quot;375&quot; *@ /&gt;" />
<span class="hint">@T("Copy this html to add this image to your site.")</span> <span class="hint">@T("Copy this html to add this image to your site.")</span>
</div> </div>
@@ -42,9 +42,6 @@
<input id="NewName" class="textMedium" name="NewName" type="text" value="@Model.Name"/> <input id="NewName" class="textMedium" name="NewName" type="text" value="@Model.Name"/>
</div> </div>
<div> <div>
<label for="Caption">@T("Caption")</label>
<input id="Caption" class="textMedium" name="Caption" type="text" value="@Model.Caption"/>
<span class="hint">@T("This will be used for the image alt tag.")</span>
<input type="hidden" id="LastUpdated" name="LastUpdated" value="@Model.LastUpdated"/> <input type="hidden" id="LastUpdated" name="LastUpdated" value="@Model.LastUpdated"/>
<input type="hidden" id="Size" name="Size" value="@Model.Size"/> <input type="hidden" id="Size" name="Size" value="@Model.Size"/>
<input type="hidden" id="FolderName" name="FolderName" value="@Model.FolderName"/> <input type="hidden" id="FolderName" name="FolderName" value="@Model.FolderName"/>
@@ -58,7 +55,7 @@
@* @*
<div class="secondary" style="border:1px solid #ff0000;"> <div class="secondary" style="border:1px solid #ff0000;">
<h2>@T("Preview")</h2> <h2>@T("Preview")</h2>
<div><img src="@Href("~/Media/" + Html.Encode(Model.RelativePath + "/" + Model.Name))" class="previewImage" alt="@Model.Caption" /></div> <div><img src="@Href("~/Media/" + Html.Encode(Model.RelativePath + "/" + Model.Name))" class="previewImage" /></div>
<ul> <ul>
@// todo: make these real (including markup) @// todo: make these real (including markup)
<li><label>@T("Dimensions: <span>500 x 375 pixels</span>")</label></li> <li><label>@T("Dimensions: <span>500 x 375 pixels</span>")</label></li>
@@ -66,7 +63,7 @@
<li><label>@T("Added on: <span>{0} by Orchard User</span>", Model.LastUpdated)</label></li> <li><label>@T("Added on: <span>{0} by Orchard User</span>", Model.LastUpdated)</label></li>
<li> <li>
<label for="embedPath">@T("Embed:")</label> <label for="embedPath">@T("Embed:")</label>
<input id="embedPath" class="text" name="embedPath" type="text" readonly="readonly" value="@T("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" alt=\"{3}\" />", ResolveUrl("~/Media/" + Model.RelativePath + "/" + Model.Name), 500, 375, Model.Caption)" /> <input id="embedPath" class="text" name="embedPath" type="text" readonly="readonly" value="@T("<img src=\"{0}\" width=\"{1}\" height=\"{2}\" />", ResolveUrl("~/Media/" + Model.RelativePath + "/" + Model.Name), 500, 375)" />
<span class="hint">@T("Copy this html to add this image to your site.")</p> <span class="hint">@T("Copy this html to add this image to your site.")</p>
</li> </li>
</ul> </ul>

View File

@@ -0,0 +1,9 @@
@model Orchard.Media.Models.MediaSettingsPartRecord
<fieldset>
<legend>@T("Media Settings")</legend>
<div>
@Html.LabelFor(m => m.UploadAllowedFileTypeWhitelist, T("Upload allowed file types (list of extensions separated by spaces)"))
@Html.TextBoxFor(m => m.UploadAllowedFileTypeWhitelist, new { @class = "textMedium" })
</div>
</fieldset>

View File

@@ -4,14 +4,15 @@
@using Orchard.Modules.ViewModels; @using Orchard.Modules.ViewModels;
@using Orchard.Utility.Extensions; @using Orchard.Utility.Extensions;
@using Orchard.Modules.Models; @using Orchard.Modules.Models;
@{ @{
Style.Require("ModulesAdmin"); Style.Require("ModulesAdmin");
Style.Require("Switchable");
Script.Require("Switchable");
} }
<h1>@Html.TitleForPage(T("Manage Features").ToString())</h1> <h1>@Html.TitleForPage(T("Manage Features").ToString())</h1>
@if (Model.Features.Count() > 0) { @if (Model.Features.Count() > 0) {
<ul class="features summary-view">@{ <ul class="features summary-view switchable">@{
var featureGroups = Model.Features.OrderBy(f => f.Descriptor.Category, new DoghouseComparer("Core")).GroupBy(f => f.Descriptor.Category); var featureGroups = Model.Features.OrderBy(f => f.Descriptor.Category, new DoghouseComparer("Core")).GroupBy(f => f.Descriptor.Category);
foreach (var featureGroup in featureGroups) { foreach (var featureGroup in featureGroups) {
var categoryName = LocalizedString.TextOrDefault(featureGroup.First().Descriptor.Category, T("Uncategorized")); var categoryName = LocalizedString.TextOrDefault(featureGroup.First().Descriptor.Category, T("Uncategorized"));

View File

@@ -6,9 +6,6 @@ namespace Orchard.Themes {
public void BuildManifests(ResourceManifestBuilder builder) { public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add(); var manifest = builder.Add();
manifest.DefineStyle("ThemesAdmin").SetUrl("admin.css"); manifest.DefineStyle("ThemesAdmin").SetUrl("admin.css");
// todo: used by core\shapes -- move it?
manifest.DefineScript("Switchable").SetUrl("jquery.switchable.js").SetDependencies("jQuery");
manifest.DefineStyle("Switchable").SetUrl("jquery.switchable.css");
// todo: include and define the min.js version too // todo: include and define the min.js version too
// todo: move EasySlider to common location, although it does not appear to be used anywhere right now // todo: move EasySlider to common location, although it does not appear to be used anywhere right now

View File

@@ -1,10 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Web.Mvc;
using Autofac.Features.OwnedInstances; using Autofac.Features.OwnedInstances;
using Orchard.Environment.Extensions.Models;
using Orchard.Logging; using Orchard.Logging;
using Orchard.Mvc.ModelBinders; using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes; using Orchard.Mvc.Routes;
@@ -17,21 +14,18 @@ namespace Orchard.Environment {
private readonly IRoutePublisher _routePublisher; private readonly IRoutePublisher _routePublisher;
private readonly IEnumerable<IModelBinderProvider> _modelBinderProviders; private readonly IEnumerable<IModelBinderProvider> _modelBinderProviders;
private readonly IModelBinderPublisher _modelBinderPublisher; private readonly IModelBinderPublisher _modelBinderPublisher;
private readonly ViewEngineCollection _viewEngines;
public DefaultOrchardShell( public DefaultOrchardShell(
Func<Owned<IOrchardShellEvents>> eventsFactory, Func<Owned<IOrchardShellEvents>> eventsFactory,
IEnumerable<IRouteProvider> routeProviders, IEnumerable<IRouteProvider> routeProviders,
IRoutePublisher routePublisher, IRoutePublisher routePublisher,
IEnumerable<IModelBinderProvider> modelBinderProviders, IEnumerable<IModelBinderProvider> modelBinderProviders,
IModelBinderPublisher modelBinderPublisher, IModelBinderPublisher modelBinderPublisher) {
ViewEngineCollection viewEngines) {
_eventsFactory = eventsFactory; _eventsFactory = eventsFactory;
_routeProviders = routeProviders; _routeProviders = routeProviders;
_routePublisher = routePublisher; _routePublisher = routePublisher;
_modelBinderProviders = modelBinderProviders; _modelBinderProviders = modelBinderProviders;
_modelBinderPublisher = modelBinderPublisher; _modelBinderPublisher = modelBinderPublisher;
_viewEngines = viewEngines;
Logger = NullLogger.Instance; Logger = NullLogger.Instance;
} }
@@ -42,8 +36,6 @@ namespace Orchard.Environment {
_routePublisher.Publish(_routeProviders.SelectMany(provider => provider.GetRoutes())); _routePublisher.Publish(_routeProviders.SelectMany(provider => provider.GetRoutes()));
_modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders())); _modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders()));
//AddOrchardLocationsFormats();
using (var events = _eventsFactory()) { using (var events = _eventsFactory()) {
events.Value.Activated(); events.Value.Activated();
} }
@@ -54,62 +46,5 @@ namespace Orchard.Environment {
events.Value.Terminating(); events.Value.Terminating();
} }
} }
/// <summary>
/// Adds view locations formats for non-themed views in custom orchard modules.
/// </summary>
private void AddOrchardLocationsFormats() {
IEnumerable<string> orchardMasterLocationFormats = new[] {
"~/Modules/{2}/Views/{1}/{0}.master",
"~/Modules/{2}/Views/Shared/{0}.master",
"~/Themes/{2}/Views/{1}/{0}.master",
"~/Themes/{2}/Views/Shared/{0}.master",
"~/Core/{2}/Views/{1}/{0}.master",
"~/Core/{2}/Views/Shared/{0}.master",
"~/Areas/{2}/Views/{1}/{0}.master",
"~/Areas/{2}/Views/Shared/{0}.master",
};
IEnumerable<string> orchardLocationFormats = new[] {
"~/Modules/{2}/Views/{1}/{0}.aspx",
"~/Modules/{2}/Views/{1}/{0}.ascx",
"~/Modules/{2}/Views/Shared/{0}.aspx",
"~/Modules/{2}/Views/Shared/{0}.ascx",
"~/Themes/{2}/Views/{1}/{0}.aspx",
"~/Themes/{2}/Views/{1}/{0}.ascx",
"~/Themes/{2}/Views/Shared/{0}.aspx",
"~/Themes/{2}/Views/Shared/{0}.ascx",
"~/Core/{2}/Views/{1}/{0}.aspx",
"~/Core/{2}/Views/{1}/{0}.ascx",
"~/Core/{2}/Views/Shared/{0}.aspx",
"~/Core/{2}/Views/Shared/{0}.ascx",
"~/Areas/{2}/Views/{1}/{0}.aspx",
"~/Areas/{2}/Views/{1}/{0}.ascx",
"~/Areas/{2}/Views/Shared/{0}.aspx",
"~/Areas/{2}/Views/Shared/{0}.ascx",
};
var viewEngine = _viewEngines.OfType<VirtualPathProviderViewEngine>().Single();
viewEngine.AreaMasterLocationFormats = orchardMasterLocationFormats
.Concat(viewEngine.AreaMasterLocationFormats)
.Distinct()
.ToArray();
viewEngine.AreaViewLocationFormats = orchardLocationFormats
.Concat(viewEngine.AreaViewLocationFormats)
.Distinct()
.ToArray();
viewEngine.AreaPartialViewLocationFormats = orchardLocationFormats
.Concat(viewEngine.AreaPartialViewLocationFormats)
.Distinct()
.ToArray();
}
private static string ModelsLocationFormat(ExtensionDescriptor descriptor) {
return Path.Combine(Path.Combine(descriptor.Location, descriptor.Name), "Views/Shared/{0}.ascx");
}
} }
} }

View File

@@ -1,6 +1,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Web; using System.Web;
using System.Web.Hosting; using System.Web.Hosting;
using Orchard.Services; using Orchard.Services;
@@ -36,7 +37,7 @@ namespace Orchard.Environment {
} }
public bool IsAssemblyLoaded(string name) { public bool IsAssemblyLoaded(string name) {
return AppDomain.CurrentDomain.GetAssemblies().Any(a => a.FullName.Contains(name)); return AppDomain.CurrentDomain.GetAssemblies().Any(assembly => new AssemblyName(assembly.FullName).Name == name);
} }
public void RestartAppDomain() { public void RestartAppDomain() {

View File

@@ -1,4 +1,5 @@
using System; #if !AZURE
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@@ -45,7 +46,7 @@ namespace Orchard.FileSystems.Media {
public string GetPublicUrl(string path) { public string GetPublicUrl(string path) {
return _publicPath + path.Replace(Path.DirectorySeparatorChar, '/'); return Map(_publicPath + path.Replace(Path.DirectorySeparatorChar, '/'));
} }
public IStorageFile GetFile(string path) { public IStorageFile GetFile(string path) {
@@ -248,4 +249,5 @@ namespace Orchard.FileSystems.Media {
} }
} }
} }
#endif

View File

@@ -2,21 +2,26 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Web.Routing; using System.Web.Routing;
using Autofac;
using Orchard.Environment;
using Orchard.Environment.Configuration; using Orchard.Environment.Configuration;
namespace Orchard.Mvc.Routes { namespace Orchard.Mvc.Routes {
public class RoutePublisher : IRoutePublisher { public class RoutePublisher : IRoutePublisher {
private readonly RouteCollection _routeCollection; private readonly RouteCollection _routeCollection;
private readonly ShellSettings _shellSettings; private readonly ShellSettings _shellSettings;
private readonly Func<RouteBase, ShellRoute> _shellRouteFactory; private readonly ILifetimeScope _shellLifetimeScope;
private readonly IRunningShellTable _runningShellTable;
public RoutePublisher( public RoutePublisher(
RouteCollection routeCollection, RouteCollection routeCollection,
ShellSettings shellSettings, ShellSettings shellSettings,
Func<RouteBase, ShellRoute> shellRouteFactory) { ILifetimeScope shellLifetimeScope,
IRunningShellTable runningShellTable) {
_routeCollection = routeCollection; _routeCollection = routeCollection;
_shellSettings = shellSettings; _shellSettings = shellSettings;
_shellRouteFactory = shellRouteFactory; _shellLifetimeScope = shellLifetimeScope;
_runningShellTable = runningShellTable;
} }
public void Publish(IEnumerable<RouteDescriptor> routes) { public void Publish(IEnumerable<RouteDescriptor> routes) {
@@ -41,8 +46,8 @@ namespace Orchard.Mvc.Routes {
// new routes are added // new routes are added
foreach (var routeDescriptor in routesArray) { foreach (var routeDescriptor in routesArray) {
//_routeCollection.Add(route.Name, _shellRouteFactory(_shellSettings.Name, route.Route)); ShellRoute shellRoute = new ShellRoute(routeDescriptor.Route, _shellSettings, _shellLifetimeScope, _runningShellTable);
_routeCollection.Add(routeDescriptor.Name, _shellRouteFactory(routeDescriptor.Route)); _routeCollection.Add(routeDescriptor.Name, shellRoute);
} }
} }
} }

View File

@@ -16,6 +16,9 @@ using PackageIndexReferenceImplementation.Services;
namespace PackageIndexReferenceImplementation.Controllers { namespace PackageIndexReferenceImplementation.Controllers {
[HandleError] [HandleError]
public class FeedController : Controller { public class FeedController : Controller {
private const string VersionTag = "Version";
private const string ExtensionsNamespace = "http://orchardproject.net";
private readonly FeedStorage _feedStorage; private readonly FeedStorage _feedStorage;
private readonly MediaStorage _mediaStorage; private readonly MediaStorage _mediaStorage;
@@ -104,7 +107,12 @@ namespace PackageIndexReferenceImplementation.Controllers {
} }
if ( !string.IsNullOrEmpty(packageProperties.Version) ) { if ( !string.IsNullOrEmpty(packageProperties.Version) ) {
item.ElementExtensions.Add("Version", "http://orchardproject.net", packageProperties.Version); var versionExtensions = item.ElementExtensions.Where(e => e.OuterName == VersionTag && e.OuterNamespace == ExtensionsNamespace);
foreach(var versionExtension in versionExtensions) {
item.ElementExtensions.Remove(versionExtension);
}
item.ElementExtensions.Add(VersionTag, ExtensionsNamespace, packageProperties.Version);
} }
var mediaIdentifier = packageProperties.Identifier + "-" + packageProperties.Version + ".zip"; var mediaIdentifier = packageProperties.Identifier + "-" + packageProperties.Version + ".zip";