Skip to content

Commit ebfbb13

Browse files
Add UmbracoDeploy.Contrib.Export project
1 parent 32dd21e commit ebfbb13

11 files changed

+658
-2
lines changed

src/Umbraco.Deploy.Contrib.sln

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,34 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio 15
4-
VisualStudioVersion = 15.0.27004.2010
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.8.34330.188
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Deploy.Contrib.Connectors", "Umbraco.Deploy.Contrib.Connectors\Umbraco.Deploy.Contrib.Connectors.csproj", "{42FB2213-0815-41CD-94F8-DFB0EC1D8061}"
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Umbraco.Deploy.Contrib.Tests", "Umbraco.Deploy.Contrib.Tests\Umbraco.Deploy.Contrib.Tests.csproj", "{48B09241-9C44-47EF-B4DF-397B39A631DC}"
99
EndProject
1010
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7AF3C28D-DBD0-4771-9A78-D6E982094DCC}"
1111
ProjectSection(SolutionItems) = preProject
12+
..\.editorconfig = ..\.editorconfig
13+
..\.gitattributes = ..\.gitattributes
14+
..\.gitignore = ..\.gitignore
1215
..\CONTRIBUTING.md = ..\CONTRIBUTING.md
1316
..\LICENSE.md = ..\LICENSE.md
1417
..\README.md = ..\README.md
1518
EndProjectSection
1619
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UmbracoDeploy.Contrib.Export", "UmbracoDeploy.Contrib.Export\UmbracoDeploy.Contrib.Export.csproj", "{60AF13D9-3576-4E8C-8111-090D28DFBF60}"
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{2BD8E58D-6094-4D89-B045-54E6966DF784}"
23+
ProjectSection(SolutionItems) = preProject
24+
..\build\version.txt = ..\build\version.txt
25+
EndProjectSection
26+
EndProject
27+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuSpecs", "NuSpecs", "{5B965111-230E-4B9A-A780-1A49D59D4FC2}"
28+
ProjectSection(SolutionItems) = preProject
29+
..\build\NuSpecs\UmbracoDeploy.Contrib.nuspec = ..\build\NuSpecs\UmbracoDeploy.Contrib.nuspec
30+
EndProjectSection
31+
EndProject
1732
Global
1833
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1934
Debug|Any CPU = Debug|Any CPU
@@ -28,10 +43,17 @@ Global
2843
{48B09241-9C44-47EF-B4DF-397B39A631DC}.Debug|Any CPU.Build.0 = Debug|Any CPU
2944
{48B09241-9C44-47EF-B4DF-397B39A631DC}.Release|Any CPU.ActiveCfg = Release|Any CPU
3045
{48B09241-9C44-47EF-B4DF-397B39A631DC}.Release|Any CPU.Build.0 = Release|Any CPU
46+
{60AF13D9-3576-4E8C-8111-090D28DFBF60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{60AF13D9-3576-4E8C-8111-090D28DFBF60}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{60AF13D9-3576-4E8C-8111-090D28DFBF60}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{60AF13D9-3576-4E8C-8111-090D28DFBF60}.Release|Any CPU.Build.0 = Release|Any CPU
3150
EndGlobalSection
3251
GlobalSection(SolutionProperties) = preSolution
3352
HideSolutionNode = FALSE
3453
EndGlobalSection
54+
GlobalSection(NestedProjects) = preSolution
55+
{5B965111-230E-4B9A-A780-1A49D59D4FC2} = {2BD8E58D-6094-4D89-B045-54E6966DF784}
56+
EndGlobalSection
3557
GlobalSection(ExtensibilityGlobals) = postSolution
3658
SolutionGuid = {20357E31-FA18-4D77-B38B-6C878A179849}
3759
EndGlobalSection
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System.Reflection;
2+
using Umbraco.Core;
3+
using Umbraco.Core.Scoping;
4+
5+
namespace UmbracoDeploy.Contrib.Export
6+
{
7+
internal static class ApplicationContextExtensions
8+
{
9+
public static IScopeProvider GetScopeProvider(this ApplicationContext applicationContext)
10+
=> applicationContext.GetType().GetProperty("ScopeProvider", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(applicationContext) as IScopeProvider;
11+
}
12+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using ICSharpCode.SharpZipLib.Zip;
5+
using Umbraco.Core;
6+
using Umbraco.Core.Deploy;
7+
using Umbraco.Deploy;
8+
using Umbraco.Deploy.Files;
9+
10+
namespace UmbracoDeploy.Contrib.Export
11+
{
12+
public static class ArtifactExportService
13+
{
14+
public static void ExportArtifacts(IEnumerable<Udi> udis, string zipArchiveFilePath, params string[] dependencyEntityTypes)
15+
{
16+
var artifacts = DeployComponent.ServiceConnectorFactory.GetArtifacts(udis, dependencyEntityTypes);
17+
18+
ExportArtifacts(artifacts, zipArchiveFilePath);
19+
}
20+
21+
public static void ExportArtifacts(IEnumerable<Udi> udis, string selector, string zipArchiveFilePath, params string[] dependencyEntityTypes)
22+
{
23+
var artifacts = DeployComponent.ServiceConnectorFactory.GetArtifacts(udis, selector, dependencyEntityTypes);
24+
25+
ExportArtifacts(artifacts, zipArchiveFilePath);
26+
}
27+
28+
public static void ExportArtifacts(IEnumerable<IArtifact> artifacts, string zipArchiveFilePath)
29+
{
30+
using (DeployComponent.DiskEntityService.SuppressFileTypeCollection(out FileTypeCollection fileTypes))
31+
using (var scope = ApplicationContext.Current.GetScopeProvider().CreateScope())
32+
{
33+
// Export to temp directory
34+
var exportPath = Path.Combine(Path.GetTempPath(), "Deploy", "export-" + Guid.NewGuid());
35+
DeployComponent.DiskEntityService.WriteArtifacts(exportPath, artifacts);
36+
DeployComponent.DiskEntityService.WriteFiles(exportPath, artifacts, fileTypes);
37+
38+
// Create export archive and delete temp directory
39+
var zipArchive = new FastZip();
40+
zipArchive.CreateZip(zipArchiveFilePath, exportPath, true, null);
41+
Directory.Delete(exportPath, true);
42+
43+
scope.Complete();
44+
}
45+
}
46+
}
47+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using Umbraco.Core.Configuration;
5+
using Umbraco.Core.Deploy;
6+
using Umbraco.Core.Models;
7+
using Umbraco.Deploy.Configuration;
8+
9+
namespace UmbracoDeploy.Contrib.Export
10+
{
11+
public sealed class DefaultValueConnector : IValueConnector
12+
{
13+
public IEnumerable<string> PropertyEditorAliases { get; } = new[] { "*" };
14+
15+
public string GetValue(Property property, ICollection<ArtifactDependency> dependencies)
16+
=> property.Value is IConvertible convertibleValue
17+
? convertibleValue.ToString(CultureInfo.InvariantCulture)
18+
: property.Value?.ToString();
19+
20+
public void SetValue(IContentBase content, string alias, string value)
21+
=> content.SetValue(alias, value);
22+
23+
public static void SetDefault()
24+
=> UmbracoConfig.For.DeploySettings().ValueConnectors.Add(new DefaultValueConnectorSetting());
25+
26+
private sealed class DefaultValueConnectorSetting : IValueConnectorSetting
27+
{
28+
public string Alias => "*";
29+
30+
public string TypeName => typeof(DefaultValueConnector).AssemblyQualifiedName;
31+
}
32+
}
33+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
5+
using System.Reflection;
6+
using Umbraco.Core;
7+
using Umbraco.Core.Deploy;
8+
using Umbraco.Deploy;
9+
using Umbraco.Deploy.Disk;
10+
using Umbraco.Deploy.Files;
11+
12+
namespace UmbracoDeploy.Contrib.Export
13+
{
14+
internal static partial class DiskEntityServiceExtensions
15+
{
16+
public static IDisposable SuppressFileTypeCollection(this IDiskEntityService diskEntityService, out FileTypeCollection fileTypes)
17+
{
18+
var field = diskEntityService.GetType().GetField("_fileTypes", BindingFlags.Instance | BindingFlags.NonPublic);
19+
var oldFileTypes = field.GetValue(diskEntityService) as FileTypeCollection;
20+
21+
// Replace with empty file type collection (so all artifacts are written as UDA files)
22+
field.SetValue(diskEntityService, new FileTypeCollection(Enumerable.Empty<IFileType>()));
23+
24+
fileTypes = oldFileTypes;
25+
26+
// Restore on dispose
27+
return new InvokeOnDispose(() => field.SetValue(diskEntityService, oldFileTypes));
28+
}
29+
30+
public static void WriteArtifacts(this IDiskEntityService diskEntityService, string path, IEnumerable<IArtifact> artifacts)
31+
{
32+
var field = diskEntityService.GetType().GetField("_afs", BindingFlags.Instance | BindingFlags.NonPublic);
33+
var oldArtifactFileSystem = field.GetValue(diskEntityService);
34+
35+
// Replace artifact file system to use custom path to write artifacts to
36+
field.SetValue(diskEntityService, new ArtifactFileSystem(path));
37+
38+
try
39+
{
40+
// Ensure directory exists
41+
Directory.CreateDirectory(path);
42+
43+
diskEntityService.WriteArtifacts(artifacts);
44+
}
45+
finally
46+
{
47+
// Restore after artifacts are written
48+
field.SetValue(diskEntityService, oldArtifactFileSystem);
49+
}
50+
}
51+
52+
public static void WriteFiles(this IDiskEntityService diskEntityService, string path, IEnumerable<IArtifact> artifacts, FileTypeCollection fileTypes)
53+
{
54+
foreach (var artifactByType in artifacts.GroupBy(x => x.Udi.EntityType))
55+
{
56+
if (!fileTypes.Contains(artifactByType.Key))
57+
{
58+
continue;
59+
}
60+
61+
var fileType = fileTypes[artifactByType.Key];
62+
foreach (var udi in artifactByType.Select(x => x.Udi as StringUdi).WhereNotNull())
63+
{
64+
// Ensure directory exists (since the path might not exist yet)
65+
var filePath = Path.Combine(path, artifactByType.Key, udi.Id);
66+
if (Path.GetDirectoryName(filePath) is string directoryPath)
67+
{
68+
Directory.CreateDirectory(directoryPath);
69+
}
70+
71+
// Write file
72+
using (Stream stream = fileType.GetStream(udi))
73+
using (Stream destination = System.IO.File.OpenWrite(filePath))
74+
{
75+
stream.CopyTo(destination);
76+
}
77+
}
78+
}
79+
}
80+
81+
private sealed class InvokeOnDispose : IDisposable
82+
{
83+
private readonly Action action;
84+
85+
public InvokeOnDispose(Action action) => this.action = action;
86+
87+
public void Dispose() => action.Invoke();
88+
}
89+
}
90+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Reflection;
2+
using System.Runtime.InteropServices;
3+
4+
[assembly: AssemblyTitle("Umbraco.Deploy.Export")]
5+
[assembly: AssemblyDescription("")]
6+
[assembly: AssemblyConfiguration("")]
7+
[assembly: AssemblyCompany("")]
8+
[assembly: AssemblyProduct("Umbraco.Deploy.Export")]
9+
[assembly: AssemblyCopyright("Copyright © Umbraco 2024")]
10+
[assembly: AssemblyTrademark("")]
11+
[assembly: AssemblyCulture("")]
12+
13+
[assembly: ComVisible(false)]
14+
[assembly: Guid("b3805713-68b3-401b-ae65-1ff6ab524b86")]
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by a tool.
4+
// Runtime Version:4.0.30319.42000
5+
//
6+
// Changes to this file may cause incorrect behavior and will be lost if
7+
// the code is regenerated.
8+
// </auto-generated>
9+
//------------------------------------------------------------------------------
10+
11+
using System;
12+
using System.Reflection;
13+
using System.Resources;
14+
using System.Runtime.CompilerServices;
15+
using System.Runtime.InteropServices;
16+
17+
[assembly: AssemblyVersion("2.0.0")]
18+
[assembly: AssemblyInformationalVersion("2.0.0")]
19+
20+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
using Umbraco.Core;
3+
using Umbraco.Core.Deploy;
4+
5+
namespace UmbracoDeploy.Contrib.Export
6+
{
7+
public static class ServiceConnectorExtensions
8+
{
9+
public static IEnumerable<IArtifact> GetArtifacts(this IServiceConnector serviceConnector, NamedUdiRange namedUdiRange)
10+
{
11+
var udis = new List<Udi>();
12+
serviceConnector.Explode(namedUdiRange, udis);
13+
14+
foreach (var udi in udis)
15+
{
16+
if (serviceConnector.GetArtifact(udi) is IArtifact artifact)
17+
{
18+
yield return artifact;
19+
}
20+
}
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)