Merge branch temp8 into feature/IContentType-removale-from-IContent
This commit is contained in:
+1
-1
@@ -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
|
||||
|
||||
@@ -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/"));
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
-1
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the levels in which the runtime can run.
|
||||
/// </summary>
|
||||
public enum RuntimeLevel
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
{
|
||||
|
||||
+1
-1
@@ -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))
|
||||
|
||||
+164
-62
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+97
-90
@@ -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
Reference in New Issue
Block a user