#16841, #16495: Fixing theme installation.

--HG--
branch : dev
This commit is contained in:
Andre Rodrigues
2010-12-02 15:27:00 -08:00
parent 625ad33476
commit 6789e39370
7 changed files with 138 additions and 74 deletions

View File

@@ -24,7 +24,7 @@
<p>@module.Description</p>} <p>@module.Description</p>}
<ul class="pageStatus" style="color:#666; margin:.6em 0 0 0;"> <ul class="pageStatus" style="color:#666; margin:.6em 0 0 0;">
<li>@T("Features: {0}", MvcHtmlString.Create(string.Join(", ", module.Features.Select(f => Html.Link(string.IsNullOrEmpty(f.Name) ? f.Id : f.Name, string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), f.Id.AsFeatureId(n => T(n)))).ToString()).OrderBy(s => s).ToArray())))</li> <li>@T("Features: {0}", MvcHtmlString.Create(string.Join(", ", module.Features.Select(f => Html.Link(string.IsNullOrEmpty(f.Name) ? f.Id : f.Name, string.Format("{0}#{1}", Url.Action("features", new { area = "Orchard.Modules" }), f.Id.AsFeatureId(n => T(n)))).ToString()).OrderBy(s => s).ToArray())))</li>
<li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(module.Author) ? module.Author : "Unknown")</li> <li>&nbsp;&#124;&nbsp;@T("Author: {0}", !string.IsNullOrEmpty(module.Author) ? module.Author : T("Unknown").ToString())</li>
<li>&nbsp;&#124;&nbsp;@T("Website: {0}", !string.IsNullOrEmpty(module.WebSite) ? module.WebSite : "http://orchardproject.net")</li> <li>&nbsp;&#124;&nbsp;@T("Website: {0}", !string.IsNullOrEmpty(module.WebSite) ? module.WebSite : "http://orchardproject.net")</li>
</ul> </ul>
</div> </div>

View File

@@ -6,6 +6,7 @@ using System.Web.Hosting;
using System.Web.Mvc; using System.Web.Mvc;
using System.Xml.Linq; using System.Xml.Linq;
using NuGet; using NuGet;
using Orchard.Environment;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.FileSystems.AppData; using Orchard.FileSystems.AppData;
using Orchard.Localization; using Orchard.Localization;
@@ -25,16 +26,19 @@ namespace Orchard.Packaging.Controllers {
private readonly IPackagingSourceManager _packagingSourceManager; private readonly IPackagingSourceManager _packagingSourceManager;
private readonly IAppDataFolderRoot _appDataFolderRoot; private readonly IAppDataFolderRoot _appDataFolderRoot;
private readonly INotifier _notifier; private readonly INotifier _notifier;
private readonly IHostEnvironment _hostEnvironment;
public GalleryController( public GalleryController(
IPackageManager packageManager, IPackageManager packageManager,
IPackagingSourceManager packagingSourceManager, IPackagingSourceManager packagingSourceManager,
INotifier notifier, INotifier notifier,
IAppDataFolderRoot appDataFolderRoot) { IAppDataFolderRoot appDataFolderRoot,
IHostEnvironment hostEnvironment) {
_packageManager = packageManager; _packageManager = packageManager;
_packagingSourceManager = packagingSourceManager; _packagingSourceManager = packagingSourceManager;
_notifier = notifier; _notifier = notifier;
_appDataFolderRoot = appDataFolderRoot; _appDataFolderRoot = appDataFolderRoot;
_hostEnvironment = hostEnvironment;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -60,12 +64,11 @@ namespace Orchard.Packaging.Controllers {
[HttpPost] [HttpPost]
public ActionResult AddSource(string url) { public ActionResult AddSource(string url) {
try { try {
if ( !String.IsNullOrEmpty(url) ) { if (!String.IsNullOrEmpty(url)) {
if (!url.StartsWith("http")) { if (!url.StartsWith("http")) {
ModelState.AddModelError("Url", T("The Url is not valid").Text); ModelState.AddModelError("Url", T("The Url is not valid").Text);
} }
} } else if (String.IsNullOrWhiteSpace(url)) {
else if ( String.IsNullOrWhiteSpace(url)) {
ModelState.AddModelError("Url", T("Url is required").Text); ModelState.AddModelError("Url", T("Url is required").Text);
} }
@@ -73,29 +76,27 @@ namespace Orchard.Packaging.Controllers {
// try to load the feed // try to load the feed
try { try {
XNamespace atomns = "http://www.w3.org/2005/Atom" ; XNamespace atomns = "http://www.w3.org/2005/Atom";
var feed = XDocument.Load(url, LoadOptions.PreserveWhitespace); var feed = XDocument.Load(url, LoadOptions.PreserveWhitespace);
var titleNode = feed.Descendants(atomns + "title").FirstOrDefault(); var titleNode = feed.Descendants(atomns + "title").FirstOrDefault();
if ( titleNode != null ) if (titleNode != null)
title = titleNode.Value; title = titleNode.Value;
if(String.IsNullOrWhiteSpace(title)) { if (String.IsNullOrWhiteSpace(title)) {
ModelState.AddModelError("Url", T("The feed has no title.").Text); ModelState.AddModelError("Url", T("The feed has no title.").Text);
} }
} } catch {
catch {
ModelState.AddModelError("Url", T("The url of the feed or its content is not valid.").Text); ModelState.AddModelError("Url", T("The url of the feed or its content is not valid.").Text);
} }
if ( !ModelState.IsValid ) if (!ModelState.IsValid)
return View(new PackagingAddSourceViewModel { Url = url }); return View(new PackagingAddSourceViewModel { Url = url });
_packagingSourceManager.AddSource(title, url); _packagingSourceManager.AddSource(title, url);
_notifier.Information(T("The feed has been added successfully.")); _notifier.Information(T("The feed has been added successfully."));
return RedirectToAction("Sources"); return RedirectToAction("Sources");
} } catch (Exception exception) {
catch ( Exception exception ) {
_notifier.Error(T("Adding feed failed: {0}", exception.Message)); _notifier.Error(T("Adding feed failed: {0}", exception.Message));
return View(new PackagingAddSourceViewModel { Url = url }); return View(new PackagingAddSourceViewModel { Url = url });
} }
@@ -104,8 +105,8 @@ namespace Orchard.Packaging.Controllers {
public ActionResult Modules(int? sourceId) { public ActionResult Modules(int? sourceId) {
var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault(); var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
var sources = selectedSource != null var sources = selectedSource != null
? new [] { selectedSource } ? new[] { selectedSource }
: _packagingSourceManager.GetSources() : _packagingSourceManager.GetSources()
; ;
@@ -134,7 +135,7 @@ namespace Orchard.Packaging.Controllers {
public ActionResult Install(string packageId, string version, int sourceId, string redirectTo) { public ActionResult Install(string packageId, string version, int sourceId, string redirectTo) {
var source = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault(); var source = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault();
if(source == null) { if (source == null) {
return HttpNotFound(); return HttpNotFound();
} }
@@ -147,15 +148,30 @@ namespace Orchard.Packaging.Controllers {
return View(); return View();
} }
[HttpPost, ActionName("AddTheme")]
public ActionResult AddThemePOST(string returnUrl) {
return InstallPackage(returnUrl, Request.RawUrl);
}
[HttpPost, ActionName("RemoveTheme")]
public ActionResult RemoveThemePOST(string themeId, string returnUrl, string retryUrl) {
return UninstallPackage(PackagingSourceManager.ThemesFilter + themeId, returnUrl, retryUrl);
}
public ActionResult AddModule(string returnUrl) { public ActionResult AddModule(string returnUrl) {
return View(); return View();
} }
[HttpPost, ActionName("AddModule")] [HttpPost, ActionName("AddModule")]
public ActionResult AddModulePOST(string returnUrl) { public ActionResult AddModulePOST(string returnUrl) {
// module not used for anything o2ther than display (and that only to not have object in the view 'T') return InstallPackage(returnUrl, Request.RawUrl);
}
public ActionResult InstallPackage(string returnUrl, string retryUrl) {
try { try {
if (string.IsNullOrWhiteSpace(Request.Files[0].FileName)) { if (Request.Files != null &&
Request.Files.Count > 0 &&
!string.IsNullOrWhiteSpace(Request.Files[0].FileName)) {
ModelState.AddModelError("File", T("Select a file to upload.").ToString()); ModelState.AddModelError("File", T("Select a file to upload.").ToString());
} }
@@ -172,16 +188,29 @@ namespace Orchard.Packaging.Controllers {
} }
} }
if (!string.IsNullOrEmpty(returnUrl)) return Redirect(returnUrl);
return Redirect(returnUrl);
return RedirectToAction("Modules");
} catch (Exception exception) { } catch (Exception exception) {
for (var scan = exception; scan != null; scan = scan.InnerException) { for (Exception scan = exception; scan != null; scan = scan.InnerException) {
_notifier.Error(T("Uploading module package failed: {0}", exception.Message)); _notifier.Error(T("Uploading module package failed: {0}", exception.Message));
} }
return View("AddModule"); return Redirect(retryUrl);
}
}
public ActionResult UninstallPackage(string id, string returnUrl, string retryUrl) {
try {
_packageManager.Uninstall(id, HostingEnvironment.MapPath("~/"));
_notifier.Information(T("Uninstalled package \"{0}\"", id));
return Redirect(returnUrl);
} catch (Exception exception) {
for (Exception scan = exception; scan != null; scan = scan.InnerException) {
_notifier.Error(T("Uninstall failed: {0}", exception.Message));
}
return Redirect(retryUrl);
} }
} }
} }

View File

@@ -1,9 +1,9 @@
using System; using System;
using System.IO; using System.IO;
using System.Web.Hosting;
using NuGet; using NuGet;
using Orchard.Environment.Extensions; using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models; using Orchard.Environment.Extensions.Models;
using Orchard.FileSystems.AppData;
using Orchard.Localization; using Orchard.Localization;
using Orchard.UI.Notify; using Orchard.UI.Notify;
using NuGetPackageManager = NuGet.PackageManager; using NuGetPackageManager = NuGet.PackageManager;
@@ -12,17 +12,15 @@ namespace Orchard.Packaging.Services {
[OrchardFeature("PackagingServices")] [OrchardFeature("PackagingServices")]
public class PackageInstaller : IPackageInstaller { public class PackageInstaller : IPackageInstaller {
private const string PackagesPath = "packages"; private const string PackagesPath = "packages";
private const string SolutionFilename = "Orchard.sln";
private readonly INotifier _notifier; private readonly INotifier _notifier;
private readonly IExtensionManager _extensionManager; private readonly IExtensionManager _extensionManager;
private readonly IAppDataFolderRoot _appDataFolderRoot;
public PackageInstaller(INotifier notifier, public PackageInstaller(INotifier notifier,
IExtensionManager extensionManager, IExtensionManager extensionManager) {
IAppDataFolderRoot appDataFolderRoot) {
_notifier = notifier; _notifier = notifier;
_extensionManager = extensionManager; _extensionManager = extensionManager;
_appDataFolderRoot = appDataFolderRoot;
T = NullLocalizer.Instance; T = NullLocalizer.Instance;
} }
@@ -57,22 +55,23 @@ namespace Orchard.Packaging.Services {
bool installed = false; bool installed = false;
// if we can access the parent directory, and the solution is inside, NuGet-install the package here // if we can access the parent directory, and the solution is inside, NuGet-install the package here
var installedPackagesPath = Path.Combine(_appDataFolderRoot.RootFolder, PackagesPath); string solutionPath;
var installedPackagesPath = String.Empty;
try if (TryGetSolutionPath(applicationPath, out solutionPath)) {
{ installedPackagesPath = Path.Combine(solutionPath, PackagesPath);
var packageManager = new NuGetPackageManager( try {
var packageManager = new NuGetPackageManager(
packageRepository, packageRepository,
new DefaultPackagePathResolver(location), new DefaultPackagePathResolver(location),
new PhysicalFileSystem(installedPackagesPath) { Logger = logger } new PhysicalFileSystem(installedPackagesPath) {Logger = logger}
) { Logger = logger }; ) {Logger = logger};
packageManager.InstallPackage(package, ignoreDependencies: true); packageManager.InstallPackage(package, true);
installed = true; installed = true;
} }
catch catch {
{ // installing the package at the solution level failed
// installing the package in the appdata folder failed }
} }
// if the package got installed successfully, use it, otherwise use the previous repository // if the package got installed successfully, use it, otherwise use the previous repository
@@ -95,7 +94,7 @@ namespace Orchard.Packaging.Services {
{ {
ExtensionName = package.Title ?? package.Id, ExtensionName = package.Title ?? package.Id,
ExtensionVersion = package.Version.ToString(), ExtensionVersion = package.Version.ToString(),
ExtensionType = package.Id.StartsWith("Orchard.Theme") ? DefaultExtensionTypes.Theme : DefaultExtensionTypes.Module, ExtensionType = package.Id.StartsWith(PackagingSourceManager.ThemesFilter) ? DefaultExtensionTypes.Theme : DefaultExtensionTypes.Module,
ExtensionPath = applicationPath ExtensionPath = applicationPath
}; };
} }
@@ -104,26 +103,58 @@ namespace Orchard.Packaging.Services {
// this logger is used to render NuGet's log on the notifier // this logger is used to render NuGet's log on the notifier
var logger = new NugetLogger(_notifier); var logger = new NugetLogger(_notifier);
var installedPackagesPath = Path.Combine(_appDataFolderRoot.RootFolder, PackagesPath); string solutionPath;
var sourcePackageRepository = new LocalPackageRepository(installedPackagesPath); // if we can access the parent directory, and the solution is inside, NuGet-uninstall the package here
var project = new FileBasedProjectSystem(applicationPath) { Logger = logger }; if (TryGetSolutionPath(applicationPath, out solutionPath)) {
var projectManager = new ProjectManager( var installedPackagesPath = Path.Combine(solutionPath, PackagesPath);
sourcePackageRepository, var sourcePackageRepository = new LocalPackageRepository(installedPackagesPath);
new DefaultPackagePathResolver(installedPackagesPath), var project = new FileBasedProjectSystem(applicationPath) { Logger = logger };
project, var projectManager = new ProjectManager(
new ExtensionReferenceRepository(project, sourcePackageRepository, _extensionManager) sourcePackageRepository,
) { Logger = logger }; new DefaultPackagePathResolver(installedPackagesPath),
project,
new ExtensionReferenceRepository(project, sourcePackageRepository, _extensionManager)
) { Logger = logger };
// add the package to the project // add the package to the project
projectManager.RemovePackageReference(packageId); projectManager.RemovePackageReference(packageId);
var packageManager = new NuGetPackageManager( var packageManager = new NuGetPackageManager(
sourcePackageRepository, sourcePackageRepository,
new DefaultPackagePathResolver(applicationPath), new DefaultPackagePathResolver(applicationPath),
new PhysicalFileSystem(installedPackagesPath) { Logger = logger } new PhysicalFileSystem(installedPackagesPath) { Logger = logger }
) { Logger = logger }; ) { Logger = logger };
packageManager.UninstallPackage(packageId); packageManager.UninstallPackage(packageId);
} else {
// otherwise delete the folder
string extensionPath = packageId.StartsWith(PackagingSourceManager.ThemesFilter)
? "~/Themes/" + packageId.Substring(PackagingSourceManager.ThemesFilter.Length)
: "~/Modules/" + packageId.Substring(PackagingSourceManager.ModulesFilter.Length);
string extensionFullPath = HostingEnvironment.MapPath(extensionPath);
if (Directory.Exists(extensionFullPath)) {
Directory.Delete(extensionFullPath, true);
}
else {
throw new OrchardException(T("Package not found: ", packageId));
}
}
}
private bool TryGetSolutionPath(string applicationPath, out string parentPath) {
try {
parentPath = Directory.GetParent(applicationPath).Parent.FullName;
var solutionPath = Path.Combine(parentPath, SolutionFilename);
return File.Exists(solutionPath);
}
catch {
// Either solution does not exist or we are running under medium trust
parentPath = null;
return false;
}
} }
} }
} }

View File

@@ -46,6 +46,7 @@ namespace Orchard.Packaging.Services {
public void Uninstall(string packageId, string applicationPath) { public void Uninstall(string packageId, string applicationPath) {
_packageExpander.Uninstall(packageId, applicationPath); _packageExpander.Uninstall(packageId, applicationPath);
} }
#endregion #endregion
} }
} }

View File

@@ -10,8 +10,8 @@ using Orchard.Packaging.Models;
namespace Orchard.Packaging.Services { namespace Orchard.Packaging.Services {
[OrchardFeature("Gallery")] [OrchardFeature("Gallery")]
public class PackagingSourceManager : IPackagingSourceManager { public class PackagingSourceManager : IPackagingSourceManager {
private const string ModulesFilter = "Orchard.Module."; public const string ModulesFilter = "Orchard.Module.";
private const string ThemesFilter = "Orchard.Theme."; public const string ThemesFilter = "Orchard.Theme.";
private readonly IRepository<PackagingSource> _packagingSourceRecordRepository; private readonly IRepository<PackagingSource> _packagingSourceRecordRepository;

View File

@@ -1,10 +1,11 @@
<h1>@Html.TitleForPage(T("Install Theme").ToString())</h1> @{
@using (Html.BeginForm("Install", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" })) { <h1>@Html.TitleForPage(T("Install a Theme").ToString())</h1>
@Html.ValidationSummary() using (Html.BeginFormAntiForgeryPost(Url.Action("AddTheme", new { area = "Orchard.Gallery" }), FormMethod.Post, new { enctype = "multipart/form-data" })) {
<fieldset> Html.ValidationSummary();
<label for="ThemeZipPath">@T("File Path to the zip file:")</label> <fieldset>
<input id="ThemeZipPath" name="ThemeZipPath" type="file" class="text" value="@T("Browse")" size="64" /><br /> <label for="ModulePackage">@T("Theme Package")</label>
<button class="primaryAction" type="submit">@T("Install")</button> <input type="file" id="ThemePackage" size="64" name="ThemePackage" />
@Html.AntiForgeryTokenOrchard() </fieldset>
</fieldset> <button type="submit" class="primaryAction">@T("Install")</button>
} }
}

View File

@@ -19,7 +19,7 @@
</p> </p>
if (Model.InstallThemes) { if (Model.InstallThemes) {
@Html.ActionLink(T("Install a new Theme").ToString(), "Install", null, new { @class = "button primaryAction" }) @Html.ActionLink(T("Install a new Theme").ToString(), "AddTheme", "Gallery", new { area = "Orchard.Packaging", returnUrl = HttpContext.Current.Request.RawUrl }, new { @class = "button primaryAction" })
} }
} }
@@ -55,11 +55,13 @@
<button type="submit" class="update">@T("Update")</button> <br/> <button type="submit" class="update">@T("Update")</button> <br/>
} }
} }
@using (Html.BeginFormAntiForgeryPost(Url.Action("Uninstall"), FormMethod.Post, new { @class = "inline link" })) {
@Html.Hidden("themeName", theme.Id) @if (Model.InstallThemes) {
<button type="submit" class="uninstall" title="@T("Uninstall")">@T("Uninstall")</button> using (Html.BeginFormAntiForgeryPost(Url.Action("RemoveTheme", "Gallery", new { area = "Orchard.Packaging", returnUrl = HttpContext.Current.Request.RawUrl, retryUrl = HttpContext.Current.Request.RawUrl, themeId = theme.Id }), FormMethod.Post, new { @class = "inline link" })) {
<button type="submit" class="uninstall" title="@T("Uninstall")">@T("Uninstall")</button>
}
} }
</div> </div>
</li> </li>
} }
} }