--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

@@ -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>