diff --git a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.cshtml index 789c389e8..54645d17f 100644 --- a/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Modules/Views/Admin/Index.cshtml @@ -24,7 +24,7 @@

@module.Description

} diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Controllers/GalleryController.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Controllers/GalleryController.cs index 3bf4341b2..1e4607f00 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Controllers/GalleryController.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Controllers/GalleryController.cs @@ -6,6 +6,7 @@ using System.Web.Hosting; using System.Web.Mvc; using System.Xml.Linq; using NuGet; +using Orchard.Environment; using Orchard.Environment.Extensions; using Orchard.FileSystems.AppData; using Orchard.Localization; @@ -25,16 +26,19 @@ namespace Orchard.Packaging.Controllers { private readonly IPackagingSourceManager _packagingSourceManager; private readonly IAppDataFolderRoot _appDataFolderRoot; private readonly INotifier _notifier; + private readonly IHostEnvironment _hostEnvironment; public GalleryController( IPackageManager packageManager, IPackagingSourceManager packagingSourceManager, INotifier notifier, - IAppDataFolderRoot appDataFolderRoot) { + IAppDataFolderRoot appDataFolderRoot, + IHostEnvironment hostEnvironment) { _packageManager = packageManager; _packagingSourceManager = packagingSourceManager; _notifier = notifier; _appDataFolderRoot = appDataFolderRoot; + _hostEnvironment = hostEnvironment; T = NullLocalizer.Instance; } @@ -60,12 +64,11 @@ namespace Orchard.Packaging.Controllers { [HttpPost] public ActionResult AddSource(string url) { try { - if ( !String.IsNullOrEmpty(url) ) { + if (!String.IsNullOrEmpty(url)) { if (!url.StartsWith("http")) { 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); } @@ -73,29 +76,27 @@ namespace Orchard.Packaging.Controllers { // try to load the feed 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 titleNode = feed.Descendants(atomns + "title").FirstOrDefault(); - if ( titleNode != null ) + if (titleNode != null) title = titleNode.Value; - if(String.IsNullOrWhiteSpace(title)) { + if (String.IsNullOrWhiteSpace(title)) { 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); } - if ( !ModelState.IsValid ) + if (!ModelState.IsValid) return View(new PackagingAddSourceViewModel { Url = url }); _packagingSourceManager.AddSource(title, url); _notifier.Information(T("The feed has been added successfully.")); return RedirectToAction("Sources"); - } - catch ( Exception exception ) { + } catch (Exception exception) { _notifier.Error(T("Adding feed failed: {0}", exception.Message)); return View(new PackagingAddSourceViewModel { Url = url }); } @@ -104,8 +105,8 @@ namespace Orchard.Packaging.Controllers { public ActionResult Modules(int? sourceId) { var selectedSource = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault(); - var sources = selectedSource != null - ? new [] { selectedSource } + var sources = selectedSource != null + ? new[] { selectedSource } : _packagingSourceManager.GetSources() ; @@ -134,7 +135,7 @@ namespace Orchard.Packaging.Controllers { public ActionResult Install(string packageId, string version, int sourceId, string redirectTo) { var source = _packagingSourceManager.GetSources().Where(s => s.Id == sourceId).FirstOrDefault(); - if(source == null) { + if (source == null) { return HttpNotFound(); } @@ -147,15 +148,30 @@ namespace Orchard.Packaging.Controllers { 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) { return View(); } [HttpPost, ActionName("AddModule")] 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 { - 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()); } @@ -172,16 +188,29 @@ namespace Orchard.Packaging.Controllers { } } - if (!string.IsNullOrEmpty(returnUrl)) - return Redirect(returnUrl); - - return RedirectToAction("Modules"); + return Redirect(returnUrl); } 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)); } - 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); } } } diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageInstaller.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageInstaller.cs index 22c2f0497..a390ca9a7 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageInstaller.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageInstaller.cs @@ -1,9 +1,9 @@ using System; using System.IO; +using System.Web.Hosting; using NuGet; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; -using Orchard.FileSystems.AppData; using Orchard.Localization; using Orchard.UI.Notify; using NuGetPackageManager = NuGet.PackageManager; @@ -12,17 +12,15 @@ namespace Orchard.Packaging.Services { [OrchardFeature("PackagingServices")] public class PackageInstaller : IPackageInstaller { private const string PackagesPath = "packages"; + private const string SolutionFilename = "Orchard.sln"; private readonly INotifier _notifier; private readonly IExtensionManager _extensionManager; - private readonly IAppDataFolderRoot _appDataFolderRoot; public PackageInstaller(INotifier notifier, - IExtensionManager extensionManager, - IAppDataFolderRoot appDataFolderRoot) { + IExtensionManager extensionManager) { _notifier = notifier; _extensionManager = extensionManager; - _appDataFolderRoot = appDataFolderRoot; T = NullLocalizer.Instance; } @@ -57,22 +55,23 @@ namespace Orchard.Packaging.Services { bool installed = false; // if we can access the parent directory, and the solution is inside, NuGet-install the package here - var installedPackagesPath = Path.Combine(_appDataFolderRoot.RootFolder, PackagesPath); - - try - { - var packageManager = new NuGetPackageManager( + string solutionPath; + var installedPackagesPath = String.Empty; + if (TryGetSolutionPath(applicationPath, out solutionPath)) { + installedPackagesPath = Path.Combine(solutionPath, PackagesPath); + try { + var packageManager = new NuGetPackageManager( packageRepository, new DefaultPackagePathResolver(location), - new PhysicalFileSystem(installedPackagesPath) { Logger = logger } - ) { Logger = logger }; + new PhysicalFileSystem(installedPackagesPath) {Logger = logger} + ) {Logger = logger}; - packageManager.InstallPackage(package, ignoreDependencies: true); - installed = true; - } - catch - { - // installing the package in the appdata folder failed + packageManager.InstallPackage(package, true); + installed = true; + } + catch { + // installing the package at the solution level failed + } } // 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, 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 }; } @@ -104,26 +103,58 @@ namespace Orchard.Packaging.Services { // this logger is used to render NuGet's log on the notifier var logger = new NugetLogger(_notifier); - var installedPackagesPath = Path.Combine(_appDataFolderRoot.RootFolder, PackagesPath); - var sourcePackageRepository = new LocalPackageRepository(installedPackagesPath); - var project = new FileBasedProjectSystem(applicationPath) { Logger = logger }; - var projectManager = new ProjectManager( - sourcePackageRepository, - new DefaultPackagePathResolver(installedPackagesPath), - project, - new ExtensionReferenceRepository(project, sourcePackageRepository, _extensionManager) - ) { Logger = logger }; + string solutionPath; + // if we can access the parent directory, and the solution is inside, NuGet-uninstall the package here + if (TryGetSolutionPath(applicationPath, out solutionPath)) { + var installedPackagesPath = Path.Combine(solutionPath, PackagesPath); + var sourcePackageRepository = new LocalPackageRepository(installedPackagesPath); + var project = new FileBasedProjectSystem(applicationPath) { Logger = logger }; + var projectManager = new ProjectManager( + sourcePackageRepository, + new DefaultPackagePathResolver(installedPackagesPath), + project, + new ExtensionReferenceRepository(project, sourcePackageRepository, _extensionManager) + ) { Logger = logger }; - // add the package to the project - projectManager.RemovePackageReference(packageId); + // add the package to the project + projectManager.RemovePackageReference(packageId); - var packageManager = new NuGetPackageManager( + var packageManager = new NuGetPackageManager( sourcePackageRepository, new DefaultPackagePathResolver(applicationPath), 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; + } } } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageManager.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageManager.cs index 9cd833854..92eda4470 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackageManager.cs @@ -46,6 +46,7 @@ namespace Orchard.Packaging.Services { public void Uninstall(string packageId, string applicationPath) { _packageExpander.Uninstall(packageId, applicationPath); } + #endregion } } \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs index a8f3aa7d9..4780a9535 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Services/PackagingSourceManager.cs @@ -10,8 +10,8 @@ using Orchard.Packaging.Models; namespace Orchard.Packaging.Services { [OrchardFeature("Gallery")] public class PackagingSourceManager : IPackagingSourceManager { - private const string ModulesFilter = "Orchard.Module."; - private const string ThemesFilter = "Orchard.Theme."; + public const string ModulesFilter = "Orchard.Module."; + public const string ThemesFilter = "Orchard.Theme."; private readonly IRepository _packagingSourceRecordRepository; diff --git a/src/Orchard.Web/Modules/Orchard.Packaging/Views/Gallery/AddTheme.cshtml b/src/Orchard.Web/Modules/Orchard.Packaging/Views/Gallery/AddTheme.cshtml index d83b0a2ce..01bff8201 100644 --- a/src/Orchard.Web/Modules/Orchard.Packaging/Views/Gallery/AddTheme.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Packaging/Views/Gallery/AddTheme.cshtml @@ -1,10 +1,11 @@ -

@Html.TitleForPage(T("Install Theme").ToString())

-@using (Html.BeginForm("Install", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" })) { - @Html.ValidationSummary() -
- -
- - @Html.AntiForgeryTokenOrchard() -
-} +@{ +

@Html.TitleForPage(T("Install a Theme").ToString())

+ using (Html.BeginFormAntiForgeryPost(Url.Action("AddTheme", new { area = "Orchard.Gallery" }), FormMethod.Post, new { enctype = "multipart/form-data" })) { + Html.ValidationSummary(); +
+ + +
+ + } +} \ No newline at end of file diff --git a/src/Orchard.Web/Modules/Orchard.Themes/Views/Admin/Index.cshtml b/src/Orchard.Web/Modules/Orchard.Themes/Views/Admin/Index.cshtml index bfd026f26..e84d5c3e9 100644 --- a/src/Orchard.Web/Modules/Orchard.Themes/Views/Admin/Index.cshtml +++ b/src/Orchard.Web/Modules/Orchard.Themes/Views/Admin/Index.cshtml @@ -19,7 +19,7 @@

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 @@
} } - @using (Html.BeginFormAntiForgeryPost(Url.Action("Uninstall"), FormMethod.Post, new { @class = "inline link" })) { - @Html.Hidden("themeName", theme.Id) - + + @if (Model.InstallThemes) { + 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" })) { + + } } - + } }