mirror of
https://github.com/OrchardCMS/Orchard.git
synced 2025-10-15 03:25:23 +08:00
Gallery: publish, browse, install, and download themes. Gallery displays theme thumbnail.
--HG-- branch : dev
This commit is contained in:
@@ -11,9 +11,11 @@ namespace Orchard.Packaging {
|
||||
|
||||
public void GetNavigation(NavigationBuilder builder) {
|
||||
builder.Add(T("Gallery"), "5", menu => menu
|
||||
.Add(T("Browse Gallery"), "1.0", item => item
|
||||
.Action("Index", "Gallery", new { area = "Orchard.Packaging" }))
|
||||
.Add(T("Gallery Feeds"), "2.0", item => item
|
||||
.Add(T("Browse Modules"), "1.0", item => item
|
||||
.Action("ModulesIndex", "Gallery", new { area = "Orchard.Packaging" }))
|
||||
.Add(T("Browse Themes"), "2.0", item => item
|
||||
.Action("ThemesIndex", "Gallery", new { area = "Orchard.Packaging" }))
|
||||
.Add(T("Gallery Feeds"), "3.0", item => item
|
||||
.Action("Sources", "Gallery", new { area = "Orchard.Packaging" })));
|
||||
}
|
||||
}
|
||||
|
@@ -63,7 +63,10 @@ namespace Orchard.Packaging.Commands {
|
||||
Context.Output.WriteLine(T("Success"));
|
||||
}
|
||||
catch (WebException webException) {
|
||||
var text = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();
|
||||
string text = "";
|
||||
if (webException.Response != null) {
|
||||
text = new StreamReader(webException.Response.GetResponseStream()).ReadToEnd();
|
||||
}
|
||||
throw new ApplicationException(text);
|
||||
}
|
||||
}
|
||||
|
@@ -34,10 +34,14 @@ namespace Orchard.Packaging.Controllers {
|
||||
|
||||
Localizer T { get; set; }
|
||||
|
||||
public ActionResult Index() {
|
||||
public ActionResult ModulesIndex() {
|
||||
return Modules(Guid.Empty);
|
||||
}
|
||||
|
||||
public ActionResult ThemesIndex() {
|
||||
return Themes(Guid.Empty);
|
||||
}
|
||||
|
||||
public ActionResult Sources() {
|
||||
return View("Sources", new PackagingSourcesViewModel {
|
||||
Sources = _packagingSourceManager.GetSources(),
|
||||
@@ -47,7 +51,7 @@ namespace Orchard.Packaging.Controllers {
|
||||
public ActionResult Remove(Guid id) {
|
||||
_packagingSourceManager.RemoveSource(id);
|
||||
_notifier.Information(T("The feed has been removed successfully."));
|
||||
Update();
|
||||
Update(null);
|
||||
return RedirectToAction("Sources");
|
||||
}
|
||||
|
||||
@@ -90,7 +94,7 @@ namespace Orchard.Packaging.Controllers {
|
||||
|
||||
_packagingSourceManager.AddSource(new PackagingSource { Id = Guid.NewGuid(), FeedUrl = url, FeedTitle = title });
|
||||
_notifier.Information(T("The feed has been added successfully."));
|
||||
Update();
|
||||
Update(null);
|
||||
return RedirectToAction("Sources");
|
||||
}
|
||||
catch ( Exception exception ) {
|
||||
@@ -102,18 +106,28 @@ namespace Orchard.Packaging.Controllers {
|
||||
|
||||
public ActionResult Modules(Guid? sourceId) {
|
||||
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
||||
|
||||
|
||||
return View("Modules", new PackagingModulesViewModel {
|
||||
Modules = _packagingSourceManager.GetModuleList(selectedSource),
|
||||
Modules = _packagingSourceManager.GetModuleList(selectedSource).Where(p => p.SyndicationItem.Categories.All(c => c.Name == "Orchard Module" || c.Name != "Orchard Theme")),
|
||||
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
||||
SelectedSource = selectedSource
|
||||
});
|
||||
}
|
||||
|
||||
public ActionResult Update() {
|
||||
public ActionResult Themes(Guid? sourceId) {
|
||||
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
|
||||
|
||||
return View("Themes", new PackagingModulesViewModel {
|
||||
Modules = _packagingSourceManager.GetModuleList(selectedSource).Where(p => p.SyndicationItem.Categories.Any(c => c.Name == "Orchard Theme")),
|
||||
Sources = _packagingSourceManager.GetSources().OrderBy(s => s.FeedTitle),
|
||||
SelectedSource = selectedSource
|
||||
});
|
||||
}
|
||||
|
||||
public ActionResult Update(string cameFrom) {
|
||||
_packagingSourceManager.UpdateLists();
|
||||
_notifier.Information(T("List of available modules and themes is updated."));
|
||||
return RedirectToAction("Index");
|
||||
return RedirectToAction(cameFrom == "Themes" ? "ThemesIndex" : "ModulesIndex");
|
||||
}
|
||||
|
||||
public ActionResult Harvest(string extensionName, string feedUrl) {
|
||||
@@ -147,16 +161,16 @@ namespace Orchard.Packaging.Controllers {
|
||||
_packageManager.Push(packageData, model.FeedUrl, model.User, model.Password);
|
||||
_notifier.Information(T("Harvested {0} and published onto {1}", model.ExtensionName, model.FeedUrl));
|
||||
|
||||
Update();
|
||||
Update(null);
|
||||
|
||||
return RedirectToAction("Harvest", new { model.ExtensionName, model.FeedUrl });
|
||||
}
|
||||
|
||||
public ActionResult Install(string syndicationId) {
|
||||
public ActionResult Install(string syndicationId, string cameFrom) {
|
||||
var packageData = _packageManager.Download(syndicationId);
|
||||
_packageManager.Install(packageData.PackageStream);
|
||||
_notifier.Information(T("Installed module"));
|
||||
return RedirectToAction("Modules");
|
||||
return RedirectToAction(cameFrom == "Themes" ? "ThemesIndex" : "ModulesIndex");
|
||||
}
|
||||
}
|
||||
}
|
@@ -121,6 +121,9 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Views\Gallery\Themes.cshtml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
|
@@ -53,7 +53,7 @@ namespace Orchard.Packaging.Services {
|
||||
}
|
||||
|
||||
public IEnumerable<PackagingEntry> GetModuleList(PackagingSource packagingSource = null) {
|
||||
IEnumerable<PackagingEntry> packageInfos = ( packagingSource == null ? GetSources() : new[] { packagingSource } )
|
||||
return (packagingSource == null ? GetSources() : new[] {packagingSource})
|
||||
.SelectMany(
|
||||
source =>
|
||||
Bind(ParseFeed(GetModuleListForSource(source)),
|
||||
@@ -64,11 +64,8 @@ namespace Orchard.Packaging.Services {
|
||||
Source = source,
|
||||
SyndicationFeed = feed,
|
||||
SyndicationItem = item,
|
||||
PackageStreamUri = item.Links.Single().GetAbsoluteUri().AbsoluteUri,
|
||||
}))));
|
||||
|
||||
|
||||
return packageInfos.ToArray();
|
||||
PackageStreamUri = item.Links.Where(l => String.IsNullOrEmpty(l.RelationshipType)).FirstOrDefault().GetAbsoluteUri().AbsoluteUri,
|
||||
})))).ToArray();
|
||||
}
|
||||
|
||||
private string GetModuleListForSource(PackagingSource source) {
|
||||
|
@@ -1,9 +1,9 @@
|
||||
@model Orchard.Packaging.ViewModels.PackagingModulesViewModel
|
||||
@{ Style.Require("PackagingAdmin"); }
|
||||
|
||||
<h1>@Html.TitleForPage(T("Browse Gallery").ToString())</h1>
|
||||
<h1>@Html.TitleForPage(T("Browse Gallery - Modules").ToString())</h1>
|
||||
|
||||
<div class="manage">@Html.ActionLink(T("Refresh").ToString(), "Update", new object{}, new { @class = "button primaryAction" })</div>
|
||||
<div class="manage">@Html.ActionLink(T("Refresh").ToString(), "Update", new { cameFrom = "Themes" }, new { @class = "button primaryAction" })</div>
|
||||
@using ( Html.BeginFormAntiForgeryPost(Url.Action("Modules", "Gallery")) ) {
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="filterResults" class="bulk-filter">@T("Feed:")</label>
|
||||
@@ -27,7 +27,7 @@
|
||||
</div>
|
||||
|
||||
<div class="related">
|
||||
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"SyndicationId",item.SyndicationItem.Id}})@T(" | ")
|
||||
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"SyndicationId",item.SyndicationItem.Id},{"cameFrom", "Modules"}})@T(" | ")
|
||||
<a href="@item.PackageStreamUri">@T("Download")</a>
|
||||
</div>
|
||||
|
||||
@@ -40,5 +40,4 @@
|
||||
</div>
|
||||
</li>}
|
||||
</ul>
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
@model Orchard.Packaging.ViewModels.PackagingModulesViewModel
|
||||
@{
|
||||
Style.Require("PackagingAdmin");
|
||||
Style.Require("ThemesAdmin");
|
||||
}
|
||||
|
||||
<h1>@Html.TitleForPage(T("Browse Gallery - Themes").ToString())</h1>
|
||||
|
||||
<div class="manage">@Html.ActionLink(T("Refresh").ToString(), "Update", new { cameFrom = "Themes" }, new { @class = "button primaryAction" })</div>
|
||||
@using ( Html.BeginFormAntiForgeryPost(Url.Action("Themes", "Gallery")) ) {
|
||||
<fieldset class="bulk-actions">
|
||||
<label for="filterResults" class="bulk-filter">@T("Feed:")</label>
|
||||
<select id="sourceId" name="sourceId">
|
||||
@Html.SelectOption("", Model.SelectedSource == null, T("Any (show all feeds)").ToString())
|
||||
@foreach (var source in Model.Sources) {
|
||||
Html.SelectOption(source.Id, Model.SelectedSource != null && Model.SelectedSource.Id == source.Id, source.FeedTitle);
|
||||
}
|
||||
</select>
|
||||
<button type="submit">@T("Apply")</button>
|
||||
</fieldset>
|
||||
}
|
||||
|
||||
@if (Model.Modules.Count() > 0) {
|
||||
<ul class="templates">
|
||||
@foreach (var item in Model.Modules) {
|
||||
<li>
|
||||
@{
|
||||
var author = item.SyndicationItem.Authors.Any() ? String.Join(", ", item.SyndicationItem.Authors.Select(a => a.Name)) : T("Unknown").Text;
|
||||
var title = item.SyndicationItem.Title == null ? T("(No title)").Text : item.SyndicationItem.Title.Text;
|
||||
var thumbnail = item.SyndicationItem.Links.Where(l=>l.RelationshipType=="thumbnail").Select(l=>l.Uri.ToString()).FirstOrDefault();
|
||||
}
|
||||
<div>
|
||||
<h3>@title</h3>
|
||||
@if(!String.IsNullOrEmpty(thumbnail)) {
|
||||
@Html.Image(thumbnail, Html.Encode(title), null)
|
||||
}
|
||||
<br/>
|
||||
@Html.ActionLink(T("Install").ToString(), "Install", new RouteValueDictionary {{"SyndicationId",item.SyndicationItem.Id},{"cameFrom", "Themes" }}, new Dictionary<string, object> { { "class", "button" } })
|
||||
<a class="button" href="@item.PackageStreamUri">@T("Download")</a>
|
||||
<h5>@T("By") @author</h5>
|
||||
<p>
|
||||
@T("Version: {0}", "1.0")<br />
|
||||
@(item.SyndicationItem.Summary == null ? T("(No description").Text : item.SyndicationItem.Summary.Text)<br />
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
@@ -1,19 +1,30 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Web.Mvc;
|
||||
|
||||
namespace PackageIndexReferenceImplementation.Controllers {
|
||||
public class StreamResult : ActionResult {
|
||||
public string ContentType { get; set; }
|
||||
public Stream Stream { get; set; }
|
||||
public DateTime? LastModifiedDate { get; set; }
|
||||
|
||||
public StreamResult(string contentType, Stream stream) {
|
||||
public StreamResult(string contentType, Stream stream) : this(contentType, stream, null) {
|
||||
}
|
||||
|
||||
public StreamResult(string contentType, Stream stream, DateTime? lastModified) {
|
||||
ContentType = contentType;
|
||||
Stream = stream;
|
||||
LastModifiedDate = lastModified;
|
||||
}
|
||||
|
||||
public override void ExecuteResult(ControllerContext context) {
|
||||
context.HttpContext.Response.ContentType = ContentType;
|
||||
Stream.CopyTo(context.HttpContext.Response.OutputStream);
|
||||
var response = context.HttpContext.Response;
|
||||
response.ContentType = ContentType;
|
||||
if (LastModifiedDate.HasValue) {
|
||||
response.Cache.SetLastModified(LastModifiedDate.Value);
|
||||
response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
|
||||
}
|
||||
Stream.CopyTo(response.OutputStream);
|
||||
}
|
||||
}
|
||||
}
|
@@ -45,7 +45,7 @@ namespace PackageIndexReferenceImplementation.Controllers {
|
||||
var password = Encoding.UTF8.GetString(Convert.FromBase64String(HttpContext.Request.Headers["Password"]));
|
||||
|
||||
if ( !FormsAuthentication.Authenticate(user, password) ) {
|
||||
throw new AuthenticationException("This credentials are not valid fo this action.");
|
||||
throw new AuthenticationException("This credentials are not valid for this action.");
|
||||
}
|
||||
|
||||
var utcNowDateString = DateTimeOffset.UtcNow.ToString("yyyy-MM-dd");
|
||||
@@ -74,7 +74,8 @@ namespace PackageIndexReferenceImplementation.Controllers {
|
||||
}
|
||||
|
||||
private string UpdateSyndicationItem(PackageProperties packageProperties, SyndicationItem item) {
|
||||
if (!string.IsNullOrEmpty(packageProperties.Category)) {
|
||||
|
||||
if (!string.IsNullOrEmpty(packageProperties.Creator)) {
|
||||
item.Authors.Clear();
|
||||
//parse package.PackageProperties.Creator into email-style authors
|
||||
item.Authors.Add(new SyndicationPerson { Name = packageProperties.Creator });
|
||||
@@ -85,6 +86,11 @@ namespace PackageIndexReferenceImplementation.Controllers {
|
||||
item.Categories.Add(new SyndicationCategory(packageProperties.Category));
|
||||
}
|
||||
|
||||
var contentType = packageProperties.ContentType;
|
||||
if (!item.Categories.Any(c => c.Name == contentType)) {
|
||||
item.Categories.Add(new SyndicationCategory(contentType));
|
||||
}
|
||||
|
||||
if (packageProperties.Modified.HasValue) {
|
||||
item.LastUpdatedTime = new DateTimeOffset(packageProperties.Modified.Value);
|
||||
}
|
||||
@@ -97,15 +103,16 @@ namespace PackageIndexReferenceImplementation.Controllers {
|
||||
item.Summary = new TextSyndicationContent(packageProperties.Description);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(packageProperties.Title)) {
|
||||
item.Title = new TextSyndicationContent(packageProperties.Title);
|
||||
}
|
||||
|
||||
var mediaIdentifier = packageProperties.Identifier + "-" + packageProperties.Version + ".zip";
|
||||
|
||||
var mediaUrl = Url.Action("Resource", "Media", new RouteValueDictionary { { "Id", mediaIdentifier }, { "ContentType", "application/x-package" } });
|
||||
item.Links.Clear();
|
||||
item.Links.Add(new SyndicationLink(new Uri(HostBaseUri(), new Uri(mediaUrl, UriKind.Relative))));
|
||||
|
||||
if (contentType == "Orchard Theme") {
|
||||
var previewUrl = Url.Action("PreviewTheme", "Media", new RouteValueDictionary { { "Id", mediaIdentifier }, { "ContentType", "application/x-package" } });
|
||||
item.Links.Add(new SyndicationLink(new Uri(HostBaseUri(), new Uri(previewUrl, UriKind.Relative)), "thumbnail", null, null, 0));
|
||||
}
|
||||
return mediaIdentifier;
|
||||
}
|
||||
|
||||
|
@@ -1,23 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.IO.Packaging;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Hosting;
|
||||
using System.Web.Mvc;
|
||||
using PackageIndexReferenceImplementation.Services;
|
||||
|
||||
namespace PackageIndexReferenceImplementation.Controllers
|
||||
{
|
||||
public class MediaController : Controller
|
||||
{
|
||||
namespace PackageIndexReferenceImplementation.Controllers {
|
||||
public class MediaController : Controller {
|
||||
private readonly MediaStorage _mediaStorage;
|
||||
|
||||
public MediaController() {
|
||||
_mediaStorage = new MediaStorage();
|
||||
}
|
||||
|
||||
public ActionResult Resource(string id, string contentType)
|
||||
{
|
||||
public ActionResult Resource(string id, string contentType) {
|
||||
return new StreamResult(contentType, _mediaStorage.GetMedia(id + ":" + contentType));
|
||||
}
|
||||
|
||||
public ActionResult PreviewTheme(string id, string contentType) {
|
||||
var stream = _mediaStorage.GetMedia(id + ":" + contentType);
|
||||
var package = Package.Open(stream, FileMode.Open, FileAccess.Read);
|
||||
if (package.PackageProperties.ContentType != "Orchard Theme") {
|
||||
return new HttpNotFoundResult();
|
||||
}
|
||||
|
||||
var themeName = package.PackageProperties.Identifier;
|
||||
var previewUri = new Uri("/" + themeName + "/Theme.png", UriKind.Relative);
|
||||
Stream previewStream;
|
||||
DateTime lastModified;
|
||||
if (package.PartExists(previewUri)) {
|
||||
lastModified = _mediaStorage.GetLastModifiedDate(id);
|
||||
previewStream = package.GetPart(new Uri("/" + themeName + "/Theme.png", UriKind.Relative)).GetStream();
|
||||
}
|
||||
else {
|
||||
var defaultPreviewPath = HostingEnvironment.MapPath("~/Content/DefaultThemePreview.png");
|
||||
if (defaultPreviewPath == null || !System.IO.File.Exists(defaultPreviewPath)) {
|
||||
return new HttpNotFoundResult();
|
||||
}
|
||||
lastModified = System.IO.File.GetLastWriteTimeUtc(defaultPreviewPath);
|
||||
previewStream = System.IO.File.Open(defaultPreviewPath, FileMode.Open, FileAccess.Read);
|
||||
}
|
||||
return new StreamResult("image/png", previewStream, lastModified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,20 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageIndexReferenceImplementation", "PackageIndexReferenceImplementation.csproj", "{8A4E42CE-79F8-4BE2-8B1E-A6B83432123B}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8A4E42CE-79F8-4BE2-8B1E-A6B83432123B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8A4E42CE-79F8-4BE2-8B1E-A6B83432123B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8A4E42CE-79F8-4BE2-8B1E-A6B83432123B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8A4E42CE-79F8-4BE2-8B1E-A6B83432123B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@@ -1,4 +1,5 @@
|
||||
using System.IO;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web.Hosting;
|
||||
|
||||
@@ -25,6 +26,15 @@ namespace PackageIndexReferenceImplementation.Services {
|
||||
return new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
}
|
||||
|
||||
public DateTime GetLastModifiedDate(string identifier) {
|
||||
if (!Directory.Exists(HostingEnvironment.MapPath("~/App_Data/Media")))
|
||||
Directory.CreateDirectory(HostingEnvironment.MapPath("~/App_Data/Media"));
|
||||
|
||||
var safeIdentifier = GetSafeIdentifier(identifier);
|
||||
var filePath = HostingEnvironment.MapPath("~/App_Data/Media/" + safeIdentifier);
|
||||
return File.GetLastWriteTimeUtc(filePath);
|
||||
}
|
||||
|
||||
static string GetSafeIdentifier(string identifier) {
|
||||
var invalidFileNameChars = Path.GetInvalidFileNameChars().Concat(Path.GetInvalidPathChars()).Distinct();
|
||||
var safeIdentifier = identifier.Replace("^", string.Format("^{0:X2}", (int)'^'));
|
||||
|
Reference in New Issue
Block a user