Temp commit
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
Reference in New Issue
Block a user