Temp commit

This commit is contained in:
Bjarke Berg
2021-08-09 06:28:26 +02:00
parent d4be031b78
commit 2a3ac1961d
4 changed files with 170 additions and 79 deletions

View File

@@ -12,17 +12,52 @@ namespace Umbraco.Cms.Core.Packaging
{ {
public static class PackageMigrationResource public static class PackageMigrationResource
{ {
private static Stream GetEmbeddedPackageStream(Type planType) private static Stream GetEmbeddedPackageZipStream(Type planType)
{ {
// lookup the embedded resource by convention // lookup the embedded resource by convention
Assembly currentAssembly = planType.Assembly; Assembly currentAssembly = planType.Assembly;
var fileName = $"{planType.Namespace}.package.zip"; var fileName = $"{planType.Namespace}.package.zip";
Stream stream = currentAssembly.GetManifestResourceStream(fileName); Stream stream = currentAssembly.GetManifestResourceStream(fileName);
return stream;
}
public static XDocument GetEmbeddedPackageDataManifest(Type planType, out ZipArchive zipArchive)
{
XDocument packageXml;
var zipStream = GetEmbeddedPackageZipStream(planType);
if (zipStream is not null)
{
zipArchive = GetPackageDataManifest(zipStream, out packageXml);
return packageXml;
}
zipArchive = null;
packageXml = GetEmbeddedPackageXmlDoc(planType);
return packageXml;
}
public static XDocument GetEmbeddedPackageDataManifest(Type planType)
{
return GetEmbeddedPackageDataManifest(planType, out _);
}
private static XDocument GetEmbeddedPackageXmlDoc(Type planType)
{
// lookup the embedded resource by convention
Assembly currentAssembly = planType.Assembly;
var fileName = $"{planType.Namespace}.package.xml";
Stream stream = currentAssembly.GetManifestResourceStream(fileName);
if (stream == null) if (stream == null)
{ {
throw new FileNotFoundException("Cannot find the embedded file.", fileName); return null;
} }
return stream; XDocument xml;
using (stream)
{
xml = XDocument.Load(stream);
}
return xml;
} }
public static string GetEmbeddedPackageDataManifestHash(Type planType) public static string GetEmbeddedPackageDataManifestHash(Type planType)
@@ -30,17 +65,46 @@ namespace Umbraco.Cms.Core.Packaging
// SEE: HashFromStreams in the benchmarks project for how fast this is. It will run // SEE: HashFromStreams in the benchmarks project for how fast this is. It will run
// on every startup for every embedded package.zip. The bigger the zip, the more time it takes. // on every startup for every embedded package.zip. The bigger the zip, the more time it takes.
// But it is still very fast ~303ms for a 100MB file. This will only be an issue if there are // But it is still very fast ~303ms for a 100MB file. This will only be an issue if there are
// several very large package.zips. // several very large package.zips.
using Stream stream = GetEmbeddedPackageStream(planType); using Stream stream = GetEmbeddedPackageZipStream(planType);
return stream.GetStreamHash();
if (stream is not null)
{
return stream.GetStreamHash();
}
var xml = GetEmbeddedPackageXmlDoc(planType);
if (xml is not null)
{
return stream.GetStreamHash();
}
throw new IOException("Missing embedded files for planType: " + planType);
} }
public static ZipArchive GetEmbeddedPackageDataManifest(Type planType, out XDocument packageXml) public static bool TryGetEmbeddedPackageDataManifest(Type planType, out XDocument packageXml, out ZipArchive zipArchive)
=> GetPackageDataManifest(GetEmbeddedPackageStream(planType), out packageXml); {
var zipStream = GetEmbeddedPackageZipStream(planType);
if (zipStream is not null)
{
zipArchive = GetPackageDataManifest(zipStream, out packageXml);
return true;
}
zipArchive = null;
packageXml = GetEmbeddedPackageXmlDoc(planType);
return packageXml is not null;
}
public static ZipArchive GetPackageDataManifest(Stream packageZipStream, out XDocument packageXml) public static ZipArchive GetPackageDataManifest(Stream packageZipStream, out XDocument packageXml)
{ {
if (packageZipStream == null)
{
throw new ArgumentNullException(nameof(packageZipStream));
}
var zip = new ZipArchive(packageZipStream, ZipArchiveMode.Read); var zip = new ZipArchive(packageZipStream, ZipArchiveMode.Read);
ZipArchiveEntry packageXmlEntry = zip.GetEntry("package.xml"); ZipArchiveEntry packageXmlEntry = zip.GetEntry("package.xml");
if (packageXmlEntry == null) if (packageXmlEntry == null)

View File

@@ -209,29 +209,46 @@ namespace Umbraco.Cms.Core.Packaging
PackageDataTypes(definition, root); PackageDataTypes(definition, root);
Dictionary<string, Stream> mediaFiles = PackageMedia(definition, root); Dictionary<string, Stream> mediaFiles = PackageMedia(definition, root);
var tempPackagePath = temporaryPath + "/package.zip"; string fileName;
string tempPackagePath;
using (FileStream fileStream = File.OpenWrite(tempPackagePath)) if (mediaFiles.Count > 0)
using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
{ {
ZipArchiveEntry packageXmlEntry = archive.CreateEntry("package.xml"); fileName = "package.zip";
using (Stream entryStream = packageXmlEntry.Open()) tempPackagePath = Path.Combine(temporaryPath, fileName);
using (FileStream fileStream = File.OpenWrite(tempPackagePath))
using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
{ {
compiledPackageXml.Save(entryStream); ZipArchiveEntry packageXmlEntry = archive.CreateEntry("package.xml");
} using (Stream entryStream = packageXmlEntry.Open())
foreach (KeyValuePair<string, Stream> mediaFile in mediaFiles)
{
var entryPath = $"media{mediaFile.Key.EnsureStartsWith('/')}";
ZipArchiveEntry mediaEntry = archive.CreateEntry(entryPath);
using (Stream entryStream = mediaEntry.Open())
using (mediaFile.Value)
{ {
mediaFile.Value.Seek(0, SeekOrigin.Begin); compiledPackageXml.Save(entryStream);
mediaFile.Value.CopyTo(entryStream); }
foreach (KeyValuePair<string, Stream> mediaFile in mediaFiles)
{
var entryPath = $"media{mediaFile.Key.EnsureStartsWith('/')}";
ZipArchiveEntry mediaEntry = archive.CreateEntry(entryPath);
using (Stream entryStream = mediaEntry.Open())
using (mediaFile.Value)
{
mediaFile.Value.Seek(0, SeekOrigin.Begin);
mediaFile.Value.CopyTo(entryStream);
}
} }
} }
} }
else
{
fileName = "package.xml";
tempPackagePath = Path.Combine(temporaryPath, fileName);
using (FileStream fileStream = File.OpenWrite(tempPackagePath))
{
compiledPackageXml.Save(fileStream);
}
}
var directoryName = var directoryName =
_hostingEnvironment.MapPathWebRoot(Path.Combine(_mediaFolderPath, definition.Name.Replace(' ', '_'))); _hostingEnvironment.MapPathWebRoot(Path.Combine(_mediaFolderPath, definition.Name.Replace(' ', '_')));
@@ -241,7 +258,7 @@ namespace Umbraco.Cms.Core.Packaging
Directory.CreateDirectory(directoryName); Directory.CreateDirectory(directoryName);
} }
var finalPackagePath = Path.Combine(directoryName, "package.zip"); var finalPackagePath = Path.Combine(directoryName, fileName);
if (File.Exists(finalPackagePath)) if (File.Exists(finalPackagePath))
{ {
@@ -347,7 +364,7 @@ namespace Umbraco.Cms.Core.Packaging
} }
else if (items.ContainsKey(dictionaryItem.ParentId.Value)) else if (items.ContainsKey(dictionaryItem.ParentId.Value))
{ {
// we know the parent exists in the dictionary but // we know the parent exists in the dictionary but
// we haven't processed it yet so we'll leave it for the next loop // we haven't processed it yet so we'll leave it for the next loop
continue; continue;
} }

View File

@@ -19,12 +19,12 @@ namespace Umbraco.Cms.Infrastructure.Packaging
{ {
internal class ImportPackageBuilderExpression : MigrationExpressionBase internal class ImportPackageBuilderExpression : MigrationExpressionBase
{ {
private readonly IPackagingService _packagingService;
private readonly IMediaService _mediaService;
private readonly MediaFileManager _mediaFileManager;
private readonly MediaUrlGeneratorCollection _mediaUrlGenerators;
private readonly IShortStringHelper _shortStringHelper;
private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider; private readonly IContentTypeBaseServiceProvider _contentTypeBaseServiceProvider;
private readonly MediaFileManager _mediaFileManager;
private readonly IMediaService _mediaService;
private readonly MediaUrlGeneratorCollection _mediaUrlGenerators;
private readonly IPackagingService _packagingService;
private readonly IShortStringHelper _shortStringHelper;
private bool _executed; private bool _executed;
public ImportPackageBuilderExpression( public ImportPackageBuilderExpression(
@@ -45,7 +45,7 @@ namespace Umbraco.Cms.Infrastructure.Packaging
} }
/// <summary> /// <summary>
/// The type of the migration which dictates the namespace of the embedded resource /// The type of the migration which dictates the namespace of the embedded resource
/// </summary> /// </summary>
public Type EmbeddedResourceMigrationType { get; set; } public Type EmbeddedResourceMigrationType { get; set; }
@@ -63,68 +63,78 @@ namespace Umbraco.Cms.Infrastructure.Packaging
if (EmbeddedResourceMigrationType == null && PackageDataManifest == null) if (EmbeddedResourceMigrationType == null && PackageDataManifest == null)
{ {
throw new InvalidOperationException($"Nothing to execute, neither {nameof(EmbeddedResourceMigrationType)} or {nameof(PackageDataManifest)} has been set."); throw new InvalidOperationException(
$"Nothing to execute, neither {nameof(EmbeddedResourceMigrationType)} or {nameof(PackageDataManifest)} has been set.");
} }
InstallationSummary installationSummary; InstallationSummary installationSummary;
if (EmbeddedResourceMigrationType != null) if (EmbeddedResourceMigrationType != null)
{ {
// get the embedded resource if (PackageMigrationResource.TryGetEmbeddedPackageDataManifest(
using (ZipArchive zipPackage = PackageMigrationResource.GetEmbeddedPackageDataManifest(
EmbeddedResourceMigrationType, EmbeddedResourceMigrationType,
out XDocument xml)) out XDocument xml, out ZipArchive zipPackage))
{ {
// first install the package // first install the package
installationSummary = _packagingService.InstallCompiledPackageData(xml); installationSummary = _packagingService.InstallCompiledPackageData(xml);
// then we need to save each file to the saved media items if (zipPackage is not null)
var mediaWithFiles = xml.XPathSelectElements(
"./umbPackage/MediaItems/MediaSet//*[@id][@mediaFilePath]")
.ToDictionary(
x => x.AttributeValue<Guid>("key"),
x => x.AttributeValue<string>("mediaFilePath"));
// Any existing media by GUID will not be installed by the package service, it will just be skipped
// so you cannot 'update' media (or content) using a package since those are not schema type items.
// This means you cannot 'update' the media file either. The installationSummary.MediaInstalled
// will be empty for any existing media which means that the files will also not be updated.
foreach (IMedia media in installationSummary.MediaInstalled)
{ {
if (mediaWithFiles.TryGetValue(media.Key, out var mediaFilePath)) // get the embedded resource
using (zipPackage)
{ {
// this is a media item that has a file, so find that file in the zip // then we need to save each file to the saved media items
var entryPath = $"media{mediaFilePath.EnsureStartsWith('/')}"; var mediaWithFiles = xml.XPathSelectElements(
ZipArchiveEntry mediaEntry = zipPackage.GetEntry(entryPath); "./umbPackage/MediaItems/MediaSet//*[@id][@mediaFilePath]")
if (mediaEntry == null) .ToDictionary(
{ x => x.AttributeValue<Guid>("key"),
throw new InvalidOperationException("No media file found in package zip for path " + entryPath); x => x.AttributeValue<string>("mediaFilePath"));
}
// read the media file and save it to the media item // Any existing media by GUID will not be installed by the package service, it will just be skipped
// using the current file system provider. // so you cannot 'update' media (or content) using a package since those are not schema type items.
using (Stream mediaStream = mediaEntry.Open()) // This means you cannot 'update' the media file either. The installationSummary.MediaInstalled
// will be empty for any existing media which means that the files will also not be updated.
foreach (IMedia media in installationSummary.MediaInstalled)
{ {
media.SetValue( if (mediaWithFiles.TryGetValue(media.Key, out var mediaFilePath))
_mediaFileManager, {
_mediaUrlGenerators, // this is a media item that has a file, so find that file in the zip
_shortStringHelper, var entryPath = $"media{mediaFilePath.EnsureStartsWith('/')}";
_contentTypeBaseServiceProvider, ZipArchiveEntry mediaEntry = zipPackage.GetEntry(entryPath);
Constants.Conventions.Media.File, if (mediaEntry == null)
Path.GetFileName(mediaFilePath), {
mediaStream); throw new InvalidOperationException(
} "No media file found in package zip for path " +
entryPath);
}
_mediaService.Save(media); // read the media file and save it to the media item
// using the current file system provider.
using (Stream mediaStream = mediaEntry.Open())
{
media.SetValue(
_mediaFileManager,
_mediaUrlGenerators,
_shortStringHelper,
_contentTypeBaseServiceProvider,
Constants.Conventions.Media.File,
Path.GetFileName(mediaFilePath),
mediaStream);
}
_mediaService.Save(media);
}
}
} }
} }
} }
} else
else {
{ installationSummary = _packagingService.InstallCompiledPackageData(PackageDataManifest);
installationSummary = _packagingService.InstallCompiledPackageData(PackageDataManifest); }
}
Logger.LogInformation($"Package migration executed. Summary: {installationSummary}"); Logger.LogInformation($"Package migration executed. Summary: {installationSummary}");
}
throw new InvalidOperationException($"The package zip or xml found.");
} }
} }
} }

View File

@@ -36,7 +36,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging
[TearDown] [TearDown]
public void DeleteTestFolder() => public void DeleteTestFolder() =>
Directory.Delete(HostingEnvironment.MapPathContentRoot("~/" + _testBaseFolder), true); Directory.Delete(HostingEnvironment.MapPathContentRoot("~/" + _testBaseFolder), true);
private IContentService ContentService => GetRequiredService<IContentService>(); private IContentService ContentService => GetRequiredService<IContentService>();
private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>(); private IContentTypeService ContentTypeService => GetRequiredService<IContentTypeService>();
@@ -164,7 +164,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging
{ {
var parent = new DictionaryItem("Parent") var parent = new DictionaryItem("Parent")
{ {
Key = Guid.NewGuid() Key = Guid.NewGuid()
}; };
LocalizationService.Save(parent); LocalizationService.Save(parent);
var child1 = new DictionaryItem(parent.Key, "Child1") var child1 = new DictionaryItem(parent.Key, "Child1")
@@ -204,7 +204,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Core.Packaging
}; };
PackageBuilder.SavePackage(def); PackageBuilder.SavePackage(def);
string packageXmlPath = PackageBuilder.ExportPackage(def); string packageXmlPath = PackageBuilder.ExportPackage(def);
using (var packageZipStream = File.OpenRead(packageXmlPath)) using (var packageZipStream = File.OpenRead(packageXmlPath))