using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Linq;
using Umbraco.Core.Collections;
using Umbraco.Core.Events;
using Umbraco.Core.Exceptions;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
using Umbraco.Core.Strings;
using Content = Umbraco.Core.Models.Content;
namespace Umbraco.Core.Services.Implement
{
///
/// Represents the Packaging Service, which provides import/export functionality for the Core models of the API
/// using xml representation. This is primarily used by the Package functionality.
///
public class PackagingService : IPackagingService
{
private readonly IPackageInstallation _packageInstallation;
private readonly IAuditService _auditService;
private readonly ICreatedPackagesRepository _createdPackages;
private readonly IInstalledPackagesRepository _installedPackages;
private static HttpClient _httpClient;
public PackagingService(
IAuditService auditService,
ICreatedPackagesRepository createdPackages,
IInstalledPackagesRepository installedPackages,
IPackageInstallation packageInstallation)
{
_auditService = auditService;
_createdPackages = createdPackages;
_installedPackages = installedPackages;
_packageInstallation = packageInstallation;
}
#region Package Files
///
public async Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId)
{
//includeHidden = true because we don't care if it's hidden we want to get the file regardless
var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true";
byte[] bytes;
try
{
if (_httpClient == null)
{
_httpClient = new HttpClient();
}
bytes = await _httpClient.GetByteArrayAsync(url);
}
catch (HttpRequestException ex)
{
throw new ConnectionException("An error occuring downloading the package from " + url, ex);
}
//successfull
if (bytes.Length > 0)
{
var packagePath = IOHelper.MapPath(SystemDirectories.Packages);
// Check for package directory
if (Directory.Exists(packagePath) == false)
Directory.CreateDirectory(packagePath);
var packageFilePath = Path.Combine(packagePath, packageId + ".umb");
using (var fs1 = new FileStream(packageFilePath, FileMode.Create))
{
fs1.Write(bytes, 0, bytes.Length);
return packageId + ".umb";
}
}
_auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}");
return null;
}
#endregion
#region Installation
public CompiledPackage GetCompiledPackageInfo(string packageFileName) => _packageInstallation.ReadPackage(packageFileName);
public IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, string packageFileName, int userId = 0)
{
if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition));
if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted");
if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information");
var compiledPackage = GetCompiledPackageInfo(packageFileName);
if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName);
var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId);
SaveInstalledPackage(packageDefinition);
_auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'.");
return files;
}
public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, string packageFileName, int userId = 0)
{
if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition));
if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted");
if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information");
var compiledPackage = GetCompiledPackageInfo(packageFileName);
if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFileName);
if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFileName, compiledPackage), this))
return new InstallationSummary { MetaData = compiledPackage };
var summary = _packageInstallation.InstallPackageData(packageDefinition, compiledPackage, userId);
SaveInstalledPackage(packageDefinition);
_auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package data installed for package '{compiledPackage.Name}'.");
ImportedPackage.RaiseEvent(new ImportPackageEventArgs(summary, compiledPackage, false), this);
return summary;
}
#endregion
#region Created/Installed Package Repositories
public void DeleteCreatedPackage(int id, int userId = 0)
{
var package = GetCreatedPackageById(id);
if (package == null) return;
_auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Created package '{package.Name}' deleted. Package id: {package.Id}");
_createdPackages.Delete(id);
}
public IEnumerable GetAllCreatedPackages() => _createdPackages.GetAll();
public PackageDefinition GetCreatedPackageById(int id) => _createdPackages.GetById(id);
public bool SaveCreatedPackage(PackageDefinition definition) => _createdPackages.SavePackage(definition);
public string ExportCreatedPackage(PackageDefinition definition) => _createdPackages.ExportPackage(definition);
public IEnumerable GetAllInstalledPackages() => _installedPackages.GetAll();
public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id);
public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition);
public void DeleteInstalledPackage(int packageId, int userId = 0)
{
var package = GetInstalledPackageById(packageId);
if (package == null) return;
_auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Installed package '{package.Name}' deleted. Package id: {package.Id}");
_installedPackages.Delete(packageId);
}
#endregion
///
/// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service.
///
///
internal static void OnUninstalledPackage(UninstallPackageEventArgs args)
{
UninstalledPackage.RaiseEvent(args, null);
}
#region Event Handlers
///
/// Occurs before Importing umbraco package
///
internal static event TypedEventHandler> ImportingPackage;
///
/// Occurs after a package is imported
///
public static event TypedEventHandler> ImportedPackage;
///
/// Occurs after a package is uninstalled
///
public static event TypedEventHandler> UninstalledPackage;
#endregion
}
}