Merge branch temp8 into feature/IContentType-removale-from-IContent

This commit is contained in:
Stephan
2019-01-17 13:33:55 +01:00
264 changed files with 6879 additions and 10999 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ _Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md).
When is Umbraco 8 coming?
=========================
When it's ready. We're done with the major parts of the architecture work and are focusing on three seperate tracks to prepare Umbraco 8 for release:
When it's ready. We're done with the major parts of the architecture work and are focusing on three separate tracks to prepare Umbraco 8 for release:
1) Editor Track (_currently in progress_). Without editors, there's no market for Umbraco. So we want to make sure that Umbraco 8 is full of love for editors.
2) Partner Track. Without anyone implementing Umbraco, there's nothing for editors to update. So we want to make sure that Umbraco 8 is a joy to implement
3) Contributor Track. Without our fabulous ecosystem of both individual Umbracians and 3rd party ISVs, Umbraco wouldn't be as rich a platform as it is today. We want to make sure that it's easy, straight forward and as backwards-compatible as possible to create packages for Umbraco
+5 -3
View File
@@ -76,11 +76,13 @@ namespace Umbraco.Core.Components
var composerTypeList = _composerTypes
.Where(x =>
{
// use the min level specified by the attribute if any
// otherwise, user composers have Run min level, anything else is Unknown (always run)
// use the min/max levels specified by the attribute if any
// otherwise, min: user composers are Run, anything else is Unknown (always run)
// max: everything is Run (always run)
var attr = x.GetCustomAttribute<RuntimeLevelAttribute>();
var minLevel = attr?.MinLevel ?? (x.Implements<IUserComposer>() ? RuntimeLevel.Run : RuntimeLevel.Unknown);
return _composition.RuntimeState.Level >= minLevel;
var maxLevel = attr?.MaxLevel ?? RuntimeLevel.Run;
return _composition.RuntimeState.Level >= minLevel && _composition.RuntimeState.Level <= maxLevel;
})
.ToList();
@@ -1,13 +1,22 @@
using System;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Components
{
/// <summary>
/// Marks a composer to indicate a minimum and/or maximum runtime level for which the composer would compose.
/// </summary>
[AttributeUsage(AttributeTargets.Class /*, AllowMultiple = false, Inherited = true*/)]
public class RuntimeLevelAttribute : Attribute
{
//public RuntimeLevelAttribute()
//{ }
/// <summary>
/// Gets or sets the minimum runtime level for which the composer would compose.
/// </summary>
public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Install;
public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Boot;
/// <summary>
/// Gets or sets the maximum runtime level for which the composer would compose.
/// </summary>
public RuntimeLevel MaxLevel { get; set; } = RuntimeLevel.Run;
}
}
@@ -6,6 +6,7 @@ using Umbraco.Core.Components;
using Umbraco.Core.Events;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
@@ -56,6 +57,22 @@ namespace Umbraco.Core.Composing.Composers
factory.GetInstance<Lazy<LocalizedTextServiceFileSources>>(),
factory.GetInstance<ILogger>()));
composition.RegisterUnique<IEntityXmlSerializer, EntityXmlSerializer>();
composition.RegisterUnique<IPackageActionRunner, PackageActionRunner>();
composition.RegisterUnique<ConflictingPackageData>();
composition.RegisterUnique<CompiledPackageXmlParser>();
composition.RegisterUnique<ICreatedPackagesRepository>(factory => CreatePackageRepository(factory, "createdPackages.config"));
composition.RegisterUnique<IInstalledPackagesRepository>(factory => CreatePackageRepository(factory, "installedPackages.config"));
composition.RegisterUnique<PackageDataInstallation>();
composition.RegisterUnique<PackageFileInstallation>();
composition.RegisterUnique<IPackageInstallation>(factory => //factory required because we need to pass in a string path
new PackageInstallation(
factory.GetInstance<PackageDataInstallation>(), factory.GetInstance<PackageFileInstallation>(),
factory.GetInstance<CompiledPackageXmlParser>(), factory.GetInstance<IPackageActionRunner>(),
new DirectoryInfo(IOHelper.GetRootDirectorySafe())));
//TODO: These are replaced in the web project - we need to declare them so that
// something is wired up, just not sure this is very nice but will work for now.
composition.RegisterUnique<IApplicationTreeService, EmptyApplicationTreeService>();
@@ -64,6 +81,17 @@ namespace Umbraco.Core.Composing.Composers
return composition;
}
/// <summary>
/// Creates an instance of PackagesRepository for either the ICreatedPackagesRepository or the IInstalledPackagesRepository
/// </summary>
/// <param name="factory"></param>
/// <param name="packageRepoFileName"></param>
/// <returns></returns>
private static PackagesRepository CreatePackageRepository(IFactory factory, string packageRepoFileName)
=> new PackagesRepository(
factory.GetInstance<IContentService>(), factory.GetInstance<IContentTypeService>(), factory.GetInstance<IDataTypeService>(), factory.GetInstance<IFileService>(), factory.GetInstance<IMacroService>(), factory.GetInstance<ILocalizationService>(), factory.GetInstance<IEntityXmlSerializer>(), factory.GetInstance<ILogger>(),
packageRepoFileName);
private static LocalizedTextServiceFileSources SourcesFactory(IFactory container)
{
var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/"));
+4
View File
@@ -5,6 +5,7 @@ using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Scoping;
@@ -161,6 +162,9 @@ namespace Umbraco.Core.Composing
internal static PackageActionCollection PackageActions
=> Factory.GetInstance<PackageActionCollection>();
internal static IPackageActionRunner PackageActionRunner
=> Factory.GetInstance<IPackageActionRunner>();
internal static PropertyValueConverterCollection PropertyValueConverters
=> Factory.GetInstance<PropertyValueConverterCollection>();
+1 -1
View File
@@ -405,7 +405,7 @@ namespace Umbraco.Core.Composing
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/TypesCache");
var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache");
_fileBasePath = Path.Combine(tempFolder, "umbraco-types." + NetworkHelper.FileSafeMachineName);
break;
}
-55
View File
@@ -1,55 +0,0 @@
namespace Umbraco.Core
{
public static partial class Constants
{
/// <summary>
/// Defines the constants used for Umbraco packages in the package.config xml
/// </summary>
public static class Packaging
{
public const string UmbPackageNodeName = "umbPackage";
public const string DataTypesNodeName = "DataTypes";
public const string PackageXmlFileName = "package.xml";
public const string UmbracoPackageExtention = ".umb";
public const string DataTypeNodeName = "DataType";
public const string LanguagesNodeName = "Languages";
public const string FilesNodeName = "files";
public const string StylesheetsNodeName = "Stylesheets";
public const string TemplatesNodeName = "Templates";
public const string NameNodeName = "Name";
public const string TemplateNodeName = "Template";
public const string AliasNodeNameSmall = "alias";
public const string AliasNodeNameCapital = "Alias";
public const string DictionaryItemsNodeName = "DictionaryItems";
public const string DictionaryItemNodeName = "DictionaryItem";
public const string MacrosNodeName = "Macros";
public const string DocumentsNodeName = "Documents";
public const string DocumentSetNodeName = "DocumentSet";
public const string DocumentTypesNodeName = "DocumentTypes";
public const string DocumentTypeNodeName = "DocumentType";
public const string FileNodeName = "file";
public const string OrgNameNodeName = "orgName";
public const string OrgPathNodeName = "orgPath";
public const string GuidNodeName = "guid";
public const string StylesheetNodeName = "styleSheet";
public const string MacroNodeName = "macro";
public const string InfoNodeName = "info";
public const string PackageRequirementsMajorXpath = "./package/requirements/major";
public const string PackageRequirementsMinorXpath = "./package/requirements/minor";
public const string PackageRequirementsPatchXpath = "./package/requirements/patch";
public const string PackageNameXpath = "./package/name";
public const string PackageVersionXpath = "./package/version";
public const string PackageUrlXpath = "./package/url";
public const string PackageLicenseXpath = "./package/license";
public const string PackageLicenseXpathUrlAttribute = "url";
public const string AuthorNameXpath = "./author/name";
public const string AuthorWebsiteXpath = "./author/website";
public const string ReadmeXpath = "./readme";
public const string ControlNodeName = "control";
public const string ActionNodeName = "Action";
public const string ActionsNodeName = "Actions";
public const string UndoNodeAttribute = "undo";
public const string RunatNodeAttribute = "runat";
}
}
}
+13 -52
View File
@@ -300,84 +300,45 @@ namespace Umbraco.Core
/// Creates the full xml representation for the <see cref="IContent"/> object and all of it's descendants
/// </summary>
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <param name="serializer"></param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
internal static XElement ToDeepXml(this IContent content, IPackagingService packagingService)
internal static XElement ToDeepXml(this IContent content, IEntityXmlSerializer serializer)
{
return packagingService.Export(content, true, raiseEvents: false);
}
[Obsolete("Use the overload that declares the IPackagingService to use")]
public static XElement ToXml(this IContent content)
{
return Current.Services.PackagingService.Export(content, raiseEvents: false);
return serializer.Serialize(content, false, true);
}
/// <summary>
/// Creates the xml representation for the <see cref="IContent"/> object
/// </summary>
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <param name="serializer"></param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
public static XElement ToXml(this IContent content, IPackagingService packagingService)
public static XElement ToXml(this IContent content, IEntityXmlSerializer serializer)
{
return packagingService.Export(content, raiseEvents: false);
}
[Obsolete("Use the overload that declares the IPackagingService to use")]
public static XElement ToXml(this IMedia media)
{
return Current.Services.PackagingService.Export(media, raiseEvents: false);
return serializer.Serialize(content, false, false);
}
/// <summary>
/// Creates the xml representation for the <see cref="IMedia"/> object
/// </summary>
/// <param name="media"><see cref="IContent"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <param name="serializer"></param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
public static XElement ToXml(this IMedia media, IPackagingService packagingService)
public static XElement ToXml(this IMedia media, IEntityXmlSerializer serializer)
{
return packagingService.Export(media, raiseEvents: false);
return serializer.Serialize(media);
}
/// <summary>
/// Creates the full xml representation for the <see cref="IMedia"/> object and all of it's descendants
/// </summary>
/// <param name="media"><see cref="IMedia"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <returns>Xml representation of the passed in <see cref="IMedia"/></returns>
internal static XElement ToDeepXml(this IMedia media, IPackagingService packagingService)
{
return packagingService.Export(media, true, raiseEvents: false);
}
/// <summary>
/// Creates the xml representation for the <see cref="IContent"/> object
/// </summary>
/// <param name="content"><see cref="IContent"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <param name="isPreview">Boolean indicating whether the xml should be generated for preview</param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
public static XElement ToXml(this IContent content, IPackagingService packagingService, bool isPreview)
{
//TODO Do a proper implementation of this
//If current IContent is published we should get latest unpublished version
return content.ToXml(packagingService);
}
/// <summary>
/// Creates the xml representation for the <see cref="IMember"/> object
/// </summary>
/// <param name="member"><see cref="IMember"/> to generate xml for</param>
/// <param name="packagingService"></param>
/// <param name="serializer"></param>
/// <returns>Xml representation of the passed in <see cref="IContent"/></returns>
public static XElement ToXml(this IMember member, IPackagingService packagingService)
public static XElement ToXml(this IMember member, IEntityXmlSerializer serializer)
{
return ((PackagingService)(packagingService)).Export(member);
return serializer.Serialize(member);
}
#endregion
@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Events
{
public class ExportEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>, IEquatable<ExportEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting a single entity instance
/// </summary>
/// <param name="eventObject"></param>
/// <param name="xml"></param>
/// <param name="canCancel"></param>
public ExportEventArgs(TEntity eventObject, XElement xml, bool canCancel)
: base(new List<TEntity> { eventObject }, canCancel)
{
Xml = xml;
}
/// <summary>
/// Constructor accepting a single entity instance
/// and cancellable by default
/// </summary>
/// <param name="eventObject"></param>
/// <param name="elementName"></param>
public ExportEventArgs(TEntity eventObject, string elementName) : base(new List<TEntity> {eventObject}, true)
{
Xml = new XElement(elementName);
}
protected ExportEventArgs(IEnumerable<TEntity> eventObject, bool canCancel) : base(eventObject, canCancel)
{
}
protected ExportEventArgs(IEnumerable<TEntity> eventObject) : base(eventObject)
{
}
/// <summary>
/// Returns all entities that were exported during the operation
/// </summary>
public IEnumerable<TEntity> ExportedEntities
{
get { return EventObject; }
}
/// <summary>
/// Returns the xml relating to the export event
/// </summary>
public XElement Xml { get; private set; }
public bool Equals(ExportEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && Equals(Xml, other.Xml);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ExportEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
}
}
public static bool operator ==(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(ExportEventArgs<TEntity> left, ExportEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
@@ -1,84 +0,0 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Events
{
public class ImportEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportEventArgs<TEntity>>
{
/// <summary>
/// Constructor accepting an XElement with the xml being imported
/// </summary>
/// <param name="xml"></param>
public ImportEventArgs(XElement xml) : base(new List<TEntity>(), true)
{
Xml = xml;
}
/// <summary>
/// Constructor accepting a list of entities and an XElement with the imported xml
/// </summary>
/// <param name="eventObject"></param>
/// <param name="xml"></param>
/// <param name="canCancel"></param>
public ImportEventArgs(IEnumerable<TEntity> eventObject, XElement xml, bool canCancel)
: base(eventObject, canCancel)
{
Xml = xml;
}
protected ImportEventArgs(IEnumerable<TEntity> eventObject, bool canCancel) : base(eventObject, canCancel)
{
}
protected ImportEventArgs(IEnumerable<TEntity> eventObject) : base(eventObject)
{
}
/// <summary>
/// Returns all entities that were imported during the operation
/// </summary>
public IEnumerable<TEntity> ImportedEntities
{
get { return EventObject; }
}
/// <summary>
/// Returns the xml relating to the import event
/// </summary>
public XElement Xml { get; private set; }
public bool Equals(ImportEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return base.Equals(other) && Equals(Xml, other.Xml);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((ImportEventArgs<TEntity>) obj);
}
public override int GetHashCode()
{
unchecked
{
return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0);
}
}
public static bool operator ==(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
{
return Equals(left, right);
}
public static bool operator !=(ImportEventArgs<TEntity> left, ImportEventArgs<TEntity> right)
{
return !Equals(left, right);
}
}
}
@@ -7,44 +7,28 @@ namespace Umbraco.Core.Events
{
public class ImportPackageEventArgs<TEntity> : CancellableEnumerableObjectEventArgs<TEntity>, IEquatable<ImportPackageEventArgs<TEntity>>
{
private readonly MetaData _packageMetaData;
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("Use the overload specifying packageMetaData instead")]
public ImportPackageEventArgs(TEntity eventObject, bool canCancel)
public ImportPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
PackageMetaData = packageMetaData ?? throw new ArgumentNullException(nameof(packageMetaData));
}
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData, bool canCancel)
: base(new[] { eventObject }, canCancel)
{
if (packageMetaData == null) throw new ArgumentNullException("packageMetaData");
_packageMetaData = packageMetaData;
}
public ImportPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
public ImportPackageEventArgs(TEntity eventObject, IPackageInfo packageMetaData)
: this(eventObject, packageMetaData, true)
{
}
public MetaData PackageMetaData
{
get { return _packageMetaData; }
}
public IPackageInfo PackageMetaData { get; }
public IEnumerable<TEntity> InstallationSummary
{
get { return EventObject; }
}
public IEnumerable<TEntity> InstallationSummary => EventObject;
public bool Equals(ImportPackageEventArgs<TEntity> other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
//TODO: MetaData for package metadata has no equality operators :/
return base.Equals(other) && _packageMetaData.Equals(other._packageMetaData);
return base.Equals(other) && PackageMetaData.Equals(other.PackageMetaData);
}
public override bool Equals(object obj)
@@ -59,7 +43,7 @@ namespace Umbraco.Core.Events
{
unchecked
{
return (base.GetHashCode() * 397) ^ _packageMetaData.GetHashCode();
return (base.GetHashCode() * 397) ^ PackageMetaData.GetHashCode();
}
}
@@ -3,20 +3,13 @@ using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Events
{
public class UninstallPackageEventArgs<TEntity> : CancellableObjectEventArgs<IEnumerable<TEntity>>
public class UninstallPackageEventArgs: CancellableObjectEventArgs<IEnumerable<UninstallationSummary>>
{
public UninstallPackageEventArgs(TEntity eventObject, bool canCancel)
: base(new[] { eventObject }, canCancel)
{ }
public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData)
: base(new[] { eventObject })
public UninstallPackageEventArgs(IEnumerable<UninstallationSummary> eventObject, bool canCancel)
: base(eventObject, canCancel)
{
PackageMetaData = packageMetaData;
}
public MetaData PackageMetaData { get; }
public IEnumerable<TEntity> UninstallationSummary => EventObject;
public IEnumerable<UninstallationSummary> UninstallationSummary => EventObject;
}
}
-54
View File
@@ -31,16 +31,6 @@ namespace Umbraco.Core.IO
public static char DirSepChar => Path.DirectorySeparatorChar;
internal static void UnZip(string zipFilePath, string unPackDirectory, bool deleteZipFile)
{
// Unzip
var tempDir = unPackDirectory;
Directory.CreateDirectory(tempDir);
ZipFile.ExtractToDirectory(zipFilePath, unPackDirectory);
if (deleteZipFile)
File.Delete(zipFilePath);
}
//helper to try and match the old path to a new virtual one
public static string FindFile(string virtualPath)
{
@@ -123,11 +113,6 @@ namespace Umbraco.Core.IO
return MapPath(path, true);
}
public static string MapPathIfVirtual(string path)
{
return path.StartsWith("~/") ? MapPath(path) : path;
}
//use a tilde character instead of the complete path
internal static string ReturnPath(string settingsKey, string standardPath, bool useTilde)
{
@@ -156,20 +141,6 @@ namespace Umbraco.Core.IO
return VerifyEditPath(filePath, new[] { validDir });
}
/// <summary>
/// Validates that the current filepath matches a directory where the user is allowed to edit a file.
/// </summary>
/// <param name="filePath">The filepath to validate.</param>
/// <param name="validDir">The valid directory.</param>
/// <returns>True, if the filepath is valid, else an exception is thrown.</returns>
/// <exception cref="FileSecurityException">The filepath is invalid.</exception>
internal static bool ValidateEditPath(string filePath, string validDir)
{
if (VerifyEditPath(filePath, validDir) == false)
throw new FileSecurityException(String.Format("The filepath '{0}' is not within an allowed directory for this type of files", filePath.Replace(MapPath(SystemDirectories.Root), "")));
return true;
}
/// <summary>
/// Verifies that the current filepath matches one of several directories where the user is allowed to edit a file.
/// </summary>
@@ -221,20 +192,6 @@ namespace Umbraco.Core.IO
return ext != null && validFileExtensions.Contains(ext.TrimStart('.'));
}
/// <summary>
/// Validates that the current filepath has one of several authorized extensions.
/// </summary>
/// <param name="filePath">The filepath to validate.</param>
/// <param name="validFileExtensions">The valid extensions.</param>
/// <returns>True, if the filepath is valid, else an exception is thrown.</returns>
/// <exception cref="FileSecurityException">The filepath is invalid.</exception>
internal static bool ValidateFileExtension(string filePath, List<string> validFileExtensions)
{
if (VerifyFileExtension(filePath, validFileExtensions) == false)
throw new FileSecurityException(String.Format("The extension for the current file '{0}' is not of an allowed type for this editor. This is typically controlled from either the installed MacroEngines or based on configuration in /config/umbracoSettings.config", filePath.Replace(MapPath(SystemDirectories.Root), "")));
return true;
}
public static bool PathStartsWith(string path, string root, char separator)
{
// either it is identical to root,
@@ -329,17 +286,6 @@ namespace Umbraco.Core.IO
Directory.CreateDirectory(absolutePath);
}
public static void EnsureFileExists(string path, string contents)
{
var absolutePath = IOHelper.MapPath(path);
if (File.Exists(absolutePath)) return;
using (var writer = File.CreateText(absolutePath))
{
writer.Write(contents);
}
}
/// <summary>
/// Checks if a given path is a full path including drive letter
/// </summary>
+1 -1
View File
@@ -7,7 +7,7 @@ namespace Umbraco.Core.IO
{
internal class ShadowWrapper : IFileSystem
{
private const string ShadowFsPath = "~/App_Data/TEMP/ShadowFs";
private static readonly string ShadowFsPath = SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs";
private readonly Func<bool> _isScoped;
private readonly IFileSystem _innerFileSystem;
+6 -2
View File
@@ -12,6 +12,10 @@ namespace Umbraco.Core.IO
public static string Data => "~/App_Data";
public static string TempData => Data + "/TEMP";
public static string TempFileUploads => TempData + "/FileUploads";
public static string Install => "~/install";
//fixme: remove this
@@ -43,9 +47,9 @@ namespace Umbraco.Core.IO
[Obsolete("Only used by legacy load balancing which is obsolete and should be removed")]
public static string WebServices => IOHelper.ReturnPath("umbracoWebservicesPath", Umbraco.EnsureEndsWith("/") + "webservices");
public static string Packages => Data + IOHelper.DirSepChar + "packages";
public static string Packages => Data + "/packages";
public static string Preview => Data + IOHelper.DirSepChar + "preview";
public static string Preview => Data + "/preview";
private static string _root;
+5
View File
@@ -57,6 +57,11 @@ namespace Umbraco.Core
/// </summary>
RuntimeLevel Level { get; }
/// <summary>
/// Gets the reason for the runtime level of execution.
/// </summary>
RuntimeLevelReason Reason { get; }
/// <summary>
/// Gets the current migration state.
/// </summary>
@@ -92,7 +92,7 @@ namespace Umbraco.Core.Logging.Serilog
/// <summary>
/// Reads settings from /config/serilog.user.config
/// That allows a seperate logging pipeline to be configured that wil not affect the main Umbraco log
/// That allows a separate logging pipeline to be configured that wil not affect the main Umbraco log
/// </summary>
/// <param name="logConfig">A Serilog LoggerConfiguration</param>
public static LoggerConfiguration ReadFromUserConfigFile(this LoggerConfiguration logConfig)
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Migrations.Install
/// <summary>
/// Creates the initial database schema during install.
/// </summary>
internal class DatabaseSchemaCreator
public class DatabaseSchemaCreator
{
private readonly IUmbracoDatabase _database;
private readonly ILogger _logger;
@@ -28,7 +28,7 @@ namespace Umbraco.Core.Migrations.Install
private ISqlSyntaxProvider SqlSyntax => _database.SqlContext.SqlSyntax;
// all tables, in order
public static readonly List<Type> OrderedTables = new List<Type>
internal static readonly List<Type> OrderedTables = new List<Type>
{
typeof (UserDto),
typeof (NodeDto),
@@ -138,7 +138,7 @@ namespace Umbraco.Core.Migrations.Install
/// <summary>
/// Validates the schema of the current database.
/// </summary>
public DatabaseSchemaResult ValidateSchema()
internal DatabaseSchemaResult ValidateSchema()
{
var result = new DatabaseSchemaResult(SqlSyntax);
@@ -387,7 +387,7 @@ namespace Umbraco.Core.Migrations.Install
/// If <typeparamref name="T"/> has been decorated with an <see cref="TableNameAttribute"/>, the name from that
/// attribute will be used for the table name. If the attribute is not present, the name
/// <typeparamref name="T"/> will be used instead.
///
///
/// If a table with the same name already exists, the <paramref name="overwrite"/> parameter will determine
/// whether the table is overwritten. If <c>true</c>, the table will be overwritten, whereas this method will
/// not do anything if the parameter is <c>false</c>.
@@ -409,14 +409,14 @@ namespace Umbraco.Core.Migrations.Install
/// If <paramref name="modelType"/> has been decorated with an <see cref="TableNameAttribute"/>, the name from
/// that attribute will be used for the table name. If the attribute is not present, the name
/// <paramref name="modelType"/> will be used instead.
///
///
/// If a table with the same name already exists, the <paramref name="overwrite"/> parameter will determine
/// whether the table is overwritten. If <c>true</c>, the table will be overwritten, whereas this method will
/// not do anything if the parameter is <c>false</c>.
///
/// This need to execute as part of a transaction.
/// </remarks>
public void CreateTable(bool overwrite, Type modelType, DatabaseDataCreator dataCreation)
internal void CreateTable(bool overwrite, Type modelType, DatabaseDataCreator dataCreation)
{
if (!_database.InTransaction)
throw new InvalidOperationException("Database is not in a transaction.");
@@ -3,6 +3,7 @@ using System.Configuration;
using Semver;
using Umbraco.Core.Configuration;
using Umbraco.Core.Migrations.Upgrade.V_7_12_0;
using Umbraco.Core.Migrations.Upgrade.V_7_14_0;
using Umbraco.Core.Migrations.Upgrade.V_8_0_0;
namespace Umbraco.Core.Migrations.Upgrade
@@ -122,6 +123,7 @@ namespace Umbraco.Core.Migrations.Upgrade
To<MakeTagsVariant>("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}");
To<MakeRedirectUrlVariant>("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}");
To<AddContentTypeIsElementColumn>("{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}");
To<UpdateMemberGroupPickerData>("{8A027815-D5CD-4872-8B88-9A51AB5986A6}"); // from 7.14.0
//FINAL
@@ -145,12 +147,23 @@ namespace Umbraco.Core.Migrations.Upgrade
// main chain, skipping the migrations
//
From("{init-7.12.0}");
// start stop target
// clone start / clone stop / target
ToWithClone("{init-7.10.0}", "{1350617A-4930-4D61-852F-E3AA9E692173}", "{BBD99901-1545-40E4-8A5A-D7A675C7D2F2}");
From("{init-7.12.1}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.12.2}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.12.3}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.12.4}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.13.0}").To("{init-7.10.0}"); // same as 7.12.0
From("{init-7.13.1}").To("{init-7.10.0}"); // same as 7.12.0
// 7.14.0 has migrations, handle it...
// clone going from 7.10 to 1350617A (the last one before we started to merge 7.12 migrations), then
// clone going from CF51B39B (after 7.12 migrations) to 0009109C (the last one before we started to merge 7.12 migrations),
// ending in 8A027815 (after 7.14 migrations)
From("{init-7.14.0}")
.ToWithClone("{init-7.10.0}", "{1350617A-4930-4D61-852F-E3AA9E692173}", "{9109B8AF-6B34-46EE-9484-7434196D0C79}")
.ToWithClone("{CF51B39B-9B9A-4740-BB7C-EAF606A7BFBF}", "{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}", "{8A027815-D5CD-4872-8B88-9A51AB5986A6}");
}
}
}
@@ -0,0 +1,30 @@
namespace Umbraco.Core.Migrations.Upgrade.V_7_14_0
{
/// <summary>
/// Migrates member group picker properties from NVarchar to NText. See https://github.com/umbraco/Umbraco-CMS/issues/3268.
/// </summary>
public class UpdateMemberGroupPickerData : MigrationBase
{
/// <summary>
/// Migrates member group picker properties from NVarchar to NText. See https://github.com/umbraco/Umbraco-CMS/issues/3268.
/// </summary>
public UpdateMemberGroupPickerData(IMigrationContext context)
: base(context)
{
}
public override void Migrate()
{
Database.Execute($@"UPDATE cmsPropertyData SET dataNtext = dataNvarchar, dataNvarchar = NULL
WHERE dataNtext IS NULL AND id IN (
SELECT id FROM cmsPropertyData WHERE propertyTypeId in (
SELECT id from cmsPropertyType where dataTypeID IN (
SELECT nodeId FROM cmsDataType WHERE propertyEditorAlias = '{Constants.PropertyEditors.Aliases.MemberGroupPicker}'
)
)
)");
Database.Execute($"UPDATE cmsDataType SET dbType = 'Ntext' WHERE propertyEditorAlias = '{Constants.PropertyEditors.Aliases.MemberGroupPicker}'");
}
}
}
@@ -0,0 +1,9 @@
namespace Umbraco.Core.Models.Packaging
{
public enum ActionRunAt
{
Undefined = 0,
Install,
Uninstall
}
}
@@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
namespace Umbraco.Core.Models.Packaging
{
/// <summary>
/// The model of the package definition within an umbraco (zip) package file
/// </summary>
public class CompiledPackage : IPackageInfo
{
public FileInfo PackageFile { get; set; }
public string Name { get; set; }
public string Version { get; set; }
public string Url { get; set; }
public string License { get; set; }
public string LicenseUrl { get; set; }
public Version UmbracoVersion { get; set; }
public RequirementsType UmbracoVersionRequirementsType { get; set; }
public string Author { get; set; }
public string AuthorUrl { get; set; }
public string Readme { get; set; }
public string Control { get; set; }
public string IconUrl { get; set; }
public string Actions { get; set; } //fixme: Should we make this strongly typed to IEnumerable<PackageAction> ?
public PreInstallWarnings Warnings { get; set; } = new PreInstallWarnings();
public List<CompiledPackageFile> Files { get; set; } = new List<CompiledPackageFile>();
public IEnumerable<XElement> Macros { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> Templates { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> Stylesheets { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> DataTypes { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> Languages { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> DictionaryItems { get; set; } //fixme: make strongly typed
public IEnumerable<XElement> DocumentTypes { get; set; } //fixme: make strongly typed
public IEnumerable<CompiledPackageDocument> Documents { get; set; }
}
}
@@ -0,0 +1,26 @@
using System;
using System.Xml.Linq;
namespace Umbraco.Core.Models.Packaging
{
public class CompiledPackageDocument
{
public static CompiledPackageDocument Create(XElement xml)
{
if (xml.Name.LocalName != "DocumentSet")
throw new ArgumentException("The xml isn't formatted correctly, a document element is defined by <DocumentSet>", nameof(xml));
return new CompiledPackageDocument
{
XmlData = xml,
ImportMode = xml.AttributeValue<string>("importMode")
};
}
public string ImportMode { get; set; } //this is never used
/// <summary>
/// The serialized version of the content
/// </summary>
public XElement XmlData { get; set; }
}
}
@@ -0,0 +1,25 @@
using System;
using System.Xml.Linq;
namespace Umbraco.Core.Models.Packaging
{
public class CompiledPackageFile
{
public static CompiledPackageFile Create(XElement xml)
{
if (xml.Name.LocalName != "file")
throw new ArgumentException("The xml isn't formatted correctly, a file element is defined by <file>", nameof(xml));
return new CompiledPackageFile
{
UniqueFileName = xml.Element("guid")?.Value,
OriginalName = xml.Element("orgName")?.Value,
OriginalPath = xml.Element("orgPath")?.Value
};
}
public string OriginalPath { get; set; }
public string UniqueFileName { get; set; }
public string OriginalName { get; set; }
}
}
@@ -0,0 +1,19 @@
using System;
namespace Umbraco.Core.Models.Packaging
{
public interface IPackageInfo
{
string Name { get; }
string Version { get; }
string Url { get; }
string License { get; }
string LicenseUrl { get; }
Version UmbracoVersion { get; }
string Author { get; }
string AuthorUrl { get; }
string Readme { get; }
string Control { get; } //fixme - this needs to be an angular view
string IconUrl { get; }
}
}
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.Packaging
@@ -8,36 +9,19 @@ namespace Umbraco.Core.Models.Packaging
[DataContract(IsReference = true)]
public class InstallationSummary
{
public MetaData MetaData { get; set; }
public IEnumerable<IDataType> DataTypesInstalled { get; set; }
public IEnumerable<ILanguage> LanguagesInstalled { get; set; }
public IEnumerable<IDictionaryItem> DictionaryItemsInstalled { get; set; }
public IEnumerable<IMacro> MacrosInstalled { get; set; }
public IEnumerable<string> FilesInstalled { get; set; }
public IEnumerable<ITemplate> TemplatesInstalled { get; set; }
public IEnumerable<IContentType> ContentTypesInstalled { get; set; }
public IEnumerable<IFile> StylesheetsInstalled { get; set; }
public IEnumerable<IContent> ContentInstalled { get; set; }
public IEnumerable<PackageAction> Actions { get; set; }
public bool PackageInstalled { get; set; }
public IPackageInfo MetaData { get; set; }
public IEnumerable<IDataType> DataTypesInstalled { get; set; } = Enumerable.Empty<IDataType>();
public IEnumerable<ILanguage> LanguagesInstalled { get; set; } = Enumerable.Empty<ILanguage>();
public IEnumerable<IDictionaryItem> DictionaryItemsInstalled { get; set; } = Enumerable.Empty<IDictionaryItem>();
public IEnumerable<IMacro> MacrosInstalled { get; set; } = Enumerable.Empty<IMacro>();
public IEnumerable<string> FilesInstalled { get; set; } = Enumerable.Empty<string>();
public IEnumerable<ITemplate> TemplatesInstalled { get; set; } = Enumerable.Empty<ITemplate>();
public IEnumerable<IContentType> DocumentTypesInstalled { get; set; } = Enumerable.Empty<IContentType>();
public IEnumerable<IFile> StylesheetsInstalled { get; set; } = Enumerable.Empty<IFile>();
public IEnumerable<IContent> ContentInstalled { get; set; } = Enumerable.Empty<IContent>();
public IEnumerable<PackageAction> Actions { get; set; } = Enumerable.Empty<PackageAction>();
public IEnumerable<string> ActionErrors { get; set; } = Enumerable.Empty<string>();
}
internal static class InstallationSummaryExtentions
{
public static InstallationSummary InitEmpty(this InstallationSummary summary)
{
summary.Actions = new List<PackageAction>();
summary.ContentInstalled = new List<IContent>();
summary.ContentTypesInstalled = new List<IContentType>();
summary.DataTypesInstalled = new List<IDataType>();
summary.DictionaryItemsInstalled = new List<IDictionaryItem>();
summary.FilesInstalled = new List<string>();
summary.LanguagesInstalled = new List<ILanguage>();
summary.MacrosInstalled = new List<IMacro>();
summary.MetaData = new MetaData();
summary.TemplatesInstalled = new List<ITemplate>();
summary.PackageInstalled = false;
return summary;
}
}
}
@@ -1,23 +0,0 @@
using System;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.Packaging
{
[Serializable]
[DataContract(IsReference = true)]
public class MetaData
{
public string Name { get; set; }
public string Version { get; set; }
public string Url { get; set; }
public string License { get; set; }
public string LicenseUrl { get; set; }
public int ReqMajor { get; set; }
public int ReqMinor { get; set; }
public int ReqPatch { get; set; }
public string AuthorName { get; set; }
public string AuthorUrl { get; set; }
public string Readme { get; set; }
public string Control { get; set; }
}
}
@@ -4,13 +4,9 @@ using System.Xml.Linq;
namespace Umbraco.Core.Models.Packaging
{
public enum ActionRunAt
{
Undefined = 0,
Install,
Uninstall
}
/// <summary>
/// Defines a package action declared within a package manifest
/// </summary>
[Serializable]
[DataContract(IsReference = true)]
public class PackageAction
@@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.Packaging
{
[DataContract(Name = "packageInstance")]
public class PackageDefinition : IPackageInfo
{
/// <summary>
/// Converts a <see cref="CompiledPackage"/> model to a <see cref="PackageDefinition"/> model
/// </summary>
/// <param name="compiled"></param>
/// <returns></returns>
/// <remarks>
/// This is used only for conversions and will not 'get' a PackageDefinition from the repository with a valid ID
/// </remarks>
internal static PackageDefinition FromCompiledPackage(CompiledPackage compiled)
{
return new PackageDefinition
{
Actions = compiled.Actions,
Author = compiled.Author,
AuthorUrl = compiled.AuthorUrl,
Control = compiled.Control,
IconUrl = compiled.IconUrl,
License = compiled.License,
LicenseUrl = compiled.LicenseUrl,
Name = compiled.Name,
Readme = compiled.Readme,
UmbracoVersion = compiled.UmbracoVersion,
Url = compiled.Url,
Version = compiled.Version,
Files = compiled.Files.Select(x => x.OriginalPath).ToList()
};
}
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "packageGuid")]
public Guid PackageId { get; set; }
[DataMember(Name = "name")]
[Required]
public string Name { get; set; } = string.Empty;
[DataMember(Name = "url")]
[Required]
[Url]
public string Url { get; set; } = string.Empty;
/// <summary>
/// The full path to the package's zip file when it was installed (or is being installed)
/// </summary>
[ReadOnly(true)]
[DataMember(Name = "packagePath")]
public string PackagePath { get; set; } = string.Empty;
[DataMember(Name = "version")]
[Required]
public string Version { get; set; } = "1.0.0";
/// <summary>
/// The minimum umbraco version that this package requires
/// </summary>
[DataMember(Name = "umbracoVersion")]
public Version UmbracoVersion { get; set; } = Configuration.UmbracoVersion.Current;
[DataMember(Name = "author")]
[Required]
public string Author { get; set; } = string.Empty;
[DataMember(Name = "authorUrl")]
[Required]
[Url]
public string AuthorUrl { get; set; } = string.Empty;
[DataMember(Name = "license")]
public string License { get; set; } = "MIT License";
[DataMember(Name = "licenseUrl")]
public string LicenseUrl { get; set; } = "http://opensource.org/licenses/MIT";
[DataMember(Name = "readme")]
public string Readme { get; set; } = string.Empty;
[DataMember(Name = "contentLoadChildNodes")]
public bool ContentLoadChildNodes { get; set; }
[DataMember(Name = "contentNodeId")]
public string ContentNodeId { get; set; } = string.Empty;
[DataMember(Name = "macros")]
public IList<string> Macros { get; set; } = new List<string>();
[DataMember(Name = "languages")]
public IList<string> Languages { get; set; } = new List<string>();
[DataMember(Name = "dictionaryItems")]
public IList<string> DictionaryItems { get; set; } = new List<string>();
[DataMember(Name = "templates")]
public IList<string> Templates { get; set; } = new List<string>();
[DataMember(Name = "documentTypes")]
public IList<string> DocumentTypes { get; set; } = new List<string>();
[DataMember(Name = "stylesheets")]
public IList<string> Stylesheets { get; set; } = new List<string>();
[DataMember(Name = "files")]
public IList<string> Files { get; set; } = new List<string>();
//fixme: Change this to angular view
[DataMember(Name = "loadControl")]
public string Control { get; set; } = string.Empty;
[DataMember(Name = "actions")]
public string Actions { get; set; } = "<actions></actions>";
[DataMember(Name = "dataTypes")]
public IList<string> DataTypes { get; set; } = new List<string>();
[DataMember(Name = "iconUrl")]
public string IconUrl { get; set; } = string.Empty;
public PackageDefinition Clone()
{
return new PackageDefinition
{
Id = Id,
PackagePath = PackagePath,
Name = Name,
Files = new List<string>(Files),
UmbracoVersion = (Version) UmbracoVersion.Clone(),
Version = Version,
Url = Url,
Readme = Readme,
AuthorUrl = AuthorUrl,
Author = Author,
LicenseUrl = LicenseUrl,
Actions = Actions,
PackageId = PackageId,
Control = Control,
DataTypes = new List<string>(DataTypes),
IconUrl = IconUrl,
License = License,
Templates = new List<string>(Templates),
Languages = new List<string>(Languages),
Macros = new List<string>(Macros),
Stylesheets = new List<string>(Stylesheets),
DocumentTypes = new List<string>(DocumentTypes),
DictionaryItems = new List<string>(DictionaryItems),
ContentNodeId = ContentNodeId,
ContentLoadChildNodes = ContentLoadChildNodes
};
}
}
}
@@ -1,17 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.Packaging
{
[Serializable]
[DataContract(IsReference = true)]
internal class PreInstallWarnings
public class PreInstallWarnings
{
public KeyValuePair<string, string>[] UnsecureFiles { get; set; }
public KeyValuePair<string, string>[] FilesReplaced { get; set; }
public IEnumerable<IMacro> ConflictingMacroAliases { get; set; }
public IEnumerable<ITemplate> ConflictingTemplateAliases { get; set; }
public IEnumerable<IFile> ConflictingStylesheetNames { get; set; }
public IEnumerable<string> UnsecureFiles { get; set; } = Enumerable.Empty<string>();
public IEnumerable<string> FilesReplaced { get; set; } = Enumerable.Empty<string>();
//TODO: Shouldn't we detect other conflicting entities too ?
public IEnumerable<IMacro> ConflictingMacros { get; set; } = Enumerable.Empty<IMacro>();
public IEnumerable<ITemplate> ConflictingTemplates { get; set; } = Enumerable.Empty<ITemplate>();
public IEnumerable<IFile> ConflictingStylesheets { get; set; } = Enumerable.Empty<IFile>();
}
}
@@ -1,4 +1,4 @@
namespace Umbraco.Web._Legacy.Packager
namespace Umbraco.Core.Models.Packaging
{
public enum RequirementsType
{
@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
namespace Umbraco.Core.Models.Packaging
{
[Serializable]
[DataContract(IsReference = true)]
public class UninstallationSummary
{
public IPackageInfo MetaData { get; set; }
public IEnumerable<IDataType> DataTypesUninstalled { get; set; } = Enumerable.Empty<IDataType>();
public IEnumerable<ILanguage> LanguagesUninstalled { get; set; } = Enumerable.Empty<ILanguage>();
public IEnumerable<IDictionaryItem> DictionaryItemsUninstalled { get; set; } = Enumerable.Empty<IDictionaryItem>();
public IEnumerable<IMacro> MacrosUninstalled { get; set; } = Enumerable.Empty<IMacro>();
public IEnumerable<string> FilesUninstalled { get; set; } = Enumerable.Empty<string>();
public IEnumerable<ITemplate> TemplatesUninstalled { get; set; } = Enumerable.Empty<ITemplate>();
public IEnumerable<IContentType> DocumentTypesUninstalled { get; set; } = Enumerable.Empty<IContentType>();
public IEnumerable<IFile> StylesheetsUninstalled { get; set; } = Enumerable.Empty<IFile>();
public IEnumerable<PackageAction> Actions { get; set; } = Enumerable.Empty<PackageAction>();
public IEnumerable<string> ActionErrors { get; set; } = Enumerable.Empty<string>();
}
}
@@ -0,0 +1,194 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using File = System.IO.File;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Parses the xml document contained in a compiled (zip) Umbraco package
/// </summary>
internal class CompiledPackageXmlParser
{
private readonly ConflictingPackageData _conflictingPackageData;
public CompiledPackageXmlParser(ConflictingPackageData conflictingPackageData)
{
_conflictingPackageData = conflictingPackageData;
}
public CompiledPackage ToCompiledPackage(XDocument xml, FileInfo packageFile, string applicationRootFolder)
{
if (xml == null) throw new ArgumentNullException(nameof(xml));
if (xml.Root == null) throw new ArgumentException(nameof(xml), "The xml document is invalid");
if (xml.Root.Name != "umbPackage") throw new FormatException("The xml document is invalid");
var info = xml.Root.Element("info");
if (info == null) throw new FormatException("The xml document is invalid");
var package = info.Element("package");
if (package == null) throw new FormatException("The xml document is invalid");
var author = info.Element("author");
if (author == null) throw new FormatException("The xml document is invalid");
var requirements = package.Element("requirements");
if (requirements == null) throw new FormatException("The xml document is invalid");
var def = new CompiledPackage
{
PackageFile = packageFile,
Name = package.Element("name")?.Value,
Author = author.Element("name")?.Value,
AuthorUrl = author.Element("website")?.Value,
Version = package.Element("version")?.Value,
Readme = info.Element("readme")?.Value,
License = package.Element("license")?.Value,
LicenseUrl = package.Element("license")?.AttributeValue<string>("url"),
Url = package.Element("url")?.Value,
IconUrl = package.Element("iconUrl")?.Value,
UmbracoVersion = new Version((int)requirements.Element("major"), (int)requirements.Element("minor"), (int)requirements.Element("patch")),
UmbracoVersionRequirementsType = requirements.AttributeValue<string>("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum<RequirementsType>.Parse(requirements.AttributeValue<string>("type"), true),
Control = package.Element("control")?.Value,
Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "<Actions></Actions>", //take the entire outer xml value
Files = xml.Root.Element("files")?.Elements("file")?.Select(CompiledPackageFile.Create).ToList() ?? new List<CompiledPackageFile>(),
Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty<XElement>(),
Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty<XElement>(),
Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty<XElement>(),
DataTypes = xml.Root.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty<XElement>(),
Languages = xml.Root.Element("Languages")?.Elements("Language") ?? Enumerable.Empty<XElement>(),
DictionaryItems = xml.Root.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty<XElement>(),
DocumentTypes = xml.Root.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty<XElement>(),
Documents = xml.Root.Element("Documents")?.Elements("DocumentSet")?.Select(CompiledPackageDocument.Create) ?? Enumerable.Empty<CompiledPackageDocument>(),
};
def.Warnings = GetPreInstallWarnings(def, applicationRootFolder);
return def;
}
private PreInstallWarnings GetPreInstallWarnings(CompiledPackage package, string applicationRootFolder)
{
var sourceDestination = ExtractSourceDestinationFileInformation(package.Files);
var installWarnings = new PreInstallWarnings
{
ConflictingMacros = _conflictingPackageData.FindConflictingMacros(package.Macros),
ConflictingTemplates = _conflictingPackageData.FindConflictingTemplates(package.Templates),
ConflictingStylesheets = _conflictingPackageData.FindConflictingStylesheets(package.Stylesheets),
UnsecureFiles = FindUnsecureFiles(sourceDestination),
FilesReplaced = FindFilesToBeReplaced(sourceDestination, applicationRootFolder)
};
return installWarnings;
}
/// <summary>
/// Returns a tuple of the zip file's unique file name and it's application relative path
/// </summary>
/// <param name="packageFiles"></param>
/// <returns></returns>
public (string packageUniqueFile, string appRelativePath)[] ExtractSourceDestinationFileInformation(IEnumerable<CompiledPackageFile> packageFiles)
{
return packageFiles
.Select(e =>
{
var fileName = PrepareAsFilePathElement(e.OriginalName);
var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(e.OriginalPath));
var relativePath = Path.Combine(relativeDir, fileName);
return (e.UniqueFileName, relativePath);
}).ToArray();
}
private IEnumerable<string> FindFilesToBeReplaced(IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestination, string applicationRootFolder)
{
return sourceDestination.Where(sd => File.Exists(Path.Combine(applicationRootFolder, sd.appRelativePath)))
.Select(x => x.appRelativePath)
.ToArray();
}
private IEnumerable<string> FindUnsecureFiles(IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestinationPair)
{
return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.appRelativePath))
.Select(x => x.appRelativePath)
.ToList();
}
private bool IsFileDestinationUnsecure(string destination)
{
var unsecureDirNames = new[] { "bin", "app_code" };
if (unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase)))
return true;
string extension = Path.GetExtension(destination);
return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase);
}
private static string PrepareAsFilePathElement(string pathElement)
{
return pathElement.TrimStart(new[] { '\\', '/', '~' }).Replace("/", "\\");
}
private static string UpdatePathPlaceholders(string path)
{
if (path.Contains("[$"))
{
//this is experimental and undocumented...
path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco);
path = path.Replace("[$CONFIG]", SystemDirectories.Config);
path = path.Replace("[$DATA]", SystemDirectories.Data);
}
return path;
}
/// <summary>
/// Parses the package actions stored in the package definition
/// </summary>
/// <param name="actionsElement"></param>
/// <param name="packageName"></param>
/// <returns></returns>
public static IEnumerable<PackageAction> GetPackageActions(XElement actionsElement, string packageName)
{
if (actionsElement == null) return Enumerable.Empty<PackageAction>();
//invariant check ... because people can realy enter anything :/
if (!string.Equals("actions", actionsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase))
throw new FormatException("Must be \"<actions>\" as root");
if (!actionsElement.HasElements) return Enumerable.Empty<PackageAction>();
var actionElementName = actionsElement.Elements().First().Name.LocalName;
//invariant check ... because people can realy enter anything :/
if (!string.Equals("action", actionElementName, StringComparison.InvariantCultureIgnoreCase))
throw new FormatException("Must be \"<action\" as element");
return actionsElement.Elements(actionElementName)
.Select(e =>
{
var aliasAttr = e.Attribute("alias") ?? e.Attribute("Alias"); //allow both ... because people can really enter anything :/
if (aliasAttr == null)
throw new ArgumentException("missing \"alias\" atribute in alias element", nameof(actionsElement));
var packageAction = new PackageAction
{
XmlData = e,
Alias = aliasAttr.Value,
PackageName = packageName,
};
var attr = e.Attribute("runat") ?? e.Attribute("Runat"); //allow both ... because people can really enter anything :/
if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; }
attr = e.Attribute("undo") ?? e.Attribute("Undo"); //allow both ... because people can really enter anything :/
if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; }
return packageAction;
}).ToArray();
}
}
}
@@ -7,82 +7,53 @@ using Umbraco.Core.Services;
namespace Umbraco.Core.Packaging
{
internal class ConflictingPackageData : IConflictingPackageData
internal class ConflictingPackageData
{
private readonly IMacroService _macroService;
private readonly IFileService _fileService;
public ConflictingPackageData(IMacroService macroService,
IFileService fileService)
public ConflictingPackageData(IMacroService macroService, IFileService fileService)
{
if (fileService != null) _fileService = fileService;
else throw new ArgumentNullException("fileService");
if (macroService != null) _macroService = macroService;
else throw new ArgumentNullException("macroService");
_fileService = fileService ?? throw new ArgumentNullException(nameof(fileService));
_macroService = macroService ?? throw new ArgumentNullException(nameof(macroService));
}
public IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes)
public IEnumerable<IFile> FindConflictingStylesheets(IEnumerable<XElement> stylesheetNodes)
{
if (string.Equals(Constants.Packaging.StylesheetsNodeName, stylesheetNotes.Name.LocalName) == false)
{
throw new ArgumentException("the root element must be \"" + Constants.Packaging.StylesheetsNodeName + "\"", "stylesheetNotes");
}
return stylesheetNotes.Elements(Constants.Packaging.StylesheetNodeName)
return stylesheetNodes
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.NameNodeName);
var xElement = n.Element("Name") ?? n.Element("name"); ;
if (xElement == null)
{
throw new ArgumentException("Missing \"" + Constants.Packaging.NameNodeName + "\" element",
"stylesheetNotes");
}
throw new FormatException("Missing \"Name\" element");
return _fileService.GetStylesheetByName(xElement.Value) as IFile;
})
.Where(v => v != null);
}
public IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes)
public IEnumerable<ITemplate> FindConflictingTemplates(IEnumerable<XElement> templateNodes)
{
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateNotes.Name.LocalName) == false)
{
throw new ArgumentException("Node must be a \"" + Constants.Packaging.TemplatesNodeName + "\" node",
"templateNotes");
}
return templateNotes.Elements(Constants.Packaging.TemplateNodeName)
return templateNodes
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameCapital) ?? n.Element(Constants.Packaging.AliasNodeNameSmall);
var xElement = n.Element("Alias") ?? n.Element("alias");
if (xElement == null)
{
throw new ArgumentException("missing a \"" + Constants.Packaging.AliasNodeNameCapital + "\" element",
"templateNotes");
}
throw new FormatException("missing a \"Alias\" element");
return _fileService.GetTemplate(xElement.Value);
})
.Where(v => v != null);
}
public IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes)
public IEnumerable<IMacro> FindConflictingMacros(IEnumerable<XElement> macroNodes)
{
if (string.Equals(Constants.Packaging.MacrosNodeName, macroNodes.Name.LocalName) == false)
{
throw new ArgumentException("Node must be a \"" + Constants.Packaging.MacrosNodeName + "\" node",
"macroNodes");
}
return macroNodes.Elements(Constants.Packaging.MacroNodeName)
return macroNodes
.Select(n =>
{
XElement xElement = n.Element(Constants.Packaging.AliasNodeNameSmall) ?? n.Element(Constants.Packaging.AliasNodeNameCapital);
var xElement = n.Element("alias") ?? n.Element("Alias");
if (xElement == null)
{
throw new ArgumentException(string.Format("missing a \"{0}\" element in {0} element", Constants.Packaging.AliasNodeNameSmall),
"macroNodes");
}
throw new FormatException("missing a \"alias\" element in alias element");
return _macroService.GetByAlias(xElement.Value);
})
@@ -1,13 +0,0 @@
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Models;
namespace Umbraco.Core.Packaging
{
internal interface IConflictingPackageData
{
IEnumerable<IFile> FindConflictingStylesheets(XElement stylesheetNotes);
IEnumerable<ITemplate> FindConflictingTemplates(XElement templateNotes);
IEnumerable<IMacro> FindConflictingMacros(XElement macroNodes);
}
}
@@ -0,0 +1,16 @@
using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Manages the storage of created package definitions
/// </summary>
public interface ICreatedPackagesRepository : IPackageDefinitionRepository
{
/// <summary>
/// Creates the package file and returns it's physical path
/// </summary>
/// <param name="definition"></param>
string ExportPackage(PackageDefinition definition);
}
}
@@ -0,0 +1,11 @@
using System;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Manages the storage of installed package definitions
/// </summary>
public interface IInstalledPackagesRepository : IPackageDefinitionRepository
{
}
}
@@ -0,0 +1,26 @@
using System.Collections.Generic;
using System.Xml.Linq;
namespace Umbraco.Core.Packaging
{
public interface IPackageActionRunner
{
/// <summary>
/// Runs the package action with the specified action alias.
/// </summary>
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
/// <param name="errors"></param>
bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors);
/// <summary>
/// Undos the package action with the specified action alias.
/// </summary>
/// <param name="packageName">Name of the package.</param>
/// <param name="actionAlias">The action alias.</param>
/// <param name="actionXml">The action XML.</param>
/// <param name="errors"></param>
bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors);
}
}
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Defines methods for persisting package definitions to storage
/// </summary>
public interface IPackageDefinitionRepository
{
IEnumerable<PackageDefinition> GetAll();
PackageDefinition GetById(int id);
void Delete(int id);
/// <summary>
/// Persists a package definition to storage
/// </summary>
/// <returns>
/// true if creating/updating the package was successful, otherwise false
/// </returns>
bool SavePackage(PackageDefinition definition);
}
}
@@ -1,62 +0,0 @@
using System.Collections.Generic;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Used to access an umbraco package file
/// Remeber that filenames must be unique
/// use "FindDubletFileNames" for sanitycheck
/// </summary>
internal interface IPackageExtraction
{
/// <summary>
/// Returns the content of the file with the given filename
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <param name="fileToRead">filename of the file for wich to get the text content</param>
/// <param name="directoryInPackage">this is the relative directory for the location of the file in the package
/// I dont know why umbraco packages contains directories in the first place??</param>
/// <returns>text content of the file</returns>
string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage);
/// <summary>
/// Copies a file from package to given destination
/// </summary>
/// <param name="packageFilePath">Full path to the ubraco package file</param>
/// <param name="fileInPackageName">filename of the file to copy</param>
/// <param name="destinationfilePath">destination path (including destination filename)</param>
/// <returns>True a file was overwritten</returns>
void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath);
/// <summary>
/// Copies a file from package to given destination
/// </summary>
/// <param name="packageFilePath">Full path to the ubraco package file</param>
/// <param name="sourceDestination">Key: Source file in package. Value: Destination path inclusive file name</param>
void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination);
/// <summary>
/// Check if given list of files can be found in the package
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <param name="expectedFiles">a list of files you would like to find in the package</param>
/// <returns>a subset if any of the files in "expectedFiles" that could not be found in the package</returns>
IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles);
/// <summary>
/// Sanitycheck - should return en empty collection if package is valid
/// </summary>
/// <param name="packageFilePath">Full path to the umbraco package file</param>
/// <returns>list of files that are found more than ones (accross directories) in the package</returns>
IEnumerable<string> FindDubletFileNames(string packageFilePath);
/// <summary>
/// Reads the given files from archive and returns them as a collection of byte arrays
/// </summary>
/// <param name="packageFilePath"></param>
/// <param name="filesToGet"></param>
/// <returns></returns>
IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet);
}
}
@@ -1,13 +1,43 @@
using System.Xml.Linq;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Packaging
{
internal interface IPackageInstallation
public interface IPackageInstallation
{
InstallationSummary InstallPackage(string packageFilePath, int userId);
MetaData GetMetaData(string packageFilePath);
PreInstallWarnings GetPreInstallWarnings(string packageFilePath);
XElement GetConfigXmlElement(string packageFilePath);
/// <summary>
/// This will run the uninstallation sequence for this <see cref="PackageDefinition"/>
/// </summary>
/// <param name="packageDefinition"></param>
/// <param name="userId"></param>
/// <returns></returns>
UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId);
/// <summary>
/// Installs a packages data and entities
/// </summary>
/// <param name="packageDefinition"></param>
/// <param name="compiledPackage"></param>
/// <param name="userId"></param>
/// <returns></returns>
InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId);
/// <summary>
/// Installs a packages files
/// </summary>
/// <param name="packageDefinition"></param>
/// <param name="compiledPackage"></param>
/// <param name="userId"></param>
/// <returns></returns>
IEnumerable<string> InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId);
/// <summary>
/// Reads the package (zip) file and returns the <see cref="CompiledPackage"/> model
/// </summary>
/// <param name="packageFile"></param>
/// <returns></returns>
CompiledPackage ReadPackage(FileInfo packageFile);
}
}
@@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Packaging.Models
{
[Serializable]
[DataContract(IsReference = true)]
public class UninstallationSummary
{
public MetaData MetaData { get; set; }
public IEnumerable<IDataType> DataTypesUninstalled { get; set; }
public IEnumerable<ILanguage> LanguagesUninstalled { get; set; }
public IEnumerable<IDictionaryItem> DictionaryItemsUninstalled { get; set; }
public IEnumerable<IMacro> MacrosUninstalled { get; set; }
public IEnumerable<string> FilesUninstalled { get; set; }
public IEnumerable<ITemplate> TemplatesUninstalled { get; set; }
public IEnumerable<IContentType> ContentTypesUninstalled { get; set; }
public IEnumerable<IFile> StylesheetsUninstalled { get; set; }
public IEnumerable<IContent> ContentUninstalled { get; set; }
public bool PackageUninstalled { get; set; }
}
internal static class UninstallationSummaryExtentions
{
public static UninstallationSummary InitEmpty(this UninstallationSummary summary)
{
summary.ContentUninstalled = new List<IContent>();
summary.ContentTypesUninstalled = new List<IContentType>();
summary.DataTypesUninstalled = new List<IDataType>();
summary.DictionaryItemsUninstalled = new List<IDictionaryItem>();
summary.FilesUninstalled = new List<string>();
summary.LanguagesUninstalled = new List<ILanguage>();
summary.MacrosUninstalled = new List<IMacro>();
summary.MetaData = new MetaData();
summary.TemplatesUninstalled = new List<ITemplate>();
summary.PackageUninstalled = false;
return summary;
}
}
}
@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Logging;
using Umbraco.Core._Legacy.PackageActions;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Package actions are executed on packge install / uninstall.
/// </summary>
internal class PackageActionRunner : IPackageActionRunner
{
private readonly ILogger _logger;
private readonly PackageActionCollection _packageActions;
public PackageActionRunner(ILogger logger, PackageActionCollection packageActions)
{
_logger = logger;
_packageActions = packageActions;
}
/// <inheritdoc />
public bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors)
{
var e = new List<string>();
foreach (var ipa in _packageActions)
{
try
{
if (ipa.Alias() == actionAlias)
ipa.Execute(packageName, actionXml);
}
catch (Exception ex)
{
e.Add($"{ipa.Alias()} - {ex.Message}");
_logger.Error<PackageActionRunner>(ex, "Error loading package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName);
}
}
errors = e;
return e.Count == 0;
}
/// <inheritdoc />
public bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable<string> errors)
{
var e = new List<string>();
foreach (var ipa in _packageActions)
{
try
{
if (ipa.Alias() == actionAlias)
ipa.Undo(packageName, actionXml);
}
catch (Exception ex)
{
e.Add($"{ipa.Alias()} - {ex.Message}");
_logger.Error<PackageActionRunner>(ex, "Error undoing package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName);
}
}
errors = e;
return e.Count == 0;
}
}
}
@@ -1,295 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using Umbraco.Core.Composing;
using Umbraco.Core.Logging;
namespace Umbraco.Core.Packaging
{
// Note
// That class uses ReflectionOnlyLoad which does NOT handle policies (bindingRedirect) and
// therefore raised warnings when installing a package, if an exact dependency could not be
// found, though it would be found via policies. So we have to explicitely apply policies
// where appropriate.
internal class PackageBinaryInspector : MarshalByRefObject
{
/// <summary>
/// Entry point to call from your code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblys"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// Will perform the assembly scan in a separate app domain
/// </remarks>
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(IEnumerable<byte[]> assemblys, out string[] errorReport)
{
// need to wrap in a safe call context in order to prevent whatever Umbraco stores
// in the logical call context from flowing to the created app domain (eg, the
// UmbracoDatabase is *not* serializable and cannot and should not flow).
using (new SafeCallContext())
{
var appDomain = GetTempAppDomain();
var type = typeof(PackageBinaryInspector);
try
{
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(assemblys.ToArray(), out errorReport);
return result;
}
finally
{
AppDomain.Unload(appDomain);
}
}
}
/// <summary>
/// Entry point to call from your code
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dllPath"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// Will perform the assembly scan in a separate app domain
/// </remarks>
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(string dllPath, out string[] errorReport)
{
var appDomain = GetTempAppDomain();
var type = typeof(PackageBinaryInspector);
try
{
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
// do NOT turn PerformScan into static (even if ReSharper says so)!
var result = value.PerformScan<T>(dllPath, out errorReport);
return result;
}
finally
{
AppDomain.Unload(appDomain);
}
}
/// <summary>
/// Performs the assembly scanning
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="assemblies"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// This method is executed in a separate app domain
/// </remarks>
private IEnumerable<string> PerformScan<T>(IEnumerable<byte[]> assemblies, out string[] errorReport)
{
//we need this handler to resolve assembly dependencies when loading below
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
var a = Assembly.ReflectionOnlyLoad(name);
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
return a;
};
//First load each dll file into the context
// do NOT apply policy here: we want to scan the dlls that are in the binaries
var loaded = assemblies.Select(Assembly.ReflectionOnlyLoad).ToList();
//scan
return PerformScan<T>(loaded, out errorReport);
}
/// <summary>
/// Performs the assembly scanning
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="dllPath"></param>
/// <param name="errorReport"></param>
/// <returns></returns>
/// <remarks>
/// This method is executed in a separate app domain
/// </remarks>
private IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
{
if (Directory.Exists(dllPath) == false)
{
throw new DirectoryNotFoundException("Could not find directory " + dllPath);
}
//we need this handler to resolve assembly dependencies when loading below
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
{
var name = AppDomain.CurrentDomain.ApplyPolicy(e.Name);
var a = Assembly.ReflectionOnlyLoad(name);
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
return a;
};
//First load each dll file into the context
// do NOT apply policy here: we want to scan the dlls that are in the path
var files = Directory.GetFiles(dllPath, "*.dll");
var loaded = files.Select(Assembly.ReflectionOnlyLoadFrom).ToList();
//scan
return PerformScan<T>(loaded, out errorReport);
}
private static IEnumerable<string> PerformScan<T>(IList<Assembly> loaded, out string[] errorReport)
{
var dllsWithReference = new List<string>();
var errors = new List<string>();
var assembliesWithErrors = new List<Assembly>();
//load each of the LoadFrom assemblies into the Load context too
foreach (var a in loaded)
{
var name = AppDomain.CurrentDomain.ApplyPolicy(a.FullName);
Assembly.ReflectionOnlyLoad(name);
}
//get the list of assembly names to compare below
var loadedNames = loaded.Select(x => x.GetName().Name).ToArray();
//Then load each referenced assembly into the context
foreach (var a in loaded)
{
//don't load any referenced assemblies that are already found in the loaded array - this is based on name
// regardless of version. We'll assume that if the assembly found in the folder matches the assembly name
// being looked for, that is the version the user has shipped their package with and therefore it 'must' be correct
foreach (var assemblyName in a.GetReferencedAssemblies().Where(ass => loadedNames.Contains(ass.Name) == false))
{
try
{
var name = AppDomain.CurrentDomain.ApplyPolicy(assemblyName.FullName);
Assembly.ReflectionOnlyLoad(name);
}
catch (FileNotFoundException)
{
//if an exception occurs it means that a referenced assembly could not be found
errors.Add(
string.Concat("This package references the assembly '",
assemblyName.Name,
"' which was not found"));
assembliesWithErrors.Add(a);
}
catch (Exception ex)
{
//if an exception occurs it means that a referenced assembly could not be found
errors.Add(
string.Concat("This package could not be verified for compatibility. An error occurred while loading a referenced assembly '",
assemblyName.Name,
"' see error log for full details."));
assembliesWithErrors.Add(a);
Current.Logger.Error<PackageBinaryInspector>(ex, "An error occurred scanning package assembly '{AssemblyName}'", assemblyName.FullName);
}
}
}
var contractType = GetLoadFromContractType<T>();
//now that we have all referenced types into the context we can look up stuff
foreach (var a in loaded.Except(assembliesWithErrors))
{
//now we need to see if they contain any type 'T'
var reflectedAssembly = a;
try
{
var found = reflectedAssembly.GetExportedTypes()
.Where(contractType.IsAssignableFrom);
if (found.Any())
{
dllsWithReference.Add(reflectedAssembly.FullName);
}
}
catch (Exception ex)
{
//This is a hack that nobody can seem to get around, I've read everything and it seems that
// this is quite a common thing when loading types into reflection only load context, so
// we're just going to ignore this specific one for now
var typeLoadEx = ex as TypeLoadException;
if (typeLoadEx != null)
{
if (typeLoadEx.Message.InvariantContains("does not have an implementation"))
{
//ignore
continue;
}
}
else
{
errors.Add(
string.Concat("This package could not be verified for compatibility. An error occurred while scanning a packaged assembly '",
a.GetName().Name,
"' see error log for full details."));
assembliesWithErrors.Add(a);
Current.Logger.Error<PackageBinaryInspector>(ex, "An error occurred scanning package assembly '{AssemblyName}'", a.GetName().FullName);
}
}
}
errorReport = errors.ToArray();
return dllsWithReference;
}
/// <summary>
/// In order to compare types, the types must be in the same context, this method will return the type that
/// we are checking against but from the Load context.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
private static Type GetLoadFromContractType<T>()
{
var name = AppDomain.CurrentDomain.ApplyPolicy(typeof(T).Assembly.FullName);
var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoad(name);
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
if (contractType == null)
{
throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies");
}
return contractType;
}
/// <summary>
/// Create an app domain
/// </summary>
/// <returns></returns>
private static AppDomain GetTempAppDomain()
{
//copy the current app domain setup but don't shadow copy files
var appName = "TempDomain" + Guid.NewGuid();
var domainSetup = new AppDomainSetup
{
ApplicationName = appName,
ShadowCopyFiles = "false",
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase,
LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile,
LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization,
PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath,
PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe
};
// create new domain with full trust
return AppDomain.CreateDomain(appName, AppDomain.CurrentDomain.Evidence, domainSetup, new PermissionSet(PermissionState.Unrestricted));
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Umbraco.Core.Logging;
using Umbraco.Core.Models.Packaging;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Converts a <see cref="PackageDefinition"/> to and from XML
/// </summary>
public class PackageDefinitionXmlParser
{
private readonly ILogger _logger;
public PackageDefinitionXmlParser(ILogger logger)
{
_logger = logger;
}
public PackageDefinition ToPackageDefinition(XElement xml)
{
if (xml == null) return null;
var retVal = new PackageDefinition
{
Id = xml.AttributeValue<int>("id"),
Name = xml.AttributeValue<string>("name") ?? string.Empty,
PackagePath = xml.AttributeValue<string>("packagePath") ?? string.Empty,
Version = xml.AttributeValue<string>("version") ?? string.Empty,
Url = xml.AttributeValue<string>("url") ?? string.Empty,
PackageId = xml.AttributeValue<Guid>("packageGuid"),
IconUrl = xml.AttributeValue<string>("iconUrl") ?? string.Empty,
UmbracoVersion = xml.AttributeValue<Version>("umbVersion"),
License = xml.Element("license")?.Value ?? string.Empty,
LicenseUrl = xml.Element("license")?.AttributeValue<string>("url") ?? string.Empty,
Author = xml.Element("author")?.Value ?? string.Empty,
AuthorUrl = xml.Element("author")?.AttributeValue<string>("url") ?? string.Empty,
Readme = xml.Element("readme")?.Value ?? string.Empty,
Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? "<actions></actions>", //take the entire outer xml value
ContentNodeId = xml.Element("content")?.AttributeValue<string>("nodeId") ?? string.Empty,
ContentLoadChildNodes = xml.Element("content")?.AttributeValue<bool>("loadChildNodes") ?? false,
Macros = xml.Element("macros")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
Templates = xml.Element("templates")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
Stylesheets = xml.Element("stylesheets")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
DocumentTypes = xml.Element("documentTypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
Languages = xml.Element("languages")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
DataTypes = xml.Element("datatypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List<string>(),
Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List<string>(),
Control = xml.Element("loadcontrol")?.Value ?? string.Empty
};
return retVal;
}
public XElement ToXml(PackageDefinition def)
{
var actionsXml = new XElement("actions");
try
{
actionsXml = XElement.Parse(def.Actions);
}
catch (Exception e)
{
_logger.Warn<PackagesRepository>(e, "Could not add package actions to the package xml definition, the xml did not parse");
}
var packageXml = new XElement("package",
new XAttribute("id", def.Id),
new XAttribute("version", def.Version ?? string.Empty),
new XAttribute("url", def.Url ?? string.Empty),
new XAttribute("name", def.Name ?? string.Empty),
new XAttribute("packagePath", def.PackagePath ?? string.Empty),
new XAttribute("iconUrl", def.IconUrl ?? string.Empty),
new XAttribute("umbVersion", def.UmbracoVersion),
new XAttribute("packageGuid", def.PackageId),
new XElement("license",
new XCData(def.License ?? string.Empty),
new XAttribute("url", def.LicenseUrl ?? string.Empty)),
new XElement("author",
new XCData(def.Author ?? string.Empty),
new XAttribute("url", def.AuthorUrl ?? string.Empty)),
new XElement("readme", new XCData(def.Readme ?? string.Empty)),
actionsXml,
new XElement("datatypes", string.Join(",", def.DataTypes ?? Array.Empty<string>())),
new XElement("content",
new XAttribute("nodeId", def.ContentNodeId ?? string.Empty),
new XAttribute("loadChildNodes", def.ContentLoadChildNodes)),
new XElement("templates", string.Join(",", def.Templates ?? Array.Empty<string>())),
new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty<string>())),
new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty<string>())),
new XElement("macros", string.Join(",", def.Macros ?? Array.Empty<string>())),
new XElement("files", (def.Files ?? Array.Empty<string>()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))),
new XElement("languages", string.Join(",", def.Languages ?? Array.Empty<string>())),
new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty<string>())),
new XElement("loadcontrol", def.Control ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view
return packageXml;
}
}
}
+35 -41
View File
@@ -6,15 +6,15 @@ using System.IO.Compression;
namespace Umbraco.Core.Packaging
{
internal class PackageExtraction : IPackageExtraction
internal class PackageExtraction
{
public string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage)
public string ReadTextFileFromArchive(FileInfo packageFile, string fileToRead, out string directoryInPackage)
{
string retVal = null;
bool fileFound = false;
string foundDir = null;
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFile, entry =>
{
string fileName = Path.GetFileName(entry.Name);
@@ -36,55 +36,50 @@ namespace Umbraco.Core.Packaging
if (fileFound == false)
{
directoryInPackage = null;
throw new FileNotFoundException(string.Format("Could not find file in package {0}", packageFilePath), fileToRead);
throw new FileNotFoundException($"Could not find file in package {packageFile}", fileToRead);
}
directoryInPackage = foundDir;
return retVal;
}
private static void CheckPackageExists(string packageFilePath)
private static void CheckPackageExists(FileInfo packageFile)
{
if (string.IsNullOrEmpty(packageFilePath))
{
throw new ArgumentNullException("packageFilePath");
}
if (packageFile == null) throw new ArgumentNullException(nameof(packageFile));
if (!packageFile.Exists)
throw new ArgumentException($"Package file: {packageFile} could not be found");
if (File.Exists(packageFilePath) == false)
{
if (File.Exists(packageFilePath) == false)
throw new ArgumentException(string.Format("Package file: {0} could not be found", packageFilePath));
}
string extension = Path.GetExtension(packageFilePath).ToLower();
var extension = packageFile.Extension;
var alowedExtension = new[] { ".umb", ".zip" };
// Check if the file is a valid package
if (alowedExtension.All(ae => ae.Equals(extension) == false))
if (alowedExtension.All(ae => ae.InvariantEquals(extension) == false))
{
throw new ArgumentException(
string.Format("Error - file isn't a package. only extentions: \"{0}\" is allowed", string.Join(", ", alowedExtension)));
throw new ArgumentException("Error - file isn't a package. only extentions: \"{string.Join(", ", alowedExtension)}\" is allowed");
}
}
public void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath)
public void CopyFileFromArchive(FileInfo packageFile, string fileInPackageName, string destinationfilePath)
{
CopyFilesFromArchive(packageFilePath, new[]{new KeyValuePair<string, string>(fileInPackageName, destinationfilePath) } );
CopyFilesFromArchive(packageFile, new[] {(fileInPackageName, destinationfilePath)});
}
public void CopyFilesFromArchive(string packageFilePath, IEnumerable<KeyValuePair<string, string>> sourceDestination)
public void CopyFilesFromArchive(FileInfo packageFile, IEnumerable<(string packageUniqueFile, string appAbsolutePath)> sourceDestination)
{
var d = sourceDestination.ToDictionary(k => k.Key.ToLower(), v => v.Value);
var d = sourceDestination.ToDictionary(k => k.packageUniqueFile.ToLower(), v => v.appAbsolutePath);
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFile, entry =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
var fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
if (fileName == string.Empty) { return true; }
string destination;
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out destination))
if (string.IsNullOrEmpty(fileName) == false && d.TryGetValue(fileName, out var destination))
{
//ensure the dir exists
Directory.CreateDirectory(Path.GetDirectoryName(destination));
using (var streamWriter = File.Open(destination, FileMode.Create))
using (var entryStream = entry.Open())
{
@@ -99,15 +94,15 @@ namespace Umbraco.Core.Packaging
if (d.Any())
{
throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFilePath));
throw new ArgumentException(string.Format("The following source file(s): \"{0}\" could not be found in archive: \"{1}\"", string.Join("\", \"",d.Keys), packageFile));
}
}
public IEnumerable<string> FindMissingFiles(string packageFilePath, IEnumerable<string> expectedFiles)
public IEnumerable<string> FindMissingFiles(FileInfo packageFile, IEnumerable<string> expectedFiles)
{
var retVal = expectedFiles.ToList();
ReadZipfileEntries(packageFilePath, zipEntry =>
ReadZipfileEntries(packageFile, zipEntry =>
{
string fileName = Path.GetFileName(zipEntry.Name);
@@ -121,17 +116,16 @@ namespace Umbraco.Core.Packaging
}
public IEnumerable<string> FindDubletFileNames(string packageFilePath)
public IEnumerable<string> FindDuplicateFileNames(FileInfo packageFile)
{
var dictionary = new Dictionary<string, List<string>>();
ReadZipfileEntries(packageFilePath, entry =>
ReadZipfileEntries(packageFile, entry =>
{
string fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
var fileName = (Path.GetFileName(entry.Name) ?? string.Empty).ToLower();
List<string> list;
if (dictionary.TryGetValue(fileName, out list) == false)
if (dictionary.TryGetValue(fileName, out var list) == false)
{
list = new List<string>();
dictionary.Add(fileName, list);
@@ -145,13 +139,13 @@ namespace Umbraco.Core.Packaging
return dictionary.Values.Where(v => v.Count > 1).SelectMany(v => v);
}
public IEnumerable<byte[]> ReadFilesFromArchive(string packageFilePath, IEnumerable<string> filesToGet)
public IEnumerable<byte[]> ReadFilesFromArchive(FileInfo packageFile, IEnumerable<string> filesToGet)
{
CheckPackageExists(packageFilePath);
CheckPackageExists(packageFile);
var files = new HashSet<string>(filesToGet.Select(f => f.ToLowerInvariant()));
using (var fs = File.OpenRead(packageFilePath))
using (var fs = packageFile.OpenRead())
using (var zipArchive = new ZipArchive(fs))
{
foreach (var zipEntry in zipArchive.Entries)
@@ -172,11 +166,11 @@ namespace Umbraco.Core.Packaging
}
}
private void ReadZipfileEntries(string packageFilePath, Func<ZipArchiveEntry, bool> entryFunc, bool skipsDirectories = true)
private void ReadZipfileEntries(FileInfo packageFile, Func<ZipArchiveEntry, bool> entryFunc, bool skipsDirectories = true)
{
CheckPackageExists(packageFilePath);
CheckPackageExists(packageFile);
using (var fs = File.OpenRead(packageFilePath))
using (var fs = packageFile.OpenRead())
using (var zipArchive = new ZipArchive(fs))
{
foreach (var zipEntry in zipArchive.Entries)
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Services;
using File = System.IO.File;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Installs package files
/// </summary>
internal class PackageFileInstallation
{
private readonly CompiledPackageXmlParser _parser;
private readonly IProfilingLogger _logger;
private readonly PackageExtraction _packageExtraction;
public PackageFileInstallation(CompiledPackageXmlParser parser, IProfilingLogger logger)
{
_parser = parser;
_logger = logger;
_packageExtraction = new PackageExtraction();
}
/// <summary>
/// Returns a list of all installed file paths
/// </summary>
/// <param name="compiledPackage"></param>
/// <param name="packageFile"></param>
/// <param name="targetRootFolder">
/// The absolute path of where to extract the package files (normally the application root)
/// </param>
/// <returns></returns>
public IEnumerable<string> InstallFiles(CompiledPackage compiledPackage, FileInfo packageFile, string targetRootFolder)
{
using (_logger.DebugDuration<PackageFileInstallation>(
"Installing package files for package " + compiledPackage.Name,
"Package file installation complete for package " + compiledPackage.Name))
{
var sourceAndRelativeDest = _parser.ExtractSourceDestinationFileInformation(compiledPackage.Files);
var sourceAndAbsDest = AppendRootToDestination(targetRootFolder, sourceAndRelativeDest);
_packageExtraction.CopyFilesFromArchive(packageFile, sourceAndAbsDest);
return sourceAndRelativeDest.Select(sd => sd.appRelativePath).ToArray();
}
}
public IEnumerable<string> UninstallFiles(PackageDefinition package)
{
var removedFiles = new List<string>();
foreach (var item in package.Files.ToArray())
{
removedFiles.Add(item.GetRelativePath());
//here we need to try to find the file in question as most packages does not support the tilde char
var file = IOHelper.FindFile(item);
if (file != null)
{
//TODO: Surely this should be ~/ ?
file = file.EnsureStartsWith("/");
var filePath = IOHelper.MapPath(file);
if (File.Exists(filePath))
File.Delete(filePath);
}
package.Files.Remove(file);
}
return removedFiles;
}
private static IEnumerable<(string packageUniqueFile, string appAbsolutePath)> AppendRootToDestination(string applicationRootFolder, IEnumerable<(string packageUniqueFile, string appRelativePath)> sourceDestination)
{
return
sourceDestination.Select(
sd => (sd.packageUniqueFile, Path.Combine(applicationRootFolder, sd.appRelativePath))).ToArray();
}
}
}
@@ -0,0 +1,9 @@
namespace Umbraco.Core.Packaging
{
public enum PackageInstallType
{
AlreadyInstalled,
NewInstall,
Upgrade
}
}
+166 -543
View File
@@ -1,585 +1,208 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Services;
using File = System.IO.File;
namespace Umbraco.Core.Packaging
{
internal class PackageInstallation : IPackageInstallation
{
private readonly IFileService _fileService;
private readonly IMacroService _macroService;
private readonly IPackagingService _packagingService;
private IConflictingPackageData _conflictingPackageData;
private readonly IPackageExtraction _packageExtraction;
private string _fullPathToRoot;
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
IFileService fileService, IPackageExtraction packageExtraction)
: this(packagingService, macroService, fileService, packageExtraction, IOHelper.GetRootDirectorySafe())
{}
public PackageInstallation(IPackagingService packagingService, IMacroService macroService,
IFileService fileService, IPackageExtraction packageExtraction, string fullPathToRoot)
{
if (packageExtraction != null) _packageExtraction = packageExtraction;
else throw new ArgumentNullException("packageExtraction");
if (macroService != null) _macroService = macroService;
else throw new ArgumentNullException("macroService");
if (fileService != null) _fileService = fileService;
else throw new ArgumentNullException("fileService");
if (packagingService != null) _packagingService = packagingService;
else throw new ArgumentNullException("packagingService");
_fullPathToRoot = fullPathToRoot;
}
public IConflictingPackageData ConflictingPackageData
{
private get
{
return _conflictingPackageData ??
(_conflictingPackageData = new ConflictingPackageData(_macroService, _fileService));
}
set
{
if (_conflictingPackageData != null)
{
throw new PropertyConstraintException("This property already have a value");
}
_conflictingPackageData = value;
}
}
public string FullPathToRoot
{
private get { return _fullPathToRoot; }
set
{
if (_fullPathToRoot != null)
{
throw new PropertyConstraintException("This property already have a value");
}
_fullPathToRoot = value;
}
}
public MetaData GetMetaData(string packageFilePath)
{
try
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
return GetMetaData(rootElement);
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFilePath, e);
}
}
public PreInstallWarnings GetPreInstallWarnings(string packageFilePath)
{
try
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
return GetPreInstallWarnings(rootElement);
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFilePath, e);
}
}
public InstallationSummary InstallPackage(string packageFile, int userId)
{
XElement dataTypes;
XElement languages;
XElement dictionaryItems;
XElement macroes;
XElement files;
XElement templates;
XElement documentTypes;
XElement styleSheets;
XElement documentSet;
XElement documents;
XElement actions;
MetaData metaData;
InstallationSummary installationSummary;
try
{
XElement rootElement = GetConfigXmlElement(packageFile);
PackageSupportedCheck(rootElement);
PackageStructureSanetyCheck(packageFile, rootElement);
dataTypes = rootElement.Element(Constants.Packaging.DataTypesNodeName);
languages = rootElement.Element(Constants.Packaging.LanguagesNodeName);
dictionaryItems = rootElement.Element(Constants.Packaging.DictionaryItemsNodeName);
macroes = rootElement.Element(Constants.Packaging.MacrosNodeName);
files = rootElement.Element(Constants.Packaging.FilesNodeName);
templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
documentTypes = rootElement.Element(Constants.Packaging.DocumentTypesNodeName);
styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
documentSet = rootElement.Element(Constants.Packaging.DocumentSetNodeName);
documents = rootElement.Element(Constants.Packaging.DocumentsNodeName);
actions = rootElement.Element(Constants.Packaging.ActionsNodeName);
metaData = GetMetaData(rootElement);
installationSummary = new InstallationSummary {MetaData = metaData};
}
catch (Exception e)
{
throw new Exception("Error reading " + packageFile, e);
}
try
{
var dataTypeDefinitions = EmptyEnumerableIfNull<IDataType>(dataTypes) ?? InstallDataTypes(dataTypes, userId);
installationSummary.DataTypesInstalled = dataTypeDefinitions;
var languagesInstalled = EmptyEnumerableIfNull<ILanguage>(languages) ?? InstallLanguages(languages, userId);
installationSummary.LanguagesInstalled = languagesInstalled;
var dictionaryInstalled = EmptyEnumerableIfNull<IDictionaryItem>(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems);
installationSummary.DictionaryItemsInstalled = dictionaryInstalled;
var macros = EmptyEnumerableIfNull<IMacro>(macroes) ?? InstallMacros(macroes, userId);
installationSummary.MacrosInstalled = macros;
var keyValuePairs = EmptyEnumerableIfNull<string>(packageFile) ?? InstallFiles(packageFile, files);
installationSummary.FilesInstalled = keyValuePairs;
var templatesInstalled = EmptyEnumerableIfNull<ITemplate>(templates) ?? InstallTemplats(templates, userId);
installationSummary.TemplatesInstalled = templatesInstalled;
var documentTypesInstalled = EmptyEnumerableIfNull<IContentType>(documentTypes) ?? InstallDocumentTypes(documentTypes, userId);
installationSummary.ContentTypesInstalled =documentTypesInstalled;
var stylesheetsInstalled = EmptyEnumerableIfNull<IFile>(styleSheets) ?? InstallStylesheets(styleSheets);
installationSummary.StylesheetsInstalled = stylesheetsInstalled;
var documentsInstalled = documents != null ? InstallDocuments(documents, userId)
: EmptyEnumerableIfNull<IContent>(documentSet)
?? InstallDocuments(documentSet, userId);
installationSummary.ContentInstalled = documentsInstalled;
var packageActions = EmptyEnumerableIfNull<PackageAction>(actions) ?? GetPackageActions(actions, metaData.Name);
installationSummary.Actions = packageActions;
installationSummary.PackageInstalled = true;
return installationSummary;
}
catch (Exception e)
{
throw new Exception("Error installing package " + packageFile, e);
}
}
private readonly PackageExtraction _packageExtraction;
private readonly PackageDataInstallation _packageDataInstallation;
private readonly PackageFileInstallation _packageFileInstallation;
private readonly CompiledPackageXmlParser _parser;
private readonly IPackageActionRunner _packageActionRunner;
private readonly DirectoryInfo _applicationRootFolder;
/// <summary>
/// Temperary check to test that we support stylesheets
/// Constructor
/// </summary>
/// <param name="rootElement"></param>
private void PackageSupportedCheck(XElement rootElement)
/// <param name="packageDataInstallation"></param>
/// <param name="packageFileInstallation"></param>
/// <param name="parser"></param>
/// <param name="packageActionRunner"></param>
/// <param name="applicationRootFolder">
/// The root folder of the application
/// </param>
public PackageInstallation(PackageDataInstallation packageDataInstallation, PackageFileInstallation packageFileInstallation, CompiledPackageXmlParser parser, IPackageActionRunner packageActionRunner,
DirectoryInfo applicationRootFolder)
{
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
if (styleSheets != null && styleSheets.Elements().Any())
throw new NotSupportedException("Stylesheets is not suported in this version of umbraco");
_packageExtraction = new PackageExtraction();
_packageFileInstallation = packageFileInstallation ?? throw new ArgumentNullException(nameof(packageFileInstallation));
_packageDataInstallation = packageDataInstallation ?? throw new ArgumentNullException(nameof(packageDataInstallation));
_parser = parser ?? throw new ArgumentNullException(nameof(parser));
_packageActionRunner = packageActionRunner ?? throw new ArgumentNullException(nameof(packageActionRunner));
_applicationRootFolder = applicationRootFolder ?? throw new ArgumentNullException(nameof(applicationRootFolder));
}
private static T[] EmptyArrayIfNull<T>(object obj)
public CompiledPackage ReadPackage(FileInfo packageFile)
{
return obj == null ? new T[0] : null;
if (packageFile == null) throw new ArgumentNullException(nameof(packageFile));
var doc = GetConfigXmlDoc(packageFile);
var compiledPackage = _parser.ToCompiledPackage(doc, packageFile, _applicationRootFolder.FullName);
ValidatePackageFile(packageFile, compiledPackage);
return compiledPackage;
}
private static IEnumerable<T> EmptyEnumerableIfNull<T>(object obj)
public IEnumerable<string> InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId)
{
return obj == null ? Enumerable.Empty<T>() : null;
if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition));
if (compiledPackage == null) throw new ArgumentNullException(nameof(compiledPackage));
//these should be the same, TODO: we should have a better validator for this
if (packageDefinition.Name != compiledPackage.Name)
throw new InvalidOperationException("The package definition does not match the compiled package manifest");
var packageZipFile = compiledPackage.PackageFile;
var files = _packageFileInstallation.InstallFiles(compiledPackage, packageZipFile, _applicationRootFolder.FullName).ToList();
packageDefinition.Files = files;
return files;
}
private XDocument GetConfigXmlDoc(string packageFilePath)
/// <inheritdoc />
public UninstallationSummary UninstallPackage(PackageDefinition package, int userId)
{
var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFilePath,
Constants.Packaging.PackageXmlFileName, out _);
//running this will update the PackageDefinition with the items being removed
var summary = _packageDataInstallation.UninstallPackageData(package, userId);
return XDocument.Parse(configXmlContent);
summary.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(package.Actions), package.Name);
//run actions before files are removed
summary.ActionErrors = UndoPackageActions(package, summary.Actions).ToList();
var filesRemoved = _packageFileInstallation.UninstallFiles(package);
summary.FilesUninstalled = filesRemoved;
return summary;
}
public XElement GetConfigXmlElement(string packageFilePath)
public InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId)
{
XDocument document = GetConfigXmlDoc(packageFilePath);
var installationSummary = new InstallationSummary
{
DataTypesInstalled = _packageDataInstallation.ImportDataTypes(compiledPackage.DataTypes.ToList(), userId),
LanguagesInstalled = _packageDataInstallation.ImportLanguages(compiledPackage.Languages, userId),
DictionaryItemsInstalled = _packageDataInstallation.ImportDictionaryItems(compiledPackage.DictionaryItems, userId),
MacrosInstalled = _packageDataInstallation.ImportMacros(compiledPackage.Macros, userId),
TemplatesInstalled = _packageDataInstallation.ImportTemplates(compiledPackage.Templates.ToList(), userId),
DocumentTypesInstalled = _packageDataInstallation.ImportDocumentTypes(compiledPackage.DocumentTypes, userId)
};
//we need a reference to the imported doc types to continue
var importedDocTypes = installationSummary.DocumentTypesInstalled.ToDictionary(x => x.Alias, x => x);
installationSummary.StylesheetsInstalled = _packageDataInstallation.ImportStylesheets(compiledPackage.Stylesheets, userId);
installationSummary.ContentInstalled = _packageDataInstallation.ImportContent(compiledPackage.Documents, importedDocTypes, userId);
installationSummary.Actions = CompiledPackageXmlParser.GetPackageActions(XElement.Parse(compiledPackage.Actions), compiledPackage.Name);
installationSummary.MetaData = compiledPackage;
installationSummary.FilesInstalled = packageDefinition.Files;
//make sure the definition is up to date with everything
foreach (var x in installationSummary.DataTypesInstalled) packageDefinition.DataTypes.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.LanguagesInstalled) packageDefinition.Languages.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.DictionaryItemsInstalled) packageDefinition.DictionaryItems.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.MacrosInstalled) packageDefinition.Macros.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.TemplatesInstalled) packageDefinition.Templates.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.DocumentTypesInstalled) packageDefinition.DocumentTypes.Add(x.Id.ToInvariantString());
foreach (var x in installationSummary.StylesheetsInstalled) packageDefinition.Stylesheets.Add(x.Id.ToInvariantString());
var contentInstalled = installationSummary.ContentInstalled.ToList();
packageDefinition.ContentNodeId = contentInstalled.Count > 0 ? contentInstalled[0].Id.ToInvariantString() : null;
//run package actions
installationSummary.ActionErrors = RunPackageActions(packageDefinition, installationSummary.Actions).ToList();
return installationSummary;
}
private IEnumerable<string> RunPackageActions(PackageDefinition packageDefinition, IEnumerable<PackageAction> actions)
{
foreach (var n in actions)
{
//if there is an undo section then save it to the definition so we can run it at uninstallation
var undo = n.Undo;
if (undo)
packageDefinition.Actions += n.XmlData.ToString();
//Run the actions tagged only for 'install'
if (n.RunAt != ActionRunAt.Install) continue;
if (n.Alias.IsNullOrWhiteSpace()) continue;
//run the actions and report errors
if (!_packageActionRunner.RunPackageAction(packageDefinition.Name, n.Alias, n.XmlData, out var err))
foreach (var e in err) yield return e;
}
}
private IEnumerable<string> UndoPackageActions(IPackageInfo packageDefinition, IEnumerable<PackageAction> actions)
{
foreach (var n in actions)
{
//Run the actions tagged only for 'uninstall'
if (n.RunAt != ActionRunAt.Uninstall) continue;
if (n.Alias.IsNullOrWhiteSpace()) continue;
//run the actions and report errors
if (!_packageActionRunner.UndoPackageAction(packageDefinition.Name, n.Alias, n.XmlData, out var err))
foreach (var e in err) yield return e;
}
}
private XDocument GetConfigXmlDoc(FileInfo packageFile)
{
var configXmlContent = _packageExtraction.ReadTextFileFromArchive(packageFile, "package.xml", out _);
var document = XDocument.Parse(configXmlContent);
if (document.Root == null ||
document.Root.Name.LocalName.Equals(Constants.Packaging.UmbPackageNodeName) == false)
document.Root.Name.LocalName.Equals("umbPackage") == false)
throw new FormatException("xml does not have a root node called \"umbPackage\"");
return document;
}
private void ValidatePackageFile(FileInfo packageFile, CompiledPackage package)
{
if (!(package.Files?.Count > 0)) return;
var sourceDestination = _parser.ExtractSourceDestinationFileInformation(package.Files).ToArray();
var missingFiles = _packageExtraction.FindMissingFiles(packageFile, sourceDestination.Select(i => i.packageUniqueFile)).ToArray();
if (missingFiles.Any())
{
throw new ArgumentException("xml does not have a root node called \"umbPackage\"", packageFilePath);
throw new Exception("The following file(s) are missing in the package: " +
string.Join(", ", missingFiles.Select(
mf =>
{
var (packageUniqueFile, appRelativePath) = sourceDestination.Single(fi => fi.packageUniqueFile == mf);
return $"source: \"{packageUniqueFile}\" destination: \"{appRelativePath}\"";
})));
}
return document.Root;
}
internal void PackageStructureSanetyCheck(string packageFilePath)
{
XElement rootElement = GetConfigXmlElement(packageFilePath);
PackageStructureSanetyCheck(packageFilePath, rootElement);
}
IEnumerable<string> duplicates = _packageExtraction.FindDuplicateFileNames(packageFile).ToArray();
private void PackageStructureSanetyCheck(string packageFilePath, XElement rootElement)
{
XElement filesElement = rootElement.Element(Constants.Packaging.FilesNodeName);
if (filesElement != null)
if (duplicates.Any())
{
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement).ToArray();
var missingFiles = _packageExtraction.FindMissingFiles(packageFilePath, sourceDestination.Select(i => i.Key)).ToArray();
if (missingFiles.Any())
{
throw new Exception("The following file(s) are missing in the package: " +
string.Join(", ", missingFiles.Select(
mf =>
{
var sd = sourceDestination.Single(fi => fi.Key == mf);
return string.Format("source: \"{0}\" destination: \"{1}\"",
sd.Key, sd.Value);
})));
}
IEnumerable<string> dubletFileNames = _packageExtraction.FindDubletFileNames(packageFilePath).ToArray();
if (dubletFileNames.Any())
{
throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " +
string.Join(", ", dubletFileNames));
}
throw new Exception("The following filename(s) are found more than one time in the package, since the filename is used ad primary key, this is not allowed: " +
string.Join(", ", duplicates));
}
}
private static IEnumerable<PackageAction> GetPackageActions(XElement actionsElement, string packageName)
{
if (actionsElement == null) { return new PackageAction[0]; }
if (string.Equals(Constants.Packaging.ActionsNodeName, actionsElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.ActionsNodeName + "\" as root",
"actionsElement");
}
return actionsElement.Elements(Constants.Packaging.ActionNodeName)
.Select(elemet =>
{
XAttribute aliasAttr = elemet.Attribute(Constants.Packaging.AliasNodeNameCapital);
if (aliasAttr == null)
throw new ArgumentException(
"missing \"" + Constants.Packaging.AliasNodeNameCapital + "\" atribute in alias element",
"actionsElement");
var packageAction = new PackageAction
{
XmlData = elemet,
Alias = aliasAttr.Value,
PackageName = packageName,
};
var attr = elemet.Attribute(Constants.Packaging.RunatNodeAttribute);
if (attr != null && Enum.TryParse(attr.Value, true, out ActionRunAt runAt)) { packageAction.RunAt = runAt; }
attr = elemet.Attribute(Constants.Packaging.UndoNodeAttribute);
if (attr != null && bool.TryParse(attr.Value, out var undo)) { packageAction.Undo = undo; }
return packageAction;
}).ToArray();
}
private IEnumerable<IContent> InstallDocuments(XElement documentsElement, int userId = 0)
{
if ((string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName) == false)
&& (string.Equals(Constants.Packaging.DocumentsNodeName, documentsElement.Name.LocalName) == false))
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DocumentsNodeName + "\" as root",
"documentsElement");
}
if (string.Equals(Constants.Packaging.DocumentSetNodeName, documentsElement.Name.LocalName))
return _packagingService.ImportContent(documentsElement, -1, userId);
return
documentsElement.Elements(Constants.Packaging.DocumentSetNodeName)
.SelectMany(documentSetElement => _packagingService.ImportContent(documentSetElement, -1, userId))
.ToArray();
}
private IEnumerable<IFile> InstallStylesheets(XElement styleSheetsElement)
{
if (string.Equals(Constants.Packaging.StylesheetsNodeName, styleSheetsElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.StylesheetsNodeName + "\" as root",
"styleSheetsElement");
}
// TODO: Call _packagingService when import stylesheets import has been implimentet
if (styleSheetsElement.HasElements == false) { return new List<IFile>(); }
throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets");
}
private IEnumerable<IContentType> InstallDocumentTypes(XElement documentTypes, int userId = 0)
{
if (string.Equals(Constants.Packaging.DocumentTypesNodeName, documentTypes.Name.LocalName) == false)
{
if (string.Equals(Constants.Packaging.DocumentTypeNodeName, documentTypes.Name.LocalName) == false)
throw new ArgumentException(
"Must be \"" + Constants.Packaging.DocumentTypesNodeName + "\" as root", "documentTypes");
documentTypes = new XElement(Constants.Packaging.DocumentTypesNodeName, documentTypes);
}
return _packagingService.ImportContentTypes(documentTypes, userId);
}
private IEnumerable<ITemplate> InstallTemplats(XElement templateElement, int userId = 0)
{
if (string.Equals(Constants.Packaging.TemplatesNodeName, templateElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.TemplatesNodeName + "\" as root",
"templateElement");
}
return _packagingService.ImportTemplates(templateElement, userId);
}
private IEnumerable<string> InstallFiles(string packageFilePath, XElement filesElement)
{
var sourceDestination = ExtractSourceDestinationFileInformation(filesElement);
sourceDestination = AppendRootToDestination(FullPathToRoot, sourceDestination);
_packageExtraction.CopyFilesFromArchive(packageFilePath, sourceDestination);
return sourceDestination.Select(sd => sd.Value).ToArray();
}
private KeyValuePair<string, string>[] AppendRootToDestination(string fullpathToRoot, IEnumerable<KeyValuePair<string, string>> sourceDestination)
{
return
sourceDestination.Select(
sd => new KeyValuePair<string, string>(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray();
}
private IEnumerable<IMacro> InstallMacros(XElement macroElements, int userId = 0)
{
if (string.Equals(Constants.Packaging.MacrosNodeName, macroElements.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.MacrosNodeName + "\" as root",
"macroElements");
}
return _packagingService.ImportMacros(macroElements, userId);
}
private IEnumerable<IDictionaryItem> InstallDictionaryItems(XElement dictionaryItemsElement)
{
if (string.Equals(Constants.Packaging.DictionaryItemsNodeName, dictionaryItemsElement.Name.LocalName) ==
false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DictionaryItemsNodeName + "\" as root",
"dictionaryItemsElement");
}
return _packagingService.ImportDictionaryItems(dictionaryItemsElement);
}
private IEnumerable<ILanguage> InstallLanguages(XElement languageElement, int userId = 0)
{
if (string.Equals(Constants.Packaging.LanguagesNodeName, languageElement.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.LanguagesNodeName + "\" as root", "languageElement");
}
return _packagingService.ImportLanguages(languageElement, userId);
}
private IEnumerable<IDataType> InstallDataTypes(XElement dataTypeElements, int userId = 0)
{
if (string.Equals(Constants.Packaging.DataTypesNodeName, dataTypeElements.Name.LocalName) == false)
{
if (string.Equals(Constants.Packaging.DataTypeNodeName, dataTypeElements.Name.LocalName) == false)
{
throw new ArgumentException("Must be \"" + Constants.Packaging.DataTypeNodeName + "\" as root", "dataTypeElements");
}
}
return _packagingService.ImportDataTypeDefinitions(dataTypeElements, userId);
}
private PreInstallWarnings GetPreInstallWarnings(XElement rootElement)
{
XElement files = rootElement.Element(Constants.Packaging.FilesNodeName);
XElement styleSheets = rootElement.Element(Constants.Packaging.StylesheetsNodeName);
XElement templates = rootElement.Element(Constants.Packaging.TemplatesNodeName);
XElement alias = rootElement.Element(Constants.Packaging.MacrosNodeName);
var sourceDestination = EmptyArrayIfNull<KeyValuePair<string, string>>(files) ?? ExtractSourceDestinationFileInformation(files);
var installWarnings = new PreInstallWarnings();
var macroAliases = EmptyEnumerableIfNull<IMacro>(alias) ?? ConflictingPackageData.FindConflictingMacros(alias);
installWarnings.ConflictingMacroAliases = macroAliases;
var templateAliases = EmptyEnumerableIfNull<ITemplate>(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates);
installWarnings.ConflictingTemplateAliases = templateAliases;
var stylesheetNames = EmptyEnumerableIfNull<IFile>(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets);
installWarnings.ConflictingStylesheetNames = stylesheetNames;
installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination);
installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination);
return installWarnings;
}
private KeyValuePair<string, string>[] FindFilesToBeReplaced(IEnumerable<KeyValuePair<string, string>> sourceDestination)
{
return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray();
}
private KeyValuePair<string, string>[] FindUnsecureFiles(IEnumerable<KeyValuePair<string, string>> sourceDestinationPair)
{
return sourceDestinationPair.Where(sd => IsFileDestinationUnsecure(sd.Value)).ToArray();
}
private bool IsFileDestinationUnsecure(string destination)
{
var unsecureDirNames = new[] {"bin", "app_code"};
if(unsecureDirNames.Any(ud => destination.StartsWith(ud, StringComparison.InvariantCultureIgnoreCase)))
return true;
string extension = Path.GetExtension(destination);
return extension != null && extension.Equals(".dll", StringComparison.InvariantCultureIgnoreCase);
}
private KeyValuePair<string, string>[] ExtractSourceDestinationFileInformation(XElement filesElement)
{
if (string.Equals(Constants.Packaging.FilesNodeName, filesElement.Name.LocalName) == false)
{
throw new ArgumentException("the root element must be \"Files\"", "filesElement");
}
return filesElement.Elements(Constants.Packaging.FileNodeName)
.Select(e =>
{
XElement guidElement = e.Element(Constants.Packaging.GuidNodeName);
if (guidElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.GuidNodeName + "\"",
"filesElement");
}
XElement orgPathElement = e.Element(Constants.Packaging.OrgPathNodeName);
if (orgPathElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgPathNodeName + "\"",
"filesElement");
}
XElement orgNameElement = e.Element(Constants.Packaging.OrgNameNodeName);
if (orgNameElement == null)
{
throw new ArgumentException("Missing element \"" + Constants.Packaging.OrgNameNodeName + "\"",
"filesElement");
}
var fileName = PrepareAsFilePathElement(orgNameElement.Value);
var relativeDir = UpdatePathPlaceholders(PrepareAsFilePathElement(orgPathElement.Value));
var relativePath = Path.Combine(relativeDir, fileName);
return new KeyValuePair<string, string>(guidElement.Value, relativePath);
}).ToArray();
}
private static string PrepareAsFilePathElement(string pathElement)
{
return pathElement.TrimStart(new[] {'\\', '/', '~'}).Replace("/", "\\");
}
private MetaData GetMetaData(XElement xRootElement)
{
XElement infoElement = xRootElement.Element(Constants.Packaging.InfoNodeName);
if (infoElement == null)
{
throw new ArgumentException("Did not hold a \"" + Constants.Packaging.InfoNodeName + "\" element",
"xRootElement");
}
var majorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMajorXpath);
var minorElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsMinorXpath);
var patchElement = infoElement.XPathSelectElement(Constants.Packaging.PackageRequirementsPatchXpath);
var nameElement = infoElement.XPathSelectElement(Constants.Packaging.PackageNameXpath);
var versionElement = infoElement.XPathSelectElement(Constants.Packaging.PackageVersionXpath);
var urlElement = infoElement.XPathSelectElement(Constants.Packaging.PackageUrlXpath);
var licenseElement = infoElement.XPathSelectElement(Constants.Packaging.PackageLicenseXpath);
var authorNameElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorNameXpath);
var authorUrlElement = infoElement.XPathSelectElement(Constants.Packaging.AuthorWebsiteXpath);
var readmeElement = infoElement.XPathSelectElement(Constants.Packaging.ReadmeXpath);
XElement controlElement = xRootElement.Element(Constants.Packaging.ControlNodeName);
return new MetaData
{
Name = StringValue(nameElement),
Version = StringValue(versionElement),
Url = StringValue(urlElement),
License = StringValue(licenseElement),
LicenseUrl = StringAttribute(licenseElement, Constants.Packaging.PackageLicenseXpathUrlAttribute),
AuthorName = StringValue(authorNameElement),
AuthorUrl = StringValue(authorUrlElement),
Readme = StringValue(readmeElement),
Control = StringValue(controlElement),
ReqMajor = IntValue(majorElement),
ReqMinor = IntValue(minorElement),
ReqPatch = IntValue(patchElement)
};
}
private static string StringValue(XElement xElement, string defaultValue = "")
{
return xElement == null ? defaultValue : xElement.Value;
}
private static string StringAttribute(XElement xElement, string attribute, string defaultValue = "")
{
return xElement == null
? defaultValue
: xElement.HasAttributes ? xElement.AttributeValue<string>(attribute) : defaultValue;
}
private static int IntValue(XElement xElement, int defaultValue = 0)
{
return xElement == null ? defaultValue : int.TryParse(xElement.Value, out var val) ? val : defaultValue;
}
private static string UpdatePathPlaceholders(string path)
{
if (path.Contains("[$"))
{
//this is experimental and undocumented...
path = path.Replace("[$UMBRACO]", SystemDirectories.Umbraco);
path = path.Replace("[$CONFIG]", SystemDirectories.Config);
path = path.Replace("[$DATA]", SystemDirectories.Data);
}
return path;
}
}
}
@@ -0,0 +1,617 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Xml.Linq;
using Umbraco.Core.Configuration;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Services;
using File = System.IO.File;
namespace Umbraco.Core.Packaging
{
/// <summary>
/// Manages the storage of installed/created package definitions
/// </summary>
internal class PackagesRepository : ICreatedPackagesRepository, IInstalledPackagesRepository
{
private readonly IContentService _contentService;
private readonly IContentTypeService _contentTypeService;
private readonly IDataTypeService _dataTypeService;
private readonly IFileService _fileService;
private readonly IMacroService _macroService;
private readonly ILocalizationService _languageService;
private readonly IEntityXmlSerializer _serializer;
private readonly ILogger _logger;
private readonly string _packageRepositoryFileName;
private readonly string _mediaFolderPath;
private readonly string _packagesFolderPath;
private readonly string _tempFolderPath;
private readonly PackageDefinitionXmlParser _parser;
/// <summary>
/// Constructor
/// </summary>
/// <param name="contentService"></param>
/// <param name="contentTypeService"></param>
/// <param name="dataTypeService"></param>
/// <param name="fileService"></param>
/// <param name="macroService"></param>
/// <param name="languageService"></param>
/// <param name="serializer"></param>
/// <param name="logger"></param>
/// <param name="packageRepositoryFileName">
/// The file name for storing the package definitions (i.e. "createdPackages.config")
/// </param>
/// <param name="tempFolderPath"></param>
/// <param name="packagesFolderPath"></param>
/// <param name="mediaFolderPath"></param>
public PackagesRepository(IContentService contentService, IContentTypeService contentTypeService,
IDataTypeService dataTypeService, IFileService fileService, IMacroService macroService,
ILocalizationService languageService,
IEntityXmlSerializer serializer, ILogger logger,
string packageRepositoryFileName,
string tempFolderPath = null, string packagesFolderPath = null, string mediaFolderPath = null)
{
if (string.IsNullOrWhiteSpace(packageRepositoryFileName)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageRepositoryFileName));
_contentService = contentService;
_contentTypeService = contentTypeService;
_dataTypeService = dataTypeService;
_fileService = fileService;
_macroService = macroService;
_languageService = languageService;
_serializer = serializer;
_logger = logger;
_packageRepositoryFileName = packageRepositoryFileName;
_tempFolderPath = tempFolderPath ?? SystemDirectories.TempData.EnsureEndsWith('/') + "PackageFiles";
_packagesFolderPath = packagesFolderPath ?? SystemDirectories.Packages;
_mediaFolderPath = mediaFolderPath ?? SystemDirectories.Media + "/created-packages";
_parser = new PackageDefinitionXmlParser(logger);
}
private string CreatedPackagesFile => _packagesFolderPath.EnsureEndsWith('/') + _packageRepositoryFileName;
public IEnumerable<PackageDefinition> GetAll()
{
var packagesXml = EnsureStorage(out _);
if (packagesXml?.Root == null)
yield break;;
foreach (var packageXml in packagesXml.Root.Elements("package"))
yield return _parser.ToPackageDefinition(packageXml);
}
public PackageDefinition GetById(int id)
{
var packagesXml = EnsureStorage(out _);
var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue<int>("id") == id);
return packageXml == null ? null : _parser.ToPackageDefinition(packageXml);
}
public void Delete(int id)
{
var packagesXml = EnsureStorage(out var packagesFile);
var packageXml = packagesXml?.Root?.Elements("package").FirstOrDefault(x => x.AttributeValue<int>("id") == id);
if (packageXml == null) return;
packageXml.Remove();
packagesXml.Save(packagesFile);
}
public bool SavePackage(PackageDefinition definition)
{
if (definition == null) throw new ArgumentNullException(nameof(definition));
var packagesXml = EnsureStorage(out var packagesFile);
if (packagesXml?.Root == null)
return false;
//ensure it's valid
ValidatePackage(definition);
if (definition.Id == default)
{
//need to gen an id and persist
// Find max id
var maxId = packagesXml.Root.Elements("package").Max(x => x.AttributeValue<int?>("id")) ?? 0;
var newId = maxId + 1;
definition.Id = newId;
definition.PackageId = definition.PackageId == default ? Guid.NewGuid() : definition.PackageId;
var packageXml = _parser.ToXml(definition);
packagesXml.Root.Add(packageXml);
}
else
{
//existing
var packageXml = packagesXml.Root.Elements("package").FirstOrDefault(x => x.AttributeValue<int>("id") == definition.Id);
if (packageXml == null)
return false;
var updatedXml = _parser.ToXml(definition);
packageXml.ReplaceWith(updatedXml);
}
packagesXml.Save(packagesFile);
return true;
}
public string ExportPackage(PackageDefinition definition)
{
if (definition.Id == default) throw new ArgumentException("The package definition does not have an ID, it must be saved before being exported");
if (definition.PackageId == default) throw new ArgumentException("the package definition does not have a GUID, it must be saved before being exported");
//ensure it's valid
ValidatePackage(definition);
//Create a folder for building this package
var temporaryPath = IOHelper.MapPath(_tempFolderPath.EnsureEndsWith('/') + Guid.NewGuid());
if (Directory.Exists(temporaryPath) == false)
Directory.CreateDirectory(temporaryPath);
try
{
//Init package file
var packageManifest = CreatePackageManifest(out var manifestRoot, out var filesXml);
//Info section
manifestRoot.Add(GetPackageInfoXml(definition));
PackageDocumentsAndTags(definition, manifestRoot);
PackageDocumentTypes(definition, manifestRoot);
PackageTemplates(definition, manifestRoot);
PackageStylesheets(definition, manifestRoot);
PackageMacros(definition, manifestRoot, filesXml, temporaryPath);
PackageDictionaryItems(definition, manifestRoot);
PackageLanguages(definition, manifestRoot);
PackageDataTypes(definition, manifestRoot);
//Files
foreach (var fileName in definition.Files)
AppendFileToManifest(fileName, temporaryPath, filesXml);
//Load control on install...
if (!string.IsNullOrEmpty(definition.Control))
{
var control = new XElement("control", definition.Control);
AppendFileToManifest(definition.Control, temporaryPath, filesXml);
manifestRoot.Add(control);
}
//Actions
if (string.IsNullOrEmpty(definition.Actions) == false)
{
var actionsXml = new XElement("Actions");
try
{
//this will be formatted like a full xml block like <actions>...</actions> and we want the child nodes
var parsed = XElement.Parse(definition.Actions);
actionsXml.Add(parsed.Elements());
manifestRoot.Add(actionsXml);
}
catch (Exception e)
{
_logger.Warn<PackagesRepository>(e, "Could not add package actions to the package manifest, the xml did not parse");
}
}
var manifestFileName = temporaryPath + "/package.xml";
if (File.Exists(manifestFileName))
File.Delete(manifestFileName);
packageManifest.Save(manifestFileName);
// check if there's a packages directory below media
if (Directory.Exists(IOHelper.MapPath(_mediaFolderPath)) == false)
Directory.CreateDirectory(IOHelper.MapPath(_mediaFolderPath));
var packPath = _mediaFolderPath.EnsureEndsWith('/') + (definition.Name + "_" + definition.Version).Replace(' ', '_') + ".zip";
ZipPackage(temporaryPath, IOHelper.MapPath(packPath));
//we need to update the package path and save it
definition.PackagePath = packPath;
SavePackage(definition);
return packPath;
}
finally
{
//Clean up
Directory.Delete(temporaryPath, true);
}
}
private void ValidatePackage(PackageDefinition definition)
{
//ensure it's valid
var context = new ValidationContext(definition, serviceProvider: null, items: null);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(definition, context, results);
if (!isValid)
throw new InvalidOperationException("Validation failed, there is invalid data on the model: " + string.Join(", ", results.Select(x => x.ErrorMessage)));
}
private void PackageDataTypes(PackageDefinition definition, XContainer manifestRoot)
{
var dataTypes = new XElement("DataTypes");
foreach (var dtId in definition.DataTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
var dataType = _dataTypeService.GetDataType(outInt);
if (dataType == null) continue;
dataTypes.Add(_serializer.Serialize(dataType));
}
manifestRoot.Add(dataTypes);
}
private void PackageLanguages(PackageDefinition definition, XContainer manifestRoot)
{
var languages = new XElement("Languages");
foreach (var langId in definition.Languages)
{
if (!int.TryParse(langId, out var outInt)) continue;
var lang = _languageService.GetLanguageById(outInt);
if (lang == null) continue;
languages.Add(_serializer.Serialize(lang));
}
manifestRoot.Add(languages);
}
private void PackageDictionaryItems(PackageDefinition definition, XContainer manifestRoot)
{
var dictionaryItems = new XElement("DictionaryItems");
foreach (var dictionaryId in definition.DictionaryItems)
{
if (!int.TryParse(dictionaryId, out var outInt)) continue;
var di = _languageService.GetDictionaryItemById(outInt);
if (di == null) continue;
dictionaryItems.Add(_serializer.Serialize(di, false));
}
manifestRoot.Add(dictionaryItems);
}
private void PackageMacros(PackageDefinition definition, XContainer manifestRoot, XContainer filesXml, string temporaryPath)
{
var macros = new XElement("Macros");
foreach (var macroId in definition.Macros)
{
if (!int.TryParse(macroId, out var outInt)) continue;
var macroXml = GetMacroXml(outInt, out var macro);
if (macroXml == null) continue;
macros.Add(macroXml);
//if the macro has a file copy it to the manifest
if (!string.IsNullOrEmpty(macro.MacroSource))
AppendFileToManifest(macro.MacroSource, temporaryPath, filesXml);
}
manifestRoot.Add(macros);
}
private void PackageStylesheets(PackageDefinition definition, XContainer manifestRoot)
{
var stylesheetsXml = new XElement("Stylesheets");
foreach (var stylesheetName in definition.Stylesheets)
{
if (stylesheetName.IsNullOrWhiteSpace()) continue;
var xml = GetStylesheetXml(stylesheetName, true);
if (xml != null)
stylesheetsXml.Add(xml);
}
manifestRoot.Add(stylesheetsXml);
}
private void PackageTemplates(PackageDefinition definition, XContainer manifestRoot)
{
var templatesXml = new XElement("Templates");
foreach (var templateId in definition.Templates)
{
if (!int.TryParse(templateId, out var outInt)) continue;
var template = _fileService.GetTemplate(outInt);
if (template == null) continue;
templatesXml.Add(_serializer.Serialize(template));
}
manifestRoot.Add(templatesXml);
}
private void PackageDocumentTypes(PackageDefinition definition, XContainer manifestRoot)
{
var contentTypes = new HashSet<IContentType>();
var docTypesXml = new XElement("DocumentTypes");
foreach (var dtId in definition.DocumentTypes)
{
if (!int.TryParse(dtId, out var outInt)) continue;
var contentType = _contentTypeService.Get(outInt);
if (contentType == null) continue;
AddDocumentType(contentType, contentTypes);
}
foreach (var contentType in contentTypes)
docTypesXml.Add(_serializer.Serialize(contentType));
manifestRoot.Add(docTypesXml);
}
private void PackageDocumentsAndTags(PackageDefinition definition, XContainer manifestRoot)
{
//Documents and tags
if (string.IsNullOrEmpty(definition.ContentNodeId) == false && int.TryParse(definition.ContentNodeId, out var contentNodeId))
{
if (contentNodeId > 0)
{
//load content from umbraco.
var content = _contentService.GetById(contentNodeId);
if (content != null)
{
var contentXml = definition.ContentLoadChildNodes ? content.ToDeepXml(_serializer) : content.ToXml(_serializer);
//Create the Documents/DocumentSet node
manifestRoot.Add(
new XElement("Documents",
new XElement("DocumentSet",
new XAttribute("importMode", "root"),
contentXml)));
//TODO: I guess tags has been broken for a very long time for packaging, we should get this working again sometime
////Create the TagProperties node - this is used to store a definition for all
//// document properties that are tags, this ensures that we can re-import tags properly
//XmlNode tagProps = new XElement("TagProperties");
////before we try to populate this, we'll do a quick lookup to see if any of the documents
//// being exported contain published tags.
//var allExportedIds = documents.SelectNodes("//@id").Cast<XmlNode>()
// .Select(x => x.Value.TryConvertTo<int>())
// .Where(x => x.Success)
// .Select(x => x.Result)
// .ToArray();
//var allContentTags = new List<ITag>();
//foreach (var exportedId in allExportedIds)
//{
// allContentTags.AddRange(
// Current.Services.TagService.GetTagsForEntity(exportedId));
//}
////This is pretty round-about but it works. Essentially we need to get the properties that are tagged
//// but to do that we need to lookup by a tag (string)
//var allTaggedEntities = new List<TaggedEntity>();
//foreach (var group in allContentTags.Select(x => x.Group).Distinct())
//{
// allTaggedEntities.AddRange(
// Current.Services.TagService.GetTaggedContentByTagGroup(group));
//}
////Now, we have all property Ids/Aliases and their referenced document Ids and tags
//var allExportedTaggedEntities = allTaggedEntities.Where(x => allExportedIds.Contains(x.EntityId))
// .DistinctBy(x => x.EntityId)
// .OrderBy(x => x.EntityId);
//foreach (var taggedEntity in allExportedTaggedEntities)
//{
// foreach (var taggedProperty in taggedEntity.TaggedProperties.Where(x => x.Tags.Any()))
// {
// XmlNode tagProp = new XElement("TagProperty");
// var docId = packageManifest.CreateAttribute("docId", "");
// docId.Value = taggedEntity.EntityId.ToString(CultureInfo.InvariantCulture);
// tagProp.Attributes.Append(docId);
// var propertyAlias = packageManifest.CreateAttribute("propertyAlias", "");
// propertyAlias.Value = taggedProperty.PropertyTypeAlias;
// tagProp.Attributes.Append(propertyAlias);
// var group = packageManifest.CreateAttribute("group", "");
// group.Value = taggedProperty.Tags.First().Group;
// tagProp.Attributes.Append(group);
// tagProp.AppendChild(packageManifest.CreateCDataSection(
// JsonConvert.SerializeObject(taggedProperty.Tags.Select(x => x.Text).ToArray())));
// tagProps.AppendChild(tagProp);
// }
//}
//manifestRoot.Add(tagProps);
}
}
}
}
/// <summary>
/// Zips the package.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="savePath">The save path.</param>
private static void ZipPackage(string path, string savePath)
{
if (File.Exists(savePath))
File.Delete(savePath);
ZipFile.CreateFromDirectory(path, savePath);
}
/// <summary>
/// Appends a file to package manifest and copies the file to the correct folder.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="packageDirectory">The package directory.</param>
/// <param name="filesXml">The files xml node</param>
private static void AppendFileToManifest(string path, string packageDirectory, XContainer filesXml)
{
if (!path.StartsWith("~/") && !path.StartsWith("/"))
path = "~/" + path;
var serverPath = IOHelper.MapPath(path);
if (File.Exists(serverPath))
AppendFileXml(new FileInfo(serverPath), path, packageDirectory, filesXml);
else if (Directory.Exists(serverPath))
ProcessDirectory(new DirectoryInfo(serverPath), path, packageDirectory, filesXml);
}
//Process files in directory and add them to package
private static void ProcessDirectory(DirectoryInfo directory, string dirPath, string packageDirectory, XContainer filesXml)
{
if (directory == null) throw new ArgumentNullException(nameof(directory));
if (string.IsNullOrWhiteSpace(packageDirectory)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageDirectory));
if (string.IsNullOrWhiteSpace(dirPath)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(dirPath));
if (!directory.Exists) return;
foreach (var file in directory.GetFiles())
AppendFileXml(new FileInfo(Path.Combine(directory.FullName, file.Name)), dirPath + "/" + file.Name, packageDirectory, filesXml);
foreach (var dir in directory.GetDirectories())
ProcessDirectory(dir, dirPath + "/" + dir.Name, packageDirectory, filesXml);
}
private static void AppendFileXml(FileInfo file, string filePath, string packageDirectory, XContainer filesXml)
{
if (file == null) throw new ArgumentNullException(nameof(file));
if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(filePath));
if (string.IsNullOrWhiteSpace(packageDirectory)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(packageDirectory));
var orgPath = filePath.Substring(0, (filePath.LastIndexOf('/')));
var orgName = filePath.Substring((filePath.LastIndexOf('/') + 1));
var newFileName = orgName;
if (File.Exists(packageDirectory.EnsureEndsWith('/') + orgName))
newFileName = Guid.NewGuid() + "_" + newFileName;
//Copy file to directory for zipping...
File.Copy(file.FullName, packageDirectory + "/" + newFileName, true);
filesXml.Add(new XElement("file",
new XElement("guid", newFileName),
new XElement("orgPath", orgPath == "" ? "/" : orgPath),
new XElement("orgName", orgName)));
}
private XElement GetMacroXml(int macroId, out IMacro macro)
{
macro = _macroService.GetById(macroId);
if (macro == null) return null;
var xml = _serializer.Serialize(macro);
return xml;
}
/// <summary>
/// Converts a umbraco stylesheet to a package xml node
/// </summary>
/// <param name="name">The name of the stylesheet.</param>
/// <param name="includeProperties">if set to <c>true</c> [incluce properties].</param>
/// <returns></returns>
private XElement GetStylesheetXml(string name, bool includeProperties)
{
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(name));
;
var sts = _fileService.GetStylesheetByName(name);
if (sts == null) return null;
var stylesheetXml = new XElement("Stylesheet");
stylesheetXml.Add(new XElement("Name", sts.Alias));
stylesheetXml.Add(new XElement("FileName", sts.Name));
stylesheetXml.Add(new XElement("Content", new XCData(sts.Content)));
if (!includeProperties) return stylesheetXml;
var properties = new XElement("Properties");
foreach (var ssP in sts.Properties)
{
var property = new XElement("Property");
property.Add(new XElement("Name", ssP.Name));
property.Add(new XElement("Alias", ssP.Alias));
property.Add(new XElement("Value", ssP.Value));
}
stylesheetXml.Add(properties);
return stylesheetXml;
}
private void AddDocumentType(IContentType dt, HashSet<IContentType> dtl)
{
if (dt.ParentId > 0)
{
var parent = _contentTypeService.Get(dt.ParentId);
if (parent != null) // could be a container
AddDocumentType(parent, dtl);
}
if (!dtl.Contains(dt))
dtl.Add(dt);
}
private static XElement GetPackageInfoXml(PackageDefinition definition)
{
var info = new XElement("info");
//Package info
var package = new XElement("package");
package.Add(new XElement("name", definition.Name));
package.Add(new XElement("version", definition.Version));
package.Add(new XElement("iconUrl", definition.IconUrl));
var license = new XElement("license", definition.License);
license.Add(new XAttribute("url", definition.LicenseUrl));
package.Add(license);
package.Add(new XElement("url", definition.Url));
var requirements = new XElement("requirements");
requirements.Add(new XElement("major", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Major.ToInvariantString() : definition.UmbracoVersion.Major.ToInvariantString()));
requirements.Add(new XElement("minor", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Minor.ToInvariantString() : definition.UmbracoVersion.Minor.ToInvariantString()));
requirements.Add(new XElement("patch", definition.UmbracoVersion == null ? UmbracoVersion.SemanticVersion.Patch.ToInvariantString() : definition.UmbracoVersion.Build.ToInvariantString()));
if (definition.UmbracoVersion != null)
requirements.Add(new XAttribute("type", RequirementsType.Strict.ToString()));
package.Add(requirements);
info.Add(package);
//Author
var author = new XElement("author", "");
author.Add(new XElement("name", definition.Author));
author.Add(new XElement("website", definition.AuthorUrl));
info.Add(author);
info.Add(new XElement("readme", new XCData(definition.Readme)));
return info;
}
private static XDocument CreatePackageManifest(out XElement root, out XElement files)
{
files = new XElement("files");
root = new XElement("umbPackage", files);
var packageManifest = new XDocument(root);
return packageManifest;
}
private XDocument EnsureStorage(out string packagesFile)
{
var packagesFolder = IOHelper.MapPath(_packagesFolderPath);
//ensure it exists
Directory.CreateDirectory(packagesFolder);
packagesFile = IOHelper.MapPath(CreatedPackagesFile);
if (!File.Exists(packagesFile))
{
var xml = new XDocument(new XElement("packages"));
xml.Save(packagesFile);
}
var packagesXml = XDocument.Load(packagesFile);
return packagesXml;
}
}
}
@@ -18,6 +18,12 @@ namespace Umbraco.Core.Persistence
/// </summary>
bool Configured { get; }
/// <summary>
/// Gets the connection string.
/// </summary>
/// <remarks>Throws if the factory is not configured.</remarks>
string ConnectionString { get; }
/// <summary>
/// Gets a value indicating whether the database can connect.
/// </summary>
@@ -1,5 +1,5 @@
namespace Umbraco.Core.Persistence.Repositories
{
interface IDataTypeContainerRepository : IEntityContainerRepository
public interface IDataTypeContainerRepository : IEntityContainerRepository
{ }
}
@@ -1,5 +1,5 @@
namespace Umbraco.Core.Persistence.Repositories
{
interface IDocumentTypeContainerRepository : IEntityContainerRepository
public interface IDocumentTypeContainerRepository : IEntityContainerRepository
{ }
}
@@ -2,6 +2,6 @@
namespace Umbraco.Core.Persistence.Repositories
{
interface IEntityContainerRepository : IReadRepository<int, EntityContainer>, IWriteRepository<EntityContainer>
public interface IEntityContainerRepository : IReadRepository<int, EntityContainer>, IWriteRepository<EntityContainer>
{ }
}
@@ -3,7 +3,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Persistence.Repositories
{
internal interface IMacroRepository : IReadWriteQueryRepository<int, IMacro>, IReadRepository<Guid, IMacro>
public interface IMacroRepository : IReadWriteQueryRepository<int, IMacro>, IReadRepository<Guid, IMacro>
{
//IEnumerable<IMacro> GetAll(params string[] aliases);
@@ -1,5 +1,5 @@
namespace Umbraco.Core.Persistence.Repositories
{
interface IMediaTypeContainerRepository : IEntityContainerRepository
public interface IMediaTypeContainerRepository : IEntityContainerRepository
{ }
}
@@ -2,6 +2,7 @@
using System.Configuration;
using System.Data.Common;
using System.Threading;
using LightInject;
using NPoco;
using NPoco.FluentMappings;
using Umbraco.Core.Exceptions;
@@ -102,6 +103,16 @@ namespace Umbraco.Core.Persistence
/// <inheritdoc />
public bool Configured { get; private set; }
/// <inheritdoc />
public string ConnectionString
{
get
{
EnsureConfigured();
return _connectionString;
}
}
/// <inheritdoc />
public bool CanConnect
{
+2 -1
View File
@@ -252,7 +252,7 @@ namespace Umbraco.Core.Runtime
{
_state.DetermineRuntimeLevel(databaseFactory, profilingLogger);
profilingLogger.Debug<CoreRuntime>("Runtime level: {RuntimeLevel}", _state.Level);
profilingLogger.Debug<CoreRuntime>("Runtime level: {RuntimeLevel} - {RuntimeLevelReason}", _state.Level, _state.Reason);
if (_state.Level == RuntimeLevel.Upgrade)
{
@@ -263,6 +263,7 @@ namespace Umbraco.Core.Runtime
catch
{
_state.Level = RuntimeLevel.BootFailed;
_state.Reason = RuntimeLevelReason.BootFailedOnException;
timer.Fail();
throw;
}
+3
View File
@@ -1,5 +1,8 @@
namespace Umbraco.Core
{
/// <summary>
/// Describes the levels in which the runtime can run.
/// </summary>
public enum RuntimeLevel
{
/// <summary>
+68
View File
@@ -0,0 +1,68 @@
namespace Umbraco.Core
{
/// <summary>
/// Describes the reason for the runtime level.
/// </summary>
public enum RuntimeLevelReason
{
/// <summary>
/// The code version is lower than the version indicated in web.config, and
/// downgrading Umbraco is not supported.
/// </summary>
BootFailedCannotDowngrade,
/// <summary>
/// The runtime cannot connect to the configured database.
/// </summary>
BootFailedCannotConnectToDatabase,
/// <summary>
/// The runtime can connect to the configured database, but it cannot
/// retrieve the migrations status.
/// </summary>
BootFailedCannotCheckUpgradeState,
/// <summary>
/// An exception was thrown during boot.
/// </summary>
BootFailedOnException,
/// <summary>
/// Umbraco is not installed at all.
/// </summary>
InstallNoVersion,
/// <summary>
/// A version is specified in web.config but the database is not configured.
/// </summary>
/// <remarks>This is a weird state.</remarks>
InstallNoDatabase,
/// <summary>
/// A version is specified in web.config and a database is configured, but the
/// database is missing, and installing over a missing database has been enabled.
/// </summary>
InstallMissingDatabase,
/// <summary>
/// A version is specified in web.config and a database is configured, but the
/// database is empty, and installing over an empty database has been enabled.
/// </summary>
InstallEmptyDatabase,
/// <summary>
/// Umbraco runs an old version.
/// </summary>
UpgradeOldVersion,
/// <summary>
/// Umbraco runs the current version but some migrations have not run.
/// </summary>
UpgradeMigrations,
/// <summary>
/// Umbraco is running.
/// </summary>
Run
}
}
+33 -4
View File
@@ -93,6 +93,9 @@ namespace Umbraco.Core
internal set { _level = value; if (value == RuntimeLevel.Run) _runLevel.Set(); }
}
/// <inheritdoc />
public RuntimeLevelReason Reason { get; internal set; }
/// <summary>
/// Ensures that the <see cref="ApplicationUrl"/> property has a value.
/// </summary>
@@ -143,6 +146,7 @@ namespace Umbraco.Core
// there is no local version, we are not installed
logger.Debug<RuntimeState>("No local version, need to install Umbraco.");
Level = RuntimeLevel.Install;
Reason = RuntimeLevelReason.InstallNoVersion;
return;
}
@@ -152,12 +156,14 @@ namespace Umbraco.Core
// need to upgrade
logger.Debug<RuntimeState>("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion);
Level = RuntimeLevel.Upgrade;
Reason = RuntimeLevelReason.UpgradeOldVersion;
}
else if (localVersion > codeVersion)
{
logger.Warn<RuntimeState>("Local version '{LocalVersion}' > code version '{CodeVersion}', downgrading is not supported.", localVersion, codeVersion);
// in fact, this is bad enough that we want to throw
Reason = RuntimeLevelReason.BootFailedCannotDowngrade;
throw new BootFailedException($"Local version \"{localVersion}\" > code version \"{codeVersion}\", downgrading is not supported.");
}
else if (databaseFactory.Configured == false)
@@ -166,16 +172,18 @@ namespace Umbraco.Core
// install (again? this is a weird situation...)
logger.Debug<RuntimeState>("Database is not configured, need to install Umbraco.");
Level = RuntimeLevel.Install;
Reason = RuntimeLevelReason.InstallNoDatabase;
return;
}
// else, keep going,
// anything other than install wants a database - see if we can connect
// (since this is an already existing database, assume localdb is ready)
for (var i = 0; i < 5; i++)
var tries = RuntimeStateOptions.InstallMissingDatabase ? 2 : 5;
for (var i = 0;;)
{
connect = databaseFactory.CanConnect;
if (connect) break;
if (connect || ++i == tries) break;
logger.Debug<RuntimeState>("Could not immediately connect to database, trying again.");
Thread.Sleep(1000);
}
@@ -185,7 +193,16 @@ namespace Umbraco.Core
// cannot connect to configured database, this is bad, fail
logger.Debug<RuntimeState>("Could not connect to database.");
// in fact, this is bad enough that we want to throw
if (RuntimeStateOptions.InstallMissingDatabase)
{
// ok to install on a configured but missing database
Level = RuntimeLevel.Install;
Reason = RuntimeLevelReason.InstallMissingDatabase;
return;
}
// else it is bad enough that we want to throw
Reason = RuntimeLevelReason.BootFailedCannotConnectToDatabase;
throw new BootFailedException("A connection string is configured but Umbraco could not connect to the database.");
}
@@ -195,7 +212,6 @@ namespace Umbraco.Core
// else
// look for a matching migration entry - bypassing services entirely - they are not 'up' yet
// fixme - in a LB scenario, ensure that the DB gets upgraded only once!
bool noUpgrade;
try
{
@@ -205,6 +221,17 @@ namespace Umbraco.Core
{
// can connect to the database but cannot check the upgrade state... oops
logger.Warn<RuntimeState>(e, "Could not check the upgrade state.");
if (RuntimeStateOptions.InstallEmptyDatabase)
{
// ok to install on an empty database
Level = RuntimeLevel.Install;
Reason = RuntimeLevelReason.InstallEmptyDatabase;
return;
}
// else it is bad enough that we want to throw
Reason = RuntimeLevelReason.BootFailedCannotCheckUpgradeState;
throw new BootFailedException("Could not check the upgrade state.", e);
}
@@ -216,6 +243,7 @@ namespace Umbraco.Core
{
// the database version matches the code & files version, all clear, can run
Level = RuntimeLevel.Run;
Reason = RuntimeLevelReason.Run;
return;
}
@@ -226,6 +254,7 @@ namespace Umbraco.Core
// which means the local files have been upgraded but not the database - need to upgrade
logger.Debug<RuntimeState>("Has not reached the final upgrade step, need to upgrade Umbraco.");
Level = RuntimeLevel.Upgrade;
Reason = RuntimeLevelReason.UpgradeMigrations;
}
protected virtual bool EnsureUmbracoUpgradeState(IUmbracoDatabaseFactory databaseFactory, ILogger logger)
+40
View File
@@ -0,0 +1,40 @@
using System.Configuration;
namespace Umbraco.Core
{
/// <summary>
/// Allows configuration of the <see cref="RuntimeState"/> in PreApplicationStart or in appSettings
/// </summary>
public static class RuntimeStateOptions
{
// configured statically or via app settings
private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing;
/// <summary>
/// If true the RuntimeState will continue the installation sequence when a database is missing
/// </summary>
/// <remarks>
/// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence
/// </remarks>
public static bool InstallMissingDatabase
{
get => _installEmptyDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false);
set => _installEmptyDatabase = value;
}
/// <summary>
/// If true the RuntimeState will continue the installation sequence when a database is available but is empty
/// </summary>
/// <remarks>
/// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence
/// </remarks>
public static bool InstallEmptyDatabase
{
get => _installMissingDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false);
set => _installMissingDatabase = value;
}
private static bool? _installMissingDatabase;
private static bool? _installEmptyDatabase;
}
}
@@ -4,7 +4,7 @@ using Umbraco.Core.Models;
namespace Umbraco.Core.Services.Changes
{
internal class ContentTypeChange<TItem>
public class ContentTypeChange<TItem>
where TItem : class, IContentTypeComposition
{
public ContentTypeChange(TItem item, ContentTypeChangeTypes changeTypes)
@@ -0,0 +1,88 @@
using System.Collections.Generic;
using System.Xml.Linq;
using Umbraco.Core.Models;
namespace Umbraco.Core.Services
{
/// <summary>
/// Serializes entities to XML
/// </summary>
public interface IEntityXmlSerializer
{
/// <summary>
/// Exports an IContent item as an XElement.
/// </summary>
XElement Serialize(IContent content,
bool published,
bool withDescendants = false) //fixme take care of usage! only used for the packager
;
/// <summary>
/// Exports an IMedia item as an XElement.
/// </summary>
XElement Serialize(
IMedia media,
bool withDescendants = false);
/// <summary>
/// Exports an IMember item as an XElement.
/// </summary>
XElement Serialize(IMember member);
/// <summary>
/// Exports a list of Data Types
/// </summary>
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
XElement Serialize(IEnumerable<IDataType> dataTypeDefinitions);
XElement Serialize(IDataType dataType);
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
XElement Serialize(IEnumerable<IDictionaryItem> dictionaryItem, bool includeChildren = true);
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
XElement Serialize(IDictionaryItem dictionaryItem, bool includeChildren);
XElement Serialize(Stylesheet stylesheet);
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage objects</returns>
XElement Serialize(IEnumerable<ILanguage> languages);
XElement Serialize(ILanguage language);
XElement Serialize(ITemplate template);
/// <summary>
/// Exports a list of <see cref="ITemplate"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="templates"></param>
/// <returns></returns>
XElement Serialize(IEnumerable<ITemplate> templates);
XElement Serialize(IMediaType mediaType);
/// <summary>
/// Exports a list of <see cref="IMacro"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macros">Macros to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro objects</returns>
XElement Serialize(IEnumerable<IMacro> macros);
XElement Serialize(IMacro macro);
XElement Serialize(IContentType contentType);
}
}
+71 -152
View File
@@ -1,200 +1,119 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Xml.Linq;
using Semver;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
namespace Umbraco.Core.Services
{
public interface IPackagingService : IService
{
/// <summary>
/// Imports and saves package xml as <see cref="IContent"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="parentId">Optional parent Id for the content being imported</param>
/// <param name="userId">Optional Id of the user performing the import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated content</returns>
IEnumerable<IContent> ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true);
#region Package Installation
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// Returns a <see cref="CompiledPackage"/> result from an umbraco package file (zip)
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin)</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
IEnumerable<IContentType> ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Imports and saves package xml as <see cref="IContentType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="importStructure">Boolean indicating whether or not to import the </param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin)</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated ContentTypes</returns>
IEnumerable<IContentType> ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Imports and saves package xml as <see cref="IDataType"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin).</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated DataTypeDefinitions</returns>
IEnumerable<IDataType> ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Imports and saves the 'DictionaryItems' part of the package xml as a list of <see cref="IDictionaryItem"/>
/// </summary>
/// <param name="dictionaryItemElementList">Xml to import</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of dictionary items</returns>
IEnumerable<IDictionaryItem> ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true);
/// <summary>
/// Imports and saves the 'Languages' part of a package xml as a list of <see cref="ILanguage"/>
/// </summary>
/// <param name="languageElementList">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin)</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumerable list of generated languages</returns>
IEnumerable<ILanguage> ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Imports and saves the 'Macros' part of a package xml as a list of <see cref="IMacro"/>
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <param name="packageFile"></param>
/// <returns></returns>
IEnumerable<IMacro> ImportMacros(XElement element, int userId = 0, bool raiseEvents = true);
CompiledPackage GetCompiledPackageInfo(FileInfo packageFile);
/// <summary>
/// Imports and saves package xml as <see cref="ITemplate"/>
/// Installs the package files contained in an umbraco package file (zip)
/// </summary>
/// <param name="element">Xml to import</param>
/// <param name="userId">Optional id of the User performing the operation. Default is zero (admin)</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns>An enumrable list of generated Templates</returns>
IEnumerable<ITemplate> ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true);
/// <param name="packageDefinition"></param>
/// <param name="packageFile"></param>
/// <param name="userId"></param>
IEnumerable<string> InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0);
/// <summary>
/// Exports an <see cref="IContentType"/> to xml as an <see cref="XElement"/>
/// Installs the data, entities, objects contained in an umbraco package file (zip)
/// </summary>
/// <param name="contentType">ContentType to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ContentType item</returns>
XElement Export(IContentType contentType, bool raiseEvents = true);
/// <param name="packageDefinition"></param>
/// <param name="packageFile"></param>
/// <param name="userId"></param>
InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0);
/// <summary>
/// Exports an <see cref="IContent"/> item to xml as an <see cref="XElement"/>
/// Uninstalls all versions of the package by name
/// </summary>
/// <param name="content">Content to export</param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Content object</returns>
XElement Export(IContent content, bool deep = false, bool raiseEvents = true);
/// <param name="packageName"></param>
/// <param name="userId"></param>
/// <returns></returns>
UninstallationSummary UninstallPackage(string packageName, int userId = 0);
#endregion
#region Installed Packages
IEnumerable<PackageDefinition> GetAllInstalledPackages();
/// <summary>
/// Exports an <see cref="IMedia"/> item to xml as an <see cref="XElement"/>
/// Returns the <see cref="PackageDefinition"/> for the installation id
/// </summary>
/// <param name="media">Media to export</param>
/// <param name="deep">Optional parameter indicating whether to include descendents</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Media object</returns>
XElement Export(IMedia media, bool deep = false, bool raiseEvents = true);
/// <param name="id"></param>
/// <returns></returns>
PackageDefinition GetInstalledPackageById(int id);
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// Returns all <see cref="PackageDefinition"/> for the package by name
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Language object</returns>
XElement Export(IEnumerable<ILanguage> languages, bool raiseEvents = true);
/// <param name="name"></param>
/// <returns>
/// A list of all package definitions installed for this package (i.e. original install and any upgrades)
/// </returns>
IEnumerable<PackageDefinition> GetInstalledPackageByName(string name);
/// <summary>
/// Exports a single <see cref="ILanguage"/> item to xml as an <see cref="XElement"/>
/// Returns a <see cref="PackageInstallType"/> for a given package name and version
/// </summary>
/// <param name="language">Language to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the Language object</returns>
XElement Export(ILanguage language, bool raiseEvents = true);
/// <param name="packageName"></param>
/// <param name="packageVersion"></param>
/// <param name="alreadyInstalled">If the package is an upgrade, the original/current PackageDefinition is returned</param>
/// <returns></returns>
PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled);
void DeleteInstalledPackage(int packageId, int userId = 0);
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// Persists a package definition to storage
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
XElement Export(IEnumerable<IDictionaryItem> dictionaryItem, bool includeChildren = true, bool raiseEvents = true);
/// <returns></returns>
bool SaveInstalledPackage(PackageDefinition definition);
#endregion
#region Created Packages
IEnumerable<PackageDefinition> GetAllCreatedPackages();
PackageDefinition GetCreatedPackageById(int id);
void DeleteCreatedPackage(int id, int userId = 0);
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// Persists a package definition to storage
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true);
/// <returns></returns>
bool SaveCreatedPackage(PackageDefinition definition);
/// <summary>
/// Exports a list of Data Types
/// Creates the package file and returns it's physical path
/// </summary>
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
XElement Export(IEnumerable<IDataType> dataTypeDefinitions, bool raiseEvents = true);
/// <param name="definition"></param>
string ExportCreatedPackage(PackageDefinition definition);
#endregion
/// <summary>
/// Exports a single Data Type
/// </summary>
/// <param name="dataType">Data type to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition object</returns>
XElement Export(IDataType dataType, bool raiseEvents = true);
/// <summary>
/// Exports a list of <see cref="ITemplate"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="templates">List of Templates to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ITemplate objects</returns>
XElement Export(IEnumerable<ITemplate> templates, bool raiseEvents = true);
/// <summary>
/// Exports a single <see cref="ITemplate"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="template">Template to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ITemplate object</returns>
XElement Export(ITemplate template, bool raiseEvents = true);
/// <summary>
/// Exports a list of <see cref="IMacro"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macros">Macros to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro objects</returns>
XElement Export(IEnumerable<IMacro> macros, bool raiseEvents = true);
/// <summary>
/// Exports a single <see cref="IMacro"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macro">Macro to export</param>
/// <param name="raiseEvents">Optional parameter indicating whether or not to raise events</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro object</returns>
XElement Export(IMacro macro, bool raiseEvents = true);
/// <summary>
/// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file
/// This will fetch an Umbraco package file from the package repository and return the file name of the downloaded package
/// </summary>
/// <param name="packageId"></param>
/// <param name="umbracoVersion"></param>
/// <param name="userId">The current user id performing the operation</param>
/// <returns></returns>
string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId);
/// <returns>
/// The file name of the downloaded package which will exist in ~/App_Data/packages
/// </returns>
Task<FileInfo> FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId);
}
}
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Represents the ContentType Service, which is an easy access to operations involving <see cref="IContentType"/>
/// </summary>
internal class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository, IContentType, IContentTypeService>, IContentTypeService
public class ContentTypeService : ContentTypeServiceBase<IContentTypeRepository, IContentType, IContentTypeService>, IContentTypeService
{
public ContentTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService,
IContentTypeRepository repository, IAuditRepository auditRepository, IDocumentTypeContainerRepository entityContainerRepository, IEntityRepository entityRepository)
@@ -4,7 +4,7 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Core.Services.Implement
{
internal abstract class ContentTypeServiceBase : ScopeRepositoryService
public abstract class ContentTypeServiceBase : ScopeRepositoryService
{
protected ContentTypeServiceBase(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory)
: base(provider, logger, eventMessagesFactory)
@@ -6,7 +6,7 @@ using Umbraco.Core.Services.Changes;
namespace Umbraco.Core.Services.Implement
{
internal abstract class ContentTypeServiceBase<TItem, TService> : ContentTypeServiceBase
public abstract class ContentTypeServiceBase<TItem, TService> : ContentTypeServiceBase
where TItem : class, IContentTypeComposition
where TService : class, IContentTypeServiceBase<TItem>
{
@@ -13,7 +13,7 @@ using Umbraco.Core.Services.Changes;
namespace Umbraco.Core.Services.Implement
{
internal abstract class ContentTypeServiceBase<TRepository, TItem, TService> : ContentTypeServiceBase<TItem, TService>, IContentTypeServiceBase<TItem>
public abstract class ContentTypeServiceBase<TRepository, TItem, TService> : ContentTypeServiceBase<TItem, TService>, IContentTypeServiceBase<TItem>
where TRepository : IContentTypeRepositoryBase<TItem>
where TItem : class, IContentTypeComposition
where TService : class, IContentTypeServiceBase<TItem>
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Represents the DataType Service, which is an easy access to operations involving <see cref="IDataType"/>
/// </summary>
internal class DataTypeService : ScopeRepositoryService, IDataTypeService
public class DataTypeService : ScopeRepositoryService, IDataTypeService
{
private readonly IDataTypeRepository _dataTypeRepository;
private readonly IDataTypeContainerRepository _dataTypeContainerRepository;
@@ -258,6 +258,9 @@ namespace Umbraco.Core.Services.Implement
public virtual IEnumerable<IEntitySlim> GetAll(UmbracoObjectTypes objectType, params int[] ids)
{
var entityType = objectType.GetClrType();
if (entityType == null)
throw new NotSupportedException($"Type \"{objectType}\" is not supported here.");
GetGetters(entityType);
using (ScopeProvider.CreateScope(autoComplete: true))
@@ -7,49 +7,61 @@ using System.Xml.Linq;
using Newtonsoft.Json;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
namespace Umbraco.Core.Services
namespace Umbraco.Core.Services.Implement
{
//TODO: Move the rest of the logic for the PackageService.Export methods to here!
/// <summary>
/// A helper class to serialize entities to XML
/// Serializes entities to XML
/// </summary>
internal class EntityXmlSerializer
internal class EntityXmlSerializer : IEntityXmlSerializer
{
/// <summary>
/// Exports an IContent item as an XElement.
/// </summary>
public static XElement Serialize(
private readonly IContentTypeService _contentTypeService;
private readonly IMediaService _mediaService;
private readonly IContentService _contentService;
private readonly IDataTypeService _dataTypeService;
private readonly IUserService _userService;
private readonly ILocalizationService _localizationService;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
public EntityXmlSerializer(
IContentService contentService,
IMediaService mediaService,
IDataTypeService dataTypeService,
IUserService userService,
ILocalizationService localizationService,
IEnumerable<IUrlSegmentProvider> urlSegmentProviders,
IContent content,
IContentTypeService contentTypeService,
UrlSegmentProviderCollection urlSegmentProviders)
{
_contentTypeService = contentTypeService;
_mediaService = mediaService;
_contentService = contentService;
_dataTypeService = dataTypeService;
_userService = userService;
_localizationService = localizationService;
_urlSegmentProviders = urlSegmentProviders;
}
/// <summary>
/// Exports an IContent item as an XElement.
/// </summary>
public XElement Serialize(IContent content,
bool published,
bool withDescendants = false) //fixme take care of usage! only used for the packager
{
if (contentService == null) throw new ArgumentNullException(nameof(contentService));
if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService));
if (userService == null) throw new ArgumentNullException(nameof(userService));
if (localizationService == null) throw new ArgumentNullException(nameof(localizationService));
if (content == null) throw new ArgumentNullException(nameof(content));
if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders));
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = content.ContentType.Alias.ToSafeAliasWithForcingCheck();
var xml = SerializeContentBase(dataTypeService, localizationService, content, content.GetUrlSegment(urlSegmentProviders), nodeName, published);
var xml = SerializeContentBase(content, content.GetUrlSegment(_urlSegmentProviders), nodeName, published);
xml.Add(new XAttribute("nodeType", content.ContentType.Id));
xml.Add(new XAttribute("nodeTypeAlias", content.ContentType.Alias));
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile(userService)?.Name ?? "??"));
xml.Add(new XAttribute("creatorName", content.GetCreatorProfile(_userService)?.Name ?? "??"));
//xml.Add(new XAttribute("creatorID", content.CreatorId));
xml.Add(new XAttribute("writerName", content.GetWriterProfile(userService)?.Name ?? "??"));
xml.Add(new XAttribute("writerName", content.GetWriterProfile(_userService)?.Name ?? "??"));
xml.Add(new XAttribute("writerID", content.WriterId));
xml.Add(new XAttribute("template", content.TemplateId?.ToString(CultureInfo.InvariantCulture) ?? ""));
@@ -63,8 +75,8 @@ namespace Umbraco.Core.Services
var total = long.MaxValue;
while(page * pageSize < total)
{
var children = contentService.GetPagedChildren(content.Id, page++, pageSize, out total);
SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml, published);
var children = _contentService.GetPagedChildren(content.Id, page++, pageSize, out total);
SerializeChildren(children, xml, published);
}
}
@@ -75,34 +87,29 @@ namespace Umbraco.Core.Services
/// <summary>
/// Exports an IMedia item as an XElement.
/// </summary>
public static XElement Serialize(
IMediaService mediaService,
IDataTypeService dataTypeService,
IUserService userService,
ILocalizationService localizationService,
IEnumerable<IUrlSegmentProvider> urlSegmentProviders,
public XElement Serialize(
IMedia media,
bool withDescendants = false)
{
if (mediaService == null) throw new ArgumentNullException(nameof(mediaService));
if (dataTypeService == null) throw new ArgumentNullException(nameof(dataTypeService));
if (userService == null) throw new ArgumentNullException(nameof(userService));
if (localizationService == null) throw new ArgumentNullException(nameof(localizationService));
if (_mediaService == null) throw new ArgumentNullException(nameof(_mediaService));
if (_dataTypeService == null) throw new ArgumentNullException(nameof(_dataTypeService));
if (_userService == null) throw new ArgumentNullException(nameof(_userService));
if (_localizationService == null) throw new ArgumentNullException(nameof(_localizationService));
if (media == null) throw new ArgumentNullException(nameof(media));
if (urlSegmentProviders == null) throw new ArgumentNullException(nameof(urlSegmentProviders));
if (_urlSegmentProviders == null) throw new ArgumentNullException(nameof(_urlSegmentProviders));
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = media.ContentType.Alias.ToSafeAliasWithForcingCheck();
const bool published = false; // always false for media
var xml = SerializeContentBase(dataTypeService, localizationService, media, media.GetUrlSegment(urlSegmentProviders), nodeName, published);
var xml = SerializeContentBase(media, media.GetUrlSegment(_urlSegmentProviders), nodeName, published);
xml.Add(new XAttribute("nodeType", media.ContentType.Id));
xml.Add(new XAttribute("nodeTypeAlias", media.ContentType.Alias));
//xml.Add(new XAttribute("creatorName", media.GetCreatorProfile(userService).Name));
//xml.Add(new XAttribute("creatorID", media.CreatorId));
xml.Add(new XAttribute("writerName", media.GetWriterProfile(userService)?.Name ?? string.Empty));
xml.Add(new XAttribute("writerName", media.GetWriterProfile(_userService)?.Name ?? string.Empty));
xml.Add(new XAttribute("writerID", media.WriterId));
//xml.Add(new XAttribute("template", 0)); // no template for media
@@ -114,8 +121,8 @@ namespace Umbraco.Core.Services
var total = long.MaxValue;
while (page * pageSize < total)
{
var children = mediaService.GetPagedChildren(media.Id, page++, pageSize, out total);
SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, children, xml);
var children = _mediaService.GetPagedChildren(media.Id, page++, pageSize, out total);
SerializeChildren(children, xml);
}
}
@@ -125,16 +132,13 @@ namespace Umbraco.Core.Services
/// <summary>
/// Exports an IMember item as an XElement.
/// </summary>
public static XElement Serialize(
IDataTypeService dataTypeService,
ILocalizationService localizationService,
IMember member)
public XElement Serialize(IMember member)
{
// nodeName should match Casing.SafeAliasWithForcingCheck(content.ContentType.Alias);
var nodeName = member.ContentType.Alias.ToSafeAliasWithForcingCheck();
const bool published = false; // always false for member
var xml = SerializeContentBase(dataTypeService, localizationService, member, "", nodeName, published);
var xml = SerializeContentBase(member, "", nodeName, published);
xml.Add(new XAttribute("nodeType", member.ContentType.Id));
xml.Add(new XAttribute("nodeTypeAlias", member.ContentType.Alias));
@@ -148,7 +152,22 @@ namespace Umbraco.Core.Services
return xml;
}
public XElement Serialize(IDataTypeService dataTypeService, IDataType dataType)
/// <summary>
/// Exports a list of Data Types
/// </summary>
/// <param name="dataTypeDefinitions">List of data types to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDataTypeDefinition objects</returns>
public XElement Serialize(IEnumerable<IDataType> dataTypeDefinitions)
{
var container = new XElement("DataTypes");
foreach (var dataTypeDefinition in dataTypeDefinitions)
{
container.Add(Serialize(dataTypeDefinition));
}
return container;
}
public XElement Serialize(IDataType dataType)
{
var xml = new XElement("DataType");
xml.Add(new XAttribute("Name", dataType.Name));
@@ -162,7 +181,7 @@ namespace Umbraco.Core.Services
if (dataType.Level != 1)
{
//get url encoded folder names
var folders = dataTypeService.GetContainers(dataType)
var folders = _dataTypeService.GetContainers(dataType)
.OrderBy(x => x.Level)
.Select(x => HttpUtility.UrlEncode(x.Name));
@@ -175,7 +194,45 @@ namespace Umbraco.Core.Services
return xml;
}
public XElement Serialize(IDictionaryItem dictionaryItem)
/// <summary>
/// Exports a list of <see cref="IDictionaryItem"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">List of dictionary items to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem objects</returns>
public XElement Serialize(IEnumerable<IDictionaryItem> dictionaryItem, bool includeChildren = true)
{
var xml = new XElement("DictionaryItems");
foreach (var item in dictionaryItem)
{
xml.Add(Serialize(item, includeChildren));
}
return xml;
}
/// <summary>
/// Exports a single <see cref="IDictionaryItem"/> item to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="dictionaryItem">Dictionary Item to export</param>
/// <param name="includeChildren">Optional boolean indicating whether or not to include children</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IDictionaryItem object</returns>
public XElement Serialize(IDictionaryItem dictionaryItem, bool includeChildren)
{
var xml = Serialize(dictionaryItem);
if (includeChildren)
{
var children = _localizationService.GetDictionaryItemChildren(dictionaryItem.Key);
foreach (var child in children)
{
xml.Add(Serialize(child, true));
}
}
return xml;
}
private XElement Serialize(IDictionaryItem dictionaryItem)
{
var xml = new XElement("DictionaryItem", new XAttribute("Key", dictionaryItem.ItemKey));
foreach (var translation in dictionaryItem.Translations)
@@ -210,6 +267,21 @@ namespace Umbraco.Core.Services
return xml;
}
/// <summary>
/// Exports a list of <see cref="ILanguage"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="languages">List of Languages to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the ILanguage objects</returns>
public XElement Serialize(IEnumerable<ILanguage> languages)
{
var xml = new XElement("Languages");
foreach (var language in languages)
{
xml.Add(Serialize(language));
}
return xml;
}
public XElement Serialize(ILanguage language)
{
var xml = new XElement("Language",
@@ -240,7 +312,22 @@ namespace Umbraco.Core.Services
return xml;
}
public XElement Serialize(IDataTypeService dataTypeService, IMediaType mediaType)
/// <summary>
/// Exports a list of <see cref="ITemplate"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="templates"></param>
/// <returns></returns>
public XElement Serialize(IEnumerable<ITemplate> templates)
{
var xml = new XElement("Templates");
foreach (var item in templates)
{
xml.Add(Serialize(item));
}
return xml;
}
public XElement Serialize(IMediaType mediaType)
{
var info = new XElement("Info",
new XElement("Name", mediaType.Name),
@@ -263,7 +350,7 @@ namespace Umbraco.Core.Services
var genericProperties = new XElement("GenericProperties"); // actually, all of them
foreach (var propertyType in mediaType.PropertyTypes)
{
var definition = dataTypeService.GetDataType(propertyType.DataTypeId);
var definition = _dataTypeService.GetDataType(propertyType.DataTypeId);
var propertyGroup = propertyType.PropertyGroupId == null // true generic property
? null
@@ -301,6 +388,21 @@ namespace Umbraco.Core.Services
return xml;
}
/// <summary>
/// Exports a list of <see cref="IMacro"/> items to xml as an <see cref="XElement"/>
/// </summary>
/// <param name="macros">Macros to export</param>
/// <returns><see cref="XElement"/> containing the xml representation of the IMacro objects</returns>
public XElement Serialize(IEnumerable<IMacro> macros)
{
var xml = new XElement("Macros");
foreach (var item in macros)
{
xml.Add(Serialize(item));
}
return xml;
}
public XElement Serialize(IMacro macro)
{
var xml = new XElement("macro");
@@ -328,7 +430,7 @@ namespace Umbraco.Core.Services
return xml;
}
public XElement Serialize(IDataTypeService dataTypeService, IContentTypeService contentTypeService, IContentType contentType)
public XElement Serialize(IContentType contentType)
{
var info = new XElement("Info",
new XElement("Name", contentType.Name),
@@ -373,7 +475,7 @@ namespace Umbraco.Core.Services
var genericProperties = new XElement("GenericProperties"); // actually, all of them
foreach (var propertyType in contentType.PropertyTypes)
{
var definition = dataTypeService.GetDataType(propertyType.DataTypeId);
var definition = _dataTypeService.GetDataType(propertyType.DataTypeId);
var propertyGroup = propertyType.PropertyGroupId == null // true generic property
? null
@@ -414,7 +516,7 @@ namespace Umbraco.Core.Services
if (contentType.Level != 1 && masterContentType == null)
{
//get url encoded folder names
var folders = contentTypeService.GetContainers(contentType)
var folders = _contentTypeService.GetContainers(contentType)
.OrderBy(x => x.Level)
.Select(x => HttpUtility.UrlEncode(x.Name));
@@ -428,7 +530,7 @@ namespace Umbraco.Core.Services
}
// exports an IContentBase (IContent, IMedia or IMember) as an XElement.
private static XElement SerializeContentBase(IDataTypeService dataTypeService, ILocalizationService localizationService, IContentBase contentBase, string urlValue, string nodeName, bool published)
private XElement SerializeContentBase(IContentBase contentBase, string urlValue, string nodeName, bool published)
{
var xml = new XElement(nodeName,
new XAttribute("id", contentBase.Id),
@@ -445,13 +547,13 @@ namespace Umbraco.Core.Services
new XAttribute("isDoc", ""));
foreach (var property in contentBase.Properties)
xml.Add(SerializeProperty(dataTypeService, localizationService, property, published));
xml.Add(SerializeProperty(property, published));
return xml;
}
// exports a property as XElements.
private static IEnumerable<XElement> SerializeProperty(IDataTypeService dataTypeService, ILocalizationService localizationService, Property property, bool published)
private IEnumerable<XElement> SerializeProperty(Property property, bool published)
{
var propertyType = property.PropertyType;
@@ -459,16 +561,16 @@ namespace Umbraco.Core.Services
var propertyEditor = Current.PropertyEditors[propertyType.PropertyEditorAlias];
return propertyEditor == null
? Array.Empty<XElement>()
: propertyEditor.GetValueEditor().ConvertDbToXml(property, dataTypeService, localizationService, published);
: propertyEditor.GetValueEditor().ConvertDbToXml(property, _dataTypeService, _localizationService, published);
}
// exports an IContent item descendants.
private static void SerializeChildren(IContentService contentService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IEnumerable<IContent> children, XElement xml, bool published)
private void SerializeChildren(IEnumerable<IContent> children, XElement xml, bool published)
{
foreach (var child in children)
{
// add the child xml
var childXml = Serialize(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, child, published);
var childXml = Serialize(child, published);
xml.Add(childXml);
const int pageSize = 500;
@@ -476,20 +578,20 @@ namespace Umbraco.Core.Services
var total = long.MaxValue;
while(page * pageSize < total)
{
var grandChildren = contentService.GetPagedChildren(child.Id, page++, pageSize, out total);
var grandChildren = _contentService.GetPagedChildren(child.Id, page++, pageSize, out total);
// recurse
SerializeChildren(contentService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml, published);
SerializeChildren(grandChildren, childXml, published);
}
}
}
// exports an IMedia item descendants.
private static void SerializeChildren(IMediaService mediaService, IDataTypeService dataTypeService, IUserService userService, ILocalizationService localizationService, IEnumerable<IUrlSegmentProvider> urlSegmentProviders, IEnumerable<IMedia> children, XElement xml)
private void SerializeChildren(IEnumerable<IMedia> children, XElement xml)
{
foreach (var child in children)
{
// add the child xml
var childXml = Serialize(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, child);
var childXml = Serialize(child);
xml.Add(childXml);
const int pageSize = 500;
@@ -497,9 +599,9 @@ namespace Umbraco.Core.Services
var total = long.MaxValue;
while (page * pageSize < total)
{
var grandChildren = mediaService.GetPagedChildren(child.Id, page++, pageSize, out total);
var grandChildren = _mediaService.GetPagedChildren(child.Id, page++, pageSize, out total);
// recurse
SerializeChildren(mediaService, dataTypeService, userService, localizationService, urlSegmentProviders, grandChildren, childXml);
SerializeChildren(grandChildren, childXml);
}
}
}
@@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement
/// <summary>
/// Represents the Macro Service, which is an easy access to operations involving <see cref="IMacro"/>
/// </summary>
internal class MacroService : ScopeRepositoryService, IMacroService
public class MacroService : ScopeRepositoryService, IMacroService
{
private readonly IMacroRepository _macroRepository;
private readonly IAuditRepository _auditRepository;
@@ -8,7 +8,7 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Core.Services.Implement
{
internal class MediaTypeService : ContentTypeServiceBase<IMediaTypeRepository, IMediaType, IMediaTypeService>, IMediaTypeService
public class MediaTypeService : ContentTypeServiceBase<IMediaTypeRepository, IMediaType, IMediaTypeService>, IMediaTypeService
{
public MediaTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMediaService mediaService,
IMediaTypeRepository mediaTypeRepository, IAuditRepository auditRepository, IMediaTypeContainerRepository entityContainerRepository,
@@ -8,7 +8,7 @@ using Umbraco.Core.Scoping;
namespace Umbraco.Core.Services.Implement
{
internal class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, IMemberType, IMemberTypeService>, IMemberTypeService
public class MemberTypeService : ContentTypeServiceBase<IMemberTypeRepository, IMemberType, IMemberTypeService>, IMemberTypeService
{
private readonly IMemberTypeRepository _memberTypeRepository;
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -892,7 +892,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Ensures that the folder path ends with a DirectorySeperatorChar
/// Ensures that the folder path ends with a DirectorySeparatorChar
/// </summary>
/// <param name="currentFolder"></param>
/// <returns></returns>
@@ -551,7 +551,7 @@ namespace Umbraco.Core.Sync
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache");
var tempFolder = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "DistCache");
distCacheFilePath = Path.Combine(tempFolder, fileName);
break;
}
+25 -10
View File
@@ -321,7 +321,6 @@
<Compile Include="Constants-Icons.cs" />
<Compile Include="Constants-ObjectTypes.cs" />
<Compile Include="Constants-PackageRepository.cs" />
<Compile Include="Constants-Packaging.cs" />
<Compile Include="Constants-PropertyEditors.cs" />
<Compile Include="Constants-PropertyTypeGroups.cs" />
<Compile Include="Constants-Security.cs" />
@@ -368,6 +367,7 @@
<Compile Include="Migrations\Upgrade\V_7_12_0\RenameTrueFalseField.cs" />
<Compile Include="Migrations\Upgrade\V_7_12_0\SetDefaultTagsStorageType.cs" />
<Compile Include="Migrations\Upgrade\V_7_12_0\UpdateUmbracoConsent.cs" />
<Compile Include="Migrations\Upgrade\V_7_14_0\UpdateMemberGroupPickerData.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddIndexToPropertyTypeAliasColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddInstructionCountColumn.cs" />
<Compile Include="Migrations\Upgrade\V_7_8_0\AddMediaVersionTable.cs" />
@@ -433,6 +433,12 @@
<Compile Include="Models\Membership\MemberExportProperty.cs" />
<Compile Include="Models\NotificationEmailBodyParams.cs" />
<Compile Include="Models\NotificationEmailSubjectParams.cs" />
<Compile Include="Models\Packaging\ActionRunAt.cs" />
<Compile Include="Models\Packaging\CompiledPackage.cs" />
<Compile Include="Models\Packaging\CompiledPackageDocument.cs" />
<Compile Include="Models\Packaging\CompiledPackageFile.cs" />
<Compile Include="Models\Packaging\IPackageInfo.cs" />
<Compile Include="Models\Packaging\PackageDefinition.cs" />
<Compile Include="Models\PathValidationExtensions.cs" />
<Compile Include="Models\Entities\TreeEntityBase.cs" />
<Compile Include="Models\PropertyTagsExtensions.cs" />
@@ -444,6 +450,19 @@
<Compile Include="Models\PublishedContent\VariationContext.cs" />
<Compile Include="Models\PublishedContent\ThreadCultureVariationContextAccessor.cs" />
<Compile Include="Models\PublishedContent\VariationContextAccessorExtensions.cs" />
<Compile Include="Packaging\CompiledPackageXmlParser.cs" />
<Compile Include="Packaging\ICreatedPackagesRepository.cs" />
<Compile Include="Packaging\IInstalledPackagesRepository.cs" />
<Compile Include="Packaging\IPackageActionRunner.cs" />
<Compile Include="Packaging\IPackageDefinitionRepository.cs" />
<Compile Include="Packaging\IPackageInstallation.cs" />
<Compile Include="Packaging\PackageActionRunner.cs" />
<Compile Include="Packaging\PackageDataInstallation.cs" />
<Compile Include="Packaging\PackageDefinitionXmlParser.cs" />
<Compile Include="Packaging\PackageFileInstallation.cs" />
<Compile Include="Packaging\PackageInstallType.cs" />
<Compile Include="Packaging\PackagesRepository.cs" />
<Compile Include="Models\Packaging\RequirementsType.cs" />
<Compile Include="Persistence\Dtos\AuditEntryDto.cs" />
<Compile Include="Persistence\Dtos\ConsentDto.cs" />
<Compile Include="Persistence\Dtos\ContentScheduleDto.cs" />
@@ -494,6 +513,8 @@
<Compile Include="PropertyEditors\VoidEditor.cs" />
<Compile Include="ReadLock.cs" />
<Compile Include="ReflectionUtilities-Unused.cs" />
<Compile Include="RuntimeLevelReason.cs" />
<Compile Include="RuntimeStateOptions.cs" />
<Compile Include="Runtime\CoreRuntime.cs" />
<Compile Include="Runtime\CoreRuntimeComponent.cs" />
<Compile Include="CustomBooleanTypeConverter.cs" />
@@ -558,13 +579,11 @@
<Compile Include="Events\EventNameExtractor.cs" />
<Compile Include="Events\EventNameExtractorError.cs" />
<Compile Include="Events\EventNameExtractorResult.cs" />
<Compile Include="Events\ExportEventArgs.cs" />
<Compile Include="Events\IDeletingMediaFilesEventArgs.cs" />
<Compile Include="Events\IEventDefinition.cs" />
<Compile Include="Events\IEventDispatcher.cs" />
<Compile Include="Events\IEventMessagesAccessor.cs" />
<Compile Include="Events\IEventMessagesFactory.cs" />
<Compile Include="Events\ImportEventArgs.cs" />
<Compile Include="Events\ImportPackageEventArgs.cs" />
<Compile Include="Events\MacroErrorEventArgs.cs" />
<Compile Include="Events\MigrationEventArgs.cs" />
@@ -764,7 +783,6 @@
<Compile Include="Models\MigrationEntry.cs" />
<Compile Include="Models\Notification.cs" />
<Compile Include="Models\Packaging\InstallationSummary.cs" />
<Compile Include="Models\Packaging\MetaData.cs" />
<Compile Include="Models\Packaging\PackageAction.cs" />
<Compile Include="Models\Packaging\PreInstallWarnings.cs" />
<Compile Include="Models\PagedResult.cs" />
@@ -884,11 +902,7 @@
<Compile Include="ObjectExtensions.cs" />
<Compile Include="Collections\OrderedHashSet.cs" />
<Compile Include="Packaging\ConflictingPackageData.cs" />
<Compile Include="Packaging\IConflictingPackageData.cs" />
<Compile Include="Packaging\IPackageExtraction.cs" />
<Compile Include="Packaging\IPackageInstallation.cs" />
<Compile Include="Packaging\Models\UninstallationSummary.cs" />
<Compile Include="Packaging\PackageBinaryInspector.cs" />
<Compile Include="Models\Packaging\UninstallationSummary.cs" />
<Compile Include="Packaging\PackageExtraction.cs" />
<Compile Include="Packaging\PackageInstallation.cs" />
<Compile Include="Persistence\BulkDataReader.cs" />
@@ -1355,6 +1369,7 @@
<Compile Include="Serialization\UdiRangeJsonConverter.cs" />
<Compile Include="ServiceContextExtensions.cs" />
<Compile Include="Services\IConsentService.cs" />
<Compile Include="Services\IEntityXmlSerializer.cs" />
<Compile Include="Services\Implement\AuditService.cs" />
<Compile Include="Services\Changes\ContentTypeChange.cs" />
<Compile Include="Services\Changes\ContentTypeChangeExtensions.cs" />
@@ -1374,7 +1389,7 @@
<Compile Include="Services\Implement\DataTypeService.cs" />
<Compile Include="Services\Implement\DomainService.cs" />
<Compile Include="Services\Implement\EntityService.cs" />
<Compile Include="Services\EntityXmlSerializer.cs" />
<Compile Include="Services\Implement\EntityXmlSerializer.cs" />
<Compile Include="Services\Implement\ExternalLoginService.cs" />
<Compile Include="Services\Implement\FileService.cs" />
<Compile Include="Services\IApplicationTreeService.cs" />
+9 -156
View File
@@ -14,7 +14,7 @@ namespace Umbraco.Core.Xml
/// <summary>
/// The XmlHelper class contains general helper methods for working with xml in umbraco.
/// </summary>
public class XmlHelper
internal class XmlHelper
{
/// <summary>
/// Creates or sets an attribute on the XmlNode if an Attributes collection is available
@@ -126,44 +126,6 @@ namespace Umbraco.Core.Xml
return false;
}
/// <summary>
/// Tries to create a new <c>XElement</c> from a property value.
/// </summary>
/// <param name="value">The value of the property.</param>
/// <param name="elt">The Xml element.</param>
/// <returns>A value indicating whether it has been possible to create the element.</returns>
/// <remarks>The value can be anything... Performance-wise, this is bad.</remarks>
public static bool TryCreateXElementFromPropertyValue(object value, out XElement elt)
{
// see note above in TryCreateXPathDocumentFromPropertyValue...
elt = null;
var xml = value as string;
if (xml == null) return false; // not a string
if (CouldItBeXml(xml) == false) return false; // string does not look like it's xml
if (IsXmlWhitespace(xml)) return false; // string is whitespace, xml-wise
try
{
elt = XElement.Parse(xml, LoadOptions.None);
}
catch
{
elt = null;
return false; // string can't be parsed into xml
}
//SD: This used to do this but the razor macros and the entire razor macros section is gone, it was all legacy, it seems this method isn't even
// used apart from for tests so don't think this matters. In any case, we no longer check for this!
//var name = elt.Name.LocalName; // must not match an excluded tag
//if (UmbracoConfig.For.UmbracoSettings().Scripting.NotDynamicXmlDocumentElements.All(x => x.Element.InvariantEquals(name) == false))
// return true;
//elt = null;
//return false;
return true;
}
/// <summary>
/// Sorts the children of a parentNode.
@@ -186,47 +148,6 @@ namespace Umbraco.Core.Xml
parentNode.AppendChild(node); // moves the node to the last position
}
/// <summary>
/// Sorts the children of a parentNode if needed.
/// </summary>
/// <param name="parentNode">The parent node.</param>
/// <param name="childNodesXPath">An XPath expression to select children of <paramref name="parentNode"/> to sort.</param>
/// <param name="orderBy">A function returning the value to order the nodes by.</param>
/// <returns>A value indicating whether sorting was needed.</returns>
/// <remarks>same as SortNodes but will do nothing if nodes are already sorted - should improve performances.</remarks>
internal static bool SortNodesIfNeeded(
XmlNode parentNode,
string childNodesXPath,
Func<XmlNode, int> orderBy)
{
// ensure orderBy runs only once per node
// checks whether nodes are already ordered
// and actually sorts only if needed
var childNodesAndOrder = parentNode.SelectNodes(childNodesXPath).Cast<XmlNode>()
.Select(x => Tuple.Create(x, orderBy(x))).ToArray();
var a = 0;
foreach (var x in childNodesAndOrder)
{
if (a > x.Item2)
{
a = -1;
break;
}
a = x.Item2;
}
if (a >= 0)
return false;
// append child nodes to last position, in sort-order
// so all child nodes will go after the property nodes
foreach (var x in childNodesAndOrder.OrderBy(x => x.Item2))
parentNode.AppendChild(x.Item1); // moves the node to the last position
return true;
}
/// <summary>
/// Sorts a single child node of a parentNode.
@@ -281,72 +202,6 @@ namespace Umbraco.Core.Xml
return false;
}
// used by DynamicNode only, see note in TryCreateXPathDocumentFromPropertyValue
public static string StripDashesInElementOrAttributeNames(string xml)
{
using (var outputms = new MemoryStream())
{
using (TextWriter outputtw = new StreamWriter(outputms))
{
using (var ms = new MemoryStream())
{
using (var tw = new StreamWriter(ms))
{
tw.Write(xml);
tw.Flush();
ms.Position = 0;
using (var tr = new StreamReader(ms))
{
bool IsInsideElement = false, IsInsideQuotes = false;
int ic = 0;
while ((ic = tr.Read()) != -1)
{
if (ic == (int)'<' && !IsInsideQuotes)
{
if (tr.Peek() != (int)'!')
{
IsInsideElement = true;
}
}
if (ic == (int)'>' && !IsInsideQuotes)
{
IsInsideElement = false;
}
if (ic == (int)'"')
{
IsInsideQuotes = !IsInsideQuotes;
}
if (!IsInsideElement || ic != (int)'-' || IsInsideQuotes)
{
outputtw.Write((char)ic);
}
}
}
}
}
outputtw.Flush();
outputms.Position = 0;
using (TextReader outputtr = new StreamReader(outputms))
{
return outputtr.ReadToEnd();
}
}
}
}
/// <summary>
/// Imports a XML node from text.
/// </summary>
/// <param name="text">The text.</param>
/// <param name="xmlDoc">The XML doc.</param>
/// <returns></returns>
public static XmlNode ImportXmlNodeFromText(string text, ref XmlDocument xmlDoc)
{
xmlDoc.LoadXml(text);
return xmlDoc.FirstChild;
}
/// <summary>
/// Opens a file as a XmlDocument.
@@ -355,16 +210,14 @@ namespace Umbraco.Core.Xml
/// <returns>Returns a XmlDocument class</returns>
public static XmlDocument OpenAsXmlDocument(string filePath)
{
var reader = new XmlTextReader(IOHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All};
var xmlDoc = new XmlDocument();
//Load the file into the XmlDocument
xmlDoc.Load(reader);
//Close off the connection to the file.
reader.Close();
return xmlDoc;
using (var reader = new XmlTextReader(IOHelper.MapPath(filePath)) {WhitespaceHandling = WhitespaceHandling.All})
{
var xmlDoc = new XmlDocument();
//Load the file into the XmlDocument
xmlDoc.Load(reader);
return xmlDoc;
}
}
/// <summary>
@@ -1,13 +1,13 @@
using System.Xml;
using System.Xml.Linq;
using Umbraco.Core.Composing;
namespace Umbraco.Core._Legacy.PackageActions
{
public interface IPackageAction : IDiscoverable
{
bool Execute(string packageName, XmlNode xmlData);
bool Execute(string packageName, XElement xmlData);
string Alias();
bool Undo(string packageName, XmlNode xmlData);
XmlNode SampleXml();
bool Undo(string packageName, XElement xmlData);
}
}
@@ -3,7 +3,7 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core._Legacy.PackageActions
{
internal class PackageActionCollection : BuilderCollectionBase<IPackageAction>
public sealed class PackageActionCollection : BuilderCollectionBase<IPackageAction>
{
public PackageActionCollection(IEnumerable<IPackageAction> items)
: base(items)
@@ -13,11 +13,11 @@ namespace Umbraco.Examine
/// </summary>
public class ContentValueSetBuilder : BaseValueSetBuilder<IContent>, IContentValueSetBuilder, IPublishedContentValueSetBuilder
{
private readonly IEnumerable<IUrlSegmentProvider> _urlSegmentProviders;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly IUserService _userService;
public ContentValueSetBuilder(PropertyEditorCollection propertyEditors,
IEnumerable<IUrlSegmentProvider> urlSegmentProviders,
UrlSegmentProviderCollection urlSegmentProviders,
IUserService userService,
bool publishedValuesOnly)
: base(propertyEditors, publishedValuesOnly)
+1 -1
View File
@@ -29,7 +29,7 @@ namespace Umbraco.Examine
public virtual Lucene.Net.Store.Directory CreateFileSystemLuceneDirectory(string folderName)
{
var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Data), "TEMP", "ExamineIndexes", folderName));
var dirInfo = new DirectoryInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.TempData), "ExamineIndexes", folderName));
if (!dirInfo.Exists)
System.IO.Directory.CreateDirectory(dirInfo.FullName);
+1 -1
View File
@@ -10,7 +10,7 @@ namespace Umbraco.Examine
{
public class MediaValueSetBuilder : BaseValueSetBuilder<IMedia>
{
private readonly IEnumerable<IUrlSegmentProvider> _urlSegmentProviders;
private readonly UrlSegmentProviderCollection _urlSegmentProviders;
private readonly IUserService _userService;
public MediaValueSetBuilder(PropertyEditorCollection propertyEditors,
@@ -6,6 +6,7 @@ using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
using Umbraco.Tests.Testing.Objects.Accessors;
@@ -66,7 +67,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var domainCache = new DomainCache(ServiceContext.DomainService, DefaultCultureAccessor);
var publishedShapshot = new Umbraco.Web.PublishedCache.XmlPublishedCache.PublishedSnapshot(
new PublishedContentCache(xmlStore, domainCache, cacheProvider, globalSettings, new SiteDomainHelper(), ContentTypesCache, null, null),
new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache),
new PublishedMediaCache(xmlStore, ServiceContext.MediaService, ServiceContext.UserService, cacheProvider, ContentTypesCache, Factory.GetInstance<IEntityXmlSerializer>()),
new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache),
domainCache);
var publishedSnapshotService = new Mock<IPublishedSnapshotService>();
@@ -15,6 +15,7 @@ using Umbraco.Tests.Testing;
using Current = Umbraco.Web.Composing.Current;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
using Umbraco.Core.Services;
using Umbraco.Tests.PublishedContent;
namespace Umbraco.Tests.Cache.PublishedCache
@@ -74,7 +75,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var mChild2 = MakeNewMedia("Child2", mType, user, mRoot2.Id);
var ctx = GetUmbracoContext("/test");
var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache);
var cache = new PublishedMediaCache(new XmlStore((XmlDocument) null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance<IEntityXmlSerializer>());
var roots = cache.GetAtRoot();
Assert.AreEqual(2, roots.Count());
Assert.IsTrue(roots.Select(x => x.Id).ContainsAll(new[] {mRoot1.Id, mRoot2.Id}));
@@ -92,7 +93,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
//var publishedMedia = PublishedMediaTests.GetNode(mRoot.Id, GetUmbracoContext("/test", 1234));
var umbracoContext = GetUmbracoContext("/test");
var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), Current.Services.MediaService, Current.Services.UserService, new StaticCacheProvider(), ContentTypesCache);
var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), Current.Services.MediaService, Current.Services.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance<IEntityXmlSerializer>());
var publishedMedia = cache.GetById(mRoot.Id);
Assert.IsNotNull(publishedMedia);
@@ -203,7 +204,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var result = new SearchResult("1234", 1, () => fields.ToDictionary(x => x.Key, x => new List<string> { x.Value }));
var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache);
var store = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance<IEntityXmlSerializer>());
var doc = store.CreateFromCacheValues(store.ConvertFromSearchResult(result));
DoAssert(doc, 1234, key, templateIdVal: null, 0, "/media/test.jpg", "Image", 23, "Shannon", "Shannon", 0, 0, "-1,1234", DateTime.Parse("2012-07-17T10:34:09"), DateTime.Parse("2012-07-16T10:34:09"), 2);
@@ -219,7 +220,7 @@ namespace Umbraco.Tests.Cache.PublishedCache
var xmlDoc = GetMediaXml();
((XmlElement)xmlDoc.DocumentElement.FirstChild).SetAttribute("key", key.ToString());
var navigator = xmlDoc.SelectSingleNode("/root/Image").CreateNavigator();
var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache);
var cache = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance<IEntityXmlSerializer>());
var doc = cache.CreateFromCacheValues(cache.ConvertFromXPathNavigator(navigator, true));
DoAssert(doc, 2000, key, templateIdVal: null, 2, "image1", "Image", 23, "Shannon", "Shannon", 33, 33, "-1,2000", DateTime.Parse("2012-06-12T14:13:17"), DateTime.Parse("2012-07-20T18:50:43"), 1);
@@ -1,6 +1,7 @@
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -41,7 +42,7 @@ namespace Umbraco.Tests.Composing
public class PackageAction1 : IPackageAction
{
public bool Execute(string packageName, XmlNode xmlData)
public bool Execute(string packageName, XElement xmlData)
{
throw new NotImplementedException();
}
@@ -51,7 +52,7 @@ namespace Umbraco.Tests.Composing
return "pa1";
}
public bool Undo(string packageName, XmlNode xmlData)
public bool Undo(string packageName, XElement xmlData)
{
throw new NotImplementedException();
}
@@ -64,7 +65,7 @@ namespace Umbraco.Tests.Composing
public class PackageAction2 : IPackageAction
{
public bool Execute(string packageName, XmlNode xmlData)
public bool Execute(string packageName, XElement xmlData)
{
throw new NotImplementedException();
}
@@ -74,7 +75,7 @@ namespace Umbraco.Tests.Composing
return "pa2";
}
public bool Undo(string packageName, XmlNode xmlData)
public bool Undo(string packageName, XElement xmlData)
{
throw new NotImplementedException();
}
@@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing
Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree]
typesFound = TypeFinder.FindClassesWithAttribute<TreeAttribute>(new[] { typeof (UmbracoContext).Assembly });
Assert.AreEqual(21, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
Assert.AreEqual(22, typesFound.Count()); // + classes in Umbraco.Web are marked with [Tree]
}
private static IProfilingLogger GetTestProfilingLogger()
@@ -30,7 +30,7 @@ namespace Umbraco.Tests.Composing
// this ensures it's reset
_typeLoader = new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of<ILogger>(), Mock.Of<IProfiler>()));
foreach (var file in Directory.GetFiles(IOHelper.MapPath("~/App_Data/TEMP/TypesCache")))
foreach (var file in Directory.GetFiles(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "TypesCache")))
File.Delete(file);
// for testing, we'll specify which assemblies are scanned for the PluginTypeResolver
@@ -41,7 +41,7 @@ namespace Umbraco.Tests.IO
private static void ClearFiles()
{
TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests"));
TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs"));
TestHelper.DeleteDirectory(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs"));
}
private static string NormPath(string path)
@@ -388,7 +388,7 @@ namespace Umbraco.Tests.IO
var logger = Mock.Of<ILogger>();
var path = IOHelper.MapPath("FileSysTests");
var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs");
var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs");
Directory.CreateDirectory(path);
Directory.CreateDirectory(shadowfs);
@@ -482,7 +482,7 @@ namespace Umbraco.Tests.IO
var logger = Mock.Of<ILogger>();
var path = IOHelper.MapPath("FileSysTests");
var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs");
var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs");
Directory.CreateDirectory(path);
var scopedFileSystems = false;
@@ -535,7 +535,7 @@ namespace Umbraco.Tests.IO
var logger = Mock.Of<ILogger>();
var path = IOHelper.MapPath("FileSysTests");
var shadowfs = IOHelper.MapPath("App_Data/TEMP/ShadowFs");
var shadowfs = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs");
Directory.CreateDirectory(path);
var scopedFileSystems = false;
+3 -1
View File
@@ -2,7 +2,9 @@
using System.Xml.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Models;
using Umbraco.Core.Services;
using Umbraco.Core.Strings;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Entities;
@@ -29,7 +31,7 @@ namespace Umbraco.Tests.Models
var urlName = content.GetUrlSegment(new[]{new DefaultUrlSegmentProvider() });
// Act
XElement element = content.ToXml();
XElement element = content.ToXml(Factory.GetInstance<IEntityXmlSerializer>());
// Assert
Assert.That(element, Is.Not.Null);
+2 -1
View File
@@ -3,6 +3,7 @@ using System.Xml.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
@@ -50,7 +51,7 @@ namespace Umbraco.Tests.Models
var urlName = media.GetUrlSegment(new[] { new DefaultUrlSegmentProvider() });
// Act
XElement element = media.ToXml();
XElement element = media.ToXml(Factory.GetInstance<IEntityXmlSerializer>());
// Assert
Assert.That(element, Is.Not.Null);
@@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Xml.Linq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Services;
using Umbraco.Tests.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
namespace Umbraco.Tests.Packaging
{
[TestFixture]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
public class CreatedPackagesRepositoryTests : TestWithDatabaseBase
{
private Guid _testBaseFolder;
public override void SetUp()
{
base.SetUp();
_testBaseFolder = Guid.NewGuid();
}
public override void TearDown()
{
base.TearDown();
//clear out files/folders
Directory.Delete(IOHelper.MapPath("~/" + _testBaseFolder), true);
}
public ICreatedPackagesRepository PackageBuilder => new PackagesRepository(
ServiceContext.ContentService, ServiceContext.ContentTypeService, ServiceContext.DataTypeService,
ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService,
Factory.GetInstance<IEntityXmlSerializer>(), Logger,
"createdPackages.config",
//temp paths
tempFolderPath: "~/" + _testBaseFolder + "/temp",
packagesFolderPath: "~/" + _testBaseFolder + "/packages",
mediaFolderPath: "~/" + _testBaseFolder + "/media");
[Test]
public void Delete()
{
var def1 = new PackageDefinition
{
Name = "test",
Url = "http://test.com",
Author = "Someone",
AuthorUrl = "http://test.com"
};
var result = PackageBuilder.SavePackage(def1);
Assert.IsTrue(result);
PackageBuilder.Delete(def1.Id);
def1 = PackageBuilder.GetById(def1.Id);
Assert.IsNull(def1);
}
[Test]
public void Create_New()
{
var def1 = new PackageDefinition
{
Name = "test",
Url = "http://test.com",
Author = "Someone",
AuthorUrl = "http://test.com"
};
var result = PackageBuilder.SavePackage(def1);
Assert.IsTrue(result);
Assert.AreEqual(1, def1.Id);
Assert.AreNotEqual(default(Guid).ToString(), def1.PackageId);
var def2 = new PackageDefinition
{
Name = "test2",
Url = "http://test2.com",
Author = "Someone2",
AuthorUrl = "http://test2.com"
};
result = PackageBuilder.SavePackage(def2);
Assert.IsTrue(result);
Assert.AreEqual(2, def2.Id);
Assert.AreNotEqual(default(Guid).ToString(), def2.PackageId);
}
[Test]
public void Update_Not_Found()
{
var def = new PackageDefinition
{
Id = 3, //doesn't exist
Name = "test",
Url = "http://test.com",
Author = "Someone",
AuthorUrl = "http://test.com"
};
var result = PackageBuilder.SavePackage(def);
Assert.IsFalse(result);
}
[Test]
public void Update()
{
var def = new PackageDefinition
{
Name = "test",
Url = "http://test.com",
Author = "Someone",
AuthorUrl = "http://test.com"
};
var result = PackageBuilder.SavePackage(def);
def.Name = "updated";
def.Files = new List<string> {"hello.txt", "world.png"};
result = PackageBuilder.SavePackage(def);
Assert.IsTrue(result);
//re-get
def = PackageBuilder.GetById(def.Id);
Assert.AreEqual("updated", def.Name);
Assert.AreEqual(2, def.Files.Count);
//TODO: There's a whole lot more assertions to be done
}
[Test]
public void Export()
{
var file1 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/package.manifest";
var file2 = $"~/{_testBaseFolder}/App_Plugins/MyPlugin/styles.css";
var mappedFile1 = IOHelper.MapPath(file1);
var mappedFile2 = IOHelper.MapPath(file2);
Directory.CreateDirectory(Path.GetDirectoryName(mappedFile1));
Directory.CreateDirectory(Path.GetDirectoryName(mappedFile2));
File.WriteAllText(mappedFile1, "hello world");
File.WriteAllText(mappedFile2, "hello world");
var def = new PackageDefinition
{
Name = "test",
Url = "http://test.com",
Author = "Someone",
AuthorUrl = "http://test.com",
Files = new List<string> { file1, file2 },
Actions = "<actions><Action alias='test' /></actions>"
};
var result = PackageBuilder.SavePackage(def);
Assert.IsTrue(result);
Assert.IsTrue(def.PackagePath.IsNullOrWhiteSpace());
var zip = PackageBuilder.ExportPackage(def);
def = PackageBuilder.GetById(def.Id); //re-get
Assert.IsNotNull(def.PackagePath);
using (var archive = ZipFile.OpenRead(IOHelper.MapPath(zip)))
{
Assert.AreEqual(3, archive.Entries.Count);
//the 2 files we manually added
Assert.IsNotNull(archive.Entries.Where(x => x.Name == "package.manifest"));
Assert.IsNotNull(archive.Entries.Where(x => x.Name == "styles.css"));
//this is the actual package definition/manifest (not the developer manifest!)
var packageXml = archive.Entries.FirstOrDefault(x => x.Name == "package.xml");
Assert.IsNotNull(packageXml);
using (var stream = packageXml.Open())
{
var xml = XDocument.Load(stream);
Assert.AreEqual("umbPackage", xml.Root.Name.ToString());
Assert.AreEqual(2, xml.Root.Element("files").Elements("file").Count());
Assert.AreEqual("<Actions><Action alias=\"test\" /></Actions>", xml.Element("umbPackage").Element("Actions").ToString(SaveOptions.DisableFormatting));
//TODO: There's a whole lot more assertions to be done
}
}
}
}
}
@@ -3,22 +3,27 @@ using System.Linq;
using System.Threading;
using System.Xml.Linq;
using NUnit.Framework;
using Umbraco.Core.Models;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Composing.Composers;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Persistence.Dtos;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Services;
using Umbraco.Tests.Services;
using Umbraco.Tests.Services.Importing;
using Umbraco.Tests.Testing;
namespace Umbraco.Tests.Services.Importing
namespace Umbraco.Tests.Packaging
{
[TestFixture]
[Category("Slow")]
[Apartment(ApartmentState.STA)]
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)]
public class PackageImportTests : TestWithSomeContentBase
public class PackageDataInstallationTests : TestWithSomeContentBase
{
[HideFromTypeFinder]
public class Editor1 : DataEditor
@@ -64,8 +69,10 @@ namespace Umbraco.Tests.Services.Importing
Composition.ComposeFileSystems();
}
private PackageDataInstallation PackageDataInstallation => Factory.GetInstance<PackageDataInstallation>();
[Test]
public void PackagingService_Can_Import_uBlogsy_ContentTypes_And_Verify_Structure()
public void Can_Import_uBlogsy_ContentTypes_And_Verify_Structure()
{
// Arrange
string strXml = ImportResources.uBlogsy_Package;
@@ -73,12 +80,11 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var templateElement = xml.Descendants("Templates").First();
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = packagingService.ImportTemplates(templateElement);
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count();
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
@@ -113,7 +119,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Inherited_ContentTypes_And_Verify_PropertyTypes_UniqueIds()
public void Can_Import_Inherited_ContentTypes_And_Verify_PropertyTypes_UniqueIds()
{
// Arrange
var strXml = ImportResources.InheritedDocTypes_Package;
@@ -121,12 +127,11 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var templateElement = xml.Descendants("Templates").First();
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = packagingService.ImportTemplates(templateElement);
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
// Assert
var mRBasePage = contentTypes.First(x => x.Alias == "MRBasePage");
@@ -139,7 +144,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Inherited_ContentTypes_And_Verify_PropertyGroups_And_PropertyTypes()
public void Can_Import_Inherited_ContentTypes_And_Verify_PropertyGroups_And_PropertyTypes()
{
// Arrange
string strXml = ImportResources.InheritedDocTypes_Package;
@@ -147,12 +152,11 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var templateElement = xml.Descendants("Templates").First();
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var dataTypes = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = packagingService.ImportTemplates(templateElement);
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var dataTypes = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
@@ -179,17 +183,17 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Template_Package_Xml()
public void Can_Import_Template_Package_Xml()
{
// Arrange
string strXml = ImportResources.StandardMvc_Package;
var xml = XElement.Parse(strXml);
var element = xml.Descendants("Templates").First();
var packagingService = ServiceContext.PackagingService;
var init = ServiceContext.FileService.GetTemplates().Count();
// Act
var templates = packagingService.ImportTemplates(element);
var templates = PackageDataInstallation.ImportTemplates(element.Elements("Template").ToList(), 0);
var numberOfTemplates = (from doc in element.Elements("Template") select doc).Count();
var allTemplates = ServiceContext.FileService.GetTemplates();
@@ -203,16 +207,16 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Single_Template()
public void Can_Import_Single_Template()
{
// Arrange
string strXml = ImportResources.StandardMvc_Package;
var xml = XElement.Parse(strXml);
var element = xml.Descendants("Templates").First().Element("Template");
var packagingService = ServiceContext.PackagingService;
var element = xml.Descendants("Templates").First();
// Act
var templates = packagingService.ImportTemplates(element);
var templates = PackageDataInstallation.ImportTemplate(element.Elements("Template").First(), 0);
// Assert
Assert.That(templates, Is.Not.Null);
@@ -221,7 +225,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_StandardMvc_ContentTypes_Package_Xml()
public void Can_Import_StandardMvc_ContentTypes_Package_Xml()
{
// Arrange
string strXml = ImportResources.StandardMvc_Package;
@@ -229,12 +233,12 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var templateElement = xml.Descendants("Templates").First();
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = packagingService.ImportTemplates(templateElement);
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
// Assert
@@ -257,7 +261,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_StandardMvc_ContentTypes_And_Templates_Xml()
public void Can_Import_StandardMvc_ContentTypes_And_Templates_Xml()
{
// Arrange
string strXml = ImportResources.StandardMvc_Package;
@@ -267,13 +271,13 @@ namespace Umbraco.Tests.Services.Importing
var docTypeElement = xml.Descendants("DocumentTypes").First();
// Act
var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = ServiceContext.PackagingService.ImportTemplates(templateElement);
var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement);
var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
//Assert - Re-Import contenttypes doesn't throw
Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement));
Assert.DoesNotThrow(() => PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0));
Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes));
Assert.That(dataTypeDefinitions, Is.Not.Null);
Assert.That(dataTypeDefinitions.Any(), Is.True);
@@ -281,7 +285,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Fanoe_Starterkit_ContentTypes_And_Templates_Xml()
public void Can_Import_Fanoe_Starterkit_ContentTypes_And_Templates_Xml()
{
// Arrange
string strXml = ImportResources.Fanoe_Package;
@@ -291,13 +295,13 @@ namespace Umbraco.Tests.Services.Importing
var docTypeElement = xml.Descendants("DocumentTypes").First();
// Act
var dataTypeDefinitions = ServiceContext.PackagingService.ImportDataTypeDefinitions(dataTypeElement);
var templates = ServiceContext.PackagingService.ImportTemplates(templateElement);
var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement);
var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
//Assert - Re-Import contenttypes doesn't throw
Assert.DoesNotThrow(() => ServiceContext.PackagingService.ImportContentTypes(docTypeElement));
Assert.DoesNotThrow(() => PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0));
Assert.That(contentTypes.Count(), Is.EqualTo(numberOfDocTypes));
Assert.That(dataTypeDefinitions, Is.Not.Null);
Assert.That(dataTypeDefinitions.Any(), Is.True);
@@ -305,7 +309,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Content_Package_Xml()
public void Can_Import_Content_Package_Xml()
{
// Arrange
string strXml = ImportResources.StandardMvc_Package;
@@ -313,12 +317,13 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var docTypesElement = xml.Descendants("DocumentTypes").First();
var element = xml.Descendants("DocumentSet").First();
var packagingService = ServiceContext.PackagingService;
var packageDocument = CompiledPackageDocument.Create(element);
// Act
var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var contentTypes = packagingService.ImportContentTypes(docTypesElement);
var contents = packagingService.ImportContent(element);
var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0);
var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x);
var contents = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0);
var numberOfDocs = (from doc in element.Descendants()
where (string) doc.Attribute("isDoc") == ""
select doc).Count();
@@ -332,7 +337,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_CheckboxList_Content_Package_Xml_With_Property_Editor_Aliases()
public void Can_Import_CheckboxList_Content_Package_Xml_With_Property_Editor_Aliases()
{
AssertCheckBoxListTests(ImportResources.CheckboxList_Content_Package);
}
@@ -346,12 +351,13 @@ namespace Umbraco.Tests.Services.Importing
var dataTypeElement = xml.Descendants("DataTypes").First();
var docTypesElement = xml.Descendants("DocumentTypes").First();
var element = xml.Descendants("DocumentSet").First();
var packagingService = ServiceContext.PackagingService;
var packageDocument = CompiledPackageDocument.Create(element);
// Act
var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement);
var contentTypes = packagingService.ImportContentTypes(docTypesElement);
var contents = packagingService.ImportContent(element);
var dataTypeDefinitions = PackageDataInstallation.ImportDataTypes(dataTypeElement.Elements("DataType").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypesElement.Elements("DocumentType"), 0);
var importedContentTypes = contentTypes.ToDictionary(x => x.Alias, x => x);
var contents = PackageDataInstallation.ImportContent(packageDocument, -1, importedContentTypes, 0);
var numberOfDocs = (from doc in element.Descendants()
where (string)doc.Attribute("isDoc") == ""
select doc).Count();
@@ -375,16 +381,16 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Templates_Package_Xml_With_Invalid_Master()
public void Can_Import_Templates_Package_Xml_With_Invalid_Master()
{
// Arrange
string strXml = ImportResources.XsltSearch_Package;
var xml = XElement.Parse(strXml);
var templateElement = xml.Descendants("Templates").First();
var packagingService = ServiceContext.PackagingService;
// Act
var templates = packagingService.ImportTemplates(templateElement);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count();
// Assert
@@ -393,15 +399,15 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Single_DocType()
public void Can_Import_Single_DocType()
{
// Arrange
string strXml = ImportResources.SingleDocType;
var docTypeElement = XElement.Parse(strXml);
var packagingService = ServiceContext.PackagingService;
// Act
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0);
// Assert
Assert.That(contentTypes.Any(), Is.True);
@@ -410,17 +416,18 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Export_Single_DocType()
public void Can_Export_Single_DocType()
{
// Arrange
string strXml = ImportResources.SingleDocType;
var docTypeElement = XElement.Parse(strXml);
var packagingService = ServiceContext.PackagingService;
var serializer = Factory.GetInstance<IEntityXmlSerializer>();
// Act
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0);
var contentType = contentTypes.FirstOrDefault();
var element = packagingService.Export(contentType);
var element = serializer.Serialize(contentType);
// Assert
Assert.That(element, Is.Not.Null);
@@ -433,15 +440,15 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_ReImport_Single_DocType()
public void Can_ReImport_Single_DocType()
{
// Arrange
string strXml = ImportResources.SingleDocType;
var docTypeElement = XElement.Parse(strXml);
// Act
var contentTypes = ServiceContext.PackagingService.ImportContentTypes(docTypeElement);
var contentTypesUpdated = ServiceContext.PackagingService.ImportContentTypes(docTypeElement);
var contentTypes = PackageDataInstallation.ImportDocumentType(docTypeElement, 0);
var contentTypesUpdated = PackageDataInstallation.ImportDocumentType(docTypeElement, 0);
// Assert
Assert.That(contentTypes.Any(), Is.True);
@@ -456,14 +463,14 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_ReImport_Templates_To_Update()
public void Can_ReImport_Templates_To_Update()
{
var newPackageXml = XElement.Parse(ImportResources.TemplateOnly_Package);
var updatedPackageXml = XElement.Parse(ImportResources.TemplateOnly_Updated_Package);
var templateElement = newPackageXml.Descendants("Templates").First();
var templateElementUpdated = updatedPackageXml.Descendants("Templates").First();
var packagingService = ServiceContext.PackagingService;
var fileService = ServiceContext.FileService;
// kill default test data
@@ -471,8 +478,8 @@ namespace Umbraco.Tests.Services.Importing
// Act
var numberOfTemplates = (from doc in templateElement.Elements("Template") select doc).Count();
var templates = packagingService.ImportTemplates(templateElement);
var templatesAfterUpdate = packagingService.ImportTemplates(templateElementUpdated);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var templatesAfterUpdate = PackageDataInstallation.ImportTemplates(templateElementUpdated.Elements("Template").ToList(), 0);
var allTemplates = fileService.GetTemplates();
// Assert
@@ -484,7 +491,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_DictionaryItems()
public void Can_Import_DictionaryItems()
{
// Arrange
const string expectedEnglishParentValue = "ParentValue";
@@ -498,7 +505,7 @@ namespace Umbraco.Tests.Services.Importing
AddLanguages();
// Act
ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0);
// Assert
AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
@@ -508,7 +515,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Nested_DictionaryItems()
public void Can_Import_Nested_DictionaryItems()
{
// Arrange
const string parentKey = "Parent";
@@ -520,7 +527,7 @@ namespace Umbraco.Tests.Services.Importing
AddLanguages();
// Act
var dictionaryItems = ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
var dictionaryItems = PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0);
// Assert
Assert.That(ServiceContext.LocalizationService.DictionaryItemExists(parentKey), "DictionaryItem parentKey does not exist");
@@ -534,7 +541,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_WhenExistingDictionaryKey_ImportsNewChildren()
public void WhenExistingDictionaryKey_ImportsNewChildren()
{
// Arrange
const string expectedEnglishParentValue = "ExistingParentValue";
@@ -549,7 +556,7 @@ namespace Umbraco.Tests.Services.Importing
AddExistingEnglishAndNorwegianParentDictionaryItem(expectedEnglishParentValue, expectedNorwegianParentValue);
// Act
ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0);
// Assert
AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
@@ -559,7 +566,7 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_WhenExistingDictionaryKey_OnlyAddsNewLanguages()
public void WhenExistingDictionaryKey_OnlyAddsNewLanguages()
{
// Arrange
const string expectedEnglishParentValue = "ExistingParentValue";
@@ -574,7 +581,7 @@ namespace Umbraco.Tests.Services.Importing
AddExistingEnglishParentDictionaryItem(expectedEnglishParentValue);
// Act
ServiceContext.PackagingService.ImportDictionaryItems(dictionaryItemsElement);
PackageDataInstallation.ImportDictionaryItems(dictionaryItemsElement.Elements("DictionaryItem"), 0);
// Assert
AssertDictionaryItem("Parent", expectedEnglishParentValue, "en-GB");
@@ -584,14 +591,14 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Languages()
public void Can_Import_Languages()
{
// Arrange
var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package);
var LanguageItemsElement = newPackageXml.Elements("Languages").First();
// Act
var languages = ServiceContext.PackagingService.ImportLanguages(LanguageItemsElement);
var languages = PackageDataInstallation.ImportLanguages(LanguageItemsElement.Elements("Language"), 0);
var allLanguages = ServiceContext.LocalizationService.GetAllLanguages();
// Assert
@@ -603,16 +610,16 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Macros()
public void Can_Import_Macros()
{
// Arrange
string strXml = ImportResources.uBlogsy_Package;
var xml = XElement.Parse(strXml);
var macrosElement = xml.Descendants("Macros").First();
var packagingService = ServiceContext.PackagingService;
// Act
var macros = packagingService.ImportMacros(macrosElement).ToList();
var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList();
// Assert
Assert.That(macros.Any(), Is.True);
@@ -625,16 +632,16 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Macros_With_Properties()
public void Can_Import_Macros_With_Properties()
{
// Arrange
string strXml = ImportResources.XsltSearch_Package;
var xml = XElement.Parse(strXml);
var macrosElement = xml.Descendants("Macros").First();
var packagingService = ServiceContext.PackagingService;
// Act
var macros = packagingService.ImportMacros(macrosElement).ToList();
var macros = PackageDataInstallation.ImportMacros(macrosElement.Elements("macro"), 0).ToList();
// Assert
Assert.That(macros.Any(), Is.True);
@@ -648,18 +655,18 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Package_With_Compositions()
public void Can_Import_Package_With_Compositions()
{
// Arrange
string strXml = ImportResources.CompositionsTestPackage;
var xml = XElement.Parse(strXml);
var templateElement = xml.Descendants("Templates").First();
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var templates = packagingService.ImportTemplates(templateElement);
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var templates = PackageDataInstallation.ImportTemplates(templateElement.Elements("Template").ToList(), 0);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
// Assert
@@ -677,16 +684,16 @@ namespace Umbraco.Tests.Services.Importing
}
[Test]
public void PackagingService_Can_Import_Package_With_Compositions_Ordered()
public void Can_Import_Package_With_Compositions_Ordered()
{
// Arrange
string strXml = ImportResources.CompositionsTestPackage_Random;
var xml = XElement.Parse(strXml);
var docTypeElement = xml.Descendants("DocumentTypes").First();
var packagingService = ServiceContext.PackagingService;
// Act
var contentTypes = packagingService.ImportContentTypes(docTypeElement);
var contentTypes = PackageDataInstallation.ImportDocumentTypes(docTypeElement.Elements("DocumentType"), 0);
var numberOfDocTypes = (from doc in docTypeElement.Elements("DocumentType") select doc).Count();
// Assert
@@ -12,11 +12,11 @@ namespace Umbraco.Tests.Packaging
{
private const string PackageFileName = "Document_Type_Picker_1.1.umb";
private static string GetTestPackagePath(string packageName)
private static FileInfo GetTestPackagePath(string packageName)
{
const string testPackagesDirName = "Packaging\\Packages";
string path = Path.Combine(IOHelper.GetRootDirectorySafe(), testPackagesDirName, packageName);
return path;
return new FileInfo(path);
}
[Test]
@@ -1,74 +1,175 @@
using Moq;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Packaging;
using Umbraco.Core.Packaging;
using Umbraco.Core.Services;
using Umbraco.Core.PropertyEditors;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.Testing;
using File = System.IO.File;
namespace Umbraco.Tests.Packaging
{
[TestFixture]
public class PackageInstallationTest
[UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerFixture)]
public class PackageInstallationTest : TestWithDatabaseBase
{
private const string Xml = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""no""?>
<umbPackage>
<files>
<file><guid>095e064b-ba4d-442d-9006-3050983c13d8.dll</guid><orgPath>/bin</orgPath><orgName>Auros.DocumentTypePicker.dll</orgName></file></files>
<info>
<package>
<name>Document Type Picker</name>
<version>1.1</version>
<license url=""http://www.opensource.org/licenses/mit-license.php"">MIT</license>
<url>http://www.auros.co.uk</url>
<requirements>
<major>3</major>
<minor>0</minor>
<patch>0</patch>
</requirements>
</package>
<author>
<name>@tentonipete</name>
<website>auros.co.uk</website>
</author>
<readme>
<![CDATA[Document Type Picker datatype that enables back office user to select one or many document types.]]>
</readme>
</info>
<DocumentTypes />
<Templates />
<Stylesheets />
<Macros />
<DictionaryItems />
<Languages />
<DataTypes>
<DataType Name=""Document Type Picker"" Id=""790aff36-7fed-47fb-8bcd-9c91ce43ba24"" Definition=""3593d8e7-8b35-47b9-beda-5e830ca8c93c"" />
</DataTypes>
</umbPackage>";
private Guid _testBaseFolder;
public override void SetUp()
{
base.SetUp();
_testBaseFolder = Guid.NewGuid();
}
public override void TearDown()
{
base.TearDown();
//clear out files/folders
var path = IOHelper.MapPath("~/" + _testBaseFolder);
if (Directory.Exists(path))
Directory.Delete(path, true);
}
private CompiledPackageXmlParser Parser => new CompiledPackageXmlParser(new ConflictingPackageData(ServiceContext.MacroService, ServiceContext.FileService));
private PackageDataInstallation PackageDataInstallation => new PackageDataInstallation(
Logger, ServiceContext.FileService, ServiceContext.MacroService, ServiceContext.LocalizationService,
ServiceContext.DataTypeService, ServiceContext.EntityService,
ServiceContext.ContentTypeService, ServiceContext.ContentService,
Factory.GetInstance<PropertyEditorCollection>());
private IPackageInstallation PackageInstallation => new PackageInstallation(
PackageDataInstallation,
new PackageFileInstallation(Parser, ProfilingLogger),
Parser, Mock.Of<IPackageActionRunner>(),
applicationRootFolder: new DirectoryInfo(IOHelper.MapPath("~/" + _testBaseFolder))); //we don't want to extract package files to the real root, so extract to a test folder
private const string DocumentTypePickerPackage = "Document_Type_Picker_1.1.umb";
private const string HelloPackage = "Hello_1.0.0.zip";
[Test]
public void Test()
public void Can_Read_Compiled_Package_1()
{
// Arrange
const string pagePath = "Test.umb";
var packageExtraction = new Mock<IPackageExtraction>();
string test;
packageExtraction.Setup(a => a.ReadTextFileFromArchive(pagePath, Constants.Packaging.PackageXmlFileName, out test)).Returns(Xml);
var fileService = new Mock<IFileService>();
var macroService = new Mock<IMacroService>();
var packagingService = new Mock<IPackagingService>();
var sut = new PackageInstallation(packagingService.Object, macroService.Object, fileService.Object, packageExtraction.Object);
// Act
InstallationSummary installationSummary = sut.InstallPackage(pagePath, -1);
// Assert
Assert.IsNotNull(installationSummary);
//Assert.Inconclusive("Lots of more tests can be written");
var package = PackageInstallation.ReadPackage(
//this is where our test zip file is
new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage)));
Assert.IsNotNull(package);
Assert.AreEqual(1, package.Files.Count);
Assert.AreEqual("095e064b-ba4d-442d-9006-3050983c13d8.dll", package.Files[0].UniqueFileName);
Assert.AreEqual("/bin", package.Files[0].OriginalPath);
Assert.AreEqual("Auros.DocumentTypePicker.dll", package.Files[0].OriginalName);
Assert.AreEqual("Document Type Picker", package.Name);
Assert.AreEqual("1.1", package.Version);
Assert.AreEqual("http://www.opensource.org/licenses/mit-license.php", package.LicenseUrl);
Assert.AreEqual("MIT", package.License);
Assert.AreEqual(3, package.UmbracoVersion.Major);
Assert.AreEqual(RequirementsType.Legacy, package.UmbracoVersionRequirementsType);
Assert.AreEqual("@tentonipete", package.Author);
Assert.AreEqual("auros.co.uk", package.AuthorUrl);
Assert.AreEqual("Document Type Picker datatype that enables back office user to select one or many document types.", package.Readme);
Assert.AreEqual(1, package.DataTypes.Count());
}
[Test]
public void Can_Read_Compiled_Package_2()
{
var package = PackageInstallation.ReadPackage(
//this is where our test zip file is
new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), HelloPackage)));
Assert.IsNotNull(package);
Assert.AreEqual(0, package.Files.Count);
Assert.AreEqual("Hello", package.Name);
Assert.AreEqual("1.0.0", package.Version);
Assert.AreEqual("http://opensource.org/licenses/MIT", package.LicenseUrl);
Assert.AreEqual("MIT License", package.License);
Assert.AreEqual(8, package.UmbracoVersion.Major);
Assert.AreEqual(0, package.UmbracoVersion.Minor);
Assert.AreEqual(0, package.UmbracoVersion.Build);
Assert.AreEqual(RequirementsType.Strict, package.UmbracoVersionRequirementsType);
Assert.AreEqual("asdf", package.Author);
Assert.AreEqual("http://hello.com", package.AuthorUrl);
Assert.AreEqual("asdf", package.Readme);
Assert.AreEqual(1, package.Documents.Count());
Assert.AreEqual("root", package.Documents.First().ImportMode);
Assert.AreEqual(1, package.DocumentTypes.Count());
Assert.AreEqual(1, package.Templates.Count());
Assert.AreEqual(1, package.DataTypes.Count());
}
[Test]
public void Can_Read_Compiled_Package_Warnings()
{
//copy a file to the same path that the package will install so we can detect file conflicts
var path = IOHelper.MapPath("~/" + _testBaseFolder);
Console.WriteLine(path);
var filePath = Path.Combine(path, "bin", "Auros.DocumentTypePicker.dll");
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
File.WriteAllText(filePath, "test");
//this is where our test zip file is
var packageFile = Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage);
Console.WriteLine(packageFile);
var package = PackageInstallation.ReadPackage(new FileInfo(packageFile));
var preInstallWarnings = package.Warnings;
Assert.IsNotNull(preInstallWarnings);
Assert.AreEqual(1, preInstallWarnings.FilesReplaced.Count());
Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", preInstallWarnings.FilesReplaced.First());
//TODO: More Asserts
}
[Test]
public void Install_Files()
{
var package = PackageInstallation.ReadPackage(
//this is where our test zip file is
new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage)));
var def = PackageDefinition.FromCompiledPackage(package);
def.Id = 1;
def.PackageId = Guid.NewGuid();
def.Files = new List<string>(); //clear out the files of the def for testing, this should be populated by the install
var result = PackageInstallation.InstallPackageFiles(def, package, -1).ToList();
Assert.AreEqual(1, result.Count);
Assert.AreEqual("bin\\Auros.DocumentTypePicker.dll", result[0]);
Assert.IsTrue(File.Exists(Path.Combine(IOHelper.MapPath("~/" + _testBaseFolder), result[0])));
//make sure the def is updated too
Assert.AreEqual(result.Count, def.Files.Count);
}
[Test]
public void Install_Data()
{
var package = PackageInstallation.ReadPackage(
//this is where our test zip file is
new FileInfo(Path.Combine(IOHelper.MapPath("~/Packaging/packages"), DocumentTypePickerPackage)));
var def = PackageDefinition.FromCompiledPackage(package);
def.Id = 1;
def.PackageId = Guid.NewGuid();
var summary = PackageInstallation.InstallPackageData(def, package, -1);
Assert.AreEqual(1, summary.DataTypesInstalled.Count());
//make sure the def is updated too
Assert.AreEqual(summary.DataTypesInstalled.Count(), def.DataTypes.Count);
}
}
}
Binary file not shown.
@@ -153,7 +153,8 @@ namespace Umbraco.Tests.PublishedContent
dataSource,
globalSettings,
new SiteDomainHelper(),
contentTypeService);
contentTypeService,
Mock.Of<IEntityXmlSerializer>());
// get a snapshot, get a published content
var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null);

Some files were not shown because too many files have changed in this diff Show More