using System; using System.IO; using System.Web.Hosting; using NuGet; using Orchard.Environment.Extensions; using Orchard.Environment.Extensions.Models; using Orchard.Localization; using Orchard.UI.Notify; using NuGetPackageManager = NuGet.PackageManager; 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; public PackageInstaller(INotifier notifier, IExtensionManager extensionManager) { _notifier = notifier; _extensionManager = extensionManager; T = NullLocalizer.Instance; } public Localizer T { get; set; } public PackageInfo Install(string packageId, string version, string location, string applicationPath) { // instantiates the appropriate package repository IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(new PackageSource(location, "Default")); // gets an IPackage instance from the repository var packageVersion = String.IsNullOrEmpty(version) ? null : new Version(version); var package = packageRepository.FindPackage(packageId, packageVersion); if (package == null) { throw new ArgumentException(T("The specified package could not be found, id:{0} version:{1}", packageId, String.IsNullOrEmpty(version) ? T("No version").Text : version).Text); } return Install(package, packageRepository, location, applicationPath); } public PackageInfo Install(IPackage package, string location, string applicationPath) { // instantiates the appropriate package repository IPackageRepository packageRepository = PackageRepositoryFactory.Default.CreateRepository(new PackageSource(location, "Default")); return Install(package, packageRepository, location, applicationPath); } protected PackageInfo Install(IPackage package, IPackageRepository packageRepository, string location, string applicationPath) { // this logger is used to render NuGet's log on the notifier var logger = new NugetLogger(_notifier); bool installed = false; // if we can access the parent directory, and the solution is inside, NuGet-install the package here 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}; 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 var sourceRepository = installed ? new LocalPackageRepository(installedPackagesPath) : packageRepository; var project = new FileBasedProjectSystem(applicationPath) { Logger = logger }; var projectManager = new ProjectManager( sourceRepository, // source repository for the package to install new DefaultPackagePathResolver(applicationPath), project, new ExtensionReferenceRepository(project, sourceRepository, _extensionManager) ) { Logger = logger }; // add the package to the project projectManager.AddPackageReference(package.Id, package.Version); return new PackageInfo { ExtensionName = package.Title ?? package.Id, ExtensionVersion = package.Version.ToString(), ExtensionType = package.Id.StartsWith(PackagingSourceManager.ThemesPrefix) ? DefaultExtensionTypes.Theme : DefaultExtensionTypes.Module, ExtensionPath = applicationPath }; } public void Uninstall(string packageId, string applicationPath) { bool removed = false; // this logger is used to render NuGet's log on the notifier var logger = new NugetLogger(_notifier); 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); try { 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); } catch { // Uninstalling the package at the solution level failed } try { var packageManager = new NuGetPackageManager( sourcePackageRepository, new DefaultPackagePathResolver(applicationPath), new PhysicalFileSystem(installedPackagesPath) {Logger = logger} ) {Logger = logger}; packageManager.UninstallPackage(packageId); removed = true; } catch { // Package doesnt exist anymore } } // If the package was not installed through nuget we still need to try to uninstall it by removing its directory // Remove theme if found string extensionFullPath = packageId.StartsWith(PackagingSourceManager.ThemesPrefix) ? HostingEnvironment.MapPath("~/Themes/" + packageId.Substring(PackagingSourceManager.ThemesPrefix.Length)) : HostingEnvironment.MapPath("~/Themes/" + packageId); if (Directory.Exists(extensionFullPath)) { Directory.Delete(extensionFullPath, true); removed = true; } // Remove module if found extensionFullPath = packageId.StartsWith(PackagingSourceManager.ModulesPrefix) ? HostingEnvironment.MapPath("~/Modules/" + packageId.Substring(PackagingSourceManager.ModulesPrefix.Length)) : HostingEnvironment.MapPath("~/Modules/" + packageId); if (Directory.Exists(extensionFullPath)) { Directory.Delete(extensionFullPath, true); removed = true; } if (!removed) { throw new OrchardException(T("Package not found: {0}", packageId)); } } private static 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; } } } }