--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
Projects="$(SrcFolder)\Orchard.Azure\Orchard.Azure.CloudService.sln"
Targets="Build"
Properties="Configuration=Release;OutputPath=$(CompileFolder);PlatformTarget=x64" />
Properties="Configuration=Release;OutputPath=$(CompileFolder);PlatformTarget=x64;DefineConstants=AZURE"
/>
<MSBuild
Projects="$(SrcFolder)\Orchard.Azure.sln"

View File

@@ -50,7 +50,7 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
Assert.AreEqual(".txt", storageFile.GetFileType());
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());
}
@@ -71,7 +71,8 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
var files = _azureBlobStorageProvider.ListFiles("");
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]
@@ -82,17 +83,28 @@ namespace Orchard.Azure.Tests.FileSystems.Media {
Assert.AreEqual(1, _azureBlobStorageProvider.ListFiles("").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("folder/folder/foo.txt", _azureBlobStorageProvider.ListFiles("folder/folder").First().GetPath());
Assert.AreEqual("foo.txt", _azureBlobStorageProvider.ListFiles("folder/folder").First().GetName());
}
[Test]
[ExpectedException(typeof(ArgumentException))]
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.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]
public void DeleteFolderShouldDeleteFilesAlso() {
_azureBlobStorageProvider.CreateFile("folder/foo1.txt");

View File

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

View File

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

View File

@@ -26,6 +26,7 @@ namespace Orchard.Tests.DataMigration {
private string _tempFolder;
private SchemaBuilder _schemaBuilder;
private DefaultDataMigrationInterpreter _interpreter;
private ISession _session;
[SetUp]
public void Setup() {
@@ -38,7 +39,7 @@ namespace Orchard.Tests.DataMigration {
var builder = new ContainerBuilder();
var session = _sessionFactory.OpenSession();
_session = _sessionFactory.OpenSession();
builder.RegisterInstance(appDataFolder).As<IAppDataFolder>();
builder.RegisterType<SqlCeDataServicesProvider>().As<IDataServicesProvider>();
builder.RegisterType<DataServicesProviderFactory>().As<IDataServicesProviderFactory>();
@@ -46,7 +47,7 @@ namespace Orchard.Tests.DataMigration {
builder.RegisterType<DefaultDataMigrationInterpreter>().As<IDataMigrationInterpreter>();
builder.RegisterType<SessionConfigurationCache>().As<ISessionConfigurationCache>();
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 ShellSettings { Name = "temp", DataProvider = "SqlCe", DataTablePrefix = "TEST" }).As<ShellSettings>();
builder.RegisterModule(new DataModule());
@@ -133,10 +134,24 @@ namespace Orchard.Tests.DataMigration {
.AlterTable("User", table => table
.AddColumn("Age", DbType.Int32))
.AlterTable("User", table => table
.AlterColumn("Lastname", column => column.WithDefault("John")))
.AlterColumn("Lastname", column => column.WithDefault("Doe")))
.AlterTable("User", table => table
.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]

View File

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

View File

@@ -298,7 +298,6 @@
<Content Include="Shapes\Views\HeadPreload.cshtml" />
<Content Include="Shapes\Views\Message.cshtml" />
<Content Include="Shapes\Views\NotFound.cshtml" />
<Content Include="Shapes\Views\UI\Switchable.cshtml" />
<Content Include="Web.config" />
<Content Include="XmlRpc\Module.txt" />
<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;
namespace Orchard.Core.Shapes {
@@ -11,6 +7,11 @@ namespace Orchard.Core.Shapes {
manifest.DefineScript("ShapesBase").SetUrl("base.js").SetDependencies("jQuery");
manifest.DefineStyle("Shapes").SetUrl("site.css"); // todo: missing
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.Web;
using System.Web.Mvc;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.Localization;
using Orchard.Media.Models;
using Orchard.Media.Services;
using Orchard.Media.ViewModels;
using Orchard.Settings;
using Orchard.UI.Notify;
using Orchard.Utility.Extensions;
@@ -153,6 +156,15 @@ namespace Orchard.Media.Controllers {
if (!ModelState.IsValid)
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) {
HttpPostedFileBase file = Request.Files[fileName];
_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();
model.Name = name;
model.Caption = caption ?? String.Empty;
// todo: reimplement
//model.Caption = caption ?? String.Empty;
model.LastUpdated = lastUpdated;
model.Size = size;
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>
<Compile Include="AdminMenu.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="Helpers\MediaHelpers.cs" />
<Compile Include="Permissions.cs" />
@@ -114,6 +118,7 @@
<Content Include="Views\Admin\Index.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\EditorTemplates\Parts\Media.MediaSettings.cshtml" />
<Content Include="Content\Web.config">
<SubType>Designer</SubType>
</Content>

View File

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

View File

@@ -5,9 +5,12 @@ using System.Text;
using System.Web;
using ICSharpCode.SharpZipLib.Zip;
using JetBrains.Annotations;
using Orchard.ContentManagement;
using Orchard.FileSystems.Media;
using Orchard.Logging;
using Orchard.Media.Models;
using Orchard.Security;
using Orchard.Settings;
namespace Orchard.Media.Services {
[UsedImplicitly]
@@ -21,6 +24,9 @@ namespace Orchard.Media.Services {
}
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) {
return _storageProvider.GetPublicUrl(path);
@@ -82,18 +88,18 @@ namespace Orchard.Media.Services {
}
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) {
if (postedFile.FileName.EndsWith(".zip")) {
UnzipMediaFileArchive(folderName, postedFile);
// Don't save the zip file.
return _storageProvider.GetPublicUrl(folderName);
}
if (postedFile.ContentLength > 0) {
if (FileAllowed(postedFile) && postedFile.ContentLength > 0) {
var filePath = Path.Combine(folderName, Path.GetFileName(postedFile.FileName));
var inputStream = postedFile.InputStream;
@@ -104,6 +110,41 @@ namespace Orchard.Media.Services {
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) {
var file = _storageProvider.CreateFile(filePath);
var outputStream = file.OpenWrite();
@@ -139,12 +180,17 @@ namespace Orchard.Media.Services {
var entryName = Path.Combine(targetFolder, entry.Name);
var directoryName = Path.GetDirectoryName(entryName);
try { _storageProvider.CreateFolder(directoryName); }
catch {
// no handling needed - this is to force the folder to exist if it doesn't
}
// skip disallowed files
if (FileAllowed(entry.Name, false)) {
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 string FolderName { get; set; }
public string MediaPath { get; set; }

View File

@@ -19,7 +19,7 @@
@Html.ValidationSummary()
<div class="primary">
<div>
<img src="@Model.PublicUrl" class="previewImage" alt="@Model.Caption" />
<img src="@Model.PublicUrl" class="previewImage"/>
</div>
<fieldset>
@* todo: make these real (including markup) *@
@@ -32,7 +32,7 @@
</div>
<div>
<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>
</div>
@@ -42,9 +42,6 @@
<input id="NewName" class="textMedium" name="NewName" type="text" value="@Model.Name"/>
</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="Size" name="Size" value="@Model.Size"/>
<input type="hidden" id="FolderName" name="FolderName" value="@Model.FolderName"/>
@@ -58,7 +55,7 @@
@*
<div class="secondary" style="border:1px solid #ff0000;">
<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>
@// todo: make these real (including markup)
<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 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>
</li>
</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.Utility.Extensions;
@using Orchard.Modules.Models;
@{
Style.Require("ModulesAdmin");
Style.Require("Switchable");
Script.Require("Switchable");
}
<h1>@Html.TitleForPage(T("Manage Features").ToString())</h1>
@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);
foreach (var featureGroup in featureGroups) {
var categoryName = LocalizedString.TextOrDefault(featureGroup.First().Descriptor.Category, T("Uncategorized"));

View File

@@ -6,9 +6,6 @@ namespace Orchard.Themes {
public void BuildManifests(ResourceManifestBuilder builder) {
var manifest = builder.Add();
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: 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Mvc;
using Autofac.Features.OwnedInstances;
using Orchard.Environment.Extensions.Models;
using Orchard.Logging;
using Orchard.Mvc.ModelBinders;
using Orchard.Mvc.Routes;
@@ -17,21 +14,18 @@ namespace Orchard.Environment {
private readonly IRoutePublisher _routePublisher;
private readonly IEnumerable<IModelBinderProvider> _modelBinderProviders;
private readonly IModelBinderPublisher _modelBinderPublisher;
private readonly ViewEngineCollection _viewEngines;
public DefaultOrchardShell(
Func<Owned<IOrchardShellEvents>> eventsFactory,
IEnumerable<IRouteProvider> routeProviders,
IRoutePublisher routePublisher,
IEnumerable<IModelBinderProvider> modelBinderProviders,
IModelBinderPublisher modelBinderPublisher,
ViewEngineCollection viewEngines) {
IModelBinderPublisher modelBinderPublisher) {
_eventsFactory = eventsFactory;
_routeProviders = routeProviders;
_routePublisher = routePublisher;
_modelBinderProviders = modelBinderProviders;
_modelBinderPublisher = modelBinderPublisher;
_viewEngines = viewEngines;
Logger = NullLogger.Instance;
}
@@ -42,8 +36,6 @@ namespace Orchard.Environment {
_routePublisher.Publish(_routeProviders.SelectMany(provider => provider.GetRoutes()));
_modelBinderPublisher.Publish(_modelBinderProviders.SelectMany(provider => provider.GetModelBinders()));
//AddOrchardLocationsFormats();
using (var events = _eventsFactory()) {
events.Value.Activated();
}
@@ -54,62 +46,5 @@ namespace Orchard.Environment {
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.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Hosting;
using Orchard.Services;
@@ -36,7 +37,7 @@ namespace Orchard.Environment {
}
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() {

View File

@@ -1,4 +1,5 @@
using System;
#if !AZURE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -45,7 +46,7 @@ namespace Orchard.FileSystems.Media {
public string GetPublicUrl(string path) {
return _publicPath + path.Replace(Path.DirectorySeparatorChar, '/');
return Map(_publicPath + path.Replace(Path.DirectorySeparatorChar, '/'));
}
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.Linq;
using System.Web.Routing;
using Autofac;
using Orchard.Environment;
using Orchard.Environment.Configuration;
namespace Orchard.Mvc.Routes {
public class RoutePublisher : IRoutePublisher {
private readonly RouteCollection _routeCollection;
private readonly ShellSettings _shellSettings;
private readonly Func<RouteBase, ShellRoute> _shellRouteFactory;
private readonly ILifetimeScope _shellLifetimeScope;
private readonly IRunningShellTable _runningShellTable;
public RoutePublisher(
RouteCollection routeCollection,
ShellSettings shellSettings,
Func<RouteBase, ShellRoute> shellRouteFactory) {
ILifetimeScope shellLifetimeScope,
IRunningShellTable runningShellTable) {
_routeCollection = routeCollection;
_shellSettings = shellSettings;
_shellRouteFactory = shellRouteFactory;
_shellLifetimeScope = shellLifetimeScope;
_runningShellTable = runningShellTable;
}
public void Publish(IEnumerable<RouteDescriptor> routes) {
@@ -41,8 +46,8 @@ namespace Orchard.Mvc.Routes {
// new routes are added
foreach (var routeDescriptor in routesArray) {
//_routeCollection.Add(route.Name, _shellRouteFactory(_shellSettings.Name, route.Route));
_routeCollection.Add(routeDescriptor.Name, _shellRouteFactory(routeDescriptor.Route));
ShellRoute shellRoute = new ShellRoute(routeDescriptor.Route, _shellSettings, _shellLifetimeScope, _runningShellTable);
_routeCollection.Add(routeDescriptor.Name, shellRoute);
}
}
}

View File

@@ -16,6 +16,9 @@ using PackageIndexReferenceImplementation.Services;
namespace PackageIndexReferenceImplementation.Controllers {
[HandleError]
public class FeedController : Controller {
private const string VersionTag = "Version";
private const string ExtensionsNamespace = "http://orchardproject.net";
private readonly FeedStorage _feedStorage;
private readonly MediaStorage _mediaStorage;
@@ -104,7 +107,12 @@ namespace PackageIndexReferenceImplementation.Controllers {
}
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";