diff --git a/build/NuSpecs/UmbracoCms.nuspec b/build/NuSpecs/UmbracoCms.nuspec index 565d693979..3cb3d0d875 100644 --- a/build/NuSpecs/UmbracoCms.nuspec +++ b/build/NuSpecs/UmbracoCms.nuspec @@ -22,7 +22,7 @@ not want this to happen as the alpha of the next major is, really, the next major already. --> - + diff --git a/src/Umbraco.Core/Components/Composers.cs b/src/Umbraco.Core/Components/Composers.cs index 1c836e9e5c..89deed934e 100644 --- a/src/Umbraco.Core/Components/Composers.cs +++ b/src/Umbraco.Core/Components/Composers.cs @@ -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(); var minLevel = attr?.MinLevel ?? (x.Implements() ? 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(); diff --git a/src/Umbraco.Core/Components/Composition.cs b/src/Umbraco.Core/Components/Composition.cs index dd0b83dcb3..6df86d793f 100644 --- a/src/Umbraco.Core/Components/Composition.cs +++ b/src/Umbraco.Core/Components/Composition.cs @@ -17,7 +17,7 @@ namespace Umbraco.Core.Components public class Composition : IRegister { private readonly Dictionary _builders = new Dictionary(); - private readonly Dictionary _uniques = new Dictionary(); + private readonly Dictionary> _uniques = new Dictionary>(); private readonly IRegister _register; /// @@ -83,11 +83,32 @@ namespace Umbraco.Core.Components /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class => _register.Register(factory, lifetime); /// - public void RegisterInstance(Type serviceType, object instance) - => _register.RegisterInstance(serviceType, instance); + public void Register(Type serviceType, object instance) + => _register.Register(serviceType, instance); + + /// + public void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(lifetime); + + /// + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(implementingType, lifetime); + + /// + public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class + => _register.RegisterFor(factory, lifetime); + + /// + public void RegisterFor(TService instance) + where TService : class + => _register.RegisterFor(instance); /// public void RegisterAuto(Type serviceBaseType) @@ -104,10 +125,12 @@ namespace Umbraco.Core.Components onCreating(); foreach (var unique in _uniques.Values) - unique.RegisterWith(_register); + unique(_register); + _uniques.Clear(); // no point keep them around foreach (var builder in _builders.Values) builder.RegisterWith(_register); + _builders.Clear(); // no point keep them around Configs.RegisterWith(_register); @@ -123,74 +146,78 @@ namespace Umbraco.Core.Components #region Unique + private string GetUniqueName() + => GetUniqueName(typeof(TService)); + + private string GetUniqueName(Type serviceType) + => serviceType.FullName; + + private string GetUniqueName() + => GetUniqueName(typeof(TService), typeof(TTarget)); + + private string GetUniqueName(Type serviceType, Type targetType) + => serviceType.FullName + "::" + targetType.FullName; + /// - /// Registers a unique service. + /// Registers a unique service as its own implementation. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType) + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, Lifetime.Singleton); + + /// + /// Registers a unique service with an implementation type. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Type serviceType, Type implementingType) - => _uniques[serviceType] = new Unique(serviceType, implementingType); + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, implementingType, Lifetime.Singleton); /// - /// Registers a unique service. - /// - /// Unique services have one single implementation, and a Singleton lifetime. - public void RegisterUnique(Type serviceType, object instance) - => _uniques[serviceType] = new Unique(serviceType, instance); - - /// - /// Registers a unique service. + /// Registers a unique service with an implementation factory. /// /// Unique services have one single implementation, and a Singleton lifetime. public void RegisterUnique(Func factory) - => _uniques[typeof(TService)] = new Unique(factory); + where TService : class + => _uniques[GetUniqueName()] = register => register.Register(factory, Lifetime.Singleton); - private class Unique - { - private readonly Type _serviceType; - private readonly Type _implementingType; - private readonly object _instance; + /// + /// Registers a unique service with an implementing instance. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUnique(Type serviceType, object instance) + => _uniques[GetUniqueName(serviceType)] = register => register.Register(serviceType, instance); - protected Unique(Type serviceType) - { - _serviceType = serviceType; - } + /// + /// Registers a unique service for a target, as its own implementation. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor() + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(Lifetime.Singleton); - public Unique(Type serviceType, Type implementingType) - : this(serviceType) - { - _implementingType = implementingType; - } + /// + /// Registers a unique service for a target, with an implementing type. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(Type implementingType) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(implementingType, Lifetime.Singleton); - public Unique(Type serviceType, object instance) - : this(serviceType) - { - _instance = instance; - } + /// + /// Registers a unique service for a target, with an implementation factory. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(Func factory) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(factory, Lifetime.Singleton); - public virtual void RegisterWith(IRegister register) - { - if (_implementingType != null) - register.Register(_serviceType, _implementingType, Lifetime.Singleton); - else if (_instance != null) - register.RegisterInstance(_serviceType, _instance); - } - } - - private class Unique : Unique - { - private readonly Func _factory; - - public Unique(Func factory) - : base(typeof(TService)) - { - _factory = factory; - } - - public override void RegisterWith(IRegister register) - { - register.Register(_factory, Lifetime.Singleton); - } - } + /// + /// Registers a unique service for a target, with an implementing instance. + /// + /// Unique services have one single implementation, and a Singleton lifetime. + public void RegisterUniqueFor(TService instance) + where TService : class + => _uniques[GetUniqueName()] = register => register.RegisterFor(instance); #endregion diff --git a/src/Umbraco.Core/Components/CompositionExtensions.cs b/src/Umbraco.Core/Components/CompositionExtensions.cs index 93d190d17e..bb23e89b81 100644 --- a/src/Umbraco.Core/Components/CompositionExtensions.cs +++ b/src/Umbraco.Core/Components/CompositionExtensions.cs @@ -26,15 +26,16 @@ namespace Umbraco.Core.Components /// The type of the filesystem. /// The implementing type. /// The composition. - /// A factory method creating the supporting filesystem. /// The register. - public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + public static void RegisterFileSystem(this Composition composition) where TImplementing : FileSystemWrapper, TFileSystem + where TFileSystem : class { composition.RegisterUnique(factory => { var fileSystems = factory.GetInstance(); - return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + var supporting = factory.GetInstance(); + return fileSystems.GetFileSystem(supporting.For()); }); } @@ -43,15 +44,15 @@ namespace Umbraco.Core.Components /// /// The type of the filesystem. /// The composition. - /// A factory method creating the supporting filesystem. /// The register. - public static void RegisterFileSystem(this Composition composition, Func supportingFileSystemFactory) + public static void RegisterFileSystem(this Composition composition) where TFileSystem : FileSystemWrapper { composition.RegisterUnique(factory => { var fileSystems = factory.GetInstance(); - return fileSystems.GetFileSystem(supportingFileSystemFactory(factory)); + var supporting = factory.GetInstance(); + return fileSystems.GetFileSystem(supporting.For()); }); } @@ -280,6 +281,22 @@ namespace Umbraco.Core.Components composition.RegisterUnique(_ => helper); } + /// + /// Sets the underlying media filesystem. + /// + /// A composition. + /// A filesystem factory. + public static void SetMediaFileSystem(this Composition composition, Func filesystemFactory) + => composition.RegisterUniqueFor(filesystemFactory); + + /// + /// Sets the underlying media filesystem. + /// + /// A composition. + /// A filesystem factory. + public static void SetMediaFileSystem(this Composition composition, Func filesystemFactory) + => composition.RegisterUniqueFor(_ => filesystemFactory()); + #endregion } } diff --git a/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs b/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs index 51920660d4..2c698a671a 100644 --- a/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs +++ b/src/Umbraco.Core/Components/RuntimeLevelAttribute.cs @@ -1,13 +1,22 @@ using System; +using Umbraco.Core.Composing; namespace Umbraco.Core.Components { + /// + /// Marks a composer to indicate a minimum and/or maximum runtime level for which the composer would compose. + /// [AttributeUsage(AttributeTargets.Class /*, AllowMultiple = false, Inherited = true*/)] public class RuntimeLevelAttribute : Attribute { - //public RuntimeLevelAttribute() - //{ } + /// + /// Gets or sets the minimum runtime level for which the composer would compose. + /// + public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Install; - public RuntimeLevel MinLevel { get; set; } = RuntimeLevel.Boot; + /// + /// Gets or sets the maximum runtime level for which the composer would compose. + /// + public RuntimeLevel MaxLevel { get; set; } = RuntimeLevel.Run; } } diff --git a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs index 7633f6b001..41038ea4e9 100644 --- a/src/Umbraco.Core/Composing/CollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/CollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class CollectionBuilderBase : ICollectionBuilder where TBuilder: CollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { private readonly List _types = new List(); private readonly object _locker = new object(); diff --git a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs index f1fb095406..4c598f27e4 100644 --- a/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/FileSystemsComposer.cs @@ -12,10 +12,9 @@ namespace Umbraco.Core.Composing.Composers * * Create a component and use it to modify the composition by adding something like: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/somewhere")); + * composition.RegisterUniqueFor(...); * - * return whatever supporting filesystem you like. + * and register whatever supporting filesystem you like. * * * HOW TO IMPLEMENT MY OWN FILESYSTEM @@ -30,12 +29,15 @@ namespace Umbraco.Core.Composing.Composers * { } * } * - * The ctor can have more parameters that will be resolved by the container. + * The ctor can have more parameters, that will be resolved by the container. * * Register your filesystem, in a component: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/my")); + * composition.RegisterFileSystem(); + * + * Register the underlying filesystem: + * + * composition.RegisterUniqueFor(...); * * And that's it, you can inject MyFileSystem wherever it's needed. * @@ -48,8 +50,8 @@ namespace Umbraco.Core.Composing.Composers * Make the class implement the interface, then * register your filesystem, in a component: * - * composition.Container.RegisterFileSystem( - * factory => new PhysicalFileSystem("~/my")); + * composition.RegisterFileSystem(); + * composition.RegisterUniqueFor(...); * * And that's it, you can inject IMyFileSystem wherever it's needed. * @@ -79,9 +81,16 @@ namespace Umbraco.Core.Composing.Composers // register the scheme for media paths composition.RegisterUnique(); - // register the IMediaFileSystem implementation with a supporting filesystem - composition.RegisterFileSystem( - factory => new PhysicalFileSystem("~/media")); + // register the IMediaFileSystem implementation + composition.RegisterFileSystem(); + + // register the supporting filesystems provider + composition.Register(factory => new SupportingFileSystems(factory), Lifetime.Singleton); + + // register the IFileSystem supporting the IMediaFileSystem + // THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM + // and, SupportingFileSystem.For() returns the underlying filesystem + composition.SetMediaFileSystem(() => new PhysicalFileSystem("~/media")); return composition; } diff --git a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs index 1b77aaa7d6..8c9ccd1088 100644 --- a/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs +++ b/src/Umbraco.Core/Composing/Composers/ServicesComposer.cs @@ -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>(), factory.GetInstance())); + composition.RegisterUnique(); + + composition.RegisterUnique(); + + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(factory => CreatePackageRepository(factory, "createdPackages.config")); + composition.RegisterUnique(factory => CreatePackageRepository(factory, "installedPackages.config")); + composition.RegisterUnique(); + composition.RegisterUnique(); + composition.RegisterUnique(factory => //factory required because we need to pass in a string path + new PackageInstallation( + factory.GetInstance(), factory.GetInstance(), + factory.GetInstance(), factory.GetInstance(), + 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(); @@ -64,6 +81,17 @@ namespace Umbraco.Core.Composing.Composers return composition; } + /// + /// Creates an instance of PackagesRepository for either the ICreatedPackagesRepository or the IInstalledPackagesRepository + /// + /// + /// + /// + private static PackagesRepository CreatePackageRepository(IFactory factory, string packageRepoFileName) + => new PackagesRepository( + factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), factory.GetInstance(), + packageRepoFileName); + private static LocalizedTextServiceFileSources SourcesFactory(IFactory container) { var mainLangFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Umbraco + "/config/lang/")); diff --git a/src/Umbraco.Core/Composing/CompositionExtensions.cs b/src/Umbraco.Core/Composing/CompositionExtensions.cs index cfc465b59d..2307d757c9 100644 --- a/src/Umbraco.Core/Composing/CompositionExtensions.cs +++ b/src/Umbraco.Core/Composing/CompositionExtensions.cs @@ -51,6 +51,13 @@ namespace Umbraco.Core.Composing public static void RegisterUnique(this Composition composition) => composition.RegisterUnique(typeof(TService), typeof(TImplementing)); + /// + /// Registers a unique service with an implementation type, for a target. + /// + public static void RegisterUniqueFor(this Composition composition) + where TService : class + => composition.RegisterUniqueFor(typeof(TImplementing)); + /// /// Registers a unique service with an implementing instance. /// diff --git a/src/Umbraco.Core/Composing/Current.cs b/src/Umbraco.Core/Composing/Current.cs index cf67409925..5c8351924f 100644 --- a/src/Umbraco.Core/Composing/Current.cs +++ b/src/Umbraco.Core/Composing/Current.cs @@ -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(); + internal static IPackageActionRunner PackageActionRunner + => Factory.GetInstance(); + internal static PropertyValueConverterCollection PropertyValueConverters => Factory.GetInstance(); diff --git a/src/Umbraco.Core/Composing/FactoryExtensions.cs b/src/Umbraco.Core/Composing/FactoryExtensions.cs index 2640b7f7e6..8027f2c7a1 100644 --- a/src/Umbraco.Core/Composing/FactoryExtensions.cs +++ b/src/Umbraco.Core/Composing/FactoryExtensions.cs @@ -17,6 +17,7 @@ namespace Umbraco.Core.Composing /// An instance of the specified type. /// Throws an exception if the factory failed to get an instance of the specified type. public static T GetInstance(this IFactory factory) + where T : class => (T)factory.GetInstance(typeof(T)); /// @@ -28,6 +29,7 @@ namespace Umbraco.Core.Composing /// of the specified type. Throws an exception if the factory does know how /// to get an instance of the specified type, but failed to do so. public static T TryGetInstance(this IFactory factory) + where T : class => (T)factory.TryGetInstance(typeof(T)); /// @@ -42,6 +44,7 @@ namespace Umbraco.Core.Composing /// The arguments are used as dependencies by the factory. /// public static T CreateInstance(this IFactory factory, params object[] args) + where T : class => (T)factory.CreateInstance(typeof(T), args); /// diff --git a/src/Umbraco.Core/Composing/IFactory.cs b/src/Umbraco.Core/Composing/IFactory.cs index 9a59b1c052..768b9207a3 100644 --- a/src/Umbraco.Core/Composing/IFactory.cs +++ b/src/Umbraco.Core/Composing/IFactory.cs @@ -3,13 +3,6 @@ using System.Collections.Generic; namespace Umbraco.Core.Composing { - // Implementing: - // - // The factory - // - always picks the constructor with the most parameters - // - supports Lazy parameters (and prefers them over non-Lazy) in constructors - // - what happens with 'releasing' is unclear - /// /// Defines a service factory for Umbraco. /// @@ -28,6 +21,15 @@ namespace Umbraco.Core.Composing /// Throws an exception if the container failed to get an instance of the specified type. object GetInstance(Type type); + /// + /// Gets a targeted instance of a service. + /// + /// The type of the service. + /// The type of the target. + /// The instance of the specified type for the specified target. + /// Throws an exception if the container failed to get an instance of the specified type. + TService GetInstanceFor(); + /// /// Tries to get an instance of a service. /// @@ -48,7 +50,8 @@ namespace Umbraco.Core.Composing /// Gets all instances of a service. /// /// The type of the service. - IEnumerable GetAllInstances(); + IEnumerable GetAllInstances() + where TService : class; /// /// Releases an instance. diff --git a/src/Umbraco.Core/Composing/IRegister.cs b/src/Umbraco.Core/Composing/IRegister.cs index 8ad3db5409..cbf12f54a3 100644 --- a/src/Umbraco.Core/Composing/IRegister.cs +++ b/src/Umbraco.Core/Composing/IRegister.cs @@ -2,17 +2,6 @@ namespace Umbraco.Core.Composing { - // Implementing: - // - // The register - // - supports registering a service, even after some instances of other services have been created - // - supports re-registering a service, as long as no instance of that service has been created - // - throws when re-registering a service, and an instance of that service has been created - // - // - registers only one implementation of a nameless service, re-registering replaces the previous - // registration - names are required to register multiple implementations - and getting an - // IEnumerable of the service, nameless, returns them all - /// /// Defines a service register for Umbraco. /// @@ -36,12 +25,53 @@ namespace Umbraco.Core.Composing /// /// Registers a service with an implementation factory. /// - void Register(Func factory, Lifetime lifetime = Lifetime.Transient); + void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class; /// /// Registers a service with an implementing instance. /// - void RegisterInstance(Type serviceType, object instance); + void Register(Type serviceType, object instance); + + /// + /// Registers a service for a target, as its own implementation. + /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// + void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service for a target, with an implementation type. + /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// + void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service for a target, with an implementation factory. + /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// + void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class; + + /// + /// Registers a service for a target, with an implementing instance. + /// + /// + /// There can only be one implementation or instanced registered for a service and target; + /// what happens if many are registered is not specified. + /// + void RegisterFor(TService instance) + where TService : class; /// /// Registers a base type for auto-registration. diff --git a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs index a1a06621e9..46b06daf7d 100644 --- a/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/LazyCollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class LazyCollectionBuilderBase : CollectionBuilderBase where TBuilder : LazyCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { private readonly List>> _producers = new List>>(); private readonly List _excluded = new List(); diff --git a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs index b39622f66a..d8a554ee8c 100644 --- a/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs +++ b/src/Umbraco.Core/Composing/LightInject/LightInjectContainer.cs @@ -102,18 +102,25 @@ namespace Umbraco.Core.Composing.LightInject /// public IFactory CreateFactory() => this; + private static string GetTargetedServiceName() => "TARGET:" + typeof(TTarget).FullName; + #region Factory /// public object GetInstance(Type type) => Container.GetInstance(type); + /// + public TService GetInstanceFor() + => Container.GetInstance(GetTargetedServiceName()); + /// public object TryGetInstance(Type type) => Container.TryGetInstance(type); /// public IEnumerable GetAllInstances() + where T : class => Container.GetAllInstances(); /// @@ -138,21 +145,7 @@ namespace Umbraco.Core.Composing.LightInject /// public void Register(Type serviceType, Lifetime lifetime = Lifetime.Transient) - { - switch (lifetime) - { - case Lifetime.Transient: - Container.Register(serviceType); - break; - case Lifetime.Request: - case Lifetime.Scope: - case Lifetime.Singleton: - Container.Register(serviceType, GetLifetime(lifetime)); - break; - default: - throw new NotSupportedException($"Lifetime {lifetime} is not supported."); - } - } + => Container.Register(serviceType, GetLifetime(lifetime)); /// public void Register(Type serviceType, Type implementingType, Lifetime lifetime = Lifetime.Transient) @@ -174,22 +167,41 @@ namespace Umbraco.Core.Composing.LightInject /// public void Register(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class { - switch (lifetime) - { - case Lifetime.Transient: - Container.Register(f => factory(this)); - break; - case Lifetime.Request: - case Lifetime.Scope: - case Lifetime.Singleton: - Container.Register(f => factory(this), GetLifetime(lifetime)); - break; - default: - throw new NotSupportedException($"Lifetime {lifetime} is not supported."); - } + Container.Register(f => factory(this), GetLifetime(lifetime)); } + /// + public void Register(Type serviceType, object instance) + => Container.RegisterInstance(serviceType, instance); + + /// + public void RegisterFor(Lifetime lifetime = Lifetime.Transient) + where TService : class + => RegisterFor(typeof(TService), lifetime); + + /// + public void RegisterFor(Type implementingType, Lifetime lifetime = Lifetime.Transient) + where TService : class + { + // note that there can only be one implementation or instance registered "for" a service + Container.Register(typeof(TService), implementingType, GetTargetedServiceName(), GetLifetime(lifetime)); + } + + /// + public void RegisterFor(Func factory, Lifetime lifetime = Lifetime.Transient) + where TService : class + { + // note that there can only be one implementation or instance registered "for" a service + Container.Register(f => factory(this), GetTargetedServiceName(), GetLifetime(lifetime)); + } + + /// + public void RegisterFor(TService instance) + where TService : class + => Container.RegisterInstance(typeof(TService), instance, GetTargetedServiceName()); + private ILifetime GetLifetime(Lifetime lifetime) { switch (lifetime) @@ -207,10 +219,6 @@ namespace Umbraco.Core.Composing.LightInject } } - /// - public void RegisterInstance(Type serviceType, object instance) - => Container.RegisterInstance(serviceType, instance); - /// public void RegisterAuto(Type serviceBaseType) { @@ -223,17 +231,6 @@ namespace Umbraco.Core.Composing.LightInject }, null); } - // was the Light-Inject specific way of dealing with args, but we've replaced it with our own - // beware! does NOT work on singletons, see https://github.com/seesharper/LightInject/issues/294 - // - ///// - //public void RegisterConstructorDependency(Func factory) - // => Container.RegisterConstructorDependency((f, x) => factory(this, x)); - // - ///// - //public void RegisterConstructorDependency(Func factory) - // => Container.RegisterConstructorDependency((f, x, a) => factory(this, x, a)); - #endregion #region Control @@ -256,21 +253,14 @@ namespace Umbraco.Core.Composing.LightInject private class AssemblyScanner : IAssemblyScanner { - //private readonly IAssemblyScanner _scanner; - - //public AssemblyScanner(IAssemblyScanner scanner) - //{ - // _scanner = scanner; - //} - public void Scan(Assembly assembly, IServiceRegistry serviceRegistry, Func lifetime, Func shouldRegister, Func serviceNameProvider) { - // nothing - we *could* scan non-Umbraco assemblies, though + // nothing - we don't want LightInject to scan } public void Scan(Assembly assembly, IServiceRegistry serviceRegistry) { - // nothing - we *could* scan non-Umbraco assemblies, though + // nothing - we don't want LightInject to scan } } diff --git a/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs index 470079c6c0..897c58dd43 100644 --- a/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs +++ b/src/Umbraco.Core/Composing/LightInject/MixedLightInjectScopeManagerProvider.cs @@ -13,6 +13,9 @@ namespace Umbraco.Core.Composing.LightInject // of PerWebRequestScopeManagerProvider - but all delegates see is the mixed one - and therefore // they can transition without issues. // + // The PerWebRequestScopeManager maintains the scope in HttpContext and LightInject registers a + // module (PreApplicationStartMethod) which disposes it on EndRequest + // // the mixed provider is installed in container.ConfigureUmbracoCore() and then, // when doing eg container.EnableMvc() or anything that does container.EnablePerWebRequestScope() // we need to take great care to preserve the mixed scope manager provider! diff --git a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs index bde1bf96c5..241b84d8d2 100644 --- a/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/OrderedCollectionBuilderBase.cs @@ -11,7 +11,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class OrderedCollectionBuilderBase : CollectionBuilderBase where TBuilder : OrderedCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { protected abstract TBuilder This { get; } diff --git a/src/Umbraco.Core/Composing/RegisterExtensions.cs b/src/Umbraco.Core/Composing/RegisterExtensions.cs index 4db1a2e9e4..d1eacc0c0f 100644 --- a/src/Umbraco.Core/Composing/RegisterExtensions.cs +++ b/src/Umbraco.Core/Composing/RegisterExtensions.cs @@ -11,22 +11,32 @@ public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) => register.Register(typeof(TService), typeof(TImplementing), lifetime); + /// + /// Registers a service with an implementation type, for a target. + /// + public static void RegisterFor(this IRegister register, Lifetime lifetime = Lifetime.Transient) + where TService : class + => register.RegisterFor(typeof(TImplementing), lifetime); + /// /// Registers a service as its own implementation. /// public static void Register(this IRegister register, Lifetime lifetime = Lifetime.Transient) + where TService : class => register.Register(typeof(TService), lifetime); /// /// Registers a service with an implementing instance. /// - public static void RegisterInstance(this IRegister register, TService instance) - => register.RegisterInstance(typeof(TService), instance); + public static void Register(this IRegister register, TService instance) + where TService : class + => register.Register(typeof(TService), instance); /// /// Registers a base type for auto-registration. /// public static void RegisterAuto(this IRegister register) + where TServiceBase : class => register.RegisterAuto(typeof(TServiceBase)); } } diff --git a/src/Umbraco.Core/Composing/TargetedServiceFactory.cs b/src/Umbraco.Core/Composing/TargetedServiceFactory.cs new file mode 100644 index 0000000000..53022c0043 --- /dev/null +++ b/src/Umbraco.Core/Composing/TargetedServiceFactory.cs @@ -0,0 +1,18 @@ +namespace Umbraco.Core.Composing +{ + /// + /// Provides a base class for targeted service factories. + /// + /// + public abstract class TargetedServiceFactory + { + private readonly IFactory _factory; + + protected TargetedServiceFactory(IFactory factory) + { + _factory = factory; + } + + public TService For() => _factory.GetInstanceFor(); + } +} diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 3121e869c3..acb12ab575 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -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; } diff --git a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs index da47c53bf8..f8ecc11d98 100644 --- a/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs +++ b/src/Umbraco.Core/Composing/WeightedCollectionBuilderBase.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Composing /// The type of the items. public abstract class WeightedCollectionBuilderBase : CollectionBuilderBase where TBuilder : WeightedCollectionBuilderBase - where TCollection : IBuilderCollection + where TCollection : class, IBuilderCollection { protected abstract TBuilder This { get; } diff --git a/src/Umbraco.Core/Configuration/Configs.cs b/src/Umbraco.Core/Configuration/Configs.cs index 3dbbe5d4ff..51e1a327a0 100644 --- a/src/Umbraco.Core/Configuration/Configs.cs +++ b/src/Umbraco.Core/Configuration/Configs.cs @@ -100,7 +100,7 @@ namespace Umbraco.Core.Configuration if (_registerings == null) throw new InvalidOperationException("Configurations have already been registered."); - register.RegisterInstance(this); + register.Register(this); foreach (var registering in _registerings.Values) registering(register); diff --git a/src/Umbraco.Core/Constants-Packaging.cs b/src/Umbraco.Core/Constants-Packaging.cs deleted file mode 100644 index 37f2c23fa3..0000000000 --- a/src/Umbraco.Core/Constants-Packaging.cs +++ /dev/null @@ -1,55 +0,0 @@ -namespace Umbraco.Core -{ - public static partial class Constants - { - /// - /// Defines the constants used for Umbraco packages in the package.config xml - /// - 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"; - } - } -} diff --git a/src/Umbraco.Core/ContentExtensions.cs b/src/Umbraco.Core/ContentExtensions.cs index 5e2d44c90d..8c27c23604 100644 --- a/src/Umbraco.Core/ContentExtensions.cs +++ b/src/Umbraco.Core/ContentExtensions.cs @@ -308,84 +308,45 @@ namespace Umbraco.Core /// Creates the full xml representation for the object and all of it's descendants /// /// to generate xml for - /// + /// /// Xml representation of the passed in - 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); } /// /// Creates the xml representation for the object /// /// to generate xml for - /// + /// /// Xml representation of the passed in - 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); } + /// /// Creates the xml representation for the object /// /// to generate xml for - /// + /// /// Xml representation of the passed in - 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); } - /// - /// Creates the full xml representation for the object and all of it's descendants - /// - /// to generate xml for - /// - /// Xml representation of the passed in - internal static XElement ToDeepXml(this IMedia media, IPackagingService packagingService) - { - return packagingService.Export(media, true, raiseEvents: false); - } - - - /// - /// Creates the xml representation for the object - /// - /// to generate xml for - /// - /// Boolean indicating whether the xml should be generated for preview - /// Xml representation of the passed in - 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); - } - - /// /// Creates the xml representation for the object /// /// to generate xml for - /// + /// /// Xml representation of the passed in - 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 diff --git a/src/Umbraco.Core/Deploy/IValueConnector.cs b/src/Umbraco.Core/Deploy/IValueConnector.cs index 35304e3fde..92589ab6cf 100644 --- a/src/Umbraco.Core/Deploy/IValueConnector.cs +++ b/src/Umbraco.Core/Deploy/IValueConnector.cs @@ -17,19 +17,19 @@ namespace Umbraco.Core.Deploy IEnumerable PropertyEditorAliases { get; } /// - /// Gets the deploy property corresponding to a content property. + /// Gets the deploy property value corresponding to a content property value, and gather dependencies. /// - /// The content property. + /// The content property value. /// The content dependencies. /// The deploy property value. - string GetValue(Property property, ICollection dependencies); + string ToArtifact(object value, ICollection dependencies); /// - /// Sets a content property value using a deploy property. + /// Gets the content property value corresponding to a deploy property value. /// - /// The content item. - /// The property alias. /// The deploy property value. - void SetValue(IContentBase content, string alias, string value); + /// The current content property value. + /// The content property value. + object FromArtifact(string value, object currentValue); } } diff --git a/src/Umbraco.Core/Events/ExportEventArgs.cs b/src/Umbraco.Core/Events/ExportEventArgs.cs deleted file mode 100644 index f46cccf05c..0000000000 --- a/src/Umbraco.Core/Events/ExportEventArgs.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; - -namespace Umbraco.Core.Events -{ - public class ExportEventArgs : CancellableObjectEventArgs>, IEquatable> - { - /// - /// Constructor accepting a single entity instance - /// - /// - /// - /// - public ExportEventArgs(TEntity eventObject, XElement xml, bool canCancel) - : base(new List { eventObject }, canCancel) - { - Xml = xml; - } - - /// - /// Constructor accepting a single entity instance - /// and cancellable by default - /// - /// - /// - public ExportEventArgs(TEntity eventObject, string elementName) : base(new List {eventObject}, true) - { - Xml = new XElement(elementName); - } - - protected ExportEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) - { - } - - protected ExportEventArgs(IEnumerable eventObject) : base(eventObject) - { - } - - /// - /// Returns all entities that were exported during the operation - /// - public IEnumerable ExportedEntities - { - get { return EventObject; } - } - - /// - /// Returns the xml relating to the export event - /// - public XElement Xml { get; private set; } - - public bool Equals(ExportEventArgs 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) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); - } - } - - public static bool operator ==(ExportEventArgs left, ExportEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(ExportEventArgs left, ExportEventArgs right) - { - return !Equals(left, right); - } - } -} diff --git a/src/Umbraco.Core/Events/ImportEventArgs.cs b/src/Umbraco.Core/Events/ImportEventArgs.cs deleted file mode 100644 index fb8f7d4936..0000000000 --- a/src/Umbraco.Core/Events/ImportEventArgs.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; - -namespace Umbraco.Core.Events -{ - public class ImportEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> - { - /// - /// Constructor accepting an XElement with the xml being imported - /// - /// - public ImportEventArgs(XElement xml) : base(new List(), true) - { - Xml = xml; - } - - /// - /// Constructor accepting a list of entities and an XElement with the imported xml - /// - /// - /// - /// - public ImportEventArgs(IEnumerable eventObject, XElement xml, bool canCancel) - : base(eventObject, canCancel) - { - Xml = xml; - } - - protected ImportEventArgs(IEnumerable eventObject, bool canCancel) : base(eventObject, canCancel) - { - } - - protected ImportEventArgs(IEnumerable eventObject) : base(eventObject) - { - } - - /// - /// Returns all entities that were imported during the operation - /// - public IEnumerable ImportedEntities - { - get { return EventObject; } - } - - /// - /// Returns the xml relating to the import event - /// - public XElement Xml { get; private set; } - - public bool Equals(ImportEventArgs 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) obj); - } - - public override int GetHashCode() - { - unchecked - { - return (base.GetHashCode() * 397) ^ (Xml != null ? Xml.GetHashCode() : 0); - } - } - - public static bool operator ==(ImportEventArgs left, ImportEventArgs right) - { - return Equals(left, right); - } - - public static bool operator !=(ImportEventArgs left, ImportEventArgs right) - { - return !Equals(left, right); - } - } -} diff --git a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs index 2e56757a25..61369af59d 100644 --- a/src/Umbraco.Core/Events/ImportPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/ImportPackageEventArgs.cs @@ -7,44 +7,28 @@ namespace Umbraco.Core.Events { public class ImportPackageEventArgs : CancellableEnumerableObjectEventArgs, IEquatable> { - 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 InstallationSummary - { - get { return EventObject; } - } + public IEnumerable InstallationSummary => EventObject; public bool Equals(ImportPackageEventArgs 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(); } } diff --git a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs index 13c260bf3e..63c5ceaba0 100644 --- a/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs +++ b/src/Umbraco.Core/Events/UninstallPackageEventArgs.cs @@ -3,20 +3,13 @@ using Umbraco.Core.Models.Packaging; namespace Umbraco.Core.Events { - public class UninstallPackageEventArgs : CancellableObjectEventArgs> + public class UninstallPackageEventArgs: CancellableObjectEventArgs> { - public UninstallPackageEventArgs(TEntity eventObject, bool canCancel) - : base(new[] { eventObject }, canCancel) - { } - - public UninstallPackageEventArgs(TEntity eventObject, MetaData packageMetaData) - : base(new[] { eventObject }) + public UninstallPackageEventArgs(IEnumerable eventObject, bool canCancel) + : base(eventObject, canCancel) { - PackageMetaData = packageMetaData; } - public MetaData PackageMetaData { get; } - - public IEnumerable UninstallationSummary => EventObject; + public IEnumerable UninstallationSummary => EventObject; } } diff --git a/src/Umbraco.Core/IO/IOHelper.cs b/src/Umbraco.Core/IO/IOHelper.cs index 7773f378a5..76e7631482 100644 --- a/src/Umbraco.Core/IO/IOHelper.cs +++ b/src/Umbraco.Core/IO/IOHelper.cs @@ -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 }); } - /// - /// Validates that the current filepath matches a directory where the user is allowed to edit a file. - /// - /// The filepath to validate. - /// The valid directory. - /// True, if the filepath is valid, else an exception is thrown. - /// The filepath is invalid. - 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; - } - /// /// Verifies that the current filepath matches one of several directories where the user is allowed to edit a file. /// @@ -221,20 +192,6 @@ namespace Umbraco.Core.IO return ext != null && validFileExtensions.Contains(ext.TrimStart('.')); } - /// - /// Validates that the current filepath has one of several authorized extensions. - /// - /// The filepath to validate. - /// The valid extensions. - /// True, if the filepath is valid, else an exception is thrown. - /// The filepath is invalid. - internal static bool ValidateFileExtension(string filePath, List 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); - } - } - /// /// Checks if a given path is a full path including drive letter /// diff --git a/src/Umbraco.Core/IO/ShadowWrapper.cs b/src/Umbraco.Core/IO/ShadowWrapper.cs index 6493238391..d71f328713 100644 --- a/src/Umbraco.Core/IO/ShadowWrapper.cs +++ b/src/Umbraco.Core/IO/ShadowWrapper.cs @@ -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 _isScoped; private readonly IFileSystem _innerFileSystem; diff --git a/src/Umbraco.Core/IO/SupportingFileSystems.cs b/src/Umbraco.Core/IO/SupportingFileSystems.cs new file mode 100644 index 0000000000..43ac2ba85a --- /dev/null +++ b/src/Umbraco.Core/IO/SupportingFileSystems.cs @@ -0,0 +1,11 @@ +using Umbraco.Core.Composing; + +namespace Umbraco.Core.IO +{ + public class SupportingFileSystems : TargetedServiceFactory + { + public SupportingFileSystems(IFactory factory) + : base(factory) + { } + } +} diff --git a/src/Umbraco.Core/IO/SystemDirectories.cs b/src/Umbraco.Core/IO/SystemDirectories.cs index 183d48e3d9..4ea3ed64d5 100644 --- a/src/Umbraco.Core/IO/SystemDirectories.cs +++ b/src/Umbraco.Core/IO/SystemDirectories.cs @@ -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; diff --git a/src/Umbraco.Core/IRuntimeState.cs b/src/Umbraco.Core/IRuntimeState.cs index 5fcfab1518..30c768ab01 100644 --- a/src/Umbraco.Core/IRuntimeState.cs +++ b/src/Umbraco.Core/IRuntimeState.cs @@ -57,6 +57,11 @@ namespace Umbraco.Core /// RuntimeLevel Level { get; } + /// + /// Gets the reason for the runtime level of execution. + /// + RuntimeLevelReason Reason { get; } + /// /// Gets the current migration state. /// diff --git a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs index 5525cc4a50..f9f3e5da30 100644 --- a/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs +++ b/src/Umbraco.Core/Migrations/Install/DatabaseSchemaCreator.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Migrations.Install /// /// Creates the initial database schema during install. /// - 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 OrderedTables = new List + internal static readonly List OrderedTables = new List { typeof (UserDto), typeof (NodeDto), @@ -138,7 +138,7 @@ namespace Umbraco.Core.Migrations.Install /// /// Validates the schema of the current database. /// - public DatabaseSchemaResult ValidateSchema() + internal DatabaseSchemaResult ValidateSchema() { var result = new DatabaseSchemaResult(SqlSyntax); @@ -387,7 +387,7 @@ namespace Umbraco.Core.Migrations.Install /// If has been decorated with an , the name from that /// attribute will be used for the table name. If the attribute is not present, the name /// will be used instead. - /// + /// /// If a table with the same name already exists, the parameter will determine /// whether the table is overwritten. If true, the table will be overwritten, whereas this method will /// not do anything if the parameter is false. @@ -409,14 +409,14 @@ namespace Umbraco.Core.Migrations.Install /// If has been decorated with an , the name from /// that attribute will be used for the table name. If the attribute is not present, the name /// will be used instead. - /// + /// /// If a table with the same name already exists, the parameter will determine /// whether the table is overwritten. If true, the table will be overwritten, whereas this method will /// not do anything if the parameter is false. /// /// This need to execute as part of a transaction. /// - 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."); diff --git a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs index b469c02a3c..51935e6517 100644 --- a/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs +++ b/src/Umbraco.Core/Migrations/Upgrade/UmbracoPlan.cs @@ -121,6 +121,8 @@ namespace Umbraco.Core.Migrations.Upgrade To("{648A2D5F-7467-48F8-B309-E99CEEE00E2A}"); // fixed version To("{C39BF2A7-1454-4047-BBFE-89E40F66ED63}"); To("{64EBCE53-E1F0-463A-B40B-E98EFCCA8AE2}"); + To("{0009109C-A0B8-4F3F-8FEB-C137BBDDA268}"); + //FINAL diff --git a/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs new file mode 100644 index 0000000000..1df11a3e99 --- /dev/null +++ b/src/Umbraco.Core/Migrations/Upgrade/V_8_0_0/AddContentTypeIsElementColumn.cs @@ -0,0 +1,15 @@ +using Umbraco.Core.Persistence.Dtos; + +namespace Umbraco.Core.Migrations.Upgrade.V_8_0_0 +{ + public class AddContentTypeIsElementColumn : MigrationBase + { + public AddContentTypeIsElementColumn(IMigrationContext context) : base(context) + { } + + public override void Migrate() + { + AddColumn("isElement"); + } + } +} diff --git a/src/Umbraco.Core/Models/ContentTypeBase.cs b/src/Umbraco.Core/Models/ContentTypeBase.cs index 88b1179f6d..b6ea9f50a0 100644 --- a/src/Umbraco.Core/Models/ContentTypeBase.cs +++ b/src/Umbraco.Core/Models/ContentTypeBase.cs @@ -26,6 +26,7 @@ namespace Umbraco.Core.Models private string _thumbnail = "folder.png"; private bool _allowedAsRoot; // note: only one that's not 'pure element type' private bool _isContainer; + private bool _isElement; private PropertyGroupCollection _propertyGroups; private PropertyTypeCollection _noGroupPropertyTypes; private IEnumerable _allowedContentTypes; @@ -90,6 +91,7 @@ namespace Umbraco.Core.Models public readonly PropertyInfo IconSelector = ExpressionHelper.GetPropertyInfo(x => x.Icon); public readonly PropertyInfo ThumbnailSelector = ExpressionHelper.GetPropertyInfo(x => x.Thumbnail); public readonly PropertyInfo AllowedAsRootSelector = ExpressionHelper.GetPropertyInfo(x => x.AllowedAsRoot); + public readonly PropertyInfo IsElementSelector = ExpressionHelper.GetPropertyInfo(x => x.IsElement); public readonly PropertyInfo IsContainerSelector = ExpressionHelper.GetPropertyInfo(x => x.IsContainer); public readonly PropertyInfo AllowedContentTypesSelector = ExpressionHelper.GetPropertyInfo>(x => x.AllowedContentTypes); public readonly PropertyInfo PropertyGroupsSelector = ExpressionHelper.GetPropertyInfo(x => x.PropertyGroups); @@ -180,6 +182,14 @@ namespace Umbraco.Core.Models set => SetPropertyValueAndDetectChanges(value, ref _isContainer, Ps.Value.IsContainerSelector); } + /// + [DataMember] + public bool IsElement + { + get => _isElement; + set => SetPropertyValueAndDetectChanges(value, ref _isElement, Ps.Value.IsElementSelector); + } + /// /// Gets or sets a list of integer Ids for allowed ContentTypes /// diff --git a/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs b/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs index 8af48bb881..adbc3de54f 100644 --- a/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs +++ b/src/Umbraco.Core/Models/ContentTypeBaseExtensions.cs @@ -15,7 +15,8 @@ namespace Umbraco.Core.Models { var type = contentType.GetType(); var itemType = PublishedItemType.Unknown; - if (typeof(IContentType).IsAssignableFrom(type)) itemType = PublishedItemType.Content; + if (contentType.IsElement) itemType = PublishedItemType.Element; + else if (typeof(IContentType).IsAssignableFrom(type)) itemType = PublishedItemType.Content; else if (typeof(IMediaType).IsAssignableFrom(type)) itemType = PublishedItemType.Media; else if (typeof(IMemberType).IsAssignableFrom(type)) itemType = PublishedItemType.Member; return itemType; diff --git a/src/Umbraco.Core/Models/IContentTypeBase.cs b/src/Umbraco.Core/Models/IContentTypeBase.cs index a1d4aee02f..787e347b37 100644 --- a/src/Umbraco.Core/Models/IContentTypeBase.cs +++ b/src/Umbraco.Core/Models/IContentTypeBase.cs @@ -25,7 +25,7 @@ namespace Umbraco.Core.Models /// the icon (eg. icon-home) along with an optional CSS class name representing the /// color (eg. icon-blue). Put together, the value for this scenario would be /// icon-home color-blue. - /// + /// /// If a class name for the color isn't specified, the icon color will default to black. /// string Icon { get; set; } @@ -48,6 +48,16 @@ namespace Umbraco.Core.Models /// bool IsContainer { get; set; } + /// + /// Gets or sets a value indicating whether this content type is for an element. + /// + /// + /// By default a content type is for a true media, member or document, but + /// it can also be for an element, ie a subset that can for instance be used in + /// nested content. + /// + bool IsElement { get; set; } + /// /// Gets or sets the content variation of the content type. /// diff --git a/src/Umbraco.Core/Models/Packaging/ActionRunAt.cs b/src/Umbraco.Core/Models/Packaging/ActionRunAt.cs new file mode 100644 index 0000000000..0023d4dbed --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/ActionRunAt.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Models.Packaging +{ + public enum ActionRunAt + { + Undefined = 0, + Install, + Uninstall + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs new file mode 100644 index 0000000000..a852fcc997 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackage.cs @@ -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 +{ + /// + /// The model of the package definition within an umbraco (zip) package file + /// + 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 ? + + public PreInstallWarnings Warnings { get; set; } = new PreInstallWarnings(); + + public List Files { get; set; } = new List(); + + public IEnumerable Macros { get; set; } //fixme: make strongly typed + public IEnumerable Templates { get; set; } //fixme: make strongly typed + public IEnumerable Stylesheets { get; set; } //fixme: make strongly typed + public IEnumerable DataTypes { get; set; } //fixme: make strongly typed + public IEnumerable Languages { get; set; } //fixme: make strongly typed + public IEnumerable DictionaryItems { get; set; } //fixme: make strongly typed + public IEnumerable DocumentTypes { get; set; } //fixme: make strongly typed + public IEnumerable Documents { get; set; } + } +} diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs new file mode 100644 index 0000000000..c41966dfe1 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackageDocument.cs @@ -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 ", nameof(xml)); + return new CompiledPackageDocument + { + XmlData = xml, + ImportMode = xml.AttributeValue("importMode") + }; + } + + public string ImportMode { get; set; } //this is never used + + /// + /// The serialized version of the content + /// + public XElement XmlData { get; set; } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs b/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs new file mode 100644 index 0000000000..2cb989b42b --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/CompiledPackageFile.cs @@ -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 ", 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; } + + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs b/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs new file mode 100644 index 0000000000..8722ee7811 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/IPackageInfo.cs @@ -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; } + } +} diff --git a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs index 3eb397d728..1cab17e220 100644 --- a/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs +++ b/src/Umbraco.Core/Models/Packaging/InstallationSummary.cs @@ -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 DataTypesInstalled { get; set; } - public IEnumerable LanguagesInstalled { get; set; } - public IEnumerable DictionaryItemsInstalled { get; set; } - public IEnumerable MacrosInstalled { get; set; } - public IEnumerable FilesInstalled { get; set; } - public IEnumerable TemplatesInstalled { get; set; } - public IEnumerable ContentTypesInstalled { get; set; } - public IEnumerable StylesheetsInstalled { get; set; } - public IEnumerable ContentInstalled { get; set; } - public IEnumerable Actions { get; set; } - public bool PackageInstalled { get; set; } + public IPackageInfo MetaData { get; set; } + public IEnumerable DataTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable LanguagesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DictionaryItemsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MacrosInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable FilesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable TemplatesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DocumentTypesInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable StylesheetsInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable ContentInstalled { get; set; } = Enumerable.Empty(); + public IEnumerable Actions { get; set; } = Enumerable.Empty(); + public IEnumerable ActionErrors { get; set; } = Enumerable.Empty(); + } - internal static class InstallationSummaryExtentions - { - public static InstallationSummary InitEmpty(this InstallationSummary summary) - { - summary.Actions = new List(); - summary.ContentInstalled = new List(); - summary.ContentTypesInstalled = new List(); - summary.DataTypesInstalled = new List(); - summary.DictionaryItemsInstalled = new List(); - summary.FilesInstalled = new List(); - summary.LanguagesInstalled = new List(); - summary.MacrosInstalled = new List(); - summary.MetaData = new MetaData(); - summary.TemplatesInstalled = new List(); - summary.PackageInstalled = false; - return summary; - } - } } diff --git a/src/Umbraco.Core/Models/Packaging/MetaData.cs b/src/Umbraco.Core/Models/Packaging/MetaData.cs deleted file mode 100644 index a1cb450c5b..0000000000 --- a/src/Umbraco.Core/Models/Packaging/MetaData.cs +++ /dev/null @@ -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; } - } -} diff --git a/src/Umbraco.Core/Models/Packaging/PackageAction.cs b/src/Umbraco.Core/Models/Packaging/PackageAction.cs index e941c5729a..ab7b120eae 100644 --- a/src/Umbraco.Core/Models/Packaging/PackageAction.cs +++ b/src/Umbraco.Core/Models/Packaging/PackageAction.cs @@ -4,13 +4,9 @@ using System.Xml.Linq; namespace Umbraco.Core.Models.Packaging { - public enum ActionRunAt - { - Undefined = 0, - Install, - Uninstall - } - + /// + /// Defines a package action declared within a package manifest + /// [Serializable] [DataContract(IsReference = true)] public class PackageAction diff --git a/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs new file mode 100644 index 0000000000..c068c57b08 --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/PackageDefinition.cs @@ -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 + { + /// + /// Converts a model to a model + /// + /// + /// + /// + /// This is used only for conversions and will not 'get' a PackageDefinition from the repository with a valid ID + /// + 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; + + /// + /// The full path to the package's zip file when it was installed (or is being installed) + /// + [ReadOnly(true)] + [DataMember(Name = "packagePath")] + public string PackagePath { get; set; } = string.Empty; + + [DataMember(Name = "version")] + [Required] + public string Version { get; set; } = "1.0.0"; + + /// + /// The minimum umbraco version that this package requires + /// + [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 Macros { get; set; } = new List(); + + [DataMember(Name = "languages")] + public IList Languages { get; set; } = new List(); + + [DataMember(Name = "dictionaryItems")] + public IList DictionaryItems { get; set; } = new List(); + + [DataMember(Name = "templates")] + public IList Templates { get; set; } = new List(); + + [DataMember(Name = "documentTypes")] + public IList DocumentTypes { get; set; } = new List(); + + [DataMember(Name = "stylesheets")] + public IList Stylesheets { get; set; } = new List(); + + [DataMember(Name = "files")] + public IList Files { get; set; } = new List(); + + //fixme: Change this to angular view + [DataMember(Name = "loadControl")] + public string Control { get; set; } = string.Empty; + + [DataMember(Name = "actions")] + public string Actions { get; set; } = ""; + + [DataMember(Name = "dataTypes")] + public IList DataTypes { get; set; } = new List(); + + [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(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(DataTypes), + IconUrl = IconUrl, + License = License, + Templates = new List(Templates), + Languages = new List(Languages), + Macros = new List(Macros), + Stylesheets = new List(Stylesheets), + DocumentTypes = new List(DocumentTypes), + DictionaryItems = new List(DictionaryItems), + ContentNodeId = ContentNodeId, + ContentLoadChildNodes = ContentLoadChildNodes + }; + } + + } + +} diff --git a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs index 5850e2321c..f0acb2a46b 100644 --- a/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs +++ b/src/Umbraco.Core/Models/Packaging/PreInstallWarnings.cs @@ -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[] UnsecureFiles { get; set; } - public KeyValuePair[] FilesReplaced { get; set; } - public IEnumerable ConflictingMacroAliases { get; set; } - public IEnumerable ConflictingTemplateAliases { get; set; } - public IEnumerable ConflictingStylesheetNames { get; set; } + public IEnumerable UnsecureFiles { get; set; } = Enumerable.Empty(); + public IEnumerable FilesReplaced { get; set; } = Enumerable.Empty(); + + //TODO: Shouldn't we detect other conflicting entities too ? + public IEnumerable ConflictingMacros { get; set; } = Enumerable.Empty(); + public IEnumerable ConflictingTemplates { get; set; } = Enumerable.Empty(); + public IEnumerable ConflictingStylesheets { get; set; } = Enumerable.Empty(); } } diff --git a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs b/src/Umbraco.Core/Models/Packaging/RequirementsType.cs similarity index 65% rename from src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs rename to src/Umbraco.Core/Models/Packaging/RequirementsType.cs index ca91626128..2b6894ed0f 100644 --- a/src/Umbraco.Web/_Legacy/Packager/RequirementsType.cs +++ b/src/Umbraco.Core/Models/Packaging/RequirementsType.cs @@ -1,4 +1,4 @@ -namespace Umbraco.Web._Legacy.Packager +namespace Umbraco.Core.Models.Packaging { public enum RequirementsType { diff --git a/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs new file mode 100644 index 0000000000..fd39954f6b --- /dev/null +++ b/src/Umbraco.Core/Models/Packaging/UninstallationSummary.cs @@ -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 DataTypesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable LanguagesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DictionaryItemsUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable MacrosUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable FilesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable TemplatesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable DocumentTypesUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable StylesheetsUninstalled { get; set; } = Enumerable.Empty(); + public IEnumerable Actions { get; set; } = Enumerable.Empty(); + public IEnumerable ActionErrors { get; set; } = Enumerable.Empty(); + } +} diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs index e55fe66945..42e9c9538d 100644 --- a/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs +++ b/src/Umbraco.Core/Models/PublishedContent/PublishedItemType.cs @@ -4,13 +4,18 @@ /// The type of published element. /// /// Can be a simple element, or a document, a media, a member. - public enum PublishedItemType // fixme - need to rename to PublishedElementType but then conflicts? + public enum PublishedItemType { /// /// Unknown. /// Unknown = 0, + /// + /// An element. + /// + Element, + /// /// A document. /// diff --git a/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs new file mode 100644 index 0000000000..0d533cfbc2 --- /dev/null +++ b/src/Umbraco.Core/Packaging/CompiledPackageXmlParser.cs @@ -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 +{ + /// + /// Parses the xml document contained in a compiled (zip) Umbraco package + /// + 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("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("type").IsNullOrWhiteSpace() ? RequirementsType.Legacy : Enum.Parse(requirements.AttributeValue("type"), true), + Control = package.Element("control")?.Value, + Actions = xml.Root.Element("Actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value + Files = xml.Root.Element("files")?.Elements("file")?.Select(CompiledPackageFile.Create).ToList() ?? new List(), + Macros = xml.Root.Element("Macros")?.Elements("macro") ?? Enumerable.Empty(), + Templates = xml.Root.Element("Templates")?.Elements("Template") ?? Enumerable.Empty(), + Stylesheets = xml.Root.Element("Stylesheets")?.Elements("styleSheet") ?? Enumerable.Empty(), + DataTypes = xml.Root.Element("DataTypes")?.Elements("DataType") ?? Enumerable.Empty(), + Languages = xml.Root.Element("Languages")?.Elements("Language") ?? Enumerable.Empty(), + DictionaryItems = xml.Root.Element("DictionaryItems")?.Elements("DictionaryItem") ?? Enumerable.Empty(), + DocumentTypes = xml.Root.Element("DocumentTypes")?.Elements("DocumentType") ?? Enumerable.Empty(), + Documents = xml.Root.Element("Documents")?.Elements("DocumentSet")?.Select(CompiledPackageDocument.Create) ?? Enumerable.Empty(), + }; + + 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; + } + + /// + /// Returns a tuple of the zip file's unique file name and it's application relative path + /// + /// + /// + public (string packageUniqueFile, string appRelativePath)[] ExtractSourceDestinationFileInformation(IEnumerable 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 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 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; + } + + /// + /// Parses the package actions stored in the package definition + /// + /// + /// + /// + public static IEnumerable GetPackageActions(XElement actionsElement, string packageName) + { + if (actionsElement == null) return Enumerable.Empty(); + + //invariant check ... because people can realy enter anything :/ + if (!string.Equals("actions", actionsElement.Name.LocalName, StringComparison.InvariantCultureIgnoreCase)) + throw new FormatException("Must be \"\" as root"); + + if (!actionsElement.HasElements) return Enumerable.Empty(); + + 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 \" + { + 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(); + } + } +} diff --git a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs index b0424067bf..82693677fb 100644 --- a/src/Umbraco.Core/Packaging/ConflictingPackageData.cs +++ b/src/Umbraco.Core/Packaging/ConflictingPackageData.cs @@ -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 FindConflictingStylesheets(XElement stylesheetNotes) + public IEnumerable FindConflictingStylesheets(IEnumerable 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 FindConflictingTemplates(XElement templateNotes) + public IEnumerable FindConflictingTemplates(IEnumerable 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 FindConflictingMacros(XElement macroNodes) + public IEnumerable FindConflictingMacros(IEnumerable 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); }) diff --git a/src/Umbraco.Core/Packaging/IConflictingPackageData.cs b/src/Umbraco.Core/Packaging/IConflictingPackageData.cs deleted file mode 100644 index 12f3e8f8a4..0000000000 --- a/src/Umbraco.Core/Packaging/IConflictingPackageData.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using System.Xml.Linq; -using Umbraco.Core.Models; - -namespace Umbraco.Core.Packaging -{ - internal interface IConflictingPackageData - { - IEnumerable FindConflictingStylesheets(XElement stylesheetNotes); - IEnumerable FindConflictingTemplates(XElement templateNotes); - IEnumerable FindConflictingMacros(XElement macroNodes); - } -} diff --git a/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs new file mode 100644 index 0000000000..42606da6ef --- /dev/null +++ b/src/Umbraco.Core/Packaging/ICreatedPackagesRepository.cs @@ -0,0 +1,16 @@ +using Umbraco.Core.Models.Packaging; + +namespace Umbraco.Core.Packaging +{ + /// + /// Manages the storage of created package definitions + /// + public interface ICreatedPackagesRepository : IPackageDefinitionRepository + { + /// + /// Creates the package file and returns it's physical path + /// + /// + string ExportPackage(PackageDefinition definition); + } +} diff --git a/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs new file mode 100644 index 0000000000..61f611edc5 --- /dev/null +++ b/src/Umbraco.Core/Packaging/IInstalledPackagesRepository.cs @@ -0,0 +1,11 @@ +using System; + +namespace Umbraco.Core.Packaging +{ + /// + /// Manages the storage of installed package definitions + /// + public interface IInstalledPackagesRepository : IPackageDefinitionRepository + { + } +} diff --git a/src/Umbraco.Core/Packaging/IPackageActionRunner.cs b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs new file mode 100644 index 0000000000..1f8c134364 --- /dev/null +++ b/src/Umbraco.Core/Packaging/IPackageActionRunner.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.Xml.Linq; + +namespace Umbraco.Core.Packaging +{ + public interface IPackageActionRunner + { + /// + /// Runs the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + /// + bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors); + + /// + /// Undos the package action with the specified action alias. + /// + /// Name of the package. + /// The action alias. + /// The action XML. + /// + bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors); + } +} diff --git a/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs b/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs new file mode 100644 index 0000000000..343a0a05ea --- /dev/null +++ b/src/Umbraco.Core/Packaging/IPackageDefinitionRepository.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using Umbraco.Core.Models.Packaging; + +namespace Umbraco.Core.Packaging +{ + /// + /// Defines methods for persisting package definitions to storage + /// + public interface IPackageDefinitionRepository + { + IEnumerable GetAll(); + PackageDefinition GetById(int id); + void Delete(int id); + + /// + /// Persists a package definition to storage + /// + /// + /// true if creating/updating the package was successful, otherwise false + /// + bool SavePackage(PackageDefinition definition); + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/IPackageExtraction.cs b/src/Umbraco.Core/Packaging/IPackageExtraction.cs deleted file mode 100644 index 21a5198f55..0000000000 --- a/src/Umbraco.Core/Packaging/IPackageExtraction.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; - -namespace Umbraco.Core.Packaging -{ - /// - /// Used to access an umbraco package file - /// Remeber that filenames must be unique - /// use "FindDubletFileNames" for sanitycheck - /// - internal interface IPackageExtraction - { - /// - /// Returns the content of the file with the given filename - /// - /// Full path to the umbraco package file - /// filename of the file for wich to get the text content - /// 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?? - /// text content of the file - string ReadTextFileFromArchive(string packageFilePath, string fileToRead, out string directoryInPackage); - - /// - /// Copies a file from package to given destination - /// - /// Full path to the ubraco package file - /// filename of the file to copy - /// destination path (including destination filename) - /// True a file was overwritten - void CopyFileFromArchive(string packageFilePath, string fileInPackageName, string destinationfilePath); - - /// - /// Copies a file from package to given destination - /// - /// Full path to the ubraco package file - /// Key: Source file in package. Value: Destination path inclusive file name - void CopyFilesFromArchive(string packageFilePath, IEnumerable> sourceDestination); - - /// - /// Check if given list of files can be found in the package - /// - /// Full path to the umbraco package file - /// a list of files you would like to find in the package - /// a subset if any of the files in "expectedFiles" that could not be found in the package - IEnumerable FindMissingFiles(string packageFilePath, IEnumerable expectedFiles); - - - /// - /// Sanitycheck - should return en empty collection if package is valid - /// - /// Full path to the umbraco package file - /// list of files that are found more than ones (accross directories) in the package - IEnumerable FindDubletFileNames(string packageFilePath); - - /// - /// Reads the given files from archive and returns them as a collection of byte arrays - /// - /// - /// - /// - IEnumerable ReadFilesFromArchive(string packageFilePath, IEnumerable filesToGet); - } -} diff --git a/src/Umbraco.Core/Packaging/IPackageInstallation.cs b/src/Umbraco.Core/Packaging/IPackageInstallation.cs index 1d0d46355c..c85a4ccd5f 100644 --- a/src/Umbraco.Core/Packaging/IPackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/IPackageInstallation.cs @@ -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); + /// + /// This will run the uninstallation sequence for this + /// + /// + /// + /// + UninstallationSummary UninstallPackage(PackageDefinition packageDefinition, int userId); + + /// + /// Installs a packages data and entities + /// + /// + /// + /// + /// + InstallationSummary InstallPackageData(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId); + + /// + /// Installs a packages files + /// + /// + /// + /// + /// + IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId); + + /// + /// Reads the package (zip) file and returns the model + /// + /// + /// + CompiledPackage ReadPackage(FileInfo packageFile); } } diff --git a/src/Umbraco.Core/Packaging/Models/UninstallationSummary.cs b/src/Umbraco.Core/Packaging/Models/UninstallationSummary.cs deleted file mode 100644 index 1c31283ee8..0000000000 --- a/src/Umbraco.Core/Packaging/Models/UninstallationSummary.cs +++ /dev/null @@ -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 DataTypesUninstalled { get; set; } - public IEnumerable LanguagesUninstalled { get; set; } - public IEnumerable DictionaryItemsUninstalled { get; set; } - public IEnumerable MacrosUninstalled { get; set; } - public IEnumerable FilesUninstalled { get; set; } - public IEnumerable TemplatesUninstalled { get; set; } - public IEnumerable ContentTypesUninstalled { get; set; } - public IEnumerable StylesheetsUninstalled { get; set; } - public IEnumerable ContentUninstalled { get; set; } - public bool PackageUninstalled { get; set; } - } - - internal static class UninstallationSummaryExtentions - { - public static UninstallationSummary InitEmpty(this UninstallationSummary summary) - { - summary.ContentUninstalled = new List(); - summary.ContentTypesUninstalled = new List(); - summary.DataTypesUninstalled = new List(); - summary.DictionaryItemsUninstalled = new List(); - summary.FilesUninstalled = new List(); - summary.LanguagesUninstalled = new List(); - summary.MacrosUninstalled = new List(); - summary.MetaData = new MetaData(); - summary.TemplatesUninstalled = new List(); - summary.PackageUninstalled = false; - return summary; - } - } -} diff --git a/src/Umbraco.Core/Packaging/PackageActionRunner.cs b/src/Umbraco.Core/Packaging/PackageActionRunner.cs new file mode 100644 index 0000000000..38275d5f0a --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageActionRunner.cs @@ -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 +{ + /// + /// Package actions are executed on packge install / uninstall. + /// + internal class PackageActionRunner : IPackageActionRunner + { + private readonly ILogger _logger; + private readonly PackageActionCollection _packageActions; + + public PackageActionRunner(ILogger logger, PackageActionCollection packageActions) + { + _logger = logger; + _packageActions = packageActions; + } + + /// + public bool RunPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors) + { + var e = new List(); + 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(ex, "Error loading package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); + } + } + + errors = e; + return e.Count == 0; + } + + /// + public bool UndoPackageAction(string packageName, string actionAlias, XElement actionXml, out IEnumerable errors) + { + var e = new List(); + 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(ex, "Error undoing package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); + } + } + errors = e; + return e.Count == 0; + } + + } +} diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs deleted file mode 100644 index 7cacb30bd3..0000000000 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ /dev/null @@ -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 - { - /// - /// Entry point to call from your code - /// - /// - /// - /// - /// - /// - /// Will perform the assembly scan in a separate app domain - /// - public static IEnumerable ScanAssembliesForTypeReference(IEnumerable 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(assemblys.ToArray(), out errorReport); - return result; - } - finally - { - AppDomain.Unload(appDomain); - } - } - } - - /// - /// Entry point to call from your code - /// - /// - /// - /// - /// - /// - /// Will perform the assembly scan in a separate app domain - /// - public static IEnumerable ScanAssembliesForTypeReference(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(dllPath, out errorReport); - return result; - } - finally - { - AppDomain.Unload(appDomain); - } - } - - /// - /// Performs the assembly scanning - /// - /// - /// - /// - /// - /// - /// This method is executed in a separate app domain - /// - private IEnumerable PerformScan(IEnumerable 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(loaded, out errorReport); - } - - /// - /// Performs the assembly scanning - /// - /// - /// - /// - /// - /// - /// This method is executed in a separate app domain - /// - private IEnumerable PerformScan(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(loaded, out errorReport); - } - - private static IEnumerable PerformScan(IList loaded, out string[] errorReport) - { - var dllsWithReference = new List(); - var errors = new List(); - var assembliesWithErrors = new List(); - - //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(ex, "An error occurred scanning package assembly '{AssemblyName}'", assemblyName.FullName); - } - } - } - - var contractType = GetLoadFromContractType(); - - //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(ex, "An error occurred scanning package assembly '{AssemblyName}'", a.GetName().FullName); - } - } - - } - - errorReport = errors.ToArray(); - return dllsWithReference; - } - - /// - /// 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. - /// - /// - /// - private static Type GetLoadFromContractType() - { - 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; - } - - /// - /// Create an app domain - /// - /// - 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)); - } - } -} diff --git a/src/Umbraco.Core/Packaging/PackageDataInstallation.cs b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs new file mode 100644 index 0000000000..2e9fee9595 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageDataInstallation.cs @@ -0,0 +1,1279 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text.RegularExpressions; +using System.Web; +using System.Xml.Linq; +using System.Xml.XPath; +using Umbraco.Core.Collections; +using Umbraco.Core.IO; +using Umbraco.Core.Logging; +using Umbraco.Core.Models; +using Umbraco.Core.Models.Entities; +using Umbraco.Core.Models.Packaging; +using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; + +namespace Umbraco.Core.Packaging +{ + internal class PackageDataInstallation + { + private readonly ILogger _logger; + private readonly IFileService _fileService; + private readonly IMacroService _macroService; + private readonly ILocalizationService _localizationService; + private readonly IDataTypeService _dataTypeService; + private readonly PropertyEditorCollection _propertyEditors; + private readonly IEntityService _entityService; + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; + + public PackageDataInstallation(ILogger logger, IFileService fileService, IMacroService macroService, ILocalizationService localizationService, + IDataTypeService dataTypeService, IEntityService entityService, IContentTypeService contentTypeService, + IContentService contentService, PropertyEditorCollection propertyEditors) + { + _logger = logger; + _fileService = fileService; + _macroService = macroService; + _localizationService = localizationService; + _dataTypeService = dataTypeService; + _propertyEditors = propertyEditors; + _entityService = entityService; + _contentTypeService = contentTypeService; + _contentService = contentService; + } + + #region Uninstall + + public UninstallationSummary UninstallPackageData(PackageDefinition package, int userId) + { + if (package == null) throw new ArgumentNullException(nameof(package)); + + var removedTemplates = new List(); + var removedMacros = new List(); + var removedContentTypes = new List(); + var removedDictionaryItems = new List(); + var removedDataTypes = new List(); + var removedLanguages = new List(); + + + //Uninstall templates + foreach (var item in package.Templates.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var found = _fileService.GetTemplate(nId); + if (found != null) + { + removedTemplates.Add(found); + _fileService.DeleteTemplate(found.Alias, userId); + } + package.Templates.Remove(nId.ToString()); + } + + //Uninstall macros + foreach (var item in package.Macros.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var macro = _macroService.GetById(nId); + if (macro != null) + { + removedMacros.Add(macro); + _macroService.Delete(macro, userId); + } + package.Macros.Remove(nId.ToString()); + } + + //Remove Document Types + var contentTypes = new List(); + var contentTypeService = _contentTypeService; + foreach (var item in package.DocumentTypes.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var contentType = contentTypeService.Get(nId); + if (contentType == null) continue; + contentTypes.Add(contentType); + package.DocumentTypes.Remove(nId.ToString(CultureInfo.InvariantCulture)); + } + + //Order the DocumentTypes before removing them + if (contentTypes.Any()) + { + //TODO: I don't think this ordering is necessary + var orderedTypes = (from contentType in contentTypes + orderby contentType.ParentId descending, contentType.Id descending + select contentType).ToList(); + removedContentTypes.AddRange(orderedTypes); + contentTypeService.Delete(orderedTypes, userId); + } + + //Remove Dictionary items + foreach (var item in package.DictionaryItems.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var di = _localizationService.GetDictionaryItemById(nId); + if (di != null) + { + removedDictionaryItems.Add(di); + _localizationService.Delete(di, userId); + } + package.DictionaryItems.Remove(nId.ToString()); + } + + //Remove Data types + foreach (var item in package.DataTypes.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var dtd = _dataTypeService.GetDataType(nId); + if (dtd != null) + { + removedDataTypes.Add(dtd); + _dataTypeService.Delete(dtd, userId); + } + package.DataTypes.Remove(nId.ToString()); + } + + //Remove Langs + foreach (var item in package.Languages.ToArray()) + { + if (int.TryParse(item, out var nId) == false) continue; + var lang = _localizationService.GetLanguageById(nId); + if (lang != null) + { + removedLanguages.Add(lang); + _localizationService.Delete(lang, userId); + } + package.Languages.Remove(nId.ToString()); + } + + // create a summary of what was actually removed, for PackagingService.UninstalledPackage + var summary = new UninstallationSummary + { + MetaData = package, + TemplatesUninstalled = removedTemplates, + MacrosUninstalled = removedMacros, + DocumentTypesUninstalled = removedContentTypes, + DictionaryItemsUninstalled = removedDictionaryItems, + DataTypesUninstalled = removedDataTypes, + LanguagesUninstalled = removedLanguages, + + }; + + return summary; + + + } + + #endregion + + #region Content + + + public IEnumerable ImportContent(IEnumerable docs, IDictionary importedDocumentTypes, int userId) + { + return docs.SelectMany(x => ImportContent(x, -1, importedDocumentTypes, userId)); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional parent Id for the content being imported + /// A dictionary of already imported document types (basically used as a cache) + /// Optional Id of the user performing the import + /// An enumrable list of generated content + public IEnumerable ImportContent(CompiledPackageDocument packageDocument, int parentId, IDictionary importedDocumentTypes, int userId) + { + var element = packageDocument.XmlData; + + var roots = from doc in element.Elements() + where (string)doc.Attribute("isDoc") == "" + select doc; + + var contents = ParseDocumentRootXml(roots, parentId, importedDocumentTypes).ToList(); + if (contents.Any()) + _contentService.Save(contents, userId); + + return contents; + + //var attribute = element.Attribute("isDoc"); + //if (attribute != null) + //{ + // //This is a single doc import + // var elements = new List { element }; + // var contents = ParseDocumentRootXml(elements, parentId, importedDocumentTypes).ToList(); + // if (contents.Any()) + // _contentService.Save(contents, userId); + + // return contents; + //} + + //throw new ArgumentException( + // "The passed in XElement is not valid! It does not contain a root element called " + + // "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); + } + + private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId, IDictionary importedContentTypes) + { + var contents = new List(); + foreach (var root in roots) + { + var contentTypeAlias = root.Name.LocalName; + + if (!importedContentTypes.ContainsKey(contentTypeAlias)) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + var content = CreateContentFromXml(root, importedContentTypes[contentTypeAlias], null, parentId); + if (content == null) continue; + + contents.Add(content); + + var children = (from child in root.Elements() + where (string)child.Attribute("isDoc") == "" + select child) + .ToList(); + + if (children.Count > 0) + contents.AddRange(CreateContentFromXml(children, content, importedContentTypes).WhereNotNull()); + } + return contents; + } + + private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent, IDictionary importedContentTypes) + { + var list = new List(); + foreach (var child in children) + { + string contentTypeAlias = child.Name.LocalName; + + if (importedContentTypes.ContainsKey(contentTypeAlias) == false) + { + var contentType = FindContentTypeByAlias(contentTypeAlias); + importedContentTypes.Add(contentTypeAlias, contentType); + } + + //Create and add the child to the list + var content = CreateContentFromXml(child, importedContentTypes[contentTypeAlias], parent, default); + list.Add(content); + + //Recursive call + var child1 = child; + var grandChildren = (from grand in child1.Elements() + where (string)grand.Attribute("isDoc") == "" + select grand).ToList(); + + if (grandChildren.Any()) + list.AddRange(CreateContentFromXml(grandChildren, content, importedContentTypes)); + } + + return list; + } + + private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) + { + var key = Guid.Empty; + if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) + { + //if a Key is supplied, then we need to check if the content already exists and if so we ignore the installation for this item + if (_contentService.GetById(key) != null) + return null; + } + + var id = element.Attribute("id").Value; + var level = element.Attribute("level").Value; + var sortOrder = element.Attribute("sortOrder").Value; + var nodeName = element.Attribute("nodeName").Value; + var path = element.Attribute("path").Value; + var templateId = element.AttributeValue("template"); + + var properties = from property in element.Elements() + where property.Attribute("isDoc") == null + select property; + + var template = templateId.HasValue ? _fileService.GetTemplate(templateId.Value) : null; + + IContent content = parent == null + ? new Content(nodeName, parentId, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key + } + : new Content(nodeName, parent, contentType) + { + Level = int.Parse(level), + SortOrder = int.Parse(sortOrder), + TemplateId = template?.Id, + Key = key + }; + + foreach (var property in properties) + { + string propertyTypeAlias = property.Name.LocalName; + if (content.HasProperty(propertyTypeAlias)) + { + var propertyValue = property.Value; + + var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); + + //set property value + content.SetValue(propertyTypeAlias, propertyValue); + } + } + + return content; + } + + #endregion + + #region DocumentTypes + + public IEnumerable ImportDocumentType(XElement docTypeElement, int userId) + { + return ImportDocumentTypes(new []{ docTypeElement }, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IEnumerable docTypeElements, int userId) + { + return ImportDocumentTypes(docTypeElements.ToList(), true, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Boolean indicating whether or not to import the + /// Optional id of the User performing the operation. Default is zero (admin). + /// An enumrable list of generated ContentTypes + public IEnumerable ImportDocumentTypes(IReadOnlyCollection unsortedDocumentTypes, bool importStructure, int userId) + { + var importedContentTypes = new Dictionary(); + + //When you are importing a single doc type we have to assume that the depedencies are already there. + //Otherwise something like uSync won't work. + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; + + var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); + + if (isSingleDocTypeImport == false) + { + //NOTE Here we sort the doctype XElements based on dependencies + //before creating the doc types - this should also allow for a better structure/inheritance support. + foreach (var documentType in unsortedDocumentTypes) + { + var elementCopy = documentType; + var infoElement = elementCopy.Element("Info"); + var dependencies = new HashSet(); + + //Add the Master as a dependency + if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) + { + dependencies.Add(infoElement.Element("Master").Value); + } + + //Add compositions as dependencies + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + dependencies.Add(composition.Value); + } + } + } + + graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); + } + } + + //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 + var documentTypes = isSingleDocTypeImport + ? unsortedDocumentTypes.ToList() + : graph.GetSortedItems().Select(x => x.Item).ToList(); + + //Iterate the sorted document types and create them as IContentType objects + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + if (importedContentTypes.ContainsKey(alias) == false) + { + var contentType = _contentTypeService.Get(alias); + importedContentTypes.Add(alias, contentType == null + ? CreateContentTypeFromXml(documentType, importedContentTypes) + : UpdateContentTypeFromXml(documentType, contentType, importedContentTypes)); + } + } + + foreach (var contentType in importedContentTypes) + { + var ct = contentType.Value; + if (importedFolders.ContainsKey(ct.Alias)) + { + ct.ParentId = importedFolders[ct.Alias]; + } + } + + //Save the newly created/updated IContentType objects + var list = importedContentTypes.Select(x => x.Value).ToList(); + _contentTypeService.Save(list, userId); + + //Now we can finish the import by updating the 'structure', + //which requires the doc types to be saved/available in the db + if (importStructure) + { + var updatedContentTypes = new List(); + //Update the structure here - we can't do it untill all DocTypes have been created + foreach (var documentType in documentTypes) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var structureElement = documentType.Element("Structure"); + //Ensure that we only update ContentTypes which has actual structure-elements + if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; + + var updated = UpdateContentTypesStructure(importedContentTypes[alias], structureElement, importedContentTypes); + updatedContentTypes.Add(updated); + } + //Update ContentTypes with a newly added structure/list of allowed children + if (updatedContentTypes.Any()) + _contentTypeService.Save(updatedContentTypes, userId); + } + + return list; + } + + private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) + { + var importedFolders = new Dictionary(); + foreach (var documentType in unsortedDocumentTypes) + { + var foldersAttribute = documentType.Attribute("Folders"); + var infoElement = documentType.Element("Info"); + if (foldersAttribute != null && infoElement != null + //don't import any folder if this is a child doc type - the parent doc type will need to + //exist which contains it's folders + && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) + { + var alias = documentType.Element("Info").Element("Alias").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //level 1 = root level folders, there can only be one with the same name + var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + var rootFolderId = tryCreateFolder.Result.Entity.Id; + current = _contentTypeService.GetContainer(rootFolderId); + } + + importedFolders.Add(alias, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateContentTypeChildFolder(folderName, current); + importedFolders[alias] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _contentTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + private IContentType CreateContentTypeFromXml(XElement documentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + + //Name of the master corresponds to the parent + var masterElement = infoElement.Element("Master"); + IContentType parent = null; + if (masterElement != null) + { + var masterAlias = masterElement.Value; + parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + } + + var alias = infoElement.Element("Alias").Value; + var contentType = parent == null + ? new ContentType(-1) { Alias = alias } + : new ContentType(parent, alias); + + if (parent != null) + contentType.AddContentType(parent); + + return UpdateContentTypeFromXml(documentType, contentType, importedContentTypes); + } + + private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType, IReadOnlyDictionary importedContentTypes) + { + var infoElement = documentType.Element("Info"); + var defaultTemplateElement = infoElement.Element("DefaultTemplate"); + + contentType.Name = infoElement.Element("Name").Value; + contentType.Icon = infoElement.Element("Icon").Value; + contentType.Thumbnail = infoElement.Element("Thumbnail").Value; + contentType.Description = infoElement.Element("Description").Value; + + //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. + var allowAtRoot = infoElement.Element("AllowAtRoot"); + if (allowAtRoot != null) + contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); + + //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. + var isListView = infoElement.Element("IsListView"); + if (isListView != null) + contentType.IsContainer = isListView.Value.InvariantEquals("true"); + + var isElement = infoElement.Element("IsElement"); + if (isElement != null) + contentType.IsElement = isElement.Value.InvariantEquals("true"); + + //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set + var masterElement = infoElement.Element("Master"); + if (masterElement != null) + { + var masterAlias = masterElement.Value; + IContentType parent = importedContentTypes.ContainsKey(masterAlias) + ? importedContentTypes[masterAlias] + : _contentTypeService.Get(masterAlias); + + contentType.SetParent(parent); + } + + //Update Compositions on the ContentType to ensure that they are as is defined in the package xml + var compositionsElement = infoElement.Element("Compositions"); + if (compositionsElement != null && compositionsElement.HasElements) + { + var compositions = compositionsElement.Elements("Composition"); + if (compositions.Any()) + { + foreach (var composition in compositions) + { + var compositionAlias = composition.Value; + var compositionContentType = importedContentTypes.ContainsKey(compositionAlias) + ? importedContentTypes[compositionAlias] + : _contentTypeService.Get(compositionAlias); + var added = contentType.AddContentType(compositionContentType); + } + } + } + + UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); + UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); + UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); + + return contentType; + } + + private void UpdateContentTypesAllowedTemplates(IContentType contentType, + XElement allowedTemplatesElement, XElement defaultTemplateElement) + { + if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) + { + var allowedTemplates = contentType.AllowedTemplates.ToList(); + foreach (var templateElement in allowedTemplatesElement.Elements("Template")) + { + var alias = templateElement.Value; + var template = _fileService.GetTemplate(alias.ToSafeAlias()); + if (template != null) + { + if (allowedTemplates.Any(x => x.Id == template.Id)) continue; + allowedTemplates.Add(template); + } + else + { + _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); + } + } + + contentType.AllowedTemplates = allowedTemplates; + } + + if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) + { + var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); + if (defaultTemplate != null) + { + contentType.SetDefaultTemplate(defaultTemplate); + } + else + { + _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); + } + } + } + + private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) + { + if (tabElement == null) + return; + + var tabs = tabElement.Elements("Tab"); + foreach (var tab in tabs) + { + var id = tab.Element("Id").Value;//Do we need to use this for tracking? + var caption = tab.Element("Caption").Value; + + if (contentType.PropertyGroups.Contains(caption) == false) + { + contentType.AddPropertyGroup(caption); + + } + + int sortOrder; + if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) + { + // Override the sort order with the imported value + contentType.PropertyGroups[caption].SortOrder = sortOrder; + } + } + } + + private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) + { + var properties = genericPropertiesElement.Elements("GenericProperty"); + foreach (var property in properties) + { + var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition + + var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + + //get the alias as a string for use below + var propertyEditorAlias = property.Element("Type").Value.Trim(); + + //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id + //We look up a DataTypeDefinition that matches + + if (dataTypeDefinition == null) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) + { + var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); + if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) + { + dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); + } + } + + // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. + // This means that the property will not be created. + if (dataTypeDefinition == null) + { + //TODO: We should expose this to the UI during install! + _logger.Warn("Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists.", + property.Element("Name").Value, dataTypeDefinitionId, property.Element("Type").Value.Trim()); + + //convert to a label! + dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); + //if for some odd reason this isn't there then ignore + if (dataTypeDefinition == null) continue; + } + + var sortOrder = 0; + var sortOrderElement = property.Element("SortOrder"); + if (sortOrderElement != null) + int.TryParse(sortOrderElement.Value, out sortOrder); + var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) + { + Name = property.Element("Name").Value, + Description = (string)property.Element("Description"), + Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, + ValidationRegExp = (string)property.Element("Validation"), + SortOrder = sortOrder + }; + + var tab = (string)property.Element("Tab"); + if (string.IsNullOrEmpty(tab)) + { + contentType.AddPropertyType(propertyType); + } + else + { + contentType.AddPropertyType(propertyType, tab); + } + } + } + + private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement, IReadOnlyDictionary importedContentTypes) + { + var allowedChildren = contentType.AllowedContentTypes.ToList(); + int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; + foreach (var element in structureElement.Elements("DocumentType")) + { + var alias = element.Value; + + var allowedChild = importedContentTypes.ContainsKey(alias) ? importedContentTypes[alias] : _contentTypeService.Get(alias); + if (allowedChild == null) + { + _logger.Warn( + "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", + alias, contentType.Alias); + continue; + } + + if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; + + allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); + sortOrder++; + } + + contentType.AllowedContentTypes = allowedChildren; + return contentType; + } + + /// + /// Used during Content import to ensure that the ContentType of a content item exists + /// + /// + /// + private IContentType FindContentTypeByAlias(string contentTypeAlias) + { + var contentType = _contentTypeService.Get(contentTypeAlias); + + if (contentType == null) + throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); + + return contentType; + } + + #endregion + + #region DataTypes + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional id of the user + /// An enumrable list of generated DataTypeDefinitions + public IEnumerable ImportDataTypes(IReadOnlyCollection dataTypeElements, int userId) + { + var dataTypes = new List(); + + var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); + + foreach (var dataTypeElement in dataTypeElements) + { + var dataTypeDefinitionName = dataTypeElement.AttributeValue("Name"); + + var dataTypeDefinitionId = dataTypeElement.AttributeValue("Definition"); + var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); + + var parentId = -1; + if (importedFolders.ContainsKey(dataTypeDefinitionName)) + parentId = importedFolders[dataTypeDefinitionName]; + + var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); + //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml + if (definition == null) + { + var databaseType = databaseTypeAttribute?.Value.EnumParse(true) ?? ValueStorageType.Ntext; + + // the Id field is actually the string property editor Alias + // however, the actual editor with this alias could be installed with the package, and + // therefore not yet part of the _propertyEditors collection, so we cannot try and get + // the actual editor - going with a void editor + + var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); + if (!_propertyEditors.TryGet(editorAlias, out var editor)) + editor = new VoidEditor(_logger) { Alias = editorAlias }; + + var dataType = new DataType(editor) + { + Key = dataTypeDefinitionId, + Name = dataTypeDefinitionName, + DatabaseType = databaseType, + ParentId = parentId + }; + + var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; + if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) + dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); + + dataTypes.Add(dataType); + } + else + { + definition.ParentId = parentId; + _dataTypeService.Save(definition, userId); + } + } + + if (dataTypes.Count > 0) + { + _dataTypeService.Save(dataTypes, userId, true); + } + + return dataTypes; + } + + private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) + { + var importedFolders = new Dictionary(); + foreach (var datatypeElement in datatypeElements) + { + var foldersAttribute = datatypeElement.Attribute("Folders"); + if (foldersAttribute != null) + { + var name = datatypeElement.Attribute("Name").Value; + var folders = foldersAttribute.Value.Split('/'); + var rootFolder = HttpUtility.UrlDecode(folders[0]); + //there will only be a single result by name for level 1 (root) containers + var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); + + if (current == null) + { + var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); + throw tryCreateFolder.Exception; + } + current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + importedFolders.Add(name, current.Id); + + for (var i = 1; i < folders.Length; i++) + { + var folderName = HttpUtility.UrlDecode(folders[i]); + current = CreateDataTypeChildFolder(folderName, current); + importedFolders[name] = current.Id; + } + } + } + + return importedFolders; + } + + private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) + { + var children = _entityService.GetChildren(current.Id).ToArray(); + var found = children.Any(x => x.Name.InvariantEquals(folderName)); + if (found) + { + var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; + return _dataTypeService.GetContainer(containerId); + } + + var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); + if (tryCreateFolder == false) + { + _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); + throw tryCreateFolder.Exception; + } + return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); + } + + #endregion + + #region Dictionary Items + + /// + /// Imports and saves the 'DictionaryItems' part of the package xml as a list of + /// + /// Xml to import + /// + /// An enumerable list of dictionary items + public IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, int userId) + { + var languages = _localizationService.GetAllLanguages().ToList(); + return ImportDictionaryItems(dictionaryItemElementList, languages, null, userId); + } + + private IEnumerable ImportDictionaryItems(IEnumerable dictionaryItemElementList, List languages, Guid? parentId, int userId) + { + var items = new List(); + foreach (var dictionaryItemElement in dictionaryItemElementList) + items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, parentId, userId)); + + return items; + } + + private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, Guid? parentId, int userId) + { + var items = new List(); + + IDictionaryItem dictionaryItem; + var key = dictionaryItemElement.Attribute("Key").Value; + if (_localizationService.DictionaryItemExists(key)) + dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); + else + dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); + _localizationService.Save(dictionaryItem, userId); + items.Add(dictionaryItem); + + items.AddRange(ImportDictionaryItems(dictionaryItemElement.Elements("DictionaryItem"), languages, dictionaryItem.Key, userId)); + return items; + } + + private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) + { + var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); + var translations = dictionaryItem.Translations.ToList(); + foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) + AddDictionaryTranslation(translations, valueElement, languages); + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) + { + var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); + var translations = new List(); + + foreach (var valueElement in dictionaryItemElement.Elements("Value")) + AddDictionaryTranslation(translations, valueElement, languages); + + dictionaryItem.Translations = translations; + return dictionaryItem; + } + + private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) + { + return translations.All(t => + String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, + StringComparison.InvariantCultureIgnoreCase) != 0 + ); + } + + private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) + { + var languageId = valueElement.Attribute("LanguageCultureAlias").Value; + var language = languages.SingleOrDefault(l => l.IsoCode == languageId); + if (language == null) + return; + var translation = new DictionaryTranslation(language, valueElement.Value); + translations.Add(translation); + } + + #endregion + + #region Languages + + + /// + /// Imports and saves the 'Languages' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// An enumerable list of generated languages + public IEnumerable ImportLanguages(IEnumerable languageElements, int userId) + { + var list = new List(); + foreach (var languageElement in languageElements) + { + var isoCode = languageElement.AttributeValue("CultureAlias"); + var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); + if (existingLanguage != null) continue; + var langauge = new Language(isoCode) + { + CultureName = languageElement.AttributeValue("FriendlyName") + }; + _localizationService.Save(langauge, userId); + list.Add(langauge); + } + + return list; + } + + #endregion + + #region Macros + + /// + /// Imports and saves the 'Macros' part of a package xml as a list of + /// + /// Xml to import + /// Optional id of the User performing the operation + /// + public IEnumerable ImportMacros(IEnumerable macroElements, int userId) + { + var macros = macroElements.Select(ParseMacroElement).ToList(); + + foreach (var macro in macros) + { + var existing = _macroService.GetByAlias(macro.Alias); + if (existing != null) + macro.Id = existing.Id; + + _macroService.Save(macro, userId); + } + + return macros; + } + + private IMacro ParseMacroElement(XElement macroElement) + { + var macroName = macroElement.Element("name").Value; + var macroAlias = macroElement.Element("alias").Value; + var macroType = Enum.Parse(macroElement.Element("macroType").Value); + var macroSource = macroElement.Element("macroSource").Value; + + //Following xml elements are treated as nullable properties + var useInEditorElement = macroElement.Element("useInEditor"); + var useInEditor = false; + if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) + { + useInEditor = bool.Parse(useInEditorElement.Value); + } + var cacheDurationElement = macroElement.Element("refreshRate"); + var cacheDuration = 0; + if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) + { + cacheDuration = int.Parse(cacheDurationElement.Value); + } + var cacheByMemberElement = macroElement.Element("cacheByMember"); + var cacheByMember = false; + if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) + { + cacheByMember = bool.Parse(cacheByMemberElement.Value); + } + var cacheByPageElement = macroElement.Element("cacheByPage"); + var cacheByPage = false; + if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) + { + cacheByPage = bool.Parse(cacheByPageElement.Value); + } + var dontRenderElement = macroElement.Element("dontRender"); + var dontRender = true; + if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) + { + dontRender = bool.Parse(dontRenderElement.Value); + } + + var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; + var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, + cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); + + var properties = macroElement.Element("properties"); + if (properties != null) + { + int sortOrder = 0; + foreach (var property in properties.Elements()) + { + var propertyName = property.Attribute("name").Value; + var propertyAlias = property.Attribute("alias").Value; + var editorAlias = property.Attribute("propertyType").Value; + var sortOrderAttribute = property.Attribute("sortOrder"); + if (sortOrderAttribute != null) + { + sortOrder = int.Parse(sortOrderAttribute.Value); + } + + if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; + macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); + sortOrder++; + } + } + return macro; + } + + + + + + #endregion + + #region Stylesheets + + public IEnumerable ImportStylesheets(IEnumerable stylesheetElements, int userId) + { + var result = new List(); + + foreach (var n in stylesheetElements) + { + var stylesheetName = n.Element("Name")?.Value; + if (stylesheetName.IsNullOrWhiteSpace()) continue; + + var s = _fileService.GetStylesheetByName(stylesheetName); + if (s == null) + { + var fileName = n.Element("FileName")?.Value; + if (fileName == null) continue; + var content = n.Element("Content")?.Value; + if (content == null) continue; + + s = new Stylesheet(fileName) { Content = content }; + _fileService.SaveStylesheet(s); + } + + foreach (var prop in n.XPathSelectElements("Properties/Property")) + { + var alias = prop.Element("Alias")?.Value; + var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); + var name = prop.Element("Name")?.Value; + if (sp == null) + { + sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); + s.AddProperty(sp); + } + else + { + //sp.Text = name; + //Changing the name requires removing the current property and then adding another new one + if (sp.Name != name) + { + s.RemoveProperty(sp.Name); + var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); + s.AddProperty(newProp); + sp = newProp; + } + } + sp.Alias = alias; + sp.Value = prop.Element("Value")?.Value; + } + _fileService.SaveStylesheet(s); + result.Add(s); + } + + return result; + } + + #endregion + + #region Templates + + public IEnumerable ImportTemplate(XElement templateElement, int userId) + { + return ImportTemplates(new[] {templateElement}, userId); + } + + /// + /// Imports and saves package xml as + /// + /// Xml to import + /// Optional user id + /// An enumrable list of generated Templates + public IEnumerable ImportTemplates(IReadOnlyCollection templateElements, int userId) + { + var templates = new List(); + + var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); + + foreach (var tempElement in templateElements) + { + var dependencies = new List(); + var elementCopy = tempElement; + //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. + if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) + { + dependencies.Add((string)elementCopy.Element("Master")); + } + else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && + templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) + { + _logger.Info( + "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", + (string)elementCopy.Element("Alias"), + (string)elementCopy.Element("Master")); + } + + graph.AddItem(TopoGraph.CreateNode((string)elementCopy.Element("Alias"), elementCopy, dependencies)); + } + + //Sort templates by dependencies to a potential master template + var sorted = graph.GetSortedItems(); + foreach (var item in sorted) + { + var templateElement = item.Item; + + var templateName = templateElement.Element("Name").Value; + var alias = templateElement.Element("Alias").Value; + var design = templateElement.Element("Design").Value; + var masterElement = templateElement.Element("Master"); + + var isMasterPage = IsMasterPageSyntax(design); + var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); + + var existingTemplate = _fileService.GetTemplate(alias) as Template; + var template = existingTemplate ?? new Template(templateName, alias); + template.Content = design; + if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) + { + template.MasterTemplateAlias = masterElement.Value; + var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); + if (masterTemplate != null) + template.MasterTemplateId = new Lazy(() => masterTemplate.Id); + } + templates.Add(template); + } + + if (templates.Any()) + _fileService.SaveTemplate(templates, userId); + + return templates; + } + + + private bool IsMasterPageSyntax(string code) + { + return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || + code.InvariantContains(" + /// Converts a to and from XML + /// + 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("id"), + Name = xml.AttributeValue("name") ?? string.Empty, + PackagePath = xml.AttributeValue("packagePath") ?? string.Empty, + Version = xml.AttributeValue("version") ?? string.Empty, + Url = xml.AttributeValue("url") ?? string.Empty, + PackageId = xml.AttributeValue("packageGuid"), + IconUrl = xml.AttributeValue("iconUrl") ?? string.Empty, + UmbracoVersion = xml.AttributeValue("umbVersion"), + License = xml.Element("license")?.Value ?? string.Empty, + LicenseUrl = xml.Element("license")?.AttributeValue("url") ?? string.Empty, + Author = xml.Element("author")?.Value ?? string.Empty, + AuthorUrl = xml.Element("author")?.AttributeValue("url") ?? string.Empty, + Readme = xml.Element("readme")?.Value ?? string.Empty, + Actions = xml.Element("actions")?.ToString(SaveOptions.None) ?? "", //take the entire outer xml value + ContentNodeId = xml.Element("content")?.AttributeValue("nodeId") ?? string.Empty, + ContentLoadChildNodes = xml.Element("content")?.AttributeValue("loadChildNodes") ?? false, + Macros = xml.Element("macros")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Templates = xml.Element("templates")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Stylesheets = xml.Element("stylesheets")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DocumentTypes = xml.Element("documentTypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Languages = xml.Element("languages")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DictionaryItems = xml.Element("dictionaryitems")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + DataTypes = xml.Element("datatypes")?.Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList() ?? new List(), + Files = xml.Element("files")?.Elements("file").Select(x => x.Value).ToList() ?? new List(), + 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(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())), + + new XElement("content", + new XAttribute("nodeId", def.ContentNodeId ?? string.Empty), + new XAttribute("loadChildNodes", def.ContentLoadChildNodes)), + + new XElement("templates", string.Join(",", def.Templates ?? Array.Empty())), + new XElement("stylesheets", string.Join(",", def.Stylesheets ?? Array.Empty())), + new XElement("documentTypes", string.Join(",", def.DocumentTypes ?? Array.Empty())), + new XElement("macros", string.Join(",", def.Macros ?? Array.Empty())), + new XElement("files", (def.Files ?? Array.Empty()).Where(x => !x.IsNullOrWhiteSpace()).Select(x => new XElement("file", x))), + new XElement("languages", string.Join(",", def.Languages ?? Array.Empty())), + new XElement("dictionaryitems", string.Join(",", def.DictionaryItems ?? Array.Empty())), + new XElement("loadcontrol", def.Control ?? string.Empty)); //fixme: no more loadcontrol, needs to be an angular view + + return packageXml; + } + + + } +} diff --git a/src/Umbraco.Core/Packaging/PackageExtraction.cs b/src/Umbraco.Core/Packaging/PackageExtraction.cs index cc3c394732..48093da45f 100644 --- a/src/Umbraco.Core/Packaging/PackageExtraction.cs +++ b/src/Umbraco.Core/Packaging/PackageExtraction.cs @@ -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(fileInPackageName, destinationfilePath) } ); + CopyFilesFromArchive(packageFile, new[] {(fileInPackageName, destinationfilePath)}); } - public void CopyFilesFromArchive(string packageFilePath, IEnumerable> 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 FindMissingFiles(string packageFilePath, IEnumerable expectedFiles) + public IEnumerable FindMissingFiles(FileInfo packageFile, IEnumerable 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 FindDubletFileNames(string packageFilePath) + public IEnumerable FindDuplicateFileNames(FileInfo packageFile) { var dictionary = new Dictionary>(); - 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 list; - if (dictionary.TryGetValue(fileName, out list) == false) + if (dictionary.TryGetValue(fileName, out var list) == false) { list = new List(); 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 ReadFilesFromArchive(string packageFilePath, IEnumerable filesToGet) + public IEnumerable ReadFilesFromArchive(FileInfo packageFile, IEnumerable filesToGet) { - CheckPackageExists(packageFilePath); + CheckPackageExists(packageFile); var files = new HashSet(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 entryFunc, bool skipsDirectories = true) + private void ReadZipfileEntries(FileInfo packageFile, Func 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) diff --git a/src/Umbraco.Core/Packaging/PackageFileInstallation.cs b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs new file mode 100644 index 0000000000..7c0891175b --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageFileInstallation.cs @@ -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 +{ + /// + /// Installs package files + /// + 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(); + } + + /// + /// Returns a list of all installed file paths + /// + /// + /// + /// + /// The absolute path of where to extract the package files (normally the application root) + /// + /// + public IEnumerable InstallFiles(CompiledPackage compiledPackage, FileInfo packageFile, string targetRootFolder) + { + using (_logger.DebugDuration( + "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 UninstallFiles(PackageDefinition package) + { + var removedFiles = new List(); + + 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(); + } + } +} diff --git a/src/Umbraco.Core/Packaging/PackageInstallType.cs b/src/Umbraco.Core/Packaging/PackageInstallType.cs new file mode 100644 index 0000000000..015b994aec --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackageInstallType.cs @@ -0,0 +1,9 @@ +namespace Umbraco.Core.Packaging +{ + public enum PackageInstallType + { + AlreadyInstalled, + NewInstall, + Upgrade + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Packaging/PackageInstallation.cs b/src/Umbraco.Core/Packaging/PackageInstallation.cs index 556c5203ec..d791295b38 100644 --- a/src/Umbraco.Core/Packaging/PackageInstallation.cs +++ b/src/Umbraco.Core/Packaging/PackageInstallation.cs @@ -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(dataTypes) ?? InstallDataTypes(dataTypes, userId); - installationSummary.DataTypesInstalled = dataTypeDefinitions; - - var languagesInstalled = EmptyEnumerableIfNull(languages) ?? InstallLanguages(languages, userId); - installationSummary.LanguagesInstalled = languagesInstalled; - - var dictionaryInstalled = EmptyEnumerableIfNull(dictionaryItems) ?? InstallDictionaryItems(dictionaryItems); - installationSummary.DictionaryItemsInstalled = dictionaryInstalled; - - var macros = EmptyEnumerableIfNull(macroes) ?? InstallMacros(macroes, userId); - installationSummary.MacrosInstalled = macros; - - var keyValuePairs = EmptyEnumerableIfNull(packageFile) ?? InstallFiles(packageFile, files); - installationSummary.FilesInstalled = keyValuePairs; - - var templatesInstalled = EmptyEnumerableIfNull(templates) ?? InstallTemplats(templates, userId); - installationSummary.TemplatesInstalled = templatesInstalled; - - var documentTypesInstalled = EmptyEnumerableIfNull(documentTypes) ?? InstallDocumentTypes(documentTypes, userId); - installationSummary.ContentTypesInstalled =documentTypesInstalled; - - var stylesheetsInstalled = EmptyEnumerableIfNull(styleSheets) ?? InstallStylesheets(styleSheets); - installationSummary.StylesheetsInstalled = stylesheetsInstalled; - - var documentsInstalled = documents != null ? InstallDocuments(documents, userId) - : EmptyEnumerableIfNull(documentSet) - ?? InstallDocuments(documentSet, userId); - installationSummary.ContentInstalled = documentsInstalled; - - var packageActions = EmptyEnumerableIfNull(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; /// - /// Temperary check to test that we support stylesheets + /// Constructor /// - /// - private void PackageSupportedCheck(XElement rootElement) + /// + /// + /// + /// + /// + /// The root folder of the application + /// + 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(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 EmptyEnumerableIfNull(object obj) + public IEnumerable InstallPackageFiles(PackageDefinition packageDefinition, CompiledPackage compiledPackage, int userId) { - return obj == null ? Enumerable.Empty() : 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) + /// + 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 RunPackageActions(PackageDefinition packageDefinition, IEnumerable 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 UndoPackageActions(IPackageInfo packageDefinition, IEnumerable 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 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 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 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 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 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(); } - - throw new NotImplementedException("The packaging service do not yes have a method for importing stylesheets"); - } - - private IEnumerable 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 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 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[] AppendRootToDestination(string fullpathToRoot, IEnumerable> sourceDestination) - { - return - sourceDestination.Select( - sd => new KeyValuePair(sd.Key, Path.Combine(fullpathToRoot, sd.Value))).ToArray(); - } - - private IEnumerable 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 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 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 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>(files) ?? ExtractSourceDestinationFileInformation(files); - - var installWarnings = new PreInstallWarnings(); - - var macroAliases = EmptyEnumerableIfNull(alias) ?? ConflictingPackageData.FindConflictingMacros(alias); - installWarnings.ConflictingMacroAliases = macroAliases; - - var templateAliases = EmptyEnumerableIfNull(templates) ?? ConflictingPackageData.FindConflictingTemplates(templates); - installWarnings.ConflictingTemplateAliases = templateAliases; - - var stylesheetNames = EmptyEnumerableIfNull(styleSheets) ?? ConflictingPackageData.FindConflictingStylesheets(styleSheets); - installWarnings.ConflictingStylesheetNames = stylesheetNames; - - installWarnings.UnsecureFiles = FindUnsecureFiles(sourceDestination); - installWarnings.FilesReplaced = FindFilesToBeReplaced(sourceDestination); - - return installWarnings; - } - - private KeyValuePair[] FindFilesToBeReplaced(IEnumerable> sourceDestination) - { - return sourceDestination.Where(sd => File.Exists(Path.Combine(FullPathToRoot, sd.Value))).ToArray(); - } - - private KeyValuePair[] FindUnsecureFiles(IEnumerable> 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[] 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(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(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; - } + + } } diff --git a/src/Umbraco.Core/Packaging/PackagesRepository.cs b/src/Umbraco.Core/Packaging/PackagesRepository.cs new file mode 100644 index 0000000000..249d02a320 --- /dev/null +++ b/src/Umbraco.Core/Packaging/PackagesRepository.cs @@ -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 +{ + /// + /// Manages the storage of installed/created package definitions + /// + 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; + + /// + /// Constructor + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// The file name for storing the package definitions (i.e. "createdPackages.config") + /// + /// + /// + /// + 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 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("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("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("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("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 ... and we want the child nodes + var parsed = XElement.Parse(definition.Actions); + actionsXml.Add(parsed.Elements()); + manifestRoot.Add(actionsXml); + } + catch (Exception e) + { + _logger.Warn(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(); + 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(); + 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() + // .Select(x => x.Value.TryConvertTo()) + // .Where(x => x.Success) + // .Select(x => x.Result) + // .ToArray(); + //var allContentTags = new List(); + //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(); + //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); + } + } + } + } + + /// + /// Zips the package. + /// + /// The path. + /// The save path. + private static void ZipPackage(string path, string savePath) + { + if (File.Exists(savePath)) + File.Delete(savePath); + ZipFile.CreateFromDirectory(path, savePath); + } + + /// + /// Appends a file to package manifest and copies the file to the correct folder. + /// + /// The path. + /// The package directory. + /// The files xml node + 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; + } + + /// + /// Converts a umbraco stylesheet to a package xml node + /// + /// The name of the stylesheet. + /// if set to true [incluce properties]. + /// + 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 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; + } + + + + + + } +} diff --git a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs index d930abc54c..4f3a67aa91 100644 --- a/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs +++ b/src/Umbraco.Core/Persistence/Dtos/ContentTypeDto.cs @@ -41,6 +41,10 @@ namespace Umbraco.Core.Persistence.Dtos [Constraint(Default = "0")] public bool IsContainer { get; set; } + [Column("isElement")] + [Constraint(Default = "0")] + public bool IsElement { get; set; } + [Column("allowAtRoot")] [Constraint(Default = "0")] public bool AllowAtRoot { get; set; } diff --git a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs index 38a1aa2aab..7a04a6d0d9 100644 --- a/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs +++ b/src/Umbraco.Core/Persistence/Factories/ContentTypeFactory.cs @@ -107,6 +107,7 @@ namespace Umbraco.Core.Persistence.Factories entity.CreatorId = dto.NodeDto.UserId ?? Constants.Security.UnknownUserId; entity.AllowedAsRoot = dto.AllowAtRoot; entity.IsContainer = dto.IsContainer; + entity.IsElement = dto.IsElement; entity.Trashed = dto.NodeDto.Trashed; entity.Variations = (ContentVariation) dto.Variations; } @@ -132,6 +133,7 @@ namespace Umbraco.Core.Persistence.Factories NodeId = entity.Id, AllowAtRoot = entity.AllowedAsRoot, IsContainer = entity.IsContainer, + IsElement = entity.IsElement, Variations = (byte) entity.Variations, NodeDto = BuildNodeDto(entity, nodeObjectType) }; diff --git a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs index 9ef320e187..0236fc4bd5 100644 --- a/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/IUmbracoDatabaseFactory.cs @@ -18,6 +18,12 @@ namespace Umbraco.Core.Persistence /// bool Configured { get; } + /// + /// Gets the connection string. + /// + /// Throws if the factory is not configured. + string ConnectionString { get; } + /// /// Gets a value indicating whether the database can connect. /// diff --git a/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs index c692a75474..a24963bace 100644 --- a/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/ContentTypeMapper.cs @@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Description, dto => dto.Description); CacheMap(src => src.Icon, dto => dto.Icon); CacheMap(src => src.IsContainer, dto => dto.IsContainer); + CacheMap(src => src.IsElement, dto => dto.IsElement); CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); } } diff --git a/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs index 3f5a6e24bc..6cf83bc7aa 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MediaTypeMapper.cs @@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Description, dto => dto.Description); CacheMap(src => src.Icon, dto => dto.Icon); CacheMap(src => src.IsContainer, dto => dto.IsContainer); + CacheMap(src => src.IsElement, dto => dto.IsElement); CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); } } diff --git a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs index 28dc19171f..9a4e4ec040 100644 --- a/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs +++ b/src/Umbraco.Core/Persistence/Mappers/MemberTypeMapper.cs @@ -35,6 +35,7 @@ namespace Umbraco.Core.Persistence.Mappers CacheMap(src => src.Description, dto => dto.Description); CacheMap(src => src.Icon, dto => dto.Icon); CacheMap(src => src.IsContainer, dto => dto.IsContainer); + CacheMap(src => src.IsElement, dto => dto.IsElement); CacheMap(src => src.Thumbnail, dto => dto.Thumbnail); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs index 460bed71d3..57d3871e5a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDataTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IDataTypeContainerRepository : IEntityContainerRepository + public interface IDataTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs index ccf8df268d..ec8dfb9110 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IDocumentTypeContainerRepository : IEntityContainerRepository + public interface IDocumentTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs index 47f0cee52b..f1c8353a0d 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IEntityContainerRepository.cs @@ -2,6 +2,6 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IEntityContainerRepository : IReadRepository, IWriteRepository + public interface IEntityContainerRepository : IReadRepository, IWriteRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs index eefb48933e..1ed08352ed 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IMacroRepository.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Persistence.Repositories { - internal interface IMacroRepository : IReadWriteQueryRepository, IReadRepository + public interface IMacroRepository : IReadWriteQueryRepository, IReadRepository { //IEnumerable GetAll(params string[] aliases); diff --git a/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs index a1872df0cd..6a133c053a 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IMediaTypeContainerRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IMediaTypeContainerRepository : IEntityContainerRepository + public interface IMediaTypeContainerRepository : IEntityContainerRepository { } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs index 662254d1ee..683df047f8 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/ContentTypeRepositoryBase.cs @@ -1283,7 +1283,7 @@ AND umbracoNode.id <> @id", if (db == null) throw new ArgumentNullException(nameof(db)); var sql = @"SELECT cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations, - cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, + cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias, ParentTypes.parentContentTypeId as chtParentId, ParentTypes.parentContentTypeKey as chtParentKey, umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser, @@ -1384,6 +1384,7 @@ AND umbracoNode.id <> @id", Description = currCt.ctDesc, Icon = currCt.ctIcon, IsContainer = currCt.ctIsContainer, + IsElement = currCt.ctIsElement, NodeId = currCt.ctId, PrimaryKey = currCt.ctPk, Thumbnail = currCt.ctThumb, @@ -1422,7 +1423,7 @@ AND umbracoNode.id <> @id", var sql = @"SELECT cmsDocumentType.IsDefault as dtIsDefault, cmsDocumentType.templateNodeId as dtTemplateId, cmsContentType.pk as ctPk, cmsContentType.alias as ctAlias, cmsContentType.allowAtRoot as ctAllowAtRoot, cmsContentType.description as ctDesc, cmsContentType.variations as ctVariations, - cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, + cmsContentType.icon as ctIcon, cmsContentType.isContainer as ctIsContainer, cmsContentType.IsElement as ctIsElement, cmsContentType.nodeId as ctId, cmsContentType.thumbnail as ctThumb, AllowedTypes.AllowedId as ctaAllowedId, AllowedTypes.SortOrder as ctaSortOrder, AllowedTypes.alias as ctaAlias, ParentTypes.parentContentTypeId as chtParentId,ParentTypes.parentContentTypeKey as chtParentKey, umbracoNode.createDate as nCreateDate, umbracoNode." + sqlSyntax.GetQuotedColumnName("level") + @" as nLevel, umbracoNode.nodeObjectType as nObjectType, umbracoNode.nodeUser as nUser, @@ -1559,6 +1560,7 @@ AND umbracoNode.id <> @id", Description = currCt.ctDesc, Icon = currCt.ctIcon, IsContainer = currCt.ctIsContainer, + IsElement = currCt.ctIsElement, NodeId = currCt.ctId, PrimaryKey = currCt.ctPk, Thumbnail = currCt.ctThumb, diff --git a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs index c9a509fe94..9ed52ca148 100644 --- a/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs +++ b/src/Umbraco.Core/Persistence/UmbracoDatabaseFactory.cs @@ -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 /// public bool Configured { get; private set; } + /// + public string ConnectionString + { + get + { + EnsureConfigured(); + return _connectionString; + } + } + /// public bool CanConnect { diff --git a/src/Umbraco.Core/Runtime/CoreRuntime.cs b/src/Umbraco.Core/Runtime/CoreRuntime.cs index cb6a1cb031..729e960896 100644 --- a/src/Umbraco.Core/Runtime/CoreRuntime.cs +++ b/src/Umbraco.Core/Runtime/CoreRuntime.cs @@ -252,7 +252,7 @@ namespace Umbraco.Core.Runtime { _state.DetermineRuntimeLevel(databaseFactory, profilingLogger); - profilingLogger.Debug("Runtime level: {RuntimeLevel}", _state.Level); + profilingLogger.Debug("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; } diff --git a/src/Umbraco.Core/RuntimeLevel.cs b/src/Umbraco.Core/RuntimeLevel.cs index f9ec3c90c5..2645e89226 100644 --- a/src/Umbraco.Core/RuntimeLevel.cs +++ b/src/Umbraco.Core/RuntimeLevel.cs @@ -1,5 +1,8 @@ namespace Umbraco.Core { + /// + /// Describes the levels in which the runtime can run. + /// public enum RuntimeLevel { /// diff --git a/src/Umbraco.Core/RuntimeLevelReason.cs b/src/Umbraco.Core/RuntimeLevelReason.cs new file mode 100644 index 0000000000..c10ac8b206 --- /dev/null +++ b/src/Umbraco.Core/RuntimeLevelReason.cs @@ -0,0 +1,68 @@ +namespace Umbraco.Core +{ + /// + /// Describes the reason for the runtime level. + /// + public enum RuntimeLevelReason + { + /// + /// The code version is lower than the version indicated in web.config, and + /// downgrading Umbraco is not supported. + /// + BootFailedCannotDowngrade, + + /// + /// The runtime cannot connect to the configured database. + /// + BootFailedCannotConnectToDatabase, + + /// + /// The runtime can connect to the configured database, but it cannot + /// retrieve the migrations status. + /// + BootFailedCannotCheckUpgradeState, + + /// + /// An exception was thrown during boot. + /// + BootFailedOnException, + + /// + /// Umbraco is not installed at all. + /// + InstallNoVersion, + + /// + /// A version is specified in web.config but the database is not configured. + /// + /// This is a weird state. + InstallNoDatabase, + + /// + /// 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. + /// + InstallMissingDatabase, + + /// + /// 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. + /// + InstallEmptyDatabase, + + /// + /// Umbraco runs an old version. + /// + UpgradeOldVersion, + + /// + /// Umbraco runs the current version but some migrations have not run. + /// + UpgradeMigrations, + + /// + /// Umbraco is running. + /// + Run + } +} diff --git a/src/Umbraco.Core/RuntimeState.cs b/src/Umbraco.Core/RuntimeState.cs index df2ee44a7d..85e8c7370d 100644 --- a/src/Umbraco.Core/RuntimeState.cs +++ b/src/Umbraco.Core/RuntimeState.cs @@ -93,6 +93,9 @@ namespace Umbraco.Core internal set { _level = value; if (value == RuntimeLevel.Run) _runLevel.Set(); } } + /// + public RuntimeLevelReason Reason { get; internal set; } + /// /// Ensures that the property has a value. /// @@ -143,6 +146,7 @@ namespace Umbraco.Core // there is no local version, we are not installed logger.Debug("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("Local version '{LocalVersion}' < code version '{CodeVersion}', need to upgrade Umbraco.", localVersion, codeVersion); Level = RuntimeLevel.Upgrade; + Reason = RuntimeLevelReason.UpgradeOldVersion; } else if (localVersion > codeVersion) { logger.Warn("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("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("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("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(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("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) diff --git a/src/Umbraco.Core/RuntimeStateOptions.cs b/src/Umbraco.Core/RuntimeStateOptions.cs new file mode 100644 index 0000000000..9262a8a990 --- /dev/null +++ b/src/Umbraco.Core/RuntimeStateOptions.cs @@ -0,0 +1,40 @@ +using System.Configuration; + +namespace Umbraco.Core +{ + /// + /// Allows configuration of the in PreApplicationStart or in appSettings + /// + public static class RuntimeStateOptions + { + // configured statically or via app settings + private static bool BoolSetting(string key, bool missing) => ConfigurationManager.AppSettings[key]?.InvariantEquals("true") ?? missing; + + /// + /// If true the RuntimeState will continue the installation sequence when a database is missing + /// + /// + /// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence + /// + public static bool InstallMissingDatabase + { + get => _installEmptyDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallMissingDatabase", false); + set => _installEmptyDatabase = value; + } + + /// + /// If true the RuntimeState will continue the installation sequence when a database is available but is empty + /// + /// + /// In this case it will be up to the implementor that is setting this value to true to take over the bootup/installation sequence + /// + public static bool InstallEmptyDatabase + { + get => _installMissingDatabase ?? BoolSetting("Umbraco.Core.RuntimeState.InstallEmptyDatabase", false); + set => _installMissingDatabase = value; + } + + private static bool? _installMissingDatabase; + private static bool? _installEmptyDatabase; + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs b/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs index d67f8f9200..57e1d288e4 100644 --- a/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs +++ b/src/Umbraco.Core/Services/Changes/ContentTypeChange.cs @@ -4,7 +4,7 @@ using Umbraco.Core.Models; namespace Umbraco.Core.Services.Changes { - internal class ContentTypeChange + public class ContentTypeChange where TItem : class, IContentTypeComposition { public ContentTypeChange(TItem item, ContentTypeChangeTypes changeTypes) diff --git a/src/Umbraco.Core/Services/IEntityXmlSerializer.cs b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs new file mode 100644 index 0000000000..405fc47c3a --- /dev/null +++ b/src/Umbraco.Core/Services/IEntityXmlSerializer.cs @@ -0,0 +1,88 @@ +using System.Collections.Generic; +using System.Xml.Linq; +using Umbraco.Core.Models; + +namespace Umbraco.Core.Services +{ + /// + /// Serializes entities to XML + /// + public interface IEntityXmlSerializer + { + /// + /// Exports an IContent item as an XElement. + /// + XElement Serialize(IContent content, + bool published, + bool withDescendants = false) //fixme take care of usage! only used for the packager + ; + + /// + /// Exports an IMedia item as an XElement. + /// + XElement Serialize( + IMedia media, + bool withDescendants = false); + + /// + /// Exports an IMember item as an XElement. + /// + XElement Serialize(IMember member); + + /// + /// Exports a list of Data Types + /// + /// List of data types to export + /// containing the xml representation of the IDataTypeDefinition objects + XElement Serialize(IEnumerable dataTypeDefinitions); + + XElement Serialize(IDataType dataType); + + /// + /// Exports a list of items to xml as an + /// + /// List of dictionary items to export + /// Optional boolean indicating whether or not to include children + /// containing the xml representation of the IDictionaryItem objects + XElement Serialize(IEnumerable dictionaryItem, bool includeChildren = true); + + /// + /// Exports a single item to xml as an + /// + /// Dictionary Item to export + /// Optional boolean indicating whether or not to include children + /// containing the xml representation of the IDictionaryItem object + XElement Serialize(IDictionaryItem dictionaryItem, bool includeChildren); + + XElement Serialize(Stylesheet stylesheet); + + /// + /// Exports a list of items to xml as an + /// + /// List of Languages to export + /// containing the xml representation of the ILanguage objects + XElement Serialize(IEnumerable languages); + + XElement Serialize(ILanguage language); + XElement Serialize(ITemplate template); + + /// + /// Exports a list of items to xml as an + /// + /// + /// + XElement Serialize(IEnumerable templates); + + XElement Serialize(IMediaType mediaType); + + /// + /// Exports a list of items to xml as an + /// + /// Macros to export + /// containing the xml representation of the IMacro objects + XElement Serialize(IEnumerable macros); + + XElement Serialize(IMacro macro); + XElement Serialize(IContentType contentType); + } +} diff --git a/src/Umbraco.Core/Services/IPackagingService.cs b/src/Umbraco.Core/Services/IPackagingService.cs index ceab6e94bf..c14882fe7a 100644 --- a/src/Umbraco.Core/Services/IPackagingService.cs +++ b/src/Umbraco.Core/Services/IPackagingService.cs @@ -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 { - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true); + #region Package Installation /// - /// Imports and saves package xml as + /// Returns a result from an umbraco package file (zip) /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true); - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true); - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events + /// /// - IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true); + CompiledPackage GetCompiledPackageInfo(FileInfo packageFile); /// - /// Imports and saves package xml as + /// Installs the package files contained in an umbraco package file (zip) /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin) - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true); + /// + /// + /// + IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); /// - /// Exports an to xml as an + /// Installs the data, entities, objects contained in an umbraco package file (zip) /// - /// ContentType to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ContentType item - XElement Export(IContentType contentType, bool raiseEvents = true); + /// + /// + /// + InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0); /// - /// Exports an item to xml as an + /// Uninstalls all versions of the package by name /// - /// Content to export - /// Optional parameter indicating whether to include descendents - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Content object - XElement Export(IContent content, bool deep = false, bool raiseEvents = true); + /// + /// + /// + UninstallationSummary UninstallPackage(string packageName, int userId = 0); + + #endregion + + #region Installed Packages + + IEnumerable GetAllInstalledPackages(); /// - /// Exports an item to xml as an + /// Returns the for the installation id /// - /// Media to export - /// Optional parameter indicating whether to include descendents - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Media object - XElement Export(IMedia media, bool deep = false, bool raiseEvents = true); + /// + /// + PackageDefinition GetInstalledPackageById(int id); /// - /// Exports a list of items to xml as an + /// Returns all for the package by name /// - /// List of Languages to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Language object - XElement Export(IEnumerable languages, bool raiseEvents = true); + /// + /// + /// A list of all package definitions installed for this package (i.e. original install and any upgrades) + /// + IEnumerable GetInstalledPackageByName(string name); /// - /// Exports a single item to xml as an + /// Returns a for a given package name and version /// - /// Language to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Language object - XElement Export(ILanguage language, bool raiseEvents = true); + /// + /// + /// If the package is an upgrade, the original/current PackageDefinition is returned + /// + PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled); + void DeleteInstalledPackage(int packageId, int userId = 0); /// - /// Exports a list of items to xml as an + /// Persists a package definition to storage /// - /// List of dictionary items to export - /// Optional boolean indicating whether or not to include children - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDictionaryItem objects - XElement Export(IEnumerable dictionaryItem, bool includeChildren = true, bool raiseEvents = true); + /// + bool SaveInstalledPackage(PackageDefinition definition); + + #endregion + + #region Created Packages + + IEnumerable GetAllCreatedPackages(); + PackageDefinition GetCreatedPackageById(int id); + void DeleteCreatedPackage(int id, int userId = 0); /// - /// Exports a single item to xml as an + /// Persists a package definition to storage /// - /// Dictionary Item to export - /// Optional boolean indicating whether or not to include children - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDictionaryItem object - XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true); + /// + bool SaveCreatedPackage(PackageDefinition definition); /// - /// Exports a list of Data Types + /// Creates the package file and returns it's physical path /// - /// List of data types to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDataTypeDefinition objects - XElement Export(IEnumerable dataTypeDefinitions, bool raiseEvents = true); + /// + string ExportCreatedPackage(PackageDefinition definition); + + #endregion /// - /// Exports a single Data Type - /// - /// Data type to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDataTypeDefinition object - XElement Export(IDataType dataType, bool raiseEvents = true); - - /// - /// Exports a list of items to xml as an - /// - /// List of Templates to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ITemplate objects - XElement Export(IEnumerable templates, bool raiseEvents = true); - - /// - /// Exports a single item to xml as an - /// - /// Template to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ITemplate object - XElement Export(ITemplate template, bool raiseEvents = true); - - /// - /// Exports a list of items to xml as an - /// - /// Macros to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IMacro objects - XElement Export(IEnumerable macros, bool raiseEvents = true); - - /// - /// Exports a single item to xml as an - /// - /// Macro to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IMacro object - XElement Export(IMacro macro, bool raiseEvents = true); - - /// - /// 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 /// /// /// /// The current user id performing the operation - /// - string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId); + /// + /// The file name of the downloaded package which will exist in ~/App_Data/packages + /// + Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId); } } diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeService.cs index f6498770ec..fa818496ff 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeService.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the ContentType Service, which is an easy access to operations involving /// - internal class ContentTypeService : ContentTypeServiceBase, IContentTypeService + public class ContentTypeService : ContentTypeServiceBase, IContentTypeService { public ContentTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IContentService contentService, IContentTypeRepository repository, IAuditRepository auditRepository, IDocumentTypeContainerRepository entityContainerRepository, IEntityRepository entityRepository) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs index d5cdd36318..26298f171c 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBase.cs @@ -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) diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs index 33fb9a0894..f4457e0991 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTItemTService.cs @@ -6,7 +6,7 @@ using Umbraco.Core.Services.Changes; namespace Umbraco.Core.Services.Implement { - internal abstract class ContentTypeServiceBase : ContentTypeServiceBase + public abstract class ContentTypeServiceBase : ContentTypeServiceBase where TItem : class, IContentTypeComposition where TService : class, IContentTypeServiceBase { diff --git a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs index be4f719bb1..8189c6524e 100644 --- a/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentTypeServiceBaseOfTRepositoryTItemTService.cs @@ -13,7 +13,7 @@ using Umbraco.Core.Services.Changes; namespace Umbraco.Core.Services.Implement { - internal abstract class ContentTypeServiceBase : ContentTypeServiceBase, IContentTypeServiceBase + public abstract class ContentTypeServiceBase : ContentTypeServiceBase, IContentTypeServiceBase where TRepository : IContentTypeRepositoryBase where TItem : class, IContentTypeComposition where TService : class, IContentTypeServiceBase diff --git a/src/Umbraco.Core/Services/Implement/DataTypeService.cs b/src/Umbraco.Core/Services/Implement/DataTypeService.cs index 79ca851de9..84d44649da 100644 --- a/src/Umbraco.Core/Services/Implement/DataTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/DataTypeService.cs @@ -16,7 +16,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the DataType Service, which is an easy access to operations involving /// - internal class DataTypeService : ScopeRepositoryService, IDataTypeService + public class DataTypeService : ScopeRepositoryService, IDataTypeService { private readonly IDataTypeRepository _dataTypeRepository; private readonly IDataTypeContainerRepository _dataTypeContainerRepository; diff --git a/src/Umbraco.Core/Services/Implement/EntityService.cs b/src/Umbraco.Core/Services/Implement/EntityService.cs index 4a3db29940..37b569b814 100644 --- a/src/Umbraco.Core/Services/Implement/EntityService.cs +++ b/src/Umbraco.Core/Services/Implement/EntityService.cs @@ -258,6 +258,9 @@ namespace Umbraco.Core.Services.Implement public virtual IEnumerable 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)) diff --git a/src/Umbraco.Core/Services/EntityXmlSerializer.cs b/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs similarity index 70% rename from src/Umbraco.Core/Services/EntityXmlSerializer.cs rename to src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs index d938e032a8..fd1067b3e5 100644 --- a/src/Umbraco.Core/Services/EntityXmlSerializer.cs +++ b/src/Umbraco.Core/Services/Implement/EntityXmlSerializer.cs @@ -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! - /// - /// A helper class to serialize entities to XML + /// Serializes entities to XML /// - internal class EntityXmlSerializer + internal class EntityXmlSerializer : IEntityXmlSerializer { - /// - /// Exports an IContent item as an XElement. - /// - 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 urlSegmentProviders, - IContent content, + IContentTypeService contentTypeService, + UrlSegmentProviderCollection urlSegmentProviders) + { + _contentTypeService = contentTypeService; + _mediaService = mediaService; + _contentService = contentService; + _dataTypeService = dataTypeService; + _userService = userService; + _localizationService = localizationService; + _urlSegmentProviders = urlSegmentProviders; + } + + /// + /// Exports an IContent item as an XElement. + /// + 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 /// /// Exports an IMedia item as an XElement. /// - public static XElement Serialize( - IMediaService mediaService, - IDataTypeService dataTypeService, - IUserService userService, - ILocalizationService localizationService, - IEnumerable 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 /// /// Exports an IMember item as an XElement. /// - 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) + /// + /// Exports a list of Data Types + /// + /// List of data types to export + /// containing the xml representation of the IDataTypeDefinition objects + public XElement Serialize(IEnumerable 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) + /// + /// Exports a list of items to xml as an + /// + /// List of dictionary items to export + /// Optional boolean indicating whether or not to include children + /// containing the xml representation of the IDictionaryItem objects + public XElement Serialize(IEnumerable dictionaryItem, bool includeChildren = true) + { + var xml = new XElement("DictionaryItems"); + foreach (var item in dictionaryItem) + { + xml.Add(Serialize(item, includeChildren)); + } + return xml; + } + + /// + /// Exports a single item to xml as an + /// + /// Dictionary Item to export + /// Optional boolean indicating whether or not to include children + /// containing the xml representation of the IDictionaryItem object + 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; } + /// + /// Exports a list of items to xml as an + /// + /// List of Languages to export + /// containing the xml representation of the ILanguage objects + public XElement Serialize(IEnumerable 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) + /// + /// Exports a list of items to xml as an + /// + /// + /// + public XElement Serialize(IEnumerable 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; } + /// + /// Exports a list of items to xml as an + /// + /// Macros to export + /// containing the xml representation of the IMacro objects + public XElement Serialize(IEnumerable 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), @@ -337,7 +439,8 @@ namespace Umbraco.Core.Services new XElement("Thumbnail", contentType.Thumbnail), new XElement("Description", contentType.Description), new XElement("AllowAtRoot", contentType.AllowedAsRoot.ToString()), - new XElement("IsListView", contentType.IsContainer.ToString())); + new XElement("IsListView", contentType.IsContainer.ToString()), + new XElement("IsElement", contentType.IsElement.ToString())); var masterContentType = contentType.ContentTypeComposition.FirstOrDefault(x => x.Id == contentType.ParentId); if(masterContentType != null) @@ -372,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 @@ -413,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)); @@ -427,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), @@ -444,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 SerializeProperty(IDataTypeService dataTypeService, ILocalizationService localizationService, Property property, bool published) + private IEnumerable SerializeProperty(Property property, bool published) { var propertyType = property.PropertyType; @@ -458,16 +561,16 @@ namespace Umbraco.Core.Services var propertyEditor = Current.PropertyEditors[propertyType.PropertyEditorAlias]; return propertyEditor == null ? Array.Empty() - : 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 urlSegmentProviders, IEnumerable children, XElement xml, bool published) + private void SerializeChildren(IEnumerable 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; @@ -475,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 urlSegmentProviders, IEnumerable children, XElement xml) + private void SerializeChildren(IEnumerable 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; @@ -496,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); } } } diff --git a/src/Umbraco.Core/Services/Implement/MacroService.cs b/src/Umbraco.Core/Services/Implement/MacroService.cs index 5176e2eb22..d4f2d95bbb 100644 --- a/src/Umbraco.Core/Services/Implement/MacroService.cs +++ b/src/Umbraco.Core/Services/Implement/MacroService.cs @@ -12,7 +12,7 @@ namespace Umbraco.Core.Services.Implement /// /// Represents the Macro Service, which is an easy access to operations involving /// - internal class MacroService : ScopeRepositoryService, IMacroService + public class MacroService : ScopeRepositoryService, IMacroService { private readonly IMacroRepository _macroRepository; private readonly IAuditRepository _auditRepository; diff --git a/src/Umbraco.Core/Services/Implement/MediaTypeService.cs b/src/Umbraco.Core/Services/Implement/MediaTypeService.cs index fe8c78e137..8cb69a655d 100644 --- a/src/Umbraco.Core/Services/Implement/MediaTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/MediaTypeService.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { - internal class MediaTypeService : ContentTypeServiceBase, IMediaTypeService + public class MediaTypeService : ContentTypeServiceBase, IMediaTypeService { public MediaTypeService(IScopeProvider provider, ILogger logger, IEventMessagesFactory eventMessagesFactory, IMediaService mediaService, IMediaTypeRepository mediaTypeRepository, IAuditRepository auditRepository, IMediaTypeContainerRepository entityContainerRepository, diff --git a/src/Umbraco.Core/Services/Implement/MemberTypeService.cs b/src/Umbraco.Core/Services/Implement/MemberTypeService.cs index 6cc7d03cfb..05f32dc99c 100644 --- a/src/Umbraco.Core/Services/Implement/MemberTypeService.cs +++ b/src/Umbraco.Core/Services/Implement/MemberTypeService.cs @@ -8,7 +8,7 @@ using Umbraco.Core.Scoping; namespace Umbraco.Core.Services.Implement { - internal class MemberTypeService : ContentTypeServiceBase, IMemberTypeService + public class MemberTypeService : ContentTypeServiceBase, IMemberTypeService { private readonly IMemberTypeRepository _memberTypeRepository; diff --git a/src/Umbraco.Core/Services/Implement/PackagingService.cs b/src/Umbraco.Core/Services/Implement/PackagingService.cs index 106d2b9f12..24ef818624 100644 --- a/src/Umbraco.Core/Services/Implement/PackagingService.cs +++ b/src/Umbraco.Core/Services/Implement/PackagingService.cs @@ -1,15 +1,14 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; +using System.Threading.Tasks; using System.Web; using System.Xml.Linq; +using Semver; using Umbraco.Core.Collections; -using Umbraco.Core.Configuration; -using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.Exceptions; using Umbraco.Core.IO; @@ -18,8 +17,6 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.Entities; using Umbraco.Core.Models.Packaging; using Umbraco.Core.Packaging; -using Umbraco.Core.Packaging.Models; -using Umbraco.Core.Persistence.Dtos; using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Repositories; using Umbraco.Core.PropertyEditors; @@ -35,1855 +32,227 @@ namespace Umbraco.Core.Services.Implement /// public class PackagingService : IPackagingService { - private readonly ILogger _logger; - private readonly IContentService _contentService; - private readonly IContentTypeService _contentTypeService; - private readonly IMediaService _mediaService; - private readonly IMacroService _macroService; - private readonly IDataTypeService _dataTypeService; - private readonly IFileService _fileService; - private readonly ILocalizationService _localizationService; - private readonly IEntityService _entityService; - private readonly IScopeProvider _scopeProvider; - private readonly IEnumerable _urlSegmentProviders; - private Dictionary _importedContentTypes; - private IPackageInstallation _packageInstallation; - private readonly IUserService _userService; - private readonly IAuditRepository _auditRepository; - private readonly IContentTypeRepository _contentTypeRepository; - private readonly PropertyEditorCollection _propertyEditors; + + private readonly IPackageInstallation _packageInstallation; + private readonly IAuditService _auditService; + private readonly ICreatedPackagesRepository _createdPackages; + private readonly IInstalledPackagesRepository _installedPackages; private static HttpClient _httpClient; public PackagingService( - ILogger logger, - IContentService contentService, - IContentTypeService contentTypeService, - IMediaService mediaService, - IMacroService macroService, - IDataTypeService dataTypeService, - IFileService fileService, - ILocalizationService localizationService, - IEntityService entityService, - IUserService userService, - IScopeProvider scopeProvider, - UrlSegmentProviderCollection urlSegmentProviders, - IAuditRepository auditRepository, IContentTypeRepository contentTypeRepository, - PropertyEditorCollection propertyEditors) + IAuditService auditService, + ICreatedPackagesRepository createdPackages, + IInstalledPackagesRepository installedPackages, + IPackageInstallation packageInstallation) { - _logger = logger; - _contentService = contentService; - _contentTypeService = contentTypeService; - _mediaService = mediaService; - _macroService = macroService; - _dataTypeService = dataTypeService; - _fileService = fileService; - _localizationService = localizationService; - _entityService = entityService; - _scopeProvider = scopeProvider; - _urlSegmentProviders = urlSegmentProviders; - _auditRepository = auditRepository; - _contentTypeRepository = contentTypeRepository; - _propertyEditors = propertyEditors; - _userService = userService; - _importedContentTypes = new Dictionary(); + _auditService = auditService; + _createdPackages = createdPackages; + _installedPackages = installedPackages; + _packageInstallation = packageInstallation; } - protected IQuery Query() => _scopeProvider.SqlContext.Query(); - - #region Content - - /// - /// Exports an item to xml as an - /// - /// Content to export - /// Optional parameter indicating whether to include descendents - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Content object - public XElement Export(IContent content, bool deep = false, bool raiseEvents = true) - { - var nodeName = content.ContentType.Alias.ToSafeAliasWithForcingCheck(); - - if (raiseEvents) - { - if (ExportingContent.IsRaisedEventCancelled(new ExportEventArgs(content, nodeName), this)) - return new XElement(nodeName); - } - - const bool published = false; // fixme - what shall we export? - var xml = EntityXmlSerializer.Serialize(_contentService, _dataTypeService, _userService, _localizationService, _urlSegmentProviders, content, published, deep); - - if (raiseEvents) - ExportedContent.RaiseEvent(new ExportEventArgs(content, xml, false), this); - - return xml; - } - - - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional parent Id for the content being imported - /// Optional Id of the user performing the import - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated content - public IEnumerable ImportContent(XElement element, int parentId = -1, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContent.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentSet")) - { - //This is a regular deep-structured import - var roots = from doc in element.Elements() - where (string)doc.Attribute("isDoc") == "" - select doc; - - var contents = ParseDocumentRootXml(roots, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - var attribute = element.Attribute("isDoc"); - if (attribute != null) - { - //This is a single doc import - var elements = new List { element }; - var contents = ParseDocumentRootXml(elements, parentId).ToList(); - if (contents.Any()) - _contentService.Save(contents, userId); - - if (raiseEvents) - ImportedContent.RaiseEvent(new ImportEventArgs(contents, element, false), this); - return contents; - } - - throw new ArgumentException( - "The passed in XElement is not valid! It does not contain a root element called " + - "'DocumentSet' (for structured imports) nor is the first element a Document (for single document import)."); - } - - private IEnumerable ParseDocumentRootXml(IEnumerable roots, int parentId) - { - var contents = new List(); - foreach (var root in roots) - { - var contentTypeAlias = root.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - var content = CreateContentFromXml(root, _importedContentTypes[contentTypeAlias], null, parentId); - contents.Add(content); - - var children = (from child in root.Elements() - where (string)child.Attribute("isDoc") == "" - select child) - .ToList(); - if (children.Any()) - contents.AddRange(CreateContentFromXml(children, content)); - } - return contents; - } - - private IEnumerable CreateContentFromXml(IEnumerable children, IContent parent) - { - var list = new List(); - foreach (var child in children) - { - string contentTypeAlias = child.Name.LocalName; - - if (_importedContentTypes.ContainsKey(contentTypeAlias) == false) - { - var contentType = FindContentTypeByAlias(contentTypeAlias); - _importedContentTypes.Add(contentTypeAlias, contentType); - } - - //Create and add the child to the list - var content = CreateContentFromXml(child, _importedContentTypes[contentTypeAlias], parent, default(int)); - list.Add(content); - - //Recursive call - XElement child1 = child; - var grandChildren = (from grand in child1.Elements() - where (string) grand.Attribute("isDoc") == "" - select grand).ToList(); - - if (grandChildren.Any()) - list.AddRange(CreateContentFromXml(grandChildren, content)); - } - - return list; - } - - private IContent CreateContentFromXml(XElement element, IContentType contentType, IContent parent, int parentId) - { - var id = element.Attribute("id").Value; - var level = element.Attribute("level").Value; - var sortOrder = element.Attribute("sortOrder").Value; - var nodeName = element.Attribute("nodeName").Value; - var path = element.Attribute("path").Value; - //TODO: Shouldn't we be using this value??? - var template = element.Attribute("template").Value; - var key = Guid.Empty; - - var properties = from property in element.Elements() - where property.Attribute("isDoc") == null - select property; - - IContent content = parent == null - ? new Content(nodeName, parentId, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - } - : new Content(nodeName, parent, contentType) - { - Level = int.Parse(level), - SortOrder = int.Parse(sortOrder) - }; - - if (element.Attribute("key") != null && Guid.TryParse(element.Attribute("key").Value, out key)) - { - // update the Guid (for UDI support) - content.Key = key; - } - - using (var scope = _scopeProvider.CreateScope(autoComplete: true)) - { - foreach (var property in properties) - { - string propertyTypeAlias = property.Name.LocalName; - if (content.HasProperty(propertyTypeAlias)) - { - var propertyValue = property.Value; - - var propertyType = contentType.PropertyTypes.FirstOrDefault(pt => pt.Alias == propertyTypeAlias); - - //TODO: It would be heaps nicer if we didn't have to hard code references to specific property editors - // we'd have to modify the packaging format to denote how to parse/store the value instead of relying on this - - if (propertyType != null) - { - // fixme - wtf is this very specific thing here?! - //if (propertyType.PropertyEditorAlias == Constants.PropertyEditors.Aliases.CheckBoxList) - //{ - - // //TODO: We need to refactor this so the packager isn't making direct db calls for an 'edge' case - // var database = scope.Database; - // var dtos = database.Fetch("WHERE datatypeNodeId = @Id", new { Id = propertyType.DataTypeId }); - - // var propertyValueList = new List(); - // foreach (var preValue in propertyValue.Split(',')) - // { - // propertyValueList.Add(dtos.Single(x => x.Value == preValue).Id.ToString(CultureInfo.InvariantCulture)); - // } - - // propertyValue = string.Join(",", propertyValueList.ToArray()); - - //} - } - //set property value - content.SetValue(propertyTypeAlias, propertyValue); - } - } - } - - return content; - } - - #endregion - - #region ContentTypes - - /// - /// Exports an to xml as an - /// - /// ContentType to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ContentType item. - public XElement Export(IContentType contentType, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingContentType.IsRaisedEventCancelled(new ExportEventArgs(contentType, "DocumentType"), this)) - return new XElement("DocumentType"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(_dataTypeService, _contentTypeService, contentType); - - if (raiseEvents) - ExportedContentType.RaiseEvent(new ExportEventArgs(contentType, xml, false), this); - - return xml; - } - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, int userId = 0, bool raiseEvents = true) - { - return ImportContentTypes(element, true, userId); - } - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Boolean indicating whether or not to import the - /// Optional id of the User performing the operation. Default is zero (admin). - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated ContentTypes - public IEnumerable ImportContentTypes(XElement element, bool importStructure, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingContentType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DocumentTypes") == false && name.Equals("DocumentType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DocumentTypes' for multiple imports or 'DocumentType' for a single import."); - } - - _importedContentTypes = new Dictionary(); - var unsortedDocumentTypes = name.Equals("DocumentTypes") - ? (from doc in element.Elements("DocumentType") select doc).ToList() - : new List { element }; - - //When you are importing a single doc type we have to assume that the depedencies are already there. - //Otherwise something like uSync won't work. - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - var isSingleDocTypeImport = unsortedDocumentTypes.Count == 1; - - var importedFolders = CreateContentTypeFolderStructure(unsortedDocumentTypes); - - if (isSingleDocTypeImport == false) - { - //NOTE Here we sort the doctype XElements based on dependencies - //before creating the doc types - this should also allow for a better structure/inheritance support. - foreach (var documentType in unsortedDocumentTypes) - { - var elementCopy = documentType; - var infoElement = elementCopy.Element("Info"); - var dependencies = new HashSet(); - - //Add the Master as a dependency - if (string.IsNullOrEmpty((string)infoElement.Element("Master")) == false) - { - dependencies.Add(infoElement.Element("Master").Value); - } - - //Add compositions as dependencies - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - dependencies.Add(composition.Value); - } - } - } - - graph.AddItem(TopoGraph.CreateNode(infoElement.Element("Alias").Value, elementCopy, dependencies.ToArray())); - } - } - - //Sorting the Document Types based on dependencies - if its not a single doc type import ref. #U4-5921 - var documentTypes = isSingleDocTypeImport - ? unsortedDocumentTypes.ToList() - : graph.GetSortedItems().Select(x => x.Item).ToList(); - - //Iterate the sorted document types and create them as IContentType objects - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - if (_importedContentTypes.ContainsKey(alias) == false) - { - var contentType = _contentTypeService.Get(alias); - _importedContentTypes.Add(alias, contentType == null - ? CreateContentTypeFromXml(documentType) - : UpdateContentTypeFromXml(documentType, contentType)); - } - } - - foreach (var contentType in _importedContentTypes) - { - var ct = contentType.Value; - if (importedFolders.ContainsKey(ct.Alias)) - { - ct.ParentId = importedFolders[ct.Alias]; - } - } - - //Save the newly created/updated IContentType objects - var list = _importedContentTypes.Select(x => x.Value).ToList(); - _contentTypeService.Save(list, userId); - - //Now we can finish the import by updating the 'structure', - //which requires the doc types to be saved/available in the db - if (importStructure) - { - var updatedContentTypes = new List(); - //Update the structure here - we can't do it untill all DocTypes have been created - foreach (var documentType in documentTypes) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var structureElement = documentType.Element("Structure"); - //Ensure that we only update ContentTypes which has actual structure-elements - if (structureElement == null || structureElement.Elements("DocumentType").Any() == false) continue; - - var updated = UpdateContentTypesStructure(_importedContentTypes[alias], structureElement); - updatedContentTypes.Add(updated); - } - //Update ContentTypes with a newly added structure/list of allowed children - if (updatedContentTypes.Any()) - _contentTypeService.Save(updatedContentTypes, userId); - } - - if (raiseEvents) - ImportedContentType.RaiseEvent(new ImportEventArgs(list, element, false), this); - - return list; - } - - private Dictionary CreateContentTypeFolderStructure(IEnumerable unsortedDocumentTypes) - { - var importedFolders = new Dictionary(); - foreach (var documentType in unsortedDocumentTypes) - { - var foldersAttribute = documentType.Attribute("Folders"); - var infoElement = documentType.Element("Info"); - if (foldersAttribute != null && infoElement != null - //don't import any folder if this is a child doc type - the parent doc type will need to - //exist which contains it's folders - && ((string)infoElement.Element("Master")).IsNullOrWhiteSpace()) - { - var alias = documentType.Element("Info").Element("Alias").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //level 1 = root level folders, there can only be one with the same name - var current = _contentTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _contentTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - var rootFolderId = tryCreateFolder.Result.Entity.Id; - current = _contentTypeService.GetContainer(rootFolderId); - } - - importedFolders.Add(alias, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateContentTypeChildFolder(folderName, current); - importedFolders[alias] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateContentTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _contentTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _contentTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _contentTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - private IContentType CreateContentTypeFromXml(XElement documentType) - { - var infoElement = documentType.Element("Info"); - - //Name of the master corresponds to the parent - var masterElement = infoElement.Element("Master"); - IContentType parent = null; - if (masterElement != null) - { - var masterAlias = masterElement.Value; - parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - } - - var alias = infoElement.Element("Alias").Value; - var contentType = parent == null - ? new ContentType(-1) { Alias = alias } - : new ContentType(parent, alias); - - if (parent != null) - contentType.AddContentType(parent); - - return UpdateContentTypeFromXml(documentType, contentType); - } - - private IContentType UpdateContentTypeFromXml(XElement documentType, IContentType contentType) - { - var infoElement = documentType.Element("Info"); - var defaultTemplateElement = infoElement.Element("DefaultTemplate"); - - contentType.Name = infoElement.Element("Name").Value; - contentType.Icon = infoElement.Element("Icon").Value; - contentType.Thumbnail = infoElement.Element("Thumbnail").Value; - contentType.Description = infoElement.Element("Description").Value; - - //NOTE AllowAtRoot is a new property in the package xml so we need to verify it exists before using it. - var allowAtRoot = infoElement.Element("AllowAtRoot"); - if (allowAtRoot != null) - contentType.AllowedAsRoot = allowAtRoot.Value.InvariantEquals("true"); - - //NOTE IsListView is a new property in the package xml so we need to verify it exists before using it. - var isListView = infoElement.Element("IsListView"); - if (isListView != null) - contentType.IsContainer = isListView.Value.InvariantEquals("true"); - - //Name of the master corresponds to the parent and we need to ensure that the Parent Id is set - var masterElement = infoElement.Element("Master"); - if (masterElement != null) - { - var masterAlias = masterElement.Value; - IContentType parent = _importedContentTypes.ContainsKey(masterAlias) - ? _importedContentTypes[masterAlias] - : _contentTypeService.Get(masterAlias); - - contentType.SetParent(parent); - } - - //Update Compositions on the ContentType to ensure that they are as is defined in the package xml - var compositionsElement = infoElement.Element("Compositions"); - if (compositionsElement != null && compositionsElement.HasElements) - { - var compositions = compositionsElement.Elements("Composition"); - if (compositions.Any()) - { - foreach (var composition in compositions) - { - var compositionAlias = composition.Value; - var compositionContentType = _importedContentTypes.ContainsKey(compositionAlias) - ? _importedContentTypes[compositionAlias] - : _contentTypeService.Get(compositionAlias); - var added = contentType.AddContentType(compositionContentType); - } - } - } - - UpdateContentTypesAllowedTemplates(contentType, infoElement.Element("AllowedTemplates"), defaultTemplateElement); - UpdateContentTypesTabs(contentType, documentType.Element("Tabs")); - UpdateContentTypesProperties(contentType, documentType.Element("GenericProperties")); - - return contentType; - } - - private void UpdateContentTypesAllowedTemplates(IContentType contentType, - XElement allowedTemplatesElement, XElement defaultTemplateElement) - { - if (allowedTemplatesElement != null && allowedTemplatesElement.Elements("Template").Any()) - { - var allowedTemplates = contentType.AllowedTemplates.ToList(); - foreach (var templateElement in allowedTemplatesElement.Elements("Template")) - { - var alias = templateElement.Value; - var template = _fileService.GetTemplate(alias.ToSafeAlias()); - if (template != null) - { - if (allowedTemplates.Any(x => x.Id == template.Id)) continue; - allowedTemplates.Add(template); - } - else - { - _logger.Warn("Packager: Error handling allowed templates. Template with alias '{TemplateAlias}' could not be found.", alias); - } - } - - contentType.AllowedTemplates = allowedTemplates; - } - - if (string.IsNullOrEmpty((string)defaultTemplateElement) == false) - { - var defaultTemplate = _fileService.GetTemplate(defaultTemplateElement.Value.ToSafeAlias()); - if (defaultTemplate != null) - { - contentType.SetDefaultTemplate(defaultTemplate); - } - else - { - _logger.Warn("Packager: Error handling default template. Default template with alias '{DefaultTemplateAlias}' could not be found.", defaultTemplateElement.Value); - } - } - } - - private void UpdateContentTypesTabs(IContentType contentType, XElement tabElement) - { - if (tabElement == null) - return; - - var tabs = tabElement.Elements("Tab"); - foreach (var tab in tabs) - { - var id = tab.Element("Id").Value;//Do we need to use this for tracking? - var caption = tab.Element("Caption").Value; - - if (contentType.PropertyGroups.Contains(caption) == false) - { - contentType.AddPropertyGroup(caption); - - } - - int sortOrder; - if (tab.Element("SortOrder") != null && int.TryParse(tab.Element("SortOrder").Value, out sortOrder)) - { - // Override the sort order with the imported value - contentType.PropertyGroups[caption].SortOrder = sortOrder; - } - } - } - - private void UpdateContentTypesProperties(IContentType contentType, XElement genericPropertiesElement) - { - var properties = genericPropertiesElement.Elements("GenericProperty"); - foreach (var property in properties) - { - var dataTypeDefinitionId = new Guid(property.Element("Definition").Value);//Unique Id for a DataTypeDefinition - - var dataTypeDefinition = _dataTypeService.GetDataType(dataTypeDefinitionId); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - - //get the alias as a string for use below - var propertyEditorAlias = property.Element("Type").Value.Trim(); - - //If no DataTypeDefinition with the guid from the xml wasn't found OR the ControlId on the DataTypeDefinition didn't match the DataType Id - //We look up a DataTypeDefinition that matches - - if (dataTypeDefinition == null) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - else if (dataTypeDefinition.EditorAlias != propertyEditorAlias) - { - var dataTypeDefinitions = _dataTypeService.GetByEditorAlias(propertyEditorAlias); - if (dataTypeDefinitions != null && dataTypeDefinitions.Any()) - { - dataTypeDefinition = dataTypeDefinitions.FirstOrDefault(); - } - } - - // For backwards compatibility, if no datatype with that ID can be found, we're letting this fail silently. - // This means that the property will not be created. - if (dataTypeDefinition == null) - { - _logger.Warn("Packager: Error handling creation of PropertyType '{PropertyType}'. Could not find DataTypeDefintion with unique id '{DataTypeDefinitionId}' nor one referencing the DataType with a property editor alias (or legacy control id) '{PropertyEditorAlias}'. Did the package creator forget to package up custom datatypes? This property will be converted to a label/readonly editor if one exists.", - property.Element("Name").Value, dataTypeDefinitionId, property.Element("Type").Value.Trim()); - - //convert to a label! - dataTypeDefinition = _dataTypeService.GetByEditorAlias(Constants.PropertyEditors.Aliases.NoEdit).FirstOrDefault(); - //if for some odd reason this isn't there then ignore - if (dataTypeDefinition == null) continue; - } - - var sortOrder = 0; - var sortOrderElement = property.Element("SortOrder"); - if (sortOrderElement != null) - int.TryParse(sortOrderElement.Value, out sortOrder); - var propertyType = new PropertyType(dataTypeDefinition, property.Element("Alias").Value) - { - Name = property.Element("Name").Value, - Description = (string)property.Element("Description"), - Mandatory = property.Element("Mandatory") != null ? property.Element("Mandatory").Value.ToLowerInvariant().Equals("true") : false, - ValidationRegExp = (string)property.Element("Validation"), - SortOrder = sortOrder - }; - - var tab = (string)property.Element("Tab"); - if (string.IsNullOrEmpty(tab)) - { - contentType.AddPropertyType(propertyType); - } - else - { - contentType.AddPropertyType(propertyType, tab); - } - } - } - - private IContentType UpdateContentTypesStructure(IContentType contentType, XElement structureElement) - { - var allowedChildren = contentType.AllowedContentTypes.ToList(); - int sortOrder = allowedChildren.Any() ? allowedChildren.Last().SortOrder : 0; - foreach (var element in structureElement.Elements("DocumentType")) - { - var alias = element.Value; - - var allowedChild = _importedContentTypes.ContainsKey(alias) ? _importedContentTypes[alias] : _contentTypeService.Get(alias); - if (allowedChild == null) - { - _logger.Warn( - "Packager: Error handling DocumentType structure. DocumentType with alias '{DoctypeAlias}' could not be found and was not added to the structure for '{DoctypeStructureAlias}'.", - alias, contentType.Alias); - continue; - } - - if (allowedChildren.Any(x => x.Id.IsValueCreated && x.Id.Value == allowedChild.Id)) continue; - - allowedChildren.Add(new ContentTypeSort(new Lazy(() => allowedChild.Id), sortOrder, allowedChild.Alias)); - sortOrder++; - } - - contentType.AllowedContentTypes = allowedChildren; - return contentType; - } - - /// - /// Used during Content import to ensure that the ContentType of a content item exists - /// - /// - /// - private IContentType FindContentTypeByAlias(string contentTypeAlias) - { - using (var scope = _scopeProvider.CreateScope()) - { - var query = Query().Where(x => x.Alias == contentTypeAlias); - var contentType = _contentTypeRepository.Get(query).FirstOrDefault(); - - if (contentType == null) - throw new Exception($"ContentType matching the passed in Alias: '{contentTypeAlias}' was null"); - - scope.Complete(); - return contentType; - } - } - - #endregion - - #region DataTypes - - /// - /// Exports a list of Data Types - /// - /// List of data types to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDataTypeDefinition objects - public XElement Export(IEnumerable dataTypeDefinitions, bool raiseEvents = true) - { - var container = new XElement("DataTypes"); - foreach (var dataTypeDefinition in dataTypeDefinitions) - { - container.Add(Export(dataTypeDefinition, raiseEvents)); - } - return container; - } - - /// - /// Exports a single Data Type - /// - /// Data type to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDataTypeDefinition object - public XElement Export(IDataType dataType, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingDataType.IsRaisedEventCancelled(new ExportEventArgs(dataType, "DataType"), this)) - return new XElement("DataType"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(_dataTypeService, dataType); - - if (raiseEvents) - ExportedDataType.RaiseEvent(new ExportEventArgs(dataType, xml, false), this); - - return xml; - } - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional id of the user - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated DataTypeDefinitions - public IEnumerable ImportDataTypeDefinitions(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDataType.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("DataTypes") == false && name.Equals("DataType") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'DataTypes' for multiple imports or 'DataType' for a single import."); - } - - var dataTypes = new List(); - var dataTypeElements = name.Equals("DataTypes") - ? (from doc in element.Elements("DataType") select doc).ToList() - : new List { element }; - - var importedFolders = CreateDataTypeFolderStructure(dataTypeElements); - - foreach (var dataTypeElement in dataTypeElements) - { - var dataTypeDefinitionName = dataTypeElement.Attribute("Name").Value; - - var dataTypeDefinitionId = new Guid(dataTypeElement.Attribute("Definition").Value); - var databaseTypeAttribute = dataTypeElement.Attribute("DatabaseType"); - - var parentId = -1; - if (importedFolders.ContainsKey(dataTypeDefinitionName)) - parentId = importedFolders[dataTypeDefinitionName]; - - var definition = _dataTypeService.GetDataType(dataTypeDefinitionId); - //If the datatypedefinition doesn't already exist we create a new new according to the one in the package xml - if (definition == null) - { - var databaseType = databaseTypeAttribute != null - ? databaseTypeAttribute.Value.EnumParse(true) - : ValueStorageType.Ntext; - - // the Id field is actually the string property editor Alias - // however, the actual editor with this alias could be installed with the package, and - // therefore not yet part of the _propertyEditors collection, so we cannot try and get - // the actual editor - going with a void editor - - var editorAlias = dataTypeElement.Attribute("Id")?.Value?.Trim(); - if (!_propertyEditors.TryGet(editorAlias, out var editor)) - editor = new VoidEditor(_logger) { Alias = editorAlias }; - - var dataType = new DataType(editor) - { - Key = dataTypeDefinitionId, - Name = dataTypeDefinitionName, - DatabaseType = databaseType, - ParentId = parentId - }; - - var configurationAttributeValue = dataTypeElement.Attribute("Configuration")?.Value; - if (!string.IsNullOrWhiteSpace(configurationAttributeValue)) - dataType.Configuration = editor.GetConfigurationEditor().FromDatabase(configurationAttributeValue); - - dataTypes.Add(dataType); - } - else - { - definition.ParentId = parentId; - _dataTypeService.Save(definition, userId); - } - } - - if (dataTypes.Count > 0) - { - _dataTypeService.Save(dataTypes, userId, true); - } - - if (raiseEvents) - ImportedDataType.RaiseEvent(new ImportEventArgs(dataTypes, element, false), this); - - return dataTypes; - } - - private Dictionary CreateDataTypeFolderStructure(IEnumerable datatypeElements) - { - var importedFolders = new Dictionary(); - foreach (var datatypeElement in datatypeElements) - { - var foldersAttribute = datatypeElement.Attribute("Folders"); - if (foldersAttribute != null) - { - var name = datatypeElement.Attribute("Name").Value; - var folders = foldersAttribute.Value.Split('/'); - var rootFolder = HttpUtility.UrlDecode(folders[0]); - //there will only be a single result by name for level 1 (root) containers - var current = _dataTypeService.GetContainers(rootFolder, 1).FirstOrDefault(); - - if (current == null) - { - var tryCreateFolder = _dataTypeService.CreateContainer(-1, rootFolder); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", rootFolder); - throw tryCreateFolder.Exception; - } - current = _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - importedFolders.Add(name, current.Id); - - for (var i = 1; i < folders.Length; i++) - { - var folderName = HttpUtility.UrlDecode(folders[i]); - current = CreateDataTypeChildFolder(folderName, current); - importedFolders[name] = current.Id; - } - } - } - - return importedFolders; - } - - private EntityContainer CreateDataTypeChildFolder(string folderName, IUmbracoEntity current) - { - var children = _entityService.GetChildren(current.Id).ToArray(); - var found = children.Any(x => x.Name.InvariantEquals(folderName)); - if (found) - { - var containerId = children.Single(x => x.Name.InvariantEquals(folderName)).Id; - return _dataTypeService.GetContainer(containerId); - } - - var tryCreateFolder = _dataTypeService.CreateContainer(current.Id, folderName); - if (tryCreateFolder == false) - { - _logger.Error(tryCreateFolder.Exception, "Could not create folder: {FolderName}", folderName); - throw tryCreateFolder.Exception; - } - return _dataTypeService.GetContainer(tryCreateFolder.Result.Entity.Id); - } - - #endregion - - #region Dictionary Items - - /// - /// Exports a list of items to xml as an - /// - /// List of dictionary items to export - /// Optional boolean indicating whether or not to include children - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDictionaryItem objects - public XElement Export(IEnumerable dictionaryItem, bool includeChildren = true, bool raiseEvents = true) - { - var xml = new XElement("DictionaryItems"); - foreach (var item in dictionaryItem) - { - xml.Add(Export(item, includeChildren, raiseEvents)); - } - return xml; - } - - /// - /// Exports a single item to xml as an - /// - /// Dictionary Item to export - /// Optional boolean indicating whether or not to include children - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IDictionaryItem object - public XElement Export(IDictionaryItem dictionaryItem, bool includeChildren, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingDictionaryItem.IsRaisedEventCancelled(new ExportEventArgs(dictionaryItem, "DictionaryItem"), this)) - return new XElement("DictionaryItem"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(dictionaryItem); - - if (includeChildren) - { - var children = _localizationService.GetDictionaryItemChildren(dictionaryItem.Key); - foreach (var child in children) - { - xml.Add(Export(child, true)); - } - } - - if (raiseEvents) - ExportedDictionaryItem.RaiseEvent(new ExportEventArgs(dictionaryItem, xml, false), this); - - return xml; - } - - /// - /// Imports and saves the 'DictionaryItems' part of the package xml as a list of - /// - /// Xml to import - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of dictionary items - public IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingDictionaryItem.IsRaisedEventCancelled(new ImportEventArgs(dictionaryItemElementList), this)) - return Enumerable.Empty(); - } - - var languages = _localizationService.GetAllLanguages().ToList(); - return ImportDictionaryItems(dictionaryItemElementList, languages, raiseEvents); - } - - private IEnumerable ImportDictionaryItems(XElement dictionaryItemElementList, List languages, bool raiseEvents, Guid? parentId = null) - { - var items = new List(); - foreach (var dictionaryItemElement in dictionaryItemElementList.Elements("DictionaryItem")) - items.AddRange(ImportDictionaryItem(dictionaryItemElement, languages, raiseEvents, parentId)); - - - if (raiseEvents) - ImportedDictionaryItem.RaiseEvent(new ImportEventArgs(items, dictionaryItemElementList, false), this); - - return items; - } - - private IEnumerable ImportDictionaryItem(XElement dictionaryItemElement, List languages, bool raiseEvents, Guid? parentId) - { - var items = new List(); - - IDictionaryItem dictionaryItem; - var key = dictionaryItemElement.Attribute("Key").Value; - if (_localizationService.DictionaryItemExists(key)) - dictionaryItem = GetAndUpdateDictionaryItem(key, dictionaryItemElement, languages); - else - dictionaryItem = CreateNewDictionaryItem(key, dictionaryItemElement, languages, parentId); - _localizationService.Save(dictionaryItem); - items.Add(dictionaryItem); - - items.AddRange(ImportDictionaryItems(dictionaryItemElement, languages, raiseEvents, dictionaryItem.Key)); - return items; - } - - private IDictionaryItem GetAndUpdateDictionaryItem(string key, XElement dictionaryItemElement, List languages) - { - var dictionaryItem = _localizationService.GetDictionaryItemByKey(key); - var translations = dictionaryItem.Translations.ToList(); - foreach (var valueElement in dictionaryItemElement.Elements("Value").Where(v => DictionaryValueIsNew(translations, v))) - AddDictionaryTranslation(translations, valueElement, languages); - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static DictionaryItem CreateNewDictionaryItem(string key, XElement dictionaryItemElement, List languages, Guid? parentId) - { - var dictionaryItem = parentId.HasValue ? new DictionaryItem(parentId.Value, key) : new DictionaryItem(key); - var translations = new List(); - - foreach (var valueElement in dictionaryItemElement.Elements("Value")) - AddDictionaryTranslation(translations, valueElement, languages); - - dictionaryItem.Translations = translations; - return dictionaryItem; - } - - private static bool DictionaryValueIsNew(IEnumerable translations, XElement valueElement) - { - return translations.All(t => - String.Compare(t.Language.IsoCode, valueElement.Attribute("LanguageCultureAlias").Value, - StringComparison.InvariantCultureIgnoreCase) != 0 - ); - } - - private static void AddDictionaryTranslation(ICollection translations, XElement valueElement, IEnumerable languages) - { - var languageId = valueElement.Attribute("LanguageCultureAlias").Value; - var language = languages.SingleOrDefault(l => l.IsoCode == languageId); - if (language == null) - return; - var translation = new DictionaryTranslation(language, valueElement.Value); - translations.Add(translation); - } - - #endregion - - #region Files - #endregion - - #region Languages - - /// - /// Exports a list of items to xml as an - /// - /// List of Languages to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ILanguage objects - public XElement Export(IEnumerable languages, bool raiseEvents = true) - { - var xml = new XElement("Languages"); - foreach (var language in languages) - { - xml.Add(Export(language, raiseEvents)); - } - return xml; - } - - /// - /// Exports a single item to xml as an - /// - /// Language to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ILanguage object - public XElement Export(ILanguage language, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingLanguage.IsRaisedEventCancelled(new ExportEventArgs(language, "Language"), this)) - return new XElement("Language"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(language); - - if (raiseEvents) - ExportedLanguage.RaiseEvent(new ExportEventArgs(language, xml, false), this); - - return xml; - } - - /// - /// Imports and saves the 'Languages' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// An enumerable list of generated languages - public IEnumerable ImportLanguages(XElement languageElementList, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingLanguage.IsRaisedEventCancelled(new ImportEventArgs(languageElementList), this)) - return Enumerable.Empty(); - } - - var list = new List(); - foreach (var languageElement in languageElementList.Elements("Language")) - { - var isoCode = languageElement.Attribute("CultureAlias").Value; - var existingLanguage = _localizationService.GetLanguageByIsoCode(isoCode); - if (existingLanguage == null) - { - var langauge = new Language(isoCode) - { - CultureName = languageElement.Attribute("FriendlyName").Value - }; - _localizationService.Save(langauge); - list.Add(langauge); - } - } - - if (raiseEvents) - ImportedLanguage.RaiseEvent(new ImportEventArgs(list, languageElementList, false), this); - - return list; - } - - #endregion - - #region Macros - - /// - /// Imports and saves the 'Macros' part of a package xml as a list of - /// - /// Xml to import - /// Optional id of the User performing the operation - /// Optional parameter indicating whether or not to raise events - /// - public IEnumerable ImportMacros(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingMacro.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - var name = element.Name.LocalName; - if (name.Equals("Macros") == false && name.Equals("macro") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Macros' for multiple imports or 'macro' for a single import."); - } - - var macroElements = name.Equals("Macros") - ? (from doc in element.Elements("macro") select doc).ToList() - : new List { element }; - - var macros = macroElements.Select(ParseMacroElement).ToList(); - - foreach (var macro in macros) - { - var existing = _macroService.GetByAlias(macro.Alias); - if (existing != null) - macro.Id = existing.Id; - - _macroService.Save(macro, userId); - } - - if (raiseEvents) - ImportedMacro.RaiseEvent(new ImportEventArgs(macros, element, false), this); - - return macros; - } - - private IMacro ParseMacroElement(XElement macroElement) - { - var macroName = macroElement.Element("name").Value; - var macroAlias = macroElement.Element("alias").Value; - var macroType = Enum.Parse(macroElement.Element("macroType").Value); - var macroSource = macroElement.Element("macroSource").Value; - - //Following xml elements are treated as nullable properties - var useInEditorElement = macroElement.Element("useInEditor"); - var useInEditor = false; - if (useInEditorElement != null && string.IsNullOrEmpty((string)useInEditorElement) == false) - { - useInEditor = bool.Parse(useInEditorElement.Value); - } - var cacheDurationElement = macroElement.Element("refreshRate"); - var cacheDuration = 0; - if (cacheDurationElement != null && string.IsNullOrEmpty((string)cacheDurationElement) == false) - { - cacheDuration = int.Parse(cacheDurationElement.Value); - } - var cacheByMemberElement = macroElement.Element("cacheByMember"); - var cacheByMember = false; - if (cacheByMemberElement != null && string.IsNullOrEmpty((string)cacheByMemberElement) == false) - { - cacheByMember = bool.Parse(cacheByMemberElement.Value); - } - var cacheByPageElement = macroElement.Element("cacheByPage"); - var cacheByPage = false; - if (cacheByPageElement != null && string.IsNullOrEmpty((string)cacheByPageElement) == false) - { - cacheByPage = bool.Parse(cacheByPageElement.Value); - } - var dontRenderElement = macroElement.Element("dontRender"); - var dontRender = true; - if (dontRenderElement != null && string.IsNullOrEmpty((string)dontRenderElement) == false) - { - dontRender = bool.Parse(dontRenderElement.Value); - } - - var existingMacro = _macroService.GetByAlias(macroAlias) as Macro; - var macro = existingMacro ?? new Macro(macroAlias, macroName, macroSource, macroType, - cacheByPage, cacheByMember, dontRender, useInEditor, cacheDuration); - - var properties = macroElement.Element("properties"); - if (properties != null) - { - int sortOrder = 0; - foreach (var property in properties.Elements()) - { - var propertyName = property.Attribute("name").Value; - var propertyAlias = property.Attribute("alias").Value; - var editorAlias = property.Attribute("propertyType").Value; - var sortOrderAttribute = property.Attribute("sortOrder"); - if (sortOrderAttribute != null) - { - sortOrder = int.Parse(sortOrderAttribute.Value); - } - - if (macro.Properties.Values.Any(x => string.Equals(x.Alias, propertyAlias, StringComparison.OrdinalIgnoreCase))) continue; - macro.Properties.Add(new MacroProperty(propertyAlias, propertyName, sortOrder, editorAlias)); - sortOrder++; - } - } - return macro; - } - - /// - /// Exports a list of items to xml as an - /// - /// Macros to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IMacro objects - public XElement Export(IEnumerable macros, bool raiseEvents = true) - { - var xml = new XElement("Macros"); - foreach (var item in macros) - { - xml.Add(Export(item, raiseEvents)); - } - return xml; - } - - /// - /// Exports a single item to xml as an - /// - /// Macro to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the IMacro object - public XElement Export(IMacro macro, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingMacro.IsRaisedEventCancelled(new ExportEventArgs(macro, "macro"), this)) - return new XElement("macro"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(macro); - - if (raiseEvents) - ExportedMacro.RaiseEvent(new ExportEventArgs(macro, xml, false), this); - - return xml; - } - - #endregion - - #region Members - - /// - /// Exports an item to xml as an - /// - /// Member to export - /// containing the xml representation of the Member object - public XElement Export(IMember member) - { - return EntityXmlSerializer.Serialize(_dataTypeService, _localizationService, member); - } - - #endregion - - #region Media - - /// - /// Exports an item to xml as an - /// - /// Media to export - /// Optional parameter indicating whether to include descendents - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the Media object - public XElement Export(IMedia media, bool deep = false, bool raiseEvents = true) - { - var nodeName = media.ContentType.Alias.ToSafeAliasWithForcingCheck(); - - if (raiseEvents) - { - if (ExportingMedia.IsRaisedEventCancelled(new ExportEventArgs(media, nodeName), this)) - return new XElement(nodeName); - } - - var xml = EntityXmlSerializer.Serialize(_mediaService, _dataTypeService, _userService, _localizationService, _urlSegmentProviders, media, deep); - - if (raiseEvents) - ExportedMedia.RaiseEvent(new ExportEventArgs(media, xml, false), this); - - return xml; - } - - - #endregion - - #region MediaTypes - - /// - /// Exports an to xml as an - /// - /// MediaType to export - /// containing the xml representation of the MediaType item. - internal XElement Export(IMediaType mediaType) - { - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(_dataTypeService, mediaType); - - return xml; - } - - #endregion - - #region Package Manifest - #endregion - #region Package Files - /// - /// This will fetch an Umbraco package file from the package repository and return the relative file path to the downloaded package file - /// - /// - /// - /// /// The current user id performing the operation - /// - public string FetchPackageFile(Guid packageId, Version umbracoVersion, int userId) + /// + public async Task FetchPackageFileAsync(Guid packageId, Version umbracoVersion, int userId) { - using (var scope = _scopeProvider.CreateScope()) + //includeHidden = true because we don't care if it's hidden we want to get the file regardless + var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; + byte[] bytes; + try { - //includeHidden = true because we don't care if it's hidden we want to get the file regardless - var url = $"{Constants.PackageRepository.RestApiBaseUrl}/{packageId}?version={umbracoVersion.ToString(3)}&includeHidden=true&asFile=true"; - byte[] bytes; - try + if (_httpClient == null) { - if (_httpClient == null) - { - _httpClient = new HttpClient(); - } - bytes = _httpClient.GetByteArrayAsync(url).GetAwaiter().GetResult(); + _httpClient = new HttpClient(); } - catch (HttpRequestException ex) + bytes = await _httpClient.GetByteArrayAsync(url); + } + catch (HttpRequestException ex) + { + throw new ConnectionException("An error occuring downloading the package from " + url, ex); + } + + //successfull + if (bytes.Length > 0) + { + var packagePath = IOHelper.MapPath(SystemDirectories.Packages); + + // Check for package directory + if (Directory.Exists(packagePath) == false) + Directory.CreateDirectory(packagePath); + + var packageFilePath = Path.Combine(packagePath, packageId + ".umb"); + + using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) { - throw new ConnectionException("An error occuring downloading the package from " + url, ex); + fs1.Write(bytes, 0, bytes.Length); + return new FileInfo(packageFilePath); } - - //successfull - if (bytes.Length > 0) - { - var packagePath = IOHelper.MapPath(SystemDirectories.Packages); - - // Check for package directory - if (Directory.Exists(packagePath) == false) - Directory.CreateDirectory(packagePath); - - var packageFilePath = Path.Combine(packagePath, packageId + ".umb"); - - using (var fs1 = new FileStream(packageFilePath, FileMode.Create)) - { - fs1.Write(bytes, 0, bytes.Length); - return "packages\\" + packageId + ".umb"; - } - } - - Audit(AuditType.PackagerInstall, $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}", userId, -1); - return null; - } - } - - private void Audit(AuditType type, string message, int userId, int objectId) - { - _auditRepository.Save(new AuditItem(objectId, type, userId, "Package", message)); - } - - #endregion - - #region Templates - - /// - /// Imports and saves package xml as - /// - /// Xml to import - /// Optional user id - /// Optional parameter indicating whether or not to raise events - /// An enumrable list of generated Templates - public IEnumerable ImportTemplates(XElement element, int userId = 0, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ImportingTemplate.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); } - var name = element.Name.LocalName; - if (name.Equals("Templates") == false && name.Equals("Template") == false) - { - throw new ArgumentException("The passed in XElement is not valid! It does not contain a root element called 'Templates' for multiple imports or 'Template' for a single import."); - } - - var templates = new List(); - var templateElements = name.Equals("Templates") - ? (from doc in element.Elements("Template") select doc).ToList() - : new List { element }; - - var graph = new TopoGraph>(x => x.Key, x => x.Dependencies); - - foreach (XElement tempElement in templateElements) - { - var dependencies = new List(); - var elementCopy = tempElement; - //Ensure that the Master of the current template is part of the import, otherwise we ignore this dependency as part of the dependency sorting. - if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master"))) - { - dependencies.Add((string)elementCopy.Element("Master")); - } - else if (string.IsNullOrEmpty((string)elementCopy.Element("Master")) == false && - templateElements.Any(x => (string)x.Element("Alias") == (string)elementCopy.Element("Master")) == false) - { - _logger.Info( - "Template '{TemplateAlias}' has an invalid Master '{TemplateMaster}', so the reference has been ignored.", - (string) elementCopy.Element("Alias"), - (string) elementCopy.Element("Master")); - } - - graph.AddItem(TopoGraph.CreateNode((string) elementCopy.Element("Alias"), elementCopy, dependencies)); - } - - //Sort templates by dependencies to a potential master template - var sorted = graph.GetSortedItems(); - foreach (var item in sorted) - { - var templateElement = item.Item; - - var templateName = templateElement.Element("Name").Value; - var alias = templateElement.Element("Alias").Value; - var design = templateElement.Element("Design").Value; - var masterElement = templateElement.Element("Master"); - - var isMasterPage = IsMasterPageSyntax(design); - var path = isMasterPage ? MasterpagePath(alias) : ViewPath(alias); - - var existingTemplate = _fileService.GetTemplate(alias) as Template; - var template = existingTemplate ?? new Template(templateName, alias); - template.Content = design; - if (masterElement != null && string.IsNullOrEmpty((string)masterElement) == false) - { - template.MasterTemplateAlias = masterElement.Value; - var masterTemplate = templates.FirstOrDefault(x => x.Alias == masterElement.Value); - if (masterTemplate != null) - template.MasterTemplateId = new Lazy(() => masterTemplate.Id); - } - templates.Add(template); - } - - if (templates.Any()) - _fileService.SaveTemplate(templates, userId); - - if (raiseEvents) - ImportedTemplate.RaiseEvent(new ImportEventArgs(templates, element, false), this); - - return templates; + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package {packageId} fetched from {Constants.PackageRepository.DefaultRepositoryId}"); + return null; } - - public IEnumerable ImportStylesheets(XElement element, int userId = 0, bool raiseEvents = true) - { - - if (raiseEvents) - { - if (ImportingStylesheets.IsRaisedEventCancelled(new ImportEventArgs(element), this)) - return Enumerable.Empty(); - } - - IEnumerable styleSheets = Enumerable.Empty(); - - if (element.Elements().Any()) - throw new NotImplementedException("This needs to be implimentet"); - - - if (raiseEvents) - ImportingStylesheets.RaiseEvent(new ImportEventArgs(styleSheets, element, false), this); - - return styleSheets; - - } - - - private bool IsMasterPageSyntax(string code) - { - return Regex.IsMatch(code, @"<%@\s*Master", RegexOptions.IgnoreCase) || - code.InvariantContains(" - /// Exports a list of items to xml as an - /// - /// List of Templates to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ITemplate objects - public XElement Export(IEnumerable templates, bool raiseEvents = true) - { - var xml = new XElement("Templates"); - foreach (var item in templates) - { - xml.Add(Export(item, raiseEvents)); - } - return xml; - } - - /// - /// Exports a single item to xml as an - /// - /// Template to export - /// Optional parameter indicating whether or not to raise events - /// containing the xml representation of the ITemplate object - public XElement Export(ITemplate template, bool raiseEvents = true) - { - if (raiseEvents) - { - if (ExportingTemplate.IsRaisedEventCancelled(new ExportEventArgs(template, "Template"), this)) - return new XElement("Template"); - } - - var exporter = new EntityXmlSerializer(); - var xml = exporter.Serialize(template); - - if (raiseEvents) - ExportedTemplate.RaiseEvent(new ExportEventArgs(template, xml, false), this); - - return xml; - } - - #endregion - - #region Stylesheets #endregion #region Installation - internal IPackageInstallation PackageInstallation + public CompiledPackage GetCompiledPackageInfo(FileInfo packageFile) => _packageInstallation.ReadPackage(packageFile); + + public IEnumerable InstallCompiledPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0) { - private get { return _packageInstallation ?? new PackageInstallation(this, _macroService, _fileService, new PackageExtraction()); } - set { _packageInstallation = value; } + if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); + if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted"); + if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information"); + + var compiledPackage = GetCompiledPackageInfo(packageFile); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); + + var files = _packageInstallation.InstallPackageFiles(packageDefinition, compiledPackage, userId).ToList(); + + SaveInstalledPackage(packageDefinition); + + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package files installed for package '{compiledPackage.Name}'."); + + return files; } - internal InstallationSummary InstallPackage(string packageFilePath, int userId = 0, bool raiseEvents = false) + public InstallationSummary InstallCompiledPackageData(PackageDefinition packageDefinition, FileInfo packageFile, int userId = 0) { - var metaData = GetPackageMetaData(packageFilePath); + if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); + if (packageDefinition.Id == default) throw new ArgumentException("The package definition has not been persisted"); + if (packageDefinition.Name == default) throw new ArgumentException("The package definition has incomplete information"); - if (raiseEvents) - { - if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFilePath, metaData), this)) - { - var initEmpty = new InstallationSummary().InitEmpty(); - initEmpty.MetaData = metaData; - return initEmpty; - } - } - var installationSummary = PackageInstallation.InstallPackage(packageFilePath, userId); + var compiledPackage = GetCompiledPackageInfo(packageFile); + if (compiledPackage == null) throw new InvalidOperationException("Could not read the package file " + packageFile); - if (raiseEvents) + if (ImportingPackage.IsRaisedEventCancelled(new ImportPackageEventArgs(packageFile.Name, compiledPackage), this)) + return new InstallationSummary { MetaData = compiledPackage }; + + var summary = _packageInstallation.InstallPackageData(packageDefinition, compiledPackage, userId); + + SaveInstalledPackage(packageDefinition); + + _auditService.Add(AuditType.PackagerInstall, userId, -1, "Package", $"Package data installed for package '{compiledPackage.Name}'."); + + ImportedPackage.RaiseEvent(new ImportPackageEventArgs(summary, compiledPackage, false), this); + + return summary; + } + + public UninstallationSummary UninstallPackage(string packageName, int userId = 0) + { + //this is ordered by descending version + var allPackageVersions = GetInstalledPackageByName(packageName)?.ToList(); + if (allPackageVersions == null || allPackageVersions.Count == 0) + throw new InvalidOperationException("No installed package found by name " + packageName); + + var summary = new UninstallationSummary { - ImportedPackage.RaiseEvent(new ImportPackageEventArgs(installationSummary, metaData, false), this); + MetaData = allPackageVersions[0] + }; + + var allSummaries = new List(); + + foreach (var packageVersion in allPackageVersions) + { + var versionUninstallSummary = _packageInstallation.UninstallPackage(packageVersion, userId); + + allSummaries.Add(versionUninstallSummary); + + //merge the summary + summary.ActionErrors = summary.ActionErrors.Concat(versionUninstallSummary.ActionErrors).Distinct().ToList(); + summary.Actions = summary.Actions.Concat(versionUninstallSummary.Actions).Distinct().ToList(); + summary.DataTypesUninstalled = summary.DataTypesUninstalled.Concat(versionUninstallSummary.DataTypesUninstalled).Distinct().ToList(); + summary.DictionaryItemsUninstalled = summary.DictionaryItemsUninstalled.Concat(versionUninstallSummary.DictionaryItemsUninstalled).Distinct().ToList(); + summary.DocumentTypesUninstalled = summary.DocumentTypesUninstalled.Concat(versionUninstallSummary.DocumentTypesUninstalled).Distinct().ToList(); + summary.FilesUninstalled = summary.FilesUninstalled.Concat(versionUninstallSummary.FilesUninstalled).Distinct().ToList(); + summary.LanguagesUninstalled = summary.LanguagesUninstalled.Concat(versionUninstallSummary.LanguagesUninstalled).Distinct().ToList(); + summary.MacrosUninstalled = summary.MacrosUninstalled.Concat(versionUninstallSummary.MacrosUninstalled).Distinct().ToList(); + summary.StylesheetsUninstalled = summary.StylesheetsUninstalled.Concat(versionUninstallSummary.StylesheetsUninstalled).Distinct().ToList(); + summary.TemplatesUninstalled = summary.TemplatesUninstalled.Concat(versionUninstallSummary.TemplatesUninstalled).Distinct().ToList(); + + SaveInstalledPackage(packageVersion); + DeleteInstalledPackage(packageVersion.Id, userId); } - return installationSummary; - } + // trigger the UninstalledPackage event + UninstalledPackage.RaiseEvent(new UninstallPackageEventArgs(allSummaries, false), this); - internal PreInstallWarnings GetPackageWarnings(string packageFilePath) - { - return PackageInstallation.GetPreInstallWarnings(packageFilePath); - } - - internal MetaData GetPackageMetaData(string packageFilePath) - { - return PackageInstallation.GetMetaData(packageFilePath); + return summary; } #endregion - #region Package Building + #region Created/Installed Package Repositories + + public void DeleteCreatedPackage(int id, int userId = 0) + { + var package = GetCreatedPackageById(id); + if (package == null) return; + + _auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Created package '{package.Name}' deleted. Package id: {package.Id}"); + _createdPackages.Delete(id); + } + + public IEnumerable GetAllCreatedPackages() => _createdPackages.GetAll(); + + public PackageDefinition GetCreatedPackageById(int id) => _createdPackages.GetById(id); + + public bool SaveCreatedPackage(PackageDefinition definition) => _createdPackages.SavePackage(definition); + + public string ExportCreatedPackage(PackageDefinition definition) => _createdPackages.ExportPackage(definition); + + + public IEnumerable GetAllInstalledPackages() => _installedPackages.GetAll(); + + public PackageDefinition GetInstalledPackageById(int id) => _installedPackages.GetById(id); + + public IEnumerable GetInstalledPackageByName(string name) + { + var found = _installedPackages.GetAll().Where(x => x.Name.InvariantEquals(name)).OrderByDescending(x => SemVersion.Parse(x.Version)); + return found; + } + + public PackageInstallType GetPackageInstallType(string packageName, SemVersion packageVersion, out PackageDefinition alreadyInstalled) + { + if (packageName == null) throw new ArgumentNullException(nameof(packageName)); + if (packageVersion == null) throw new ArgumentNullException(nameof(packageVersion)); + + //get the latest version installed + alreadyInstalled = GetInstalledPackageByName(packageName)?.OrderByDescending(x => SemVersion.Parse(x.Version)).FirstOrDefault(); + if (alreadyInstalled == null) return PackageInstallType.NewInstall; + + if (!SemVersion.TryParse(alreadyInstalled.Version, out var installedVersion)) + throw new InvalidOperationException("Could not parse the currently installed package version " + alreadyInstalled.Version); + + //compare versions + if (installedVersion >= packageVersion) return PackageInstallType.AlreadyInstalled; + + //it's an upgrade + return PackageInstallType.Upgrade; + } + + public bool SaveInstalledPackage(PackageDefinition definition) => _installedPackages.SavePackage(definition); + + public void DeleteInstalledPackage(int packageId, int userId = 0) + { + var package = GetInstalledPackageById(packageId); + if (package == null) return; + + _auditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", $"Installed package '{package.Name}' deleted. Package id: {package.Id}"); + _installedPackages.Delete(packageId); + } + #endregion - /// - /// This method can be used to trigger the 'ImportedPackage' event when a package is installed by something else but this service. - /// - /// - internal static void OnImportedPackage(ImportPackageEventArgs args) - { - ImportedPackage.RaiseEvent(args, null); - } - - /// - /// This method can be used to trigger the 'UninstalledPackage' event when a package is uninstalled by something else but this service. - /// - /// - internal static void OnUninstalledPackage(UninstallPackageEventArgs args) - { - UninstalledPackage.RaiseEvent(args, null); - } - #region Event Handlers - /// - /// Occurs before Importing Content - /// - public static event TypedEventHandler> ImportingContent; - - /// - /// Occurs after Content is Imported and Saved - /// - public static event TypedEventHandler> ImportedContent; - - - public static event TypedEventHandler> ExportingContent; - - /// - /// Occurs after Content is Exported to Xml - /// - public static event TypedEventHandler> ExportedContent; - - /// - /// Occurs before Exporting Media - /// - public static event TypedEventHandler> ExportingMedia; - - /// - /// Occurs after Media is Exported to Xml - /// - public static event TypedEventHandler> ExportedMedia; - - /// - /// Occurs before Importing ContentType - /// - public static event TypedEventHandler> ImportingContentType; - - /// - /// Occurs after ContentType is Imported and Saved - /// - public static event TypedEventHandler> ImportedContentType; - - /// - /// Occurs before Exporting ContentType - /// - public static event TypedEventHandler> ExportingContentType; - - /// - /// Occurs after ContentType is Exported to Xml - /// - public static event TypedEventHandler> ExportedContentType; - - /// - /// Occurs before Importing DataType - /// - public static event TypedEventHandler> ImportingDataType; - - /// - /// Occurs after DataType is Imported and Saved - /// - public static event TypedEventHandler> ImportedDataType; - - /// - /// Occurs before Exporting DataType - /// - public static event TypedEventHandler> ExportingDataType; - - /// - /// Occurs after DataType is Exported to Xml - /// - public static event TypedEventHandler> ExportedDataType; - - /// - /// Occurs before Importing DictionaryItem - /// - public static event TypedEventHandler> ImportingDictionaryItem; - - /// - /// Occurs after DictionaryItem is Imported and Saved - /// - public static event TypedEventHandler> ImportedDictionaryItem; - - /// - /// Occurs before Exporting DictionaryItem - /// - public static event TypedEventHandler> ExportingDictionaryItem; - - /// - /// Occurs after DictionaryItem is Exported to Xml - /// - public static event TypedEventHandler> ExportedDictionaryItem; - - /// - /// Occurs before Importing Macro - /// - public static event TypedEventHandler> ImportingMacro; - - /// - /// Occurs after Macro is Imported and Saved - /// - public static event TypedEventHandler> ImportedMacro; - - /// - /// Occurs before Exporting Macro - /// - public static event TypedEventHandler> ExportingMacro; - - /// - /// Occurs after Macro is Exported to Xml - /// - public static event TypedEventHandler> ExportedMacro; - - /// - /// Occurs before Importing Language - /// - public static event TypedEventHandler> ImportingLanguage; - - /// - /// Occurs after Language is Imported and Saved - /// - public static event TypedEventHandler> ImportedLanguage; - - /// - /// Occurs before Exporting Language - /// - public static event TypedEventHandler> ExportingLanguage; - - /// - /// Occurs after Language is Exported to Xml - /// - public static event TypedEventHandler> ExportedLanguage; - - /// - /// Occurs before Importing Template - /// - public static event TypedEventHandler> ImportingTemplate; - - /// - /// Occurs before Importing Stylesheets - /// - public static event TypedEventHandler> ImportingStylesheets; - - /// - /// Occurs after Template is Imported and Saved - /// - public static event TypedEventHandler> ImportedTemplate; - - /// - /// Occurs before Exporting Template - /// - public static event TypedEventHandler> ExportingTemplate; - - /// - /// Occurs after Template is Exported to Xml - /// - public static event TypedEventHandler> ExportedTemplate; /// /// Occurs before Importing umbraco package /// - internal static event TypedEventHandler> ImportingPackage; + public static event TypedEventHandler> ImportingPackage; /// /// Occurs after a package is imported @@ -1893,8 +262,10 @@ namespace Umbraco.Core.Services.Implement /// /// Occurs after a package is uninstalled /// - public static event TypedEventHandler> UninstalledPackage; + public static event TypedEventHandler UninstalledPackage; #endregion + + } } diff --git a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs index 5cfcb501e5..6844e6e75c 100644 --- a/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs +++ b/src/Umbraco.Core/Sync/DatabaseServerMessenger.cs @@ -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; } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index a01bbf1746..2236854351 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -196,6 +196,7 @@ + @@ -320,7 +321,6 @@ - @@ -343,6 +343,7 @@ + @@ -375,6 +376,7 @@ + @@ -429,6 +431,12 @@ + + + + + + @@ -440,6 +448,19 @@ + + + + + + + + + + + + + @@ -490,6 +511,8 @@ + + @@ -554,13 +577,11 @@ - - @@ -760,7 +781,6 @@ - @@ -880,11 +900,7 @@ - - - - - + @@ -1351,6 +1367,7 @@ + @@ -1370,7 +1387,7 @@ - + diff --git a/src/Umbraco.Core/Xml/XmlHelper.cs b/src/Umbraco.Core/Xml/XmlHelper.cs index d4f2d40656..e058858799 100644 --- a/src/Umbraco.Core/Xml/XmlHelper.cs +++ b/src/Umbraco.Core/Xml/XmlHelper.cs @@ -14,7 +14,7 @@ namespace Umbraco.Core.Xml /// /// The XmlHelper class contains general helper methods for working with xml in umbraco. /// - public class XmlHelper + internal class XmlHelper { /// /// Creates or sets an attribute on the XmlNode if an Attributes collection is available @@ -126,44 +126,6 @@ namespace Umbraco.Core.Xml return false; } - /// - /// Tries to create a new XElement from a property value. - /// - /// The value of the property. - /// The Xml element. - /// A value indicating whether it has been possible to create the element. - /// The value can be anything... Performance-wise, this is bad. - 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; - } /// /// Sorts the children of a parentNode. @@ -186,47 +148,6 @@ namespace Umbraco.Core.Xml parentNode.AppendChild(node); // moves the node to the last position } - /// - /// Sorts the children of a parentNode if needed. - /// - /// The parent node. - /// An XPath expression to select children of to sort. - /// A function returning the value to order the nodes by. - /// A value indicating whether sorting was needed. - /// same as SortNodes but will do nothing if nodes are already sorted - should improve performances. - internal static bool SortNodesIfNeeded( - XmlNode parentNode, - string childNodesXPath, - Func 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() - .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; - } /// /// 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(); - } - } - } - } - - - /// - /// Imports a XML node from text. - /// - /// The text. - /// The XML doc. - /// - public static XmlNode ImportXmlNodeFromText(string text, ref XmlDocument xmlDoc) - { - xmlDoc.LoadXml(text); - return xmlDoc.FirstChild; - } /// /// Opens a file as a XmlDocument. @@ -355,16 +210,14 @@ namespace Umbraco.Core.Xml /// Returns a XmlDocument class 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; + } } /// diff --git a/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs b/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs index 502aee36b3..b58ea34a60 100644 --- a/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs +++ b/src/Umbraco.Core/_Legacy/PackageActions/IPackageAction.cs @@ -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); } } diff --git a/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollection.cs b/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollection.cs index 46ea3c1e39..a38c8cea56 100644 --- a/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollection.cs +++ b/src/Umbraco.Core/_Legacy/PackageActions/PackageActionCollection.cs @@ -3,7 +3,7 @@ using Umbraco.Core.Composing; namespace Umbraco.Core._Legacy.PackageActions { - internal class PackageActionCollection : BuilderCollectionBase + public sealed class PackageActionCollection : BuilderCollectionBase { public PackageActionCollection(IEnumerable items) : base(items) diff --git a/src/Umbraco.Examine/ContentValueSetBuilder.cs b/src/Umbraco.Examine/ContentValueSetBuilder.cs index d8e698cdad..a6262c53fc 100644 --- a/src/Umbraco.Examine/ContentValueSetBuilder.cs +++ b/src/Umbraco.Examine/ContentValueSetBuilder.cs @@ -13,11 +13,11 @@ namespace Umbraco.Examine /// public class ContentValueSetBuilder : BaseValueSetBuilder, IContentValueSetBuilder, IPublishedContentValueSetBuilder { - private readonly IEnumerable _urlSegmentProviders; + private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; public ContentValueSetBuilder(PropertyEditorCollection propertyEditors, - IEnumerable urlSegmentProviders, + UrlSegmentProviderCollection urlSegmentProviders, IUserService userService, bool publishedValuesOnly) : base(propertyEditors, publishedValuesOnly) diff --git a/src/Umbraco.Examine/LuceneIndexCreator.cs b/src/Umbraco.Examine/LuceneIndexCreator.cs index 0e83b37dc5..806d0edc7a 100644 --- a/src/Umbraco.Examine/LuceneIndexCreator.cs +++ b/src/Umbraco.Examine/LuceneIndexCreator.cs @@ -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); diff --git a/src/Umbraco.Examine/MediaValueSetBuilder.cs b/src/Umbraco.Examine/MediaValueSetBuilder.cs index 2676093eeb..23d0414d5d 100644 --- a/src/Umbraco.Examine/MediaValueSetBuilder.cs +++ b/src/Umbraco.Examine/MediaValueSetBuilder.cs @@ -10,7 +10,7 @@ namespace Umbraco.Examine { public class MediaValueSetBuilder : BaseValueSetBuilder { - private readonly IEnumerable _urlSegmentProviders; + private readonly UrlSegmentProviderCollection _urlSegmentProviders; private readonly IUserService _userService; public MediaValueSetBuilder(PropertyEditorCollection propertyEditors, diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs index 28e753cadc..12ea87087d 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedContentCacheTests.cs @@ -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()), new PublishedMemberCache(null, cacheProvider, Current.Services.MemberService, ContentTypesCache), domainCache); var publishedSnapshotService = new Mock(); diff --git a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs index 5e75f47304..6add88009d 100644 --- a/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs +++ b/src/Umbraco.Tests/Cache/PublishedCache/PublishedMediaCacheTests.cs @@ -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()); 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()); 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 { 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()); 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()); 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); diff --git a/src/Umbraco.Tests/Composing/ContainerConformingTests.cs b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs index 9585c98cb2..21ea961636 100644 --- a/src/Umbraco.Tests/Composing/ContainerConformingTests.cs +++ b/src/Umbraco.Tests/Composing/ContainerConformingTests.cs @@ -194,8 +194,8 @@ namespace Umbraco.Tests.Composing var register = GetRegister(); // define two instances - register.RegisterInstance(typeof(Thing1), new Thing1()); - register.RegisterInstance(typeof(Thing1), new Thing2()); + register.Register(typeof(Thing1), new Thing1()); + register.Register(typeof(Thing1), new Thing2()); var factory = register.CreateFactory(); @@ -212,8 +212,8 @@ namespace Umbraco.Tests.Composing var register = GetRegister(); // define two instances - register.RegisterInstance(typeof(IThing), new Thing1()); - register.RegisterInstance(typeof(IThing), new Thing2()); + register.Register(typeof(IThing), new Thing1()); + register.Register(typeof(IThing), new Thing2()); var factory = register.CreateFactory(); diff --git a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs index d100713102..75030decbf 100644 --- a/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs +++ b/src/Umbraco.Tests/Composing/PackageActionCollectionTests.cs @@ -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(); } diff --git a/src/Umbraco.Tests/Composing/TypeFinderTests.cs b/src/Umbraco.Tests/Composing/TypeFinderTests.cs index 2b9474310b..49c807b19f 100644 --- a/src/Umbraco.Tests/Composing/TypeFinderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeFinderTests.cs @@ -90,7 +90,7 @@ namespace Umbraco.Tests.Composing Assert.AreEqual(0, typesFound.Count()); // 0 classes in _assemblies are marked with [Tree] typesFound = TypeFinder.FindClassesWithAttribute(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() diff --git a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs index 5148c7eb1b..1649f3675d 100644 --- a/src/Umbraco.Tests/Composing/TypeLoaderTests.cs +++ b/src/Umbraco.Tests/Composing/TypeLoaderTests.cs @@ -30,7 +30,7 @@ namespace Umbraco.Tests.Composing // this ensures it's reset _typeLoader = new TypeLoader(NullCacheProvider.Instance, LocalTempStorage.Default, new ProfilingLogger(Mock.Of(), Mock.Of())); - 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 diff --git a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs index 262b026cae..2244f9085d 100644 --- a/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs +++ b/src/Umbraco.Tests/IO/ShadowFileSystemTests.cs @@ -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(); 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(); 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(); 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; diff --git a/src/Umbraco.Tests/Models/ContentXmlTest.cs b/src/Umbraco.Tests/Models/ContentXmlTest.cs index a6a5e3a9ef..5cc86a4322 100644 --- a/src/Umbraco.Tests/Models/ContentXmlTest.cs +++ b/src/Umbraco.Tests/Models/ContentXmlTest.cs @@ -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()); // Assert Assert.That(element, Is.Not.Null); diff --git a/src/Umbraco.Tests/Models/MediaXmlTest.cs b/src/Umbraco.Tests/Models/MediaXmlTest.cs index 1a56fac4eb..d0d00c64a6 100644 --- a/src/Umbraco.Tests/Models/MediaXmlTest.cs +++ b/src/Umbraco.Tests/Models/MediaXmlTest.cs @@ -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()); // Assert Assert.That(element, Is.Not.Null); diff --git a/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs new file mode 100644 index 0000000000..010572abec --- /dev/null +++ b/src/Umbraco.Tests/Packaging/CreatedPackagesRepositoryTests.cs @@ -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(), 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 {"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 { file1, file2 }, + 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("", xml.Element("umbPackage").Element("Actions").ToString(SaveOptions.DisableFormatting)); + + //TODO: There's a whole lot more assertions to be done + } + } + } + } +} diff --git a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs similarity index 78% rename from src/Umbraco.Tests/Services/Importing/PackageImportTests.cs rename to src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs index 8e67aa4e1f..ba71caad95 100644 --- a/src/Umbraco.Tests/Services/Importing/PackageImportTests.cs +++ b/src/Umbraco.Tests/Packaging/PackageDataInstallationTests.cs @@ -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(); + [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(); // 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 diff --git a/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs b/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs index 5416162ed2..61494166ff 100644 --- a/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs +++ b/src/Umbraco.Tests/Packaging/PackageExtractionTests.cs @@ -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] diff --git a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs index 0855d81548..4256a66a2d 100644 --- a/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs +++ b/src/Umbraco.Tests/Packaging/PackageInstallationTest.cs @@ -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 = @" - - - 095e064b-ba4d-442d-9006-3050983c13d8.dll/binAuros.DocumentTypePicker.dll - - - Document Type Picker - 1.1 - MIT - http://www.auros.co.uk - - 3 - 0 - 0 - - - - @tentonipete - auros.co.uk - - - - - - - - - - - - - - -"; + 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()); + + private IPackageInstallation PackageInstallation => new PackageInstallation( + PackageDataInstallation, + new PackageFileInstallation(Parser, ProfilingLogger), + Parser, Mock.Of(), + 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(); - - string test; - packageExtraction.Setup(a => a.ReadTextFileFromArchive(pagePath, Constants.Packaging.PackageXmlFileName, out test)).Returns(Xml); - - var fileService = new Mock(); - var macroService = new Mock(); - var packagingService = new Mock(); - - 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(); //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); + } + + } } diff --git a/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip b/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip new file mode 100644 index 0000000000..c95cb282d1 Binary files /dev/null and b/src/Umbraco.Tests/Packaging/Packages/Hello_1.0.0.zip differ diff --git a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs index 3ba0ef64ce..ef37a822c1 100644 --- a/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs +++ b/src/Umbraco.Tests/PublishedContent/NuCacheTests.cs @@ -152,7 +152,8 @@ namespace Umbraco.Tests.PublishedContent new TestDefaultCultureAccessor(), dataSource, globalSettings, - new SiteDomainHelper()); + new SiteDomainHelper(), + Mock.Of()); // get a snapshot, get a published content var snapshot = snapshotService.CreatePublishedSnapshot(previewToken: null); diff --git a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs index 914956dce1..603464e18b 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedContentTests.cs @@ -69,8 +69,9 @@ namespace Umbraco.Tests.PublishedContent factory.CreatePropertyType("testRecursive", 1), }; var compositionAliases = new[] { "MyCompositionAlias" }; - var type = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes); - ContentTypesCache.GetPublishedContentTypeByAlias = alias => type; + var anythingType = new AutoPublishedContentType(0, "anything", compositionAliases, propertyTypes); + var homeType = new AutoPublishedContentType(0, "home", compositionAliases, propertyTypes); + ContentTypesCache.GetPublishedContentTypeByAlias = alias => alias.InvariantEquals("home") ? homeType : anythingType; } protected override TypeLoader CreateTypeLoader(IRuntimeCacheProvider runtimeCache, IGlobalSettings globalSettings, IProfilingLogger logger) @@ -233,10 +234,10 @@ namespace Umbraco.Tests.PublishedContent } [Test] - [Ignore("Fails as long as PublishedContentModel is internal.")] // fixme public void Is_Last_From_Where_Filter2() { var doc = GetNode(1173); + var ct = doc.ContentType; var items = doc.Children .Select(x => x.CreateModel()) // linq, returns IEnumerable @@ -455,11 +456,11 @@ namespace Umbraco.Tests.PublishedContent { var doc = GetNode(1046); // has child nodes - var model = doc.FirstChild(x => true); // predicate + var model = doc.FirstChild(x => true); // predicate Assert.IsNotNull(model); Assert.IsTrue(model.Id == 1173); - Assert.IsInstanceOf(model); + Assert.IsInstanceOf(model); Assert.IsInstanceOf(model); doc = GetNode(1175); // does not have child nodes diff --git a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs index 88b211d0ee..4f55b4fd71 100644 --- a/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs +++ b/src/Umbraco.Tests/PublishedContent/PublishedMediaTests.cs @@ -22,6 +22,7 @@ using Umbraco.Tests.Testing; using Umbraco.Core.Composing; using Umbraco.Core.Models.Membership; using Umbraco.Core.PropertyEditors; +using Umbraco.Core.Services; namespace Umbraco.Tests.PublishedContent { @@ -66,7 +67,9 @@ namespace Umbraco.Tests.PublishedContent /// internal IPublishedContent GetNode(int id, UmbracoContext umbracoContext) { - 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), + ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, + Factory.GetInstance()); var doc = cache.GetById(id); Assert.IsNotNull(doc); return doc; @@ -123,7 +126,7 @@ namespace Umbraco.Tests.PublishedContent var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); @@ -153,7 +156,7 @@ namespace Umbraco.Tests.PublishedContent var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //ensure it is found var publishedMedia = cache.GetById(3113); @@ -200,7 +203,7 @@ namespace Umbraco.Tests.PublishedContent var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); @@ -228,7 +231,7 @@ namespace Umbraco.Tests.PublishedContent var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); @@ -256,7 +259,7 @@ namespace Umbraco.Tests.PublishedContent var searcher = indexer.GetSearcher(); var ctx = GetUmbracoContext("/test"); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(1111); @@ -285,7 +288,7 @@ namespace Umbraco.Tests.PublishedContent var ctx = GetUmbracoContext("/test"); var searcher = indexer.GetSearcher(); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(3113); @@ -311,7 +314,7 @@ namespace Umbraco.Tests.PublishedContent var ctx = GetUmbracoContext("/test"); var searcher = indexer.GetSearcher(); - var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, indexer, new StaticCacheProvider(), ContentTypesCache); + var cache = new PublishedMediaCache(ServiceContext.MediaService, ServiceContext.UserService, searcher, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); //we are using the media.xml media to test the examine results implementation, see the media.xml file in the ExamineHelpers namespace var publishedMedia = cache.GetById(3113); @@ -479,7 +482,7 @@ namespace Umbraco.Tests.PublishedContent "); var node = xml.DescendantsAndSelf("Image").Single(x => (int)x.Attribute("id") == nodeId); - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); + var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); var nav = node.CreateNavigator(); @@ -499,7 +502,7 @@ namespace Umbraco.Tests.PublishedContent var errorXml = new XElement("error", string.Format("No media is maching '{0}'", 1234)); var nav = errorXml.CreateNavigator(); - var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache); + var publishedMedia = new PublishedMediaCache(new XmlStore((XmlDocument)null, null, null, null), ServiceContext.MediaService, ServiceContext.UserService, new StaticCacheProvider(), ContentTypesCache, Factory.GetInstance()); var converted = publishedMedia.ConvertFromXPathNodeIterator(nav.Select("/"), 1234); Assert.IsNull(converted); diff --git a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs index 0ef0e9362b..0a23e4a1b9 100644 --- a/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopeFileSystemsTests.cs @@ -45,7 +45,7 @@ namespace Umbraco.Tests.Scoping { TestHelper.DeleteDirectory(IOHelper.MapPath("media")); TestHelper.DeleteDirectory(IOHelper.MapPath("FileSysTests")); - TestHelper.DeleteDirectory(IOHelper.MapPath("App_Data/TEMP/ShadowFs")); + TestHelper.DeleteDirectory(IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "ShadowFs")); } [TestCase(true)] diff --git a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs index b273ee9526..e8f3463ca7 100644 --- a/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs +++ b/src/Umbraco.Tests/Scoping/ScopedNuCacheTests.cs @@ -95,7 +95,8 @@ namespace Umbraco.Tests.Scoping documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Factory.GetInstance(), new SiteDomainHelper()); + Factory.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance()); } protected UmbracoContext GetUmbracoContextNu(string url, int templateId = 1234, RouteData routeData = null, bool setSingleton = false, IUmbracoSettingsSection umbracoSettings = null, IEnumerable urlProviders = null) diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs index b1a8fa26a8..8dc8a2b45c 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceTests.cs @@ -22,6 +22,36 @@ namespace Umbraco.Tests.Services [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest, PublishedRepositoryEvents = true)] public class ContentTypeServiceTests : TestWithSomeContentBase { + [Test] + public void CanSaveAndGetIsElement() + { + //create content type with a property type that varies by culture + IContentType contentType = MockedContentTypes.CreateBasicContentType(); + contentType.Variations = ContentVariation.Nothing; + var contentCollection = new PropertyTypeCollection(true); + contentCollection.Add(new PropertyType("test", ValueStorageType.Ntext) + { + Alias = "title", + Name = "Title", + Description = "", + Mandatory = false, + SortOrder = 1, + DataTypeId = -88, + Variations = ContentVariation.Nothing + }); + contentType.PropertyGroups.Add(new PropertyGroup(contentCollection) { Name = "Content", SortOrder = 1 }); + ServiceContext.ContentTypeService.Save(contentType); + + contentType = ServiceContext.ContentTypeService.Get(contentType.Id); + Assert.IsFalse(contentType.IsElement); + + contentType.IsElement = true; + ServiceContext.ContentTypeService.Save(contentType); + + contentType = ServiceContext.ContentTypeService.Get(contentType.Id); + Assert.IsTrue(contentType.IsElement); + } + [Test] public void Change_Content_Type_Variation_Clears_Redirects() { diff --git a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs index 94d2126bd2..d0c0b93b48 100644 --- a/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs +++ b/src/Umbraco.Tests/Services/ContentTypeServiceVariantsTests.cs @@ -67,7 +67,8 @@ namespace Umbraco.Tests.Services documentRepository, mediaRepository, memberRepository, DefaultCultureAccessor, new DatabaseDataSource(), - Factory.GetInstance(), new SiteDomainHelper()); + Factory.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance()); } public class LocalServerMessenger : ServerMessengerBase diff --git a/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs b/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs new file mode 100644 index 0000000000..28c69344b7 --- /dev/null +++ b/src/Umbraco.Tests/Services/EntityXmlSerializerTests.cs @@ -0,0 +1,101 @@ +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Xml.Linq; +using NUnit.Framework; +using Umbraco.Core.Composing; +using Umbraco.Core.Models; +using Umbraco.Core.Services; +using Umbraco.Tests.Services.Importing; +using Umbraco.Tests.Testing; + +namespace Umbraco.Tests.Services +{ + [TestFixture] + [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] + public class EntityXmlSerializerTests : TestWithSomeContentBase + { + private IEntityXmlSerializer Serializer => Factory.GetInstance(); + + [Test] + public void Can_Export_Macro() + { + // Arrange + var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); + ServiceContext.MacroService.Save(macro); + + // Act + var element = Serializer.Serialize(macro); + + // Assert + Assert.That(element, Is.Not.Null); + Assert.That(element.Element("name").Value, Is.EqualTo("Test")); + Assert.That(element.Element("alias").Value, Is.EqualTo("test1")); + Debug.Print(element.ToString()); + } + + [Test] + public void Can_Export_DictionaryItems() + { + // Arrange + CreateDictionaryData(); + var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent"); + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); + + // Act + var xml = Serializer.Serialize(new[] { dictionaryItem }); + + // Assert + Assert.That(xml.ToString(), Is.EqualTo(dictionaryItemsElement.ToString())); + } + + [Test] + public void Can_Export_Languages() + { + // Arrange + var languageNbNo = new Language("nb-NO") { CultureName = "Norwegian" }; + ServiceContext.LocalizationService.Save(languageNbNo); + + var languageEnGb = new Language("en-GB") { CultureName = "English (United Kingdom)" }; + ServiceContext.LocalizationService.Save(languageEnGb); + + var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); + var languageItemsElement = newPackageXml.Elements("Languages").First(); + + // Act + var xml = Serializer.Serialize(new[] { languageNbNo, languageEnGb }); + + // Assert + Assert.That(xml.ToString(), Is.EqualTo(languageItemsElement.ToString())); + } + + private void CreateDictionaryData() + { + var languageNbNo = new Language("nb-NO") { CultureName = "nb-NO" }; + ServiceContext.LocalizationService.Save(languageNbNo); + + var languageEnGb = new Language("en-GB") { CultureName = "en-GB" }; + ServiceContext.LocalizationService.Save(languageEnGb); + + var parentItem = new DictionaryItem("Parent"); + var parentTranslations = new List + { + new DictionaryTranslation(languageNbNo, "ForelderVerdi"), + new DictionaryTranslation(languageEnGb, "ParentValue") + }; + parentItem.Translations = parentTranslations; + ServiceContext.LocalizationService.Save(parentItem); + + var childItem = new DictionaryItem(parentItem.Key, "Child"); + var childTranslations = new List + { + new DictionaryTranslation(languageNbNo, "BarnVerdi"), + new DictionaryTranslation(languageEnGb, "ChildValue") + }; + childItem.Translations = childTranslations; + ServiceContext.LocalizationService.Save(childItem); + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Tests/Services/PackagingServiceTests.cs b/src/Umbraco.Tests/Services/PackagingServiceTests.cs index 87225c1288..f6878e9407 100644 --- a/src/Umbraco.Tests/Services/PackagingServiceTests.cs +++ b/src/Umbraco.Tests/Services/PackagingServiceTests.cs @@ -1,16 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Xml.Linq; +using System.IO; using NUnit.Framework; using Umbraco.Core.IO; -using Umbraco.Core.Models; using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; -using Umbraco.Tests.Services.Importing; using Umbraco.Tests.TestHelpers; using Umbraco.Tests.Testing; @@ -20,134 +12,10 @@ namespace Umbraco.Tests.Services [UmbracoTest(Database = UmbracoTestOptions.Database.NewSchemaPerTest)] public class PackagingServiceTests : TestWithSomeContentBase { - [Test] - public void PackagingService_Can_Export_Macro() - { - // Arrange - var macro = new Macro("test1", "Test", "~/views/macropartials/test.cshtml", MacroTypes.PartialView); - ServiceContext.MacroService.Save(macro); + - // Act - var element = ServiceContext.PackagingService.Export(macro); + - // Assert - Assert.That(element, Is.Not.Null); - Assert.That(element.Element("name").Value, Is.EqualTo("Test")); - Assert.That(element.Element("alias").Value, Is.EqualTo("test1")); - Debug.Print(element.ToString()); - } - - [Test] - public void PackagingService_Can_Export_DictionaryItems() - { - // Arrange - CreateDictionaryData(); - var dictionaryItem = ServiceContext.LocalizationService.GetDictionaryItemByKey("Parent"); - - var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); - var dictionaryItemsElement = newPackageXml.Elements("DictionaryItems").First(); - - // Act - var xml = ServiceContext.PackagingService.Export(new []{dictionaryItem}); - - // Assert - Assert.That(xml.ToString(), Is.EqualTo(dictionaryItemsElement.ToString())); - } - - [Test] - public void PackagingService_Can_Export_Languages() - { - // Arrange - var languageNbNo = new Language("nb-NO") { CultureName = "Norwegian" }; - ServiceContext.LocalizationService.Save(languageNbNo); - - var languageEnGb = new Language("en-GB") { CultureName = "English (United Kingdom)" }; - ServiceContext.LocalizationService.Save(languageEnGb); - - var newPackageXml = XElement.Parse(ImportResources.Dictionary_Package); - var languageItemsElement = newPackageXml.Elements("Languages").First(); - - // Act - var xml = ServiceContext.PackagingService.Export(new[] { languageNbNo, languageEnGb }); - - // Assert - Assert.That(xml.ToString(), Is.EqualTo(languageItemsElement.ToString())); - } - - private static string GetTestPackagePath(string packageName) - { - const string testPackagesDirName = "Packaging\\Packages"; - string path = Path.Combine(IOHelper.GetRootDirectorySafe(), testPackagesDirName, packageName); - return path; - } - - - [Test] - public void PackagingService_Can_ImportPackage() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - InstallationSummary installationSummary = packagingService.InstallPackage(testPackagePath); - - Assert.IsNotNull(installationSummary); - } - - - [Test] - public void PackagingService_Can_GetPackageMetaData() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - MetaData packageMetaData = packagingService.GetPackageMetaData(testPackagePath); - Assert.IsNotNull(packageMetaData); - } - - [Test] - public void PackagingService_Can_GetPackageWarnings() - { - var packagingService = (PackagingService)ServiceContext.PackagingService; - - const string documentTypePickerUmb = "Document_Type_Picker_1.1.umb"; - - string testPackagePath = GetTestPackagePath(documentTypePickerUmb); - - PreInstallWarnings preInstallWarnings = packagingService.GetPackageWarnings(testPackagePath); - Assert.IsNotNull(preInstallWarnings); - } - - private void CreateDictionaryData() - { - var languageNbNo = new Language("nb-NO") { CultureName = "nb-NO" }; - ServiceContext.LocalizationService.Save(languageNbNo); - - var languageEnGb = new Language("en-GB") { CultureName = "en-GB" }; - ServiceContext.LocalizationService.Save(languageEnGb); - - var parentItem = new DictionaryItem("Parent"); - var parentTranslations = new List - { - new DictionaryTranslation(languageNbNo, "ForelderVerdi"), - new DictionaryTranslation(languageEnGb, "ParentValue") - }; - parentItem.Translations = parentTranslations; - ServiceContext.LocalizationService.Save(parentItem); - - var childItem = new DictionaryItem(parentItem.Key, "Child"); - var childTranslations = new List - { - new DictionaryTranslation(languageNbNo, "BarnVerdi"), - new DictionaryTranslation(languageEnGb, "ChildValue") - }; - childItem.Translations = childTranslations; - ServiceContext.LocalizationService.Save(childItem); - } + } } diff --git a/src/Umbraco.Tests/TestHelpers/TestObjects.cs b/src/Umbraco.Tests/TestHelpers/TestObjects.cs index cb36e6ca5f..14ffeb743f 100644 --- a/src/Umbraco.Tests/TestHelpers/TestObjects.cs +++ b/src/Umbraco.Tests/TestHelpers/TestObjects.cs @@ -12,6 +12,7 @@ using Umbraco.Core.Configuration.UmbracoSettings; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; +using Umbraco.Core.Packaging; using Umbraco.Core.Persistence; using Umbraco.Core.Persistence.Mappers; using Umbraco.Core.Persistence.Repositories; @@ -21,6 +22,7 @@ using Umbraco.Core.Scoping; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Core.Strings; +using Umbraco.Tests.TestHelpers.Stubs; using Umbraco.Web.Services; namespace Umbraco.Tests.TestHelpers @@ -77,9 +79,11 @@ namespace Umbraco.Tests.TestHelpers /// A cache. /// A logger. /// + /// /// An event messages factory. /// Some url segment providers. - /// A container. + /// + /// A container. /// /// A ServiceContext. /// Should be used sparingly for integration tests only - for unit tests @@ -167,7 +171,22 @@ namespace Umbraco.Tests.TestHelpers GetRepo(c))); var macroService = GetLazyService(factory, c => new MacroService(scopeProvider, logger, eventMessagesFactory, GetRepo(c), GetRepo(c))); - var packagingService = GetLazyService(factory, c => new PackagingService(logger, contentService.Value, contentTypeService.Value, mediaService.Value, macroService.Value, dataTypeService.Value, fileService.Value, localizationService.Value, entityService.Value, userService.Value, scopeProvider, urlSegmentProviders, GetRepo(c), GetRepo(c), new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())))); + var packagingService = GetLazyService(factory, c => + { + var propertyEditorCollection = new PropertyEditorCollection(new DataEditorCollection(Enumerable.Empty())); + var compiledPackageXmlParser = new CompiledPackageXmlParser(new ConflictingPackageData(macroService.Value, fileService.Value)); + return new PackagingService( + auditService.Value, + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "createdPackages.config"), + new PackagesRepository(contentService.Value, contentTypeService.Value, dataTypeService.Value, fileService.Value, macroService.Value, localizationService.Value, + new EntityXmlSerializer(contentService.Value, mediaService.Value, dataTypeService.Value, userService.Value, localizationService.Value, contentTypeService.Value, urlSegmentProviders), logger, "installedPackages.config"), + new PackageInstallation( + new PackageDataInstallation(logger, fileService.Value, macroService.Value, localizationService.Value, dataTypeService.Value, entityService.Value, contentTypeService.Value, contentService.Value, propertyEditorCollection), + new PackageFileInstallation(compiledPackageXmlParser, new ProfilingLogger(logger, new TestProfiler())), + compiledPackageXmlParser, Mock.Of(), + new DirectoryInfo(IOHelper.GetRootDirectorySafe()))); + }); var relationService = GetLazyService(factory, c => new RelationService(scopeProvider, logger, eventMessagesFactory, entityService.Value, GetRepo(c), GetRepo(c))); var treeService = GetLazyService(factory, c => new ApplicationTreeService(logger, cache, typeLoader)); var tagService = GetLazyService(factory, c => new TagService(scopeProvider, logger, eventMessagesFactory, GetRepo(c))); diff --git a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs index a2a0c35c56..0729aa0b6e 100644 --- a/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs +++ b/src/Umbraco.Tests/TestHelpers/TestWithDatabaseBase.cs @@ -268,6 +268,7 @@ namespace Umbraco.Tests.TestHelpers DefaultCultureAccessor, Logger, Factory.GetInstance(), new SiteDomainHelper(), + Factory.GetInstance(), ContentTypesCache, null, true, Options.PublishedRepositoryEvents); diff --git a/src/Umbraco.Tests/UI/LegacyDialogTests.cs b/src/Umbraco.Tests/UI/LegacyDialogTests.cs index be9b0d4d7e..5c8a621e10 100644 --- a/src/Umbraco.Tests/UI/LegacyDialogTests.cs +++ b/src/Umbraco.Tests/UI/LegacyDialogTests.cs @@ -24,7 +24,6 @@ namespace Umbraco.Tests.UI } [TestCase(typeof(macroTasks), Constants.Applications.Settings)] - [TestCase(typeof(CreatedPackageTasks), Constants.Applications.Packages)] public void Check_Assigned_Apps_For_Tasks(Type taskType, string app) { var task = (LegacyDialogTask)Activator.CreateInstance(taskType); diff --git a/src/Umbraco.Tests/Umbraco.Tests.csproj b/src/Umbraco.Tests/Umbraco.Tests.csproj index 1b048f1df0..e49ba250fa 100644 --- a/src/Umbraco.Tests/Umbraco.Tests.csproj +++ b/src/Umbraco.Tests/Umbraco.Tests.csproj @@ -62,6 +62,8 @@ + + @@ -139,6 +141,8 @@ + + @@ -440,7 +444,7 @@ - + True True diff --git a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs index 0b36398dd6..3019138809 100644 --- a/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs +++ b/src/Umbraco.Tests/UmbracoExamine/IndexInitializer.cs @@ -31,7 +31,7 @@ namespace Umbraco.Tests.UmbracoExamine { public static ContentValueSetBuilder GetContentValueSetBuilder(PropertyEditorCollection propertyEditors, bool publishedValuesOnly) { - var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new[] { new DefaultUrlSegmentProvider() }, GetMockUserService(), publishedValuesOnly); + var contentValueSetBuilder = new ContentValueSetBuilder(propertyEditors, new UrlSegmentProviderCollection(new[] { new DefaultUrlSegmentProvider() }), GetMockUserService(), publishedValuesOnly); return contentValueSetBuilder; } diff --git a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs index 0ad3bff109..192b0975d1 100644 --- a/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs +++ b/src/Umbraco.Tests/Web/Mvc/UmbracoViewPageTests.cs @@ -419,11 +419,13 @@ namespace Umbraco.Tests.Web.Mvc //var provider = new ScopeUnitOfWorkProvider(databaseFactory, new RepositoryFactory(Mock.Of())); var scopeProvider = TestObjects.GetScopeProvider(Mock.Of()); var factory = Mock.Of(); - _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, Enumerable.Empty(), + _service = new PublishedSnapshotService(svcCtx, factory, scopeProvider, cache, null, null, null, null, null, new TestDefaultCultureAccessor(), - Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), null, true, false); // no events + Current.Logger, TestObjects.GetGlobalSettings(), new SiteDomainHelper(), + Factory.GetInstance(), + null, true, false); // no events var http = GetHttpContextFactory(url, routeData).HttpContext; diff --git a/src/Umbraco.Web.UI.Client/package-lock.json b/src/Umbraco.Web.UI.Client/package-lock.json index b12843fbf7..f6d915eede 100644 --- a/src/Umbraco.Web.UI.Client/package-lock.json +++ b/src/Umbraco.Web.UI.Client/package-lock.json @@ -937,7 +937,7 @@ }, "ansi-colors": { "version": "1.1.0", - "resolved": "http://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { @@ -955,7 +955,7 @@ }, "ansi-escapes": { "version": "3.1.0", - "resolved": "http://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.1.0.tgz", "integrity": "sha512-UgAb8H9D41AQnu/PbWlCofQVcnV4Gs2bBJi9eZPxfU/hgglFh3SMDMENRIqdr7H6XFnXdoknctFByVsCOotTVw==", "dev": true }, @@ -1170,7 +1170,7 @@ "array-slice": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", - "integrity": "sha1-42jqFfibxwaff/uJrsOmx9SsItQ=", + "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true }, "array-sort": { @@ -1216,7 +1216,7 @@ "arraybuffer.slice": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", - "integrity": "sha1-O7xCdd1YTMGxCAm4nU6LY6aednU=", + "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==", "dev": true }, "asap": { @@ -1269,7 +1269,7 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha1-ePrtjD0HSrgfIrTphdeehzj3IPg=", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", "dev": true }, "asynckit": { @@ -2379,7 +2379,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -2459,7 +2459,7 @@ "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", - "integrity": "sha1-4TjMdeBAxyexlm/l5fjJruJW/js=", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==", "dev": true }, "continuable-cache": { @@ -2502,7 +2502,7 @@ "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", - "integrity": "sha1-+XJgj/DOrWi4QaFqky0LGDeRgU4=", + "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==", "dev": true }, "core-util-is": { @@ -3425,7 +3425,7 @@ "doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha1-XNAfwQFiG0LEzX9dGmYkNxbT850=", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", "dev": true, "requires": { "esutils": "^2.0.2" @@ -3953,7 +3953,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -3989,7 +3989,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -4153,7 +4153,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "optional": true } @@ -4248,7 +4248,7 @@ "eslint-scope": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz", - "integrity": "sha1-UL8wcekzi83EMzF5Sgy1M/ATYXI=", + "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==", "dev": true, "requires": { "esrecurse": "^4.1.0", @@ -4258,13 +4258,13 @@ "eslint-utils": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", - "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", + "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==", "dev": true }, "eslint-visitor-keys": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", - "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", + "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==", "dev": true }, "espree": { @@ -4287,7 +4287,7 @@ "esquery": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", + "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", "dev": true, "requires": { "estraverse": "^4.0.0" @@ -4296,7 +4296,7 @@ "esrecurse": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { "estraverse": "^4.1.0" @@ -4356,7 +4356,7 @@ "eventemitter3": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.0.tgz", - "integrity": "sha1-CQtNbNvWRe0Qv3UNS1QHlC17oWM=", + "integrity": "sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA==", "dev": true }, "exec-buffer": { @@ -4571,7 +4571,7 @@ "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", + "integrity": "sha1-6x53OrsFbc2N8r/favWbizqTZWU=", "dev": true, "requires": { "is-number": "^2.1.0", @@ -5885,7 +5885,7 @@ "global-modules": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", - "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { "global-prefix": "^1.0.1", @@ -6449,7 +6449,7 @@ "gulp-eslint": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gulp-eslint/-/gulp-eslint-5.0.0.tgz", - "integrity": "sha1-KiaECV93Syz3kxAmIHjFbMehK1I=", + "integrity": "sha512-9GUqCqh85C7rP9120cpxXuZz2ayq3BZc85pCTuPJS03VQYxne0aWPIXWx6LSvsGPa3uRqtSO537vaugOh+5cXg==", "dev": true, "requires": { "eslint": "^5.0.1", @@ -7307,7 +7307,7 @@ "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", - "integrity": "sha1-d3asYn8+p3JQz8My2rfd9eT10R0=", + "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==", "dev": true, "requires": { "isarray": "2.0.1" @@ -7458,7 +7458,7 @@ "http-proxy": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz", - "integrity": "sha1-etOElGWPhGBeL220Q230EPTlvpo=", + "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { "eventemitter3": "^3.0.0", @@ -7752,7 +7752,7 @@ "is-absolute": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz", - "integrity": "sha1-OV4a6EsR8mrReV5zwXN45IowFXY=", + "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { "is-relative": "^1.0.0", @@ -8047,7 +8047,7 @@ "is-relative": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz", - "integrity": "sha1-obtpNc6MXboei5dUubLcwCDiJg0=", + "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { "is-unc-path": "^1.0.0" @@ -8056,7 +8056,7 @@ "is-resolvable": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", - "integrity": "sha1-+xj4fOH+uSUWnJpAfBkxijIG7Yg=", + "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, "is-retry-allowed": { @@ -8104,7 +8104,7 @@ "is-unc-path": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz", - "integrity": "sha1-1zHoiY7QkKEsNSrS6u1Qla0yLJ0=", + "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { "unc-path-regex": "^0.1.2" @@ -8261,7 +8261,7 @@ "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, "json-stable-stringify-without-jsonify": { @@ -8388,7 +8388,7 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true } } @@ -9038,7 +9038,7 @@ "make-iterator": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha1-KbM/MSqo9UfEpeSQ9Wr87JkTOtY=", + "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { "kind-of": "^6.0.2" @@ -9219,7 +9219,7 @@ "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, "minimatch": { @@ -12735,7 +12735,7 @@ "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", - "integrity": "sha1-KYuJ34uTsCIdv0Ia0rGx6iP8Z3c=", + "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, "posix-character-classes": { @@ -13158,7 +13158,7 @@ }, "pretty-hrtime": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", + "resolved": "http://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=", "dev": true }, @@ -13225,7 +13225,7 @@ "qjobs": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz", - "integrity": "sha1-xF6cYYAL0IfviNfiVkI73Unl0HE=", + "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==", "dev": true }, "qs": { @@ -13421,7 +13421,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { @@ -13919,7 +13919,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, "sax": { @@ -14106,7 +14106,7 @@ "setprototypeof": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha1-0L2FU2iHtv58DYGMuWLZ2RxU5lY=", + "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", "dev": true }, "shebang-command": { @@ -14390,7 +14390,7 @@ "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -14670,7 +14670,7 @@ "stream-consume": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz", - "integrity": "sha1-0721mMK9CugrjKx6xQsRB6eZbEg=", + "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg==", "dev": true }, "stream-shift": { @@ -14682,7 +14682,7 @@ "streamroller": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-0.7.0.tgz", - "integrity": "sha1-odG3z4PTmvsNYwSaWsv5NJO99ks=", + "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", "dev": true, "requires": { "date-format": "^1.2.0", @@ -14709,7 +14709,7 @@ "readable-stream": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -14724,7 +14724,7 @@ "string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { "safe-buffer": "~5.1.0" @@ -14741,7 +14741,7 @@ "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { "is-fullwidth-code-point": "^2.0.0", @@ -15283,7 +15283,7 @@ "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { "os-tmpdir": "~1.0.2" @@ -15447,7 +15447,7 @@ "type-is": { "version": "1.6.16", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", - "integrity": "sha1-+JzjQVQcZysl7nrjxz3uOyvlAZQ=", + "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", "dev": true, "requires": { "media-typer": "0.3.0", @@ -15489,7 +15489,7 @@ "ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha1-n+FTahCmZKZSZqHjzPhf02MCvJw=", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", "dev": true }, "unc-path-regex": { @@ -15651,13 +15651,13 @@ "upath": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha1-NSVll+RqWB20eT0M5H+prr/J+r0=", + "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", "dev": true }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -15712,7 +15712,7 @@ "dependencies": { "lru-cache": { "version": "2.2.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "resolved": "http://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", "integrity": "sha1-bGWGGb7PFAMdDQtZSxYELOTcBj0=", "dev": true } @@ -16092,7 +16092,7 @@ "ws": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha1-8c+E/i1ekB686U767OeF8YeiKPI=", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { "async-limiter": "~1.0.0", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js index 8bad5ae8fd..3d784c999f 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/tags/umbtagseditor.directive.js @@ -130,8 +130,7 @@ if (!changes.value.isFirstChange() && changes.value.currentValue !== changes.value.previousValue) { configureViewModel(); - //this is required to re-validate - vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length); + reValidate() } } @@ -182,6 +181,8 @@ else { vm.onValueChanged({ value: [] }); } + + reValidate(); } /** @@ -189,7 +190,7 @@ */ function validateMandatory() { return { - isValid: !vm.validation.mandatory || (vm.viewModel != null && vm.viewModel.length > 0), + isValid: !vm.validation.mandatory || (vm.viewModel != null && vm.viewModel.length > 0)|| (vm.value != null && vm.value.length > 0), errorMsg: "Value cannot be empty", errorKey: "required" }; @@ -271,6 +272,10 @@ }); } + function reValidate() { + //this is required to re-validate + vm.tagEditorForm.tagCount.$setViewValue(vm.viewModel.length); + } } diff --git a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js index b6c87027b1..ec1175ab6c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js +++ b/src/Umbraco.Web.UI.Client/src/common/mocks/services/localization.mocks.js @@ -473,7 +473,6 @@ angular.module('umbraco.mocks'). "packager_packageUpgradeInstructions": "Upgrade instructions", "packager_packageUpgradeText": " There's an upgrade available for this package. You can download it directly from the Umbraco package repository.", "packager_packageVersion": "Package version", - "packager_packageVersionHistory": "Package version history", "packager_viewPackageWebsite": "View package website", "paste_doNothing": "Paste with full formatting (Not recommended)", "paste_errorMessage": "The text you're trying to paste contains special characters or formatting. This could be caused by copying text from Microsoft Word. Umbraco can remove special characters or formatting automatically, so the pasted content will be more suitable for the web.", diff --git a/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js b/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js index 8fd6836884..0d74d0fdd3 100644 --- a/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js +++ b/src/Umbraco.Web.UI.Client/src/common/resources/package.resource.js @@ -19,7 +19,7 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { return umbRequestHelper.resourcePromise( $http.get( umbRequestHelper.getApiUrl( - "packageInstallApiBaseUrl", + "packageApiBaseUrl", "GetInstalled")), 'Failed to get installed packages'); }, @@ -33,15 +33,6 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { 'Failed to validate package ' + name); }, - deleteCreatedPackage: function (packageId) { - return umbRequestHelper.resourcePromise( - $http.post( - umbRequestHelper.getApiUrl( - "packageInstallApiBaseUrl", - "DeleteCreatedPackage", { packageId: packageId })), - 'Failed to delete package ' + packageId); - }, - uninstall: function(packageId) { return umbRequestHelper.resourcePromise( $http.post( @@ -151,6 +142,84 @@ function packageResource($q, $http, umbDataFormatter, umbRequestHelper) { "packageInstallApiBaseUrl", "CleanUp"), umbPackage), 'Failed to install package. Error during the step "CleanUp" '); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#getCreated + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Gets a list of created packages + */ + getAllCreated: function() { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "GetCreatedPackages")), + 'Failed to get created packages'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#getCreatedById + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Gets a created package by id + */ + getCreatedById: function(id) { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "GetCreatedPackageById", + [{ id: id }])), + 'Failed to get package'); + }, + + getEmpty: function () { + return umbRequestHelper.resourcePromise( + $http.get( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "getEmpty")), + 'Failed to get scaffold'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#savePackage + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Creates or updates a package + */ + savePackage: function (umbPackage) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "PostSavePackage"), umbPackage), + 'Failed to create package'); + }, + + /** + * @ngdoc method + * @name umbraco.resources.packageInstallResource#deleteCreatedPackage + * @methodOf umbraco.resources.packageInstallResource + * + * @description + * Detes a created package + */ + deleteCreatedPackage: function (packageId) { + return umbRequestHelper.resourcePromise( + $http.post( + umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "DeleteCreatedPackage", { packageId: packageId })), + 'Failed to delete package ' + packageId); } }; } diff --git a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js index 6cdb8fd0f3..eecdd7eadc 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/editor.service.js @@ -162,7 +162,7 @@ When building a custom infinite editor view you can use the same components as a (function () { "use strict"; - function editorService(eventsService, keyboardService) { + function editorService(eventsService, keyboardService, $timeout) { let editorsKeyboardShorcuts = []; var editors = []; @@ -245,10 +245,14 @@ When building a custom infinite editor view you can use the same components as a // emit event to let components know an editor has been removed eventsService.emit("appState.editors.close", args); - - // rebind keyboard shortcuts for the new editor in focus - rebindKeyboardShortcuts(); + // delay required to map the properties to the correct editor due + // to another delay in the closing animation of the editor + $timeout(function() { + // rebind keyboard shortcuts for the new editor in focus + rebindKeyboardShortcuts(); + }, 0); + } /** diff --git a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js index 6bab8fda81..51f63e6787 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/events.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/events.service.js @@ -6,7 +6,6 @@ app.ready app.authenticated app.notAuthenticated - app.ysod app.reInitialize app.userRefresh app.navigationReady diff --git a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js index f3b64e0c28..191e0a22c0 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/formhelper.service.js @@ -110,7 +110,7 @@ function formHelper(angularHelper, serverValidationManager, notificationsService //Or, some strange server error if (err.status === 400) { //now we need to look through all the validation errors - if (err.data && (err.data.ModelState)) { + if (err.data && err.data.ModelState) { //wire up the server validation errs this.handleServerValidation(err.data.ModelState); @@ -118,9 +118,11 @@ function formHelper(angularHelper, serverValidationManager, notificationsService //execute all server validation events and subscribers serverValidationManager.notifyAndClearAllSubscriptions(); } - else { - overlayService.ysod(err); - } + } + else { + + //TODO: All YSOD handling should be done with an interceptor + overlayService.ysod(err); } }, diff --git a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js index 6c50e58490..6de0b4170b 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/overlay.service.js @@ -27,6 +27,11 @@ overlay.position = "center"; } + // use a default empty view if nothing is set + if(!overlay.view) { + overlay.view = "views/common/overlays/default/default.html"; + } + // option to disable backdrop clicks if(overlay.disableBackdropClick) { backdropOptions.disableEventsOnClick = true; diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js index e31742e660..1e6fc5d643 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbdataformatter.service.js @@ -64,7 +64,7 @@ var saveModel = _.pick(displayModel, 'compositeContentTypes', 'isContainer', 'allowAsRoot', 'allowedTemplates', 'allowedContentTypes', 'alias', 'description', 'thumbnail', 'name', 'id', 'icon', 'trashed', - 'key', 'parentId', 'alias', 'path', 'allowCultureVariant'); + 'key', 'parentId', 'alias', 'path', 'allowCultureVariant', 'isElement'); //TODO: Map these saveModel.allowedTemplates = _.map(displayModel.allowedTemplates, function (t) { return t.alias; }); @@ -262,7 +262,7 @@ saveModel[props[m]] = startId.id; } - saveModel.parentId = -1; + saveModel.parentId = -1; return saveModel; }, @@ -293,7 +293,7 @@ }); saveModel.email = propEmail.value.trim(); saveModel.username = propLogin.value.trim(); - + saveModel.password = this.formatChangePasswordModel(propPass.value); var selectedGroups = []; @@ -336,7 +336,7 @@ /** formats the display model used to display the media to the model used to save the media */ formatMediaPostData: function (displayModel, action) { - //NOTE: the display model inherits from the save model so we can in theory just post up the display model but + //NOTE: the display model inherits from the save model so we can in theory just post up the display model but // we don't want to post all of the data as it is unecessary. var saveModel = { id: displayModel.id, @@ -354,7 +354,7 @@ /** formats the display model used to display the content to the model used to save the content */ formatContentPostData: function (displayModel, action) { - //NOTE: the display model inherits from the save model so we can in theory just post up the display model but + //NOTE: the display model inherits from the save model so we can in theory just post up the display model but // we don't want to post all of the data as it is unecessary. var saveModel = { id: displayModel.id, @@ -379,7 +379,7 @@ var propExpireDate = displayModel.removeDate; var propReleaseDate = displayModel.releaseDate; var propTemplate = displayModel.template; - + saveModel.expireDate = propExpireDate ? propExpireDate : null; saveModel.releaseDate = propReleaseDate ? propReleaseDate : null; saveModel.templateAlias = propTemplate ? propTemplate : null; @@ -389,8 +389,8 @@ /** * This formats the server GET response for a content display item - * @param {} displayModel - * @returns {} + * @param {} displayModel + * @returns {} */ formatContentGetData: function(displayModel) { @@ -418,7 +418,7 @@ } }); }); - + //now assign this same invariant property instance to the same index of the other variants property array for (var j = 1; j < displayModel.variants.length; j++) { diff --git a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js index fcb5585d5d..fb1a1b8d5e 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js @@ -3,7 +3,7 @@ * @name umbraco.services.umbRequestHelper * @description A helper object used for sending requests to the server **/ -function umbRequestHelper($http, $q, notificationsService, eventsService, formHelper) { +function umbRequestHelper($http, $q, notificationsService, eventsService, formHelper, overlayService) { return { @@ -176,11 +176,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe //show a ysod dialog if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { - eventsService.emit('app.ysod', - { - errorMsg: 'An error occured', - data: response.data - }); + const error = { errorMsg: 'An error occured', data: response.data }; + //TODO: All YSOD handling should be done with an interceptor + overlayService.ysod(error); } else { //show a simple error notification @@ -290,11 +288,9 @@ function umbRequestHelper($http, $q, notificationsService, eventsService, formHe } else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { //show a ysod dialog - eventsService.emit('app.ysod', - { - errorMsg: 'An error occured', - data: response.data - }); + const error = { errorMsg: 'An error occured', data: response.data }; + //TODO: All YSOD handling should be done with an interceptor + overlayService.ysod(error); } else { //show a simple error notification diff --git a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js index 30a7e2ac7d..c2b2ba26d7 100644 --- a/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js +++ b/src/Umbraco.Web.UI.Client/src/controllers/main.controller.js @@ -103,14 +103,6 @@ function MainController($scope, $location, appState, treeService, notificationsS })); - evts.push(eventsService.on("app.ysod", function (name, error) { - $scope.ysodOverlay = { - view: "ysod", - error: error, - show: true - }; - })); - // events for search evts.push(eventsService.on("appState.searchState.changed", function (e, args) { if (args.key === "show") { diff --git a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js index a8e0530417..cc92935b4d 100644 --- a/src/Umbraco.Web.UI.Client/src/installer/installer.service.js +++ b/src/Umbraco.Web.UI.Client/src/installer/installer.service.js @@ -1,4 +1,4 @@ -angular.module("umbraco.install").factory('installerService', function($rootScope, $q, $timeout, $http, $location, $log){ +angular.module("umbraco.install").factory('installerService', function ($rootScope, $q, $timeout, $http, $templateRequest){ var _status = { index: 0, @@ -106,19 +106,26 @@ angular.module("umbraco.install").factory('installerService', function($rootScop //loads the needed steps and sets the intial state init : function(){ service.status.loading = true; - if(!_status.all){ - service.getSteps().then(function(response){ - service.status.steps = response.data.steps; - service.status.index = 0; - _installerModel.installId = response.data.installId; - service.findNextStep(); + if (!_status.all) { + //pre-load the error page, if an error occurs, the page might not be able to load + // so we want to make sure it's available in the templatecache first + $templateRequest("views/install/error.html").then(x => { + service.getSteps().then(response => { + service.status.steps = response.data.steps; + service.status.index = 0; + _installerModel.installId = response.data.installId; + service.findNextStep(); - $timeout(function(){ - service.status.loading = false; - service.status.configuring = true; - }, 2000); - }); - } + $timeout(function() { + service.status.loading = false; + service.status.configuring = true; + }, + 2000); + }); + }); + + + } }, //loads available packages from our.umbraco.com diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less index 75f58f983b..99759fcee7 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-package-local-install.less @@ -15,7 +15,7 @@ height: 300px; border: 2px dashed @gray-8; border-radius: 3px; - background: @gray-10; + background: @white; display: flex; flex-direction: column; justify-content: center; @@ -74,7 +74,6 @@ // Info state .umb-info-local-items { - border: 2px solid @gray-8; border-radius: 3px; background: @gray-10; display: flex; @@ -84,6 +83,8 @@ margin: 0 20px; width: 100%; max-width: 540px; + background: @white; + box-shadow: 0 1px 1px 0 rgba(0,0,0,.16); } .umb-info-local-items a { diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less index e2cfb0bded..a517605c4a 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-packages.less @@ -23,9 +23,7 @@ .umb-packages-search { width: 100%; - background: @gray-10; border-radius: 3px; - padding: 30px; box-sizing: border-box; } @@ -49,60 +47,14 @@ } .umb-packages { - margin: 0 -10px; - display: flex; - flex-wrap: wrap; + display: grid; + grid-gap: 20px; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); } // Cards .umb-package { - padding: 10px; box-sizing: border-box; - flex: 0 0 100%; - max-width: 100%; -} - -@media (min-width: 768px) { - .umb-package { - flex: 0 0 50%; - max-width: 50%; - } -} - -@media (min-width: 1200px) { - .umb-package { - flex: 0 0 33.33%; - max-width: 33.33%; - } -} - -@media (min-width: 1400px) { - .umb-package { - flex: 0 0 25%; - max-width: 25%; - } -} - -@media (min-width: 1700px) { - .umb-package { - flex: 0 0 20%; - max-width: 20%; - } -} - - -@media (min-width: 1900px) { - .umb-package { - flex: 0 0 16.66%; - max-width: 16.66%; - } -} - -@media (min-width: 2200px) { - .umb-package { - flex: 0 0 14.28%; - max-width: 14.28%; - } } .umb-package-link { @@ -114,10 +66,11 @@ box-sizing: border-box; height: 100%; width: 100%; - border: 1px solid @gray-9; border-radius: 3px; text-decoration: none !important; transition: border-color 100ms ease; + background-color: @white; + box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16); &:hover { border-color: @turquoise; @@ -151,15 +104,8 @@ // Info .umb-package-info { - padding-right: 15px; - padding-bottom: 15px; - padding-left: 15px; - padding-top: 15px; + padding: 15px; text-align: center; - background: @gray-10; - border-bottom-left-radius: 3px; - border-bottom-right-radius: 3px; - border-top: 1px solid @gray-9; } @@ -251,6 +197,7 @@ border-bottom: 1px solid @gray-8; border-right: 1px solid @gray-8; padding: 10px 0; + background: @white; } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html index 84fbab7cb2..bf74431d96 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/compositions/compositions.html @@ -110,6 +110,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> @@ -138,4 +139,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html index b4ff894c4d..620f9f1731 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/datatypesettings/datatypesettings.html @@ -52,6 +52,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html index f14fb364ab..e48ec84b25 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/embed/embed.html @@ -54,6 +54,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> @@ -75,4 +76,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html index 2ccbf11cc1..ea247c77e5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertcodesnippet/insertcodesnippet.html @@ -47,6 +47,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html index 56bd498fd1..16f4bfb919 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/insertfield/insertfield.html @@ -207,6 +207,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html index a50ab4242d..71fcf2f493 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/linkpicker/linkpicker.html @@ -120,6 +120,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html index 13af3e4108..7240ec8f06 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/membergrouppicker/membergrouppicker.html @@ -32,6 +32,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html index df5bbe8ca5..4c7f2613b5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/propertysettings/propertysettings.html @@ -155,6 +155,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html index 4b6c6cc179..725871337d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html @@ -193,6 +193,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> @@ -209,4 +210,4 @@ - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html index b5b925b266..d7ba57c1af 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/rollback/rollback.html @@ -86,6 +86,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/sectionpicker/sectionpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/sectionpicker/sectionpicker.html index 8ca1993dcc..2e88bf709c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/sectionpicker/sectionpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/sectionpicker/sectionpicker.html @@ -38,6 +38,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html index d6e3996287..5b946976d7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/templatesections/templatesections.html @@ -87,6 +87,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/usergrouppicker/usergrouppicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/usergrouppicker/usergrouppicker.html index e97d80648b..e2ae1ab524 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/usergrouppicker/usergrouppicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/usergrouppicker/usergrouppicker.html @@ -84,6 +84,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html index bc6c8b5761..e39d693b47 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/userpicker/userpicker.html @@ -74,6 +74,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - \ No newline at end of file + diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js index 92e02d0d14..e594bae2f4 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.delete.controller.js @@ -65,6 +65,7 @@ function ContentDeleteController($scope, $timeout, contentResource, treeService, //check if response is ysod if (err.status && err.status >= 500) { + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(err); } }); diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js index 4a7a870618..317fe094ae 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.controller.js @@ -25,6 +25,7 @@ vm.removeChild = removeChild; vm.toggleAllowAsRoot = toggleAllowAsRoot; vm.toggleAllowCultureVariants = toggleAllowCultureVariants; + vm.toggleIsElement = toggleIsElement; /* ---------- INIT ---------- */ @@ -84,25 +85,18 @@ $scope.model.allowedContentTypes.splice(selectedChildIndex, 1); } - /** - * Toggle the $scope.model.allowAsRoot value to either true or false - */ - function toggleAllowAsRoot(){ - if($scope.model.allowAsRoot){ - $scope.model.allowAsRoot = false; - return; - } + // note: "safe toggling" here ie handling cases where the value is undefined, etc - $scope.model.allowAsRoot = true; + function toggleAllowAsRoot() { + $scope.model.allowAsRoot = $scope.model.allowAsRoot ? false : true; } function toggleAllowCultureVariants() { - if ($scope.model.allowCultureVariant) { - $scope.model.allowCultureVariant = false; - return; - } + $scope.model.allowCultureVariant = $scope.model.allowCultureVariant ? false : true; + } - $scope.model.allowCultureVariant = true; + function toggleIsElement() { + $scope.model.isElement = $scope.model.isElement ? false : true; } } diff --git a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html index ec1e528f8c..0d74c655d7 100644 --- a/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html +++ b/src/Umbraco.Web.UI.Client/src/views/documenttypes/views/permissions/permissions.html @@ -53,9 +53,25 @@ hotkey="alt+shift+v"> - + - +
+ +
+
+ +
+ +
+ + +
+ +
+ + diff --git a/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js b/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js index e5e95e94df..70b5120ebe 100644 --- a/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/media/media.delete.controller.js @@ -59,6 +59,7 @@ function MediaDeleteController($scope, mediaResource, treeService, navigationSer //check if response is ysod if (err.status && err.status >= 500) { + //TODO: All YSOD handling should be done with an interceptor overlayService.ysod(err); } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js new file mode 100644 index 0000000000..4afe62786e --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.controller.js @@ -0,0 +1,252 @@ +(function () { + "use strict"; + + function EditController($scope, $location, $routeParams, umbRequestHelper, entityResource, stylesheetResource, languageResource, packageResource, dictionaryResource, editorService, formHelper) { + + const vm = this; + + vm.showBackButton = true; + + // open all expansion panels + vm.propertiesOpen = true; + vm.contentOpen = true; + vm.filesOpen = true; + vm.actionsOpen = true; + vm.loading = true; + vm.back = back; + vm.createOrUpdatePackage = createOrUpdatePackage; + vm.removeContentItem = removeContentItem; + vm.openContentPicker = openContentPicker; + vm.openFilePicker = openFilePicker; + vm.removeFile = removeFile; + vm.openControlPicker = openControlPicker; + vm.removeControl = removeControl; + vm.downloadFile = downloadFile; + + const packageId = $routeParams.id; + const create = $routeParams.create; + + function onInit() { + + if(create) { + //pre populate package with some values + packageResource.getEmpty().then(scaffold => { + vm.package = scaffold; + vm.loading = false; + }); + vm.buttonLabel = "Create"; + } else { + // load package + packageResource.getCreatedById(packageId).then(createdPackage => { + vm.package = createdPackage; + vm.loading = false; + // get render model for content node + if(vm.package.contentNodeId) { + entityResource.getById(vm.package.contentNodeId, "Document") + .then((entity) => { + vm.contentNodeDisplayModel = entity; + }); + } + + }); + vm.buttonLabel = "Save"; + } + + // get all doc types + entityResource.getAll("DocumentType").then(documentTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + documentTypes.forEach(documentType => { + documentType.id = documentType.id.toString(); + }); + vm.documentTypes = documentTypes; + }); + + // get all templates + entityResource.getAll("Template").then(templates => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + templates.forEach(template => { + template.id = template.id.toString(); + }); + vm.templates = templates; + }); + + // get all stylesheets + stylesheetResource.getAll().then(stylesheets => { + vm.stylesheets = stylesheets; + }); + + entityResource.getAll("Macro").then(macros => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + macros.forEach(macro => { + macro.id = macro.id.toString(); + }); + vm.macros = macros; + }); + + // get all languages + languageResource.getAll().then(languages => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + languages.forEach(language => { + language.id = language.id.toString(); + }); + vm.languages = languages; + }); + + // get all dictionary items + dictionaryResource.getList().then(dictionaryItems => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + dictionaryItems.forEach(dictionaryItem => { + dictionaryItem.id = dictionaryItem.id.toString(); + }); + vm.dictionaryItems = dictionaryItems; + }); + + // get all data types items + entityResource.getAll("DataType").then(dataTypes => { + // a package stores the id as a string so we + // need to convert all ids to string for comparison + dataTypes.forEach(dataType => { + dataType.id = dataType.id.toString(); + }); + vm.dataTypes = dataTypes; + }); + + } + + function downloadFile(id) { + var url = umbRequestHelper.getApiUrl( + "packageApiBaseUrl", + "DownloadCreatedPackage", + { id: id }); + + umbRequestHelper.downloadFile(url).then(function () { + + }); + } + + function back() { + $location.path("packages/packages/overview").search('create', null);; + } + + function createOrUpdatePackage(editPackageForm) { + + if (formHelper.submitForm({ formCtrl: editPackageForm, scope: $scope })) { + + vm.buttonState = "busy"; + + packageResource.savePackage(vm.package).then((updatedPackage) => { + + vm.package = updatedPackage; + vm.buttonState = "success"; + + formHelper.resetForm({ scope: $scope }); + + if (create) { + //if we are creating, then redirect to the correct url and reload + $location.path("packages/packages/edit/" + vm.package.id).search("subview", "created").search("create", null); + //don't add a browser history for this + $location.replace(); + } + + }, function(err){ + formHelper.handleError(err); + vm.buttonState = "error"; + }); + } + } + + function removeContentItem() { + vm.package.contentNodeId = null; + } + + function openContentPicker() { + const contentPicker = { + submit: function(model) { + if(model.selection && model.selection.length > 0) { + vm.package.contentNodeId = model.selection[0].id.toString(); + vm.contentNodeDisplayModel = model.selection[0]; + } + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.contentPicker(contentPicker); + } + + function openFilePicker() { + + let selection = angular.copy(vm.package.files); + + const filePicker = { + title: "Select files", + section: "settings", + treeAlias: "files", + entityType: "file", + multiPicker: true, + onlyInitialized: false, + select: function(node) { + node.selected = !node.selected; + + const id = unescape(node.id); + const index = selection.indexOf(id); + + if(node.selected) { + if(index === -1) { + selection.push(id); + } + } else { + selection.splice(index, 1); + } + }, + submit: function() { + vm.package.files = selection; + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.treePicker(filePicker); + } + + function removeFile(index) { + vm.package.files.splice(index, 1); + } + + function openControlPicker() { + const controlPicker = { + title: "Select control", + section: "settings", + treeAlias: "files", + entityType: "file", + onlyInitialized: false, + select: function(node) { + const id = unescape(node.id); + vm.package.loadControl = id; + editorService.close(); + }, + close: function() { + editorService.close(); + } + }; + editorService.treePicker(controlPicker); + } + + function removeControl() { + vm.package.loadControl = null; + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Packages.EditController", EditController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/edit.html b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html new file mode 100644 index 0000000000..8ed1a43e8a --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/edit.html @@ -0,0 +1,336 @@ +
+ +
+ + + + + + + + + + +
+ +
+
Package Properties
+   +
+ +
+ + + + + + Required + {{editPackageForm.url.errorMsg}} + + + + + + + + Required + {{editPackageForm.version.errorMsg}} + + + + + + + + + + + + + + {{editPackageForm.umbracoVersion.errorMsg}} + + + + + + + + Required + {{editPackageForm.author.errorMsg}} + + + + + + + + Required + {{editPackageForm.authorUrl.errorMsg}} + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+
Package Content
+   +
+ +
+ + + + + + + + Add + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+ +
+ + +
+ +
+
+ + +
+ +
+
+ + +
+ +
+
+ +
+ +
+ +
+ +
+
Package Files
+   +
+ +
+ + + + + + + + Add + + + + + + + + + + + Add + + + +
+ +
+ +
+
+
Package Actions
+   +
+
+ +
+ Documentation +
+ +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + +
+ +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js index 42fcddaa56..ccd86c6f6c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesOverviewController($scope, $route, $location, navigationService, $timeout, localStorageService) { + function PackagesOverviewController($scope, $location, localStorageService) { //Hack! // if there is a cookie value for packageInstallUri then we need to redirect there, @@ -9,10 +9,13 @@ // because it will double load it. // we will refresh and then navigate there. - var installPackageUri = localStorageService.get("packageInstallUri"); + let installPackageUri = localStorageService.get("packageInstallUri"); + let packageUri = $location.search().subview; + if (installPackageUri) { localStorageService.remove("packageInstallUri"); } + if (installPackageUri && installPackageUri !== "installed") { //navigate to the custom installer screen, if it is just "installed", then we'll //show the installed view @@ -21,6 +24,8 @@ else { var vm = this; + packageUri = installPackageUri ? installPackageUri : packageUri; //use the path stored in storage over the one in the current path + vm.page = {}; vm.page.name = "Packages"; vm.page.navigation = [ @@ -28,28 +33,44 @@ "name": "Packages", "icon": "icon-cloud", "view": "views/packages/views/repo.html", - "active": !installPackageUri || installPackageUri === "navigation", - "alias": "umbPackages" + "active": !packageUri || packageUri === "navigation", + "alias": "umbPackages", + "action": function() { + $location.search("subview", "navigation"); + } }, { "name": "Installed", "icon": "icon-box", "view": "views/packages/views/installed.html", - "active": installPackageUri === "installed", - "alias": "umbInstalled" + "active": packageUri === "installed", + "alias": "umbInstalled", + "action": function() { + $location.search("subview", "installed"); + } }, { "name": "Install local", "icon": "icon-add", "view": "views/packages/views/install-local.html", - "active": installPackageUri === "local", - "alias": "umbInstallLocal" + "active": packageUri === "local", + "alias": "umbInstallLocal", + "action": function() { + $location.search("subview", "local"); + } + }, + { + "name": "Created", + "icon": "icon-add", + "view": "views/packages/views/created.html", + "active": packageUri === "created", + "alias": "umbCreatedPackages", + "action": function() { + $location.search("subview", "created"); + } } ]; - $timeout(function () { - navigationService.syncTree({ tree: "packages", path: "-1" }); - }); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html index 250dc889d6..84f594b48d 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/overview.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/overview.html @@ -1,28 +1,24 @@
-
+ - + + - - + - + + - - + - - - - - +
diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js new file mode 100644 index 0000000000..9d06ea1092 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.controller.js @@ -0,0 +1,72 @@ +(function () { + "use strict"; + + function CreatedController($timeout, $location, packageResource, localizationService, overlayService) { + + const vm = this; + + vm.deleteCreatedPackage = deleteCreatedPackage; + vm.goToPackage = goToPackage; + vm.createPackage = createPackage; + + function onInit() { + + vm.createdPackages = []; + + packageResource.getAllCreated().then(createdPackages => { + vm.createdPackages = createdPackages; + }, angular.noop); + + } + + function deleteCreatedPackage(event, index, createdPackage) { + + event.stopPropagation(); + event.preventDefault(); + + const dialog = { + submitButtonLabelKey: "contentTypeEditor_yesDelete", + submit: function (model) { + performDelete(index, createdPackage); + overlayService.close(); + }, + close: function () { + overlayService.close(); + } + }; + + const keys = [ + "general_delete", + "defaultdialogs_confirmdelete" + ]; + + localizationService.localizeMany(keys).then(values => { + dialog.title = values[0]; + dialog.content = values[1]; + overlayService.open(dialog); + }); + + } + + function performDelete(index, createdPackage) { + packageResource.deleteCreatedPackage(createdPackage.id).then(()=> { + vm.createdPackages.splice(index, 1); + }, angular.noop); + } + + function goToPackage(createdPackage) { + $location.path("packages/packages/edit/" + createdPackage.id); + } + + function createPackage() { + $location.search('create', null); + $location.path("packages/packages/edit/-1").search("create", "true"); + } + + onInit(); + + } + + angular.module("umbraco").controller("Umbraco.Editors.Packages.CreatedController", CreatedController); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html new file mode 100644 index 0000000000..632d2e2b3c --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/created.html @@ -0,0 +1,51 @@ +
+ + + + + + + + + + + + + + + + + +
+
+ + +
+
+
{{ createdPackage.name }}
+
+ {{ createdPackage.version }} | {{ createdPackage.url }}| {{ createdPackage.author }} +
+
+
+ + +
+ + + No packages have been created yet + + +
\ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js index 8be7d1b17a..17b417de48 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js @@ -1,7 +1,7 @@ (function () { "use strict"; - function PackagesInstallLocalController($scope, $route, $location, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService, $q) { + function PackagesInstallLocalController($scope, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService, $q) { var vm = this; vm.state = "upload"; @@ -10,7 +10,7 @@ vm.installPackage = installPackage; vm.installState = { status: "", - progress:0 + progress: 0 }; vm.installCompleted = false; vm.zipFile = { @@ -27,6 +27,23 @@ } }; + var labels = {}; + var labelKeys = [ + "packager_installStateImporting", + "packager_installStateInstalling", + "packager_installStateRestarting", + "packager_installStateComplete", + "packager_installStateCompleted" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.installStateImporting = values[0]; + labels.installStateInstalling = values[1]; + labels.installStateRestarting = values[2]; + labels.installStateComplete = values[3]; + labels.installStateCompleted = values[4]; + }); + function upload(file) { Upload.upload({ @@ -34,10 +51,10 @@ fields: {}, file: file }).progress(function (evt) { - + // hack: in some browsers the progress event is called after success // this prevents the UI from going back to a uploading state - if(vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") { + if (vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") { // set view state to uploading vm.state = 'uploading'; @@ -110,83 +127,85 @@ } function installPackage() { - vm.installState.status = localizationService.localize("packager_installStateImporting"); + + vm.installState.status = labels.installStateImporting; vm.installState.progress = "0"; packageResource - .import(vm.localPackage) - .then(function(pack) { - vm.installState.progress = "25"; - vm.installState.status = localizationService.localize("packager_installStateInstalling"); - return packageResource.installFiles(pack); - }, + .import(vm.localPackage) + .then(function (pack) { + vm.installState.progress = "25"; + vm.installState.status = labels.installStateInstalling; + return packageResource.installFiles(pack); + }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "50"; - var deferred = $q.defer(); + .then(function (pack) { + vm.installState.status = labels.installStateRestarting; + vm.installState.progress = "50"; + var deferred = $q.defer(); - //check if the app domain is restarted ever 2 seconds - var count = 0; - function checkRestart() { - $timeout(function () { + //check if the app domain is restarted ever 2 seconds + var count = 0; + + function checkRestart() { + $timeout(function () { packageResource.checkRestart(pack).then(function (d) { count++; //if there is an id it means it's not restarted yet but we'll limit it to only check 10 times if (d.isRestarting && count < 10) { - checkRestart(); + checkRestart(); } else { - //it's restarted! - deferred.resolve(d); + //it's restarted! + deferred.resolve(d); } - }, - installError); - }, 2000); - } + }, + installError); + }, + 2000); + } - checkRestart(); - - return deferred.promise; - }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "75"; - return packageResource.installData(pack); - }, + checkRestart(); + + return deferred.promise; + }, installError) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateComplete"); - vm.installState.progress = "100"; - return packageResource.cleanUp(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateInstalling; + vm.installState.progress = "75"; + return packageResource.installData(pack); + }, installError) - .then(function(result) { + .then(function (pack) { + vm.installState.status = labels.installStateComplete; + vm.installState.progress = "100"; + return packageResource.cleanUp(pack); + }, + installError) + .then(function (result) { - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - localStorageService.set("packageInstallUri", result.postInstallationPath); - } - else { - //set to a constant value so it knows to just go to the installed view - localStorageService.set("packageInstallUri", "installed"); - } + if (result.postInstallationPath) { + //Put the redirect Uri in a cookie so we can use after reloading + localStorageService.set("packageInstallUri", result.postInstallationPath); + } + else { + //set to a constant value so it knows to just go to the installed view + localStorageService.set("packageInstallUri", "installed"); + } - vm.installState.status = localizationService.localize("packager_installStateCompleted"); - vm.installCompleted = true; - - + vm.installState.status = labels.installStateCompleted; + vm.installCompleted = true; - }, - installError); + + }, installError); } - + function installError() { //This will return a rejection meaning that the promise change above will stop return $q.reject(); } - vm.reloadPage = function() { + vm.reloadPage = function () { //reload on next digest (after cookie) $timeout(function () { $window.location.reload(true); diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html index f7843fb0fc..b657c42877 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html @@ -4,7 +4,7 @@
-
+
Install a local package by selecting it from your machine. Only install packages from sources you know and trust.

- +
@@ -118,6 +118,14 @@
Version {{ vm.localPackage.version }} +

+ + + Upgrading from version + {{ vm.localPackage.originalVersion }} + + +

@@ -128,7 +136,7 @@
Read me
- +
@@ -136,21 +144,19 @@ I accept terms of use - +
- +
@@ -165,9 +171,8 @@
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js index 395800d329..b77326d2fc 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.controller.js @@ -32,6 +32,8 @@ vm.search = search; vm.installCompleted = false; + var labels = {}; + var currSort = "Latest"; //used to cancel any request in progress if another one needs to take it's place var canceler = null; @@ -52,22 +54,38 @@ vm.loading = true; + var labelKeys = [ + "packager_installStateImporting", + "packager_installStateInstalling", + "packager_installStateRestarting", + "packager_installStateComplete", + "packager_installStateCompleted" + ]; + + localizationService.localizeMany(labelKeys).then(function (values) { + labels.installStateImporting = values[0]; + labels.installStateInstalling = values[1]; + labels.installStateRestarting = values[2]; + labels.installStateComplete = values[3]; + labels.installStateCompleted = values[4]; + }); + $q.all([ - ourPackageRepositoryResource.getCategories() - .then(function(cats) { + ourPackageRepositoryResource.getCategories() + .then(function (cats) { vm.categories = cats; }), - ourPackageRepositoryResource.getPopular(8) - .then(function(pack) { + ourPackageRepositoryResource.getPopular(8) + .then(function (pack) { vm.popular = pack.packages; }), - ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort) - .then(function(pack) { + ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); }) - ]) - .then(function() { + ]) + .then(function () { vm.loading = false; }); @@ -94,18 +112,18 @@ currSort = "Latest"; $q.all([ - ourPackageRepositoryResource.getPopular(8, searchCategory) - .then(function(pack) { + ourPackageRepositoryResource.getPopular(8, searchCategory) + .then(function (pack) { vm.popular = pack.packages; }), - ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort, searchCategory, vm.searchQuery) - .then(function(pack) { + ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, vm.pagination.pageSize, currSort, searchCategory, vm.searchQuery) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); vm.pagination.pageNumber = 1; }) - ]) - .then(function() { + ]) + .then(function () { vm.loading = false; selectedCategory.active = reset === false; }); @@ -115,12 +133,12 @@ ourPackageRepositoryResource.getDetails(selectedPackage.id) .then(function (pack) { packageResource.validateInstalled(pack.name, pack.latestVersion) - .then(function() { + .then(function () { //ok, can install vm.package = pack; vm.package.isValid = true; vm.packageViewState = "packageDetails"; - }, function() { + }, function () { //nope, cannot install vm.package = pack; vm.package.isValid = false; @@ -130,7 +148,7 @@ } function setPackageViewState(state) { - if(state) { + if (state) { vm.packageViewState = state; } } @@ -164,13 +182,13 @@ packageResource .fetch(selectedPackage.id) - .then(function(pack) { - vm.packageViewState = "packageInstall"; - vm.loading = false; - vm.localPackage = pack; - vm.localPackage.allowed = true; + .then(function (pack) { + vm.packageViewState = "packageInstall"; + vm.loading = false; + vm.localPackage = pack; + vm.localPackage.allowed = true; }, function (evt, status, headers, config) { - + if (status == 400) { //it's a validation error vm.installState.type = "error"; @@ -186,68 +204,68 @@ function installPackage(selectedPackage) { - vm.installState.status = localizationService.localize("packager_installStateImporting"); + vm.installState.status = labels.installStateImporting; vm.installState.progress = "0"; packageResource .import(selectedPackage) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateInstalling"); - vm.installState.progress = "25"; - return packageResource.installFiles(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateInstalling; + vm.installState.progress = "25"; + return packageResource.installFiles(pack); + }, error) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "50"; - var deferred = $q.defer(); + .then(function (pack) { + vm.installState.status = labels.installStateRestarting; + vm.installState.progress = "50"; + var deferred = $q.defer(); - //check if the app domain is restarted ever 2 seconds - var count = 0; - function checkRestart() { - $timeout(function () { + //check if the app domain is restarted ever 2 seconds + var count = 0; + function checkRestart() { + $timeout(function () { packageResource.checkRestart(pack).then(function (d) { count++; //if there is an id it means it's not restarted yet but we'll limit it to only check 10 times if (d.isRestarting && count < 10) { - checkRestart(); + checkRestart(); } else { - //it's restarted! - deferred.resolve(d); + //it's restarted! + deferred.resolve(d); } - }, - error); - }, 2000); - } + }, + error); + }, 2000); + } - checkRestart(); - - return deferred.promise; - }, error) + checkRestart(); + + return deferred.promise; + }, error) .then(function (pack) { - vm.installState.status = localizationService.localize("packager_installStateRestarting"); - vm.installState.progress = "75"; - return packageResource.installData(pack); - }, + vm.installState.status = labels.installStateInstalling; + vm.installState.progress = "75"; + return packageResource.installData(pack); + }, error) - .then(function(pack) { - vm.installState.status = localizationService.localize("packager_installStateComplete"); - vm.installState.progress = "100"; - return packageResource.cleanUp(pack); - }, + .then(function (pack) { + vm.installState.status = labels.installStateComplete; + vm.installState.progress = "100"; + return packageResource.cleanUp(pack); + }, error) - .then(function(result) { + .then(function (result) { - if (result.postInstallationPath) { - //Put the redirect Uri in a cookie so we can use after reloading - localStorageService.set("packageInstallUri", result.postInstallationPath); - } + if (result.postInstallationPath) { + //Put the redirect Uri in a cookie so we can use after reloading + localStorageService.set("packageInstallUri", result.postInstallationPath); + } - vm.installState.status = localizationService.localize("packager_installStateCompleted"); - vm.installCompleted = true; + vm.installState.status = labels.installStateCompleted; + vm.installCompleted = true; - }, + }, error); } @@ -265,7 +283,7 @@ } - var searchDebounced = _.debounce(function(e) { + var searchDebounced = _.debounce(function (e) { $scope.$apply(function () { @@ -281,12 +299,12 @@ currSort = vm.searchQuery ? "Default" : "Latest"; ourPackageRepositoryResource.search(vm.pagination.pageNumber - 1, - vm.pagination.pageSize, - currSort, - "", - vm.searchQuery, - canceler) - .then(function(pack) { + vm.pagination.pageSize, + currSort, + "", + vm.searchQuery, + canceler) + .then(function (pack) { vm.packages = pack.packages; vm.pagination.totalPages = Math.ceil(pack.total / vm.pagination.pageSize); vm.pagination.pageNumber = 1; diff --git a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html index 7b4b960c2c..93c2ce05ed 100644 --- a/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html +++ b/src/Umbraco.Web.UI.Client/src/views/packages/views/repo.html @@ -27,7 +27,7 @@

Popular

-
+
@@ -61,7 +61,7 @@

New Releases

Results for '{{ vm.searchQuery }}'

-
+
@@ -129,147 +129,153 @@
-
{{ vm.package.name }}
+ + +
{{ vm.package.name }}
+
+
+ + -
- - - - - + +
-
- - - - - - - -
- -
-
- -
- + + - -
+ action="vm.downloadPackage(vm.package)" + label-key="packager_packageInstall"> + + + + + + + +
+ +
+ + +
+ +
+
{{ vm.package.ownerInfo.owner }}
+
+ {{ vm.package.ownerInfo.owner }} has {{ vm.package.ownerInfo.karma }} karma points +
+
+
+
+
+ + + +
Information
-
{{ vm.package.ownerInfo.owner }}
-
- {{ vm.package.ownerInfo.owner }} has {{ vm.package.ownerInfo.karma }} karma points + +
+
Owner:
+
{{vm.package.ownerInfo.owner}}
+
+ +
+
Contributors:
+
+ {{ contributor }} +
+
+ +
+
Created:
+
{{vm.package.created | date:'yyyy-MM-dd HH:mm:ss'}}
+
+ +
+
Current version:
+
{{vm.package.latestVersion}}
+
+ +
+
.NET Version:
+
{{vm.package.information.netVersion}}
+
+ +
+
License:
+
{{vm.package.licenseName}}
+
+ +
+
Downloads:
+
{{vm.package.downloads}}
+
+ +
+
Likes:
+
{{vm.package.likes}}
+
+ +
+ + + + + +
Compatibility
+
This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be gauranteed for versions reported below 100%
+
+
+ {{compatibility.version}} + ({{compatibility.percentage}}%) +
+ + +
+
+
+ + + +
External sources
+ -
-
- -
-
Information
-
- -
-
Owner:
-
{{vm.package.ownerInfo.owner}}
-
- -
-
Contributors:
-
- {{ contributor }} -
-
- -
-
Created:
-
{{vm.package.created | date:'yyyy-MM-dd HH:mm:ss'}}
-
- -
-
Current version:
-
{{vm.package.latestVersion}}
-
- -
-
.NET Version:
-
{{vm.package.information.netVersion}}
-
- -
-
License:
-
{{vm.package.licenseName}}
-
- -
-
Downloads:
-
{{vm.package.downloads}}
-
- -
-
Likes:
-
{{vm.package.likes}}
-
- -
-
- -
-
Compatibility
-
This package is compatible with the following versions of Umbraco, as reported by community members. Full compatability cannot be gauranteed for versions reported below 100%
-
-
- {{compatibility.version}} - ({{compatibility.percentage}}%) -
- - - - -
-
- -
-
External sources
- - -
+ +
@@ -319,7 +325,7 @@
Read me
- +
diff --git a/src/Umbraco.Web.UI.Client/src/views/stylesheets/infiniteeditors/richtextrule/richtextrule.html b/src/Umbraco.Web.UI.Client/src/views/stylesheets/infiniteeditors/richtextrule/richtextrule.html index a36f3ef6a2..557341fe1b 100644 --- a/src/Umbraco.Web.UI.Client/src/views/stylesheets/infiniteeditors/richtextrule/richtextrule.html +++ b/src/Umbraco.Web.UI.Client/src/views/stylesheets/infiniteeditors/richtextrule/richtextrule.html @@ -53,6 +53,7 @@ type="button" button-style="link" label-key="general_close" + shortcut="esc" action="vm.close()"> - 8.0.0-alpha.31 + 8.0.0-alpha.33 @@ -157,13 +157,6 @@ editMacro.aspx - - directoryBrowser.aspx - ASPXCodeBehind - - - directoryBrowser.aspx - default.Master ASPXCodeBehind @@ -209,7 +202,6 @@ - @@ -299,8 +291,6 @@ - - Designer diff --git a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml index 6b7906dac5..b6432166c9 100644 --- a/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml +++ b/src/Umbraco.Web.UI/Umbraco/Views/Default.cshtml @@ -107,12 +107,6 @@ parent-scope="overlay.parentScope"> - - - diff --git a/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml b/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml index 0ebdb5cd48..635174b9da 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/create/UI.Release.xml @@ -15,32 +15,4 @@ - -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
diff --git a/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml b/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml index d6be62ff88..c075a0b8b9 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/create/UI.xml @@ -15,32 +15,4 @@ - -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
- -
Package
- /create/simple.ascx - - - -
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml index 537558867f..f3b16def54 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/cs.xml @@ -640,7 +640,6 @@ Pokyny pro aktualizaci Pro tento balíček je dostupná aktualizace. Můžete si ji stáhnout přímo z úložiště balíčků umbraco. Verze balíčku - Historie verzí balíčku Zobrazit web balíčku diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml index 7a370ba90e..523531947a 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/da.xml @@ -861,7 +861,6 @@ Mange hilsner fra Umbraco robotten Opdateringsinstrukser Der er en tilgængelig opdatering til denne pakke. Du kan downloade den direkte fra Umbracos pakke opbevaringsbase. Pakke version - Pakke versionshistorik Se pakkeudviklerens website Pakke allerede installeret Denne pakke kan ikke installeres, den kræver en minimum Umbraco version af diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml index 53e87f8dfd..915b25208e 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/de.xml @@ -630,7 +630,6 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die Hinweise für die Durchführung des Updates Es ist ein Update für dieses Paket verfügbar. Sie können es direkt vom Umbraco-Paket-Repository herunterladen. Version des Pakets - Versionsverlauf des Pakets Paket-Webseite aufrufen diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml index 386d3af518..dad27d99fe 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml @@ -1113,7 +1113,6 @@ To manage your website, simply open the Umbraco back office and start adding con Upgrade instructions There's an upgrade available for this package. You can download it directly from the Umbraco package repository. Package version - Package version history View package website Package already installed This package cannot be installed, it requires a minimum Umbraco version of @@ -1518,6 +1517,8 @@ To manage your website, simply open the Umbraco back office and start adding con Allow varying by culture Allow editors to create content of this type in different languages Allow varying by culture + Is an Element type + An Element type is meant to be used for instance in Nested Content, and not in the tree Building models diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml index 5de373f571..ab37cf8025 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml @@ -1140,7 +1140,7 @@ To manage your website, simply open the Umbraco back office and start adding con Upgrade instructions There's an upgrade available for this package. You can download it directly from the Umbraco package repository. Package version - Package version history + Upgrading from version View package website Package already installed This package cannot be installed, it requires a minimum Umbraco version of @@ -1559,6 +1559,8 @@ To manage your website, simply open the Umbraco back office and start adding con Allow varying by culture Allow editors to create content of this type in different languages Allow varying by culture + Is an Element type + An Element type is meant to be used for instance in Nested Content, and not in the tree Add language diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml index 581ce754e0..04b2cea4df 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/fr.xml @@ -1048,7 +1048,6 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à Instructions de mise à jour Il y a une mise à jour disponible pour ce package. Vous pouvez la télécharger directement depuis le repository des packages Umbraco. Version du package - Historique des versions du package Voir le site internet du package Package déjà installé Ce package ne peut pas être installé, il nécessite au minimum la version Umbraco %0% diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml index 3b23ccf877..993a99ceec 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ja.xml @@ -715,7 +715,6 @@ Runwayをインストールして作られた新しいウェブサイトがど 更新の手順 このパッケージの更新があります。Umbracoのパッケージリポジトリから直接ダウンロードできます。 パッケージのバージョン - パッケージのバージョン履歴 パッケージのウェブサイトを見る diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml index d51d41e558..a3c8abb946 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nb.xml @@ -609,7 +609,6 @@ Vennlig hilsen Umbraco roboten Oppgraderingsinstrukser Det er en oppdatering tilgjengelig for denne pakken. Du kan laste den ned direkte fra pakkebrønnen. Pakkeversjon - Pakkeversjonshistorie Se pakkens nettsted diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml index 4aa3126701..8189be9429 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/nl.xml @@ -723,7 +723,6 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je Upgrade instructies Er is een upgrade beschikbaar voor deze package. Je kunt het direct downloaden uit de Umbraco package repository. Package versie - Package versiehistorie Bekijk de package website Package reeds geinstalleerd Deze package kan niet worden geinstalleerd omdat minimaal Umbraco versie %0% benodigd is. diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml index e3e0fe8e7d..1f64a5ab21 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/pl.xml @@ -882,7 +882,6 @@ Naciśnij przycisk instaluj, aby zainstalować bazę danych Umb Instrukcja aktualizacji Jest dostępna aktualizacja dla tego pakietu. Możesz ją pobrać wprost z repozytorium pakietów Umbraco. Wersja pakietu - Historia wersji pakietu Odwiedź stronę pakietu Pakiet jest już zainstalowany Ten pakiet nie może być zainstalowany, ponieważ wymaga Umbraco w wersji przynajmniej %0% diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml index fc8504fb13..61c42d9d6d 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/ru.xml @@ -1205,7 +1205,6 @@ Руководство по обновлению Для данного пакета доступно обновление. Вы можете загрузить это обновление непосредственно из центрального репозитория пакетов Umbraco. Версия пакета - История версий пакета Перейти на веб-сайт пакета Этот пакет уже установлен в системе Этот пакет не может быть установлен, он требует наличия Umbraco версии как минимум diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml index 77e213159b..05c6dc927d 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/sv.xml @@ -564,7 +564,6 @@ Uppdateringsinstruktioner Det finns an uppdaterad version av paketet. Du kan hämta den direkt från Umbracos paketvalv. Paketversion - Versionshistorik för paket Besök paketets webbplats diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml index ddf9aafaae..4ff9d22c86 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/tr.xml @@ -632,7 +632,6 @@ To manage your website, simply open the Umbraco back office and start adding con Upgrade instructions There's an upgrade available for this package. You can download it directly from the Umbraco package repository. Package version - Package version history View package website diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml index 05f8c3ecf7..7286c3f914 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh.xml @@ -751,7 +751,6 @@ 更新说明 此软件包有一个可用的升级。您可以直接从 Umbraco 软件包存储库下载。 版本 - 版本历史 访问扩展包网站 已安装软件包 此软件包无法安装, 它需要一个最小的 Umbraco 版本的%0% diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml index 0a87631329..b61e1154b8 100644 --- a/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml +++ b/src/Umbraco.Web.UI/Umbraco/config/lang/zh_tw.xml @@ -737,7 +737,6 @@ 更新說明 擴展包有可用的更新,您可以從程式庫網站更新。 版本 - 版本歷史 訪問擴展包網站 擴展包已安裝 這個擴展包無法安裝,它需要Umbraco至少是版本 %0% diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs b/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs deleted file mode 100644 index 3e2edd1471..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Web; -using System.Web.UI; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web.UI.Pages; - -namespace Umbraco.Web.UI.Umbraco.Developer.Packages -{ - public partial class DirectoryBrowser : UmbracoEnsuredPage - { - public DirectoryBrowser() - { - CurrentApp = Constants.Applications.Packages; - } - - string _lsScriptName; - string _lsWebPath; - protected string Target = ""; - private readonly Regex _xssElementIdClean = new Regex(@"^([a-zA-Z0-9-_:\.]+)"); - - private readonly StringBuilder _sb = new StringBuilder(); - - protected override void OnLoad(EventArgs e) - { - base.OnLoad(e); - - Response.Cache.SetExpires(DateTime.Now.AddSeconds(5)); - Response.Cache.SetCacheability(HttpCacheability.Public); - - //we need to clean this string: - //http://issues.umbraco.org/issue/U4-2027 - var target = Request.QueryString.Get("target"); - if (target.IsNullOrWhiteSpace()) - throw new InvalidOperationException("The target query string must be set to a valid html element id"); - var matched = _xssElementIdClean.Matches(target); - if (matched.Count == 0) - throw new InvalidOperationException("The target query string must be set to a valid html element id"); - - Target = matched[0].Value; - - try - { - - //Variables used in script - var sebChar = IOHelper.DirSepChar.ToString(); - - //Work on path and ensure no back tracking - string sSubDir = Request.QueryString.Get("path"); - if (string.IsNullOrEmpty(sSubDir)) { sSubDir = "/"; } - - sSubDir = sSubDir.Replace(IOHelper.DirSepChar.ToString(), ""); - sSubDir = sSubDir.Replace("//", "/"); - sSubDir = sSubDir.Replace("..", "./"); - sSubDir = sSubDir.Replace('/', IOHelper.DirSepChar); - - //Clean path for processing and collect path varitations - if (sSubDir.Substring(0, 1) != sebChar) { sSubDir = sebChar + sSubDir; } - if (sSubDir.Substring(sSubDir.Length - 1, 1) != "\\") { sSubDir = sSubDir + sebChar; } - - //Get name of the browser script file - _lsScriptName = Request.ServerVariables.Get("SCRIPT_NAME"); - var j = _lsScriptName.LastIndexOf("/"); - if (j > 0) { _lsScriptName = _lsScriptName.Substring(j + 1, _lsScriptName.Length - (j + 1)).ToLower(); } - - //Create navigation string and other path strings - GetNavLink("", "root"); - if (sSubDir != sebChar) - { - j = 0; int i = 0; - do - { - i = sSubDir.IndexOf(sebChar, j + 1); - _lsWebPath += sSubDir.Substring(j + 1, i - (j + 1)) + "/"; - GetNavLink(_lsWebPath, sSubDir.Substring(j + 1, i - (j + 1))); - j = i; - } while (i != sSubDir.Length - 1); - } - - //Output header - _sb.Append(""); - - //Output directorys - var oDirInfo = new DirectoryInfo(IOHelper.MapPath("~/" + sSubDir)); - var oDirs = oDirInfo.GetDirectories(); - foreach (var oDir in oDirs) - { - try - { - _sb.Append(""); - } - catch (Exception) - { - _sb.Append(""); - } - } - - //Ouput files - var oFiles = oDirInfo.GetFiles(); - foreach (var oFile in oFiles.Where(oFile => oFile.Name.ToLower() != _lsScriptName)) - { - decimal iLen = oFile.Length; - string sLen; - if (iLen >= 1048960) { iLen = iLen / 1048960; sLen = "mb"; } else { iLen = iLen / 1024; sLen = "kb"; } - sLen = Decimal.Round(iLen, 2).ToString() + sLen; - _sb.Append(""); - } - - //Output footer - _sb.Append("
" + oDir.Name + " (Include entire folder)
" + oDir.Name + " (Access Denied)
" + oFile.Name + "
"); - - } - catch (Exception ex) - { - RptErr(ex.Message); - } - } - - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - Output.Controls.Add(new LiteralControl(_sb.ToString())); - } - - private void RptErr(string psMessage) - { - _sb.Append("
Script Reported Error:  " + psMessage + "

"); - } - - private string GetNavLink(string psHref, string psText) - { - return ("/" + psText + ""); - } - - } -} diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs b/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs deleted file mode 100644 index 22bf0892b7..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/DirectoryBrowser.aspx.designer.cs +++ /dev/null @@ -1,42 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace Umbraco.Web.UI.Umbraco.Developer.Packages { - - - public partial class DirectoryBrowser { - - /// - /// CssInclude1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::ClientDependency.Core.Controls.CssInclude CssInclude1; - - /// - /// pane control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane pane; - - /// - /// Output control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder Output; - } -} diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx deleted file mode 100644 index c6d2645ab3..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/directoryBrowser.aspx +++ /dev/null @@ -1,26 +0,0 @@ -<%@ Page Language="C#" AutoEventWireup="True" MasterPageFile="../../masterpages/umbracoDialog.Master" CodeBehind="DirectoryBrowser.aspx.cs" Inherits="Umbraco.Web.UI.Umbraco.Developer.Packages.DirectoryBrowser" %> - -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> -<%@ Register TagPrefix="cdf" Namespace="ClientDependency.Core.Controls" Assembly="ClientDependency.Core" %> - - - - - - - - - - -
- - - -
-
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index 956c17fe4a..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx b/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx deleted file mode 100644 index 1beda45dae..0000000000 --- a/src/Umbraco.Web.UI/Umbraco/developer/Packages/installer.aspx +++ /dev/null @@ -1,10 +0,0 @@ -<%@ Page Language="c#" MasterPageFile="../../masterpages/umbracoPage.Master" -AutoEventWireup="True" Inherits="umbraco.presentation.developer.packages.Installer" Trace="false" ValidateRequest="false" %> -<%@ Register TagPrefix="cc1" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - diff --git a/src/Umbraco.Web.UI/config/trees.config b/src/Umbraco.Web.UI/config/trees.config index 7f7aeca8a7..5892545682 100644 --- a/src/Umbraco.Web.UI/config/trees.config +++ b/src/Umbraco.Web.UI/config/trees.config @@ -20,8 +20,10 @@ + - + + diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml b/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml deleted file mode 100644 index d51d41e558..0000000000 --- a/src/Umbraco.Web.UI/umbraco/config/lang/nb.xml +++ /dev/null @@ -1,963 +0,0 @@ - - - - The Umbraco community - http://our.umbraco.org/documentation/Extending-Umbraco/Language-Files - - - Angi domene - Revisjoner - Bla gjennom - Skift dokumenttype - Kopier - Opprett - Opprett pakke - Slett - Deaktiver - Tøm papirkurv - Eksporter dokumenttype - Importer dokumenttype - Importer pakke - Rediger i Canvas - Logg av - Flytt - Varslinger - Offentlig tilgang - Publiser - Avpubliser - Oppdater noder - Republiser hele siten - Gjenopprett - Rettigheter - Reverser - Send til publisering - Send til oversetting - Sorter - Oversett - Oppdater - - - Ingen tilgang. - Legg til domene - Fjern - Ugyldig node. - Ugyldig domeneformat. - Domene er allerede tilknyttet. - Språk - Domene - Domene '%0%' er nå opprettet og tilknyttet siden - Domenet '%0%' er nå slettet - Domenet '%0%' er allerede tilknyttet - Domenet '%0%' er nå oppdatert - eller rediger eksisterende domener -
Stier med ett nivå støttes, f.eks. "eksempel.com/no". Imidlertid bør det unngås. Bruk heller språkinnstillingen over.]]>
- Arv - Språk - Vil også gjelde denne noden, med mindre et underordnet domene også gjelder.]]> - Domener - - - Viser for - - - Velg - Gjør noe annet - Fet - Reduser innrykk - Sett inn skjemafelt - Sett inn grafisk overskrift - Rediger HTML - Øk innrykk - Kursiv - Midtstill - Juster tekst venstre - Juster tekst høyre - Sett inn lenke - Sett inn lokal lenke (anker) - Punktmerking - Nummerering - Sett inn makro - Sett inn bilde - Rediger relasjoner - Tilbake til listen - Lagre - Lagre og publiser - Lagre og planlegge - Lagre og send til publisering - Forhåndsvis - Forhåndsvisning er deaktivert siden det ikke er angitt noen mal - Velg formattering - Vis stiler - Sett inn tabell - - - For å endre det valge innholdets dokumenttype, velger du først en ny dokumenttype som er gyldig på gjeldende plassering. - Kontroller deretter at alle egenskaper blir overført riktig til den nye dokumenttypen og klikk på Lagre. - Innholdet har blitt republisert. - Nåværende egenskap - Nåværende type - Du kan ikke endre dokumenttype, ettersom det ikke er andre gyldige dokumenttyper på denne plasseringen. - Dokumenttype endret - Overfør egenskaper - Overfør til egenskap - Ny mal - Ny type - ingen - Innhold - Velg ny dokumenttype - Dokumenttypen på det valgte innhold ble endret til [new type], og følgende egenskaper ble overført: - til - Overføringen av egenskaper kunne ikke fullføres da en eller flere egenskaper er satt til å bli overført mer enn en gang. - Kun andre dokumenttyper som er gyldige for denne plasseringen vises. - - - Publisert - Om siden - Alias - (hvordan du ville beskrevet bildet over telefon) - Alternative lenker - Klikk for å redigere denne noden - Opprettet av - Opprinnelig forfatter - Oppdatert av - Opprettet den - Tidspunkt for opprettelse - Dokumenttype - Redigerer - Utløpsdato - Denne noden er endret siden siste publisering - Denne noden er enda ikke publisert - Sist publisert - Det er ingen elementer å vise i listen. - Mediatype - Link til media - Medlemsgruppe - Rolle - Medlemstype - Ingen dato valgt - Sidetittel - Egenskaper - Dette dokumentet er publisert, men ikke synlig ettersom den overliggende siden '%0%' ikke er publisert - Intern feil: dokumentet er publisert men finnes ikke i hurtigbuffer - Publisert - Publiseringsstatus - Publiseringsdato - Dato for avpublisering - Fjern dato - Sorteringsrekkefølgen er oppdatert - Trekk og slipp nodene eller klikk på kolonneoverskriftene for å sortere. Du kan velge flere noder ved å holde shift eller control tastene mens du velger. - Statistikk - Tittel (valgfri) - Alternativ tekst (valgfri) - Type - Avpubliser - Sist endret - Tidspunkt for siste endring - Fjern fil - Lenke til dokument - Medlem av gruppe(ne) - Ikke medlem av gruppe(ne) - Undersider - Åpne i vindu - - - Klikk for å laste opp - - - Opprett et nytt medlem - Alle medlemmer - - - Hvor ønsker du å oprette den nye %0% - Opprett under - Velg en type og skriv en tittel - "dokumenttyper".]]> - "mediatyper".]]> - - - Til ditt nettsted - - Skjul - Hvis Umbraco ikke starter, kan det skyldes at pop-up vinduer ikke er tillatt - er åpnet i nytt vindu - Omstart - Besøk - Velkommen - - - Stay - Discard changes - You have unsaved changes - Are you sure you want to navigate away from this page? - you have unsaved changes - - - Done - Deleted %0% item - Deleted %0% items - Deleted %0% out of %1% item - Deleted %0% out of %1% items - Published %0% item - Published %0% items - Published %0% out of %1% item - Published %0% out of %1% items - Unpublished %0% item - Unpublished %0% items - Unpublished %0% out of %1% item - Unpublished %0% out of %1% items - Moved %0% item - Moved %0% items - Moved %0% out of %1% item - Moved %0% out of %1% items - Copied %0% item - Copied %0% items - Copied %0% out of %1% item - Copied %0% out of %1% items - - - Navn på lokal link - Rediger domener - Lukk dette vinduet - Er du sikker på at du vil slette - Er du sikker på at du vil deaktivere - Er du sikker på at du vil forlate Umbraco? - Er du sikker? - Klipp ut - Rediger ordboksnøkkel - Rediger språk - Sett inn lokal link - Sett inn spesialtegn - Sett inn grafisk overskrift - Sett inn bilde - Sett inn lenke - Sett inn makro - Sett inn tabell - Sist redigert - Lenke - Intern link: - Ved lokal link, sett inn "#" foran link - Åpne i nytt vindu? - Makroinnstillinger - Denne makroen har ingen egenskaper du kan endre - Lim inn - Endre rettigheter for - Innholdet i papirkurven blir nå slettet. Vennligst ikke lukk dette vinduet mens denne operasjonen foregår - Papirkurven er nå tom - Når elementer blir slettet fra papirkurven vil de være slettet for alltid - regexlib.com tjenesten opplever for tiden problemer som vi ikke har kontroll over. Vi beklager denne ubeleiligheten.]]> - Søk etter et regulært uttrykk for å legge inn validering til et felt. Eksempel: 'email, 'zip-code' 'url' - Fjern makro - Obligatorisk - Nettstedet er indeksert - Hurtigbufferen er blitt oppdatert. Alt publisert innhold er nå à jour. Alt upublisert innhold er fortsatt ikke publisert. - Hurtigbufferen for siden vil bli oppdatert. Alt publisert innhold vil bli oppdatert, mens upublisert innhold vil forbli upublisert. - Antall kolonner - Antall rader - Sett en plassholder-ID
Ved å sette en ID på plassholderen kan du legge inn innhold i denne malen fra underliggende maler, ved å referere denne ID'en ved hjelp av et <asp:content /> element.]]>
- Velg en plassholder ID fra listen under. Du kan bare velge ID'er fra den gjeldende malens overordnede mal.]]> - Klikk på bildet for å se det i full størrelse - Velg punkt - Se buffret node - - - %0%' under.
Du kan legge til flere språk under 'språk' i menyen til venstre.]]>
- Språk - - - Skriv inn ditt brukernavn - Skriv inn ditt passord - Navngi %0%... - Skriv inn navn... - Søk... - Filtrer... - Skriv inn nøkkelord (trykk på Enter etter hvert nøkkelord)... - - - Tillatte underordnede noder - Opprett - Slett arkfane - Beskrivelse - Ny arkfane - Arkfane - Miniatyrbilde - Opprett brukerdefinert listevisning - Fjern brukerdefinert listevisning - - - Legg til forhåndsverdi - Database datatype - Kontrollelement GUID - Kontrollelement - Knapper - Aktiver avanserte instillinger for - Aktiver kontektsmeny - Maksimum standard størrelse på innsatte bilder - Beslektede stilark - Vis etikett - Bredde og høyde - - - Dine data har blitt lagret, men før du kan publisere denne siden må du rette noen feil: - Den gjeldende Membership Provider støtter ikke endring av passord. (EnablePasswordRetrieval må være satt til sann) - %0% finnes allerede - Det var feil i dokumentet: - Det var feil i skjemaet: - Passordet bør være minst %0% tegn og inneholde minst %1% numeriske tegn - %0% må være et heltall - %0% under %1% er obligatorisk - %0% er obligatorisk - %0% under %1% er ikke i et korrekt format - %0% er ikke i et korrekt format - - - Filtypen er deaktivert av administrator - NB! Selv om CodeMirror er aktivert i konfigurasjon er det deaktivert i Internet Explorer pga. ustabilitet. - Fyll ut både alias og navn på den nye egenskapstypen! - Det er et problem med lese/skrive rettighetene til en fil eller mappe - Tittel mangler - Type mangler - Du er i ferd med å gjøre bildet større enn originalen. Det vil forringe kvaliteten på bildet, ønsker du å fortsette? - Startnode er slettet. Kontakt din administrator - Du må markere innhold før du kan endre stil - Det er ingen aktive stiler eller formateringer på denne siden - Sett markøren til venstre i de 2 cellene du ønsker å slå sammen - Du kan ikke dele en celle som allerede er delt. - - - Om - Handling - Muligheter - Legg til - Alias - Er du sikker? - Ramme - av - Avbryt - Cellemargin - Velg - Lukk - Lukk vindu - Kommentar - Bekreft - Behold proposjoner - Fortsett - Kopier - Opprett - Database - Dato - Standard - Slett - Slettet - Sletter... - Design - Dimensjoner - Ned - Last ned - Rediger - Endret - Elementer - E-post - Feil - Finn - Høyde - Hjelp - Ikon - Importer - Indre margin - Sett inn - Installer - Justering - Språk - Layout - Laster - Låst - Logg inn - Logg ut - Logg ut - Makro - Flytt - Navn - Ny - Neste - Nei - av - OK - Åpne - eller - Passord - Sti - Plassholder ID - Ett øyeblikk... - Forrige - Egenskaper - E-post som innholdet i skjemaet skal sendes til - Papirkurv - Gjenværende - Gi nytt navn - Forny - Påkrevd - Prøv igjen - Rettigheter - Søk - Server - Vis - Hvilken side skal vises etter at skjemaet er sendt - Størrelse - Sorter - Send - Type - Søk... - Opp - Oppdater - Oppgrader - Last opp - Url - Bruker - Brukernavn - Verdi - Visning - Velkommen... - Bredde - Ja - Mappe - Søkeresultater - Sorter - Avslutt sortering - Eksempel - Bytt passord - til - Listevisning - Lagrer... - nåværende - Innbygging - Hent - valgt - - - Bakgrunnsfarge - Fet - Tekstfarge - Skrifttype - Tekst - - - Side - - - Installasjonsprogrammet kan ikke koble til databasen - Kunne ikke lagre Web.Config-filen. Vennligst endre databasens tilkoblingsstreng manuelt. - Din database er funnet og identifisert som - Databasekonfigurasjon - installer-knappen for å installere Umbraco %0% databasen]]> - Neste for å fortsette.]]> - Databasen ble ikke funnet! Vennligst sjekk at informasjonen i "connection string" i "web.config"-filen er korrekt.

For å fortsette, vennligst rediger "web.config"-filen (bruk Visual Studio eller din favoritteditor), rull ned til bunnen, og legg til tilkoblingsstrengen for din database i nøkkelen "umbracoDbDSN" og lagre filen.

Klikk prøv på nytt når du er ferdig.
Mer informasjon om redigering av web.config her.

]]>
- Vennligst kontakt din ISP om nødvendig. Hvis du installerer på en lokal maskin eller server, må du kanskje skaffe informasjonen fra din systemadministrator.]]> - Trykk på knappen oppgrader for å oppgradere databasen din til Umbraco %0%

Ikke vær urolig - intet innhold vil bli slettet og alt vil fortsette å virke etterpå!

]]>
- Trykk Neste for å fortsette.]]> - neste for å fortsette konfigurasjonsveiviseren]]> - Passordet til standardbrukeren må endres!]]> - Standardbrukeren har blitt deaktivert eller har ingen tilgang til Umbraco!

Ingen videre handling er nødvendig. Klikk neste for å fortsette.]]> - Passordet til standardbrukeren har blitt forandret etter installasjonen!

Ingen videre handling er nødvendig. Klikk Neste for å fortsette.]]> - Passordet er blitt endret! - Få en god start med våre introduksjonsvideoer - Ved å klikke på Neste-knappen (eller endre UmbracoConfigurationStatus i Web.config), godtar du lisensen for denne programvaren som angitt i boksen nedenfor. Legg merke til at denne Umbraco distribusjon består av to ulike lisenser, åpen kilde MIT lisens for rammen og Umbraco frivareverktøy lisens som dekker brukergrensesnittet. - Ikke installert. - Berørte filer og mapper - Mer informasjon om å sette opp rettigheter for Umbraco her - Du må gi ASP.NET brukeren rettigheter til å endre de følgende filer og mapper - Rettighetene er nesten perfekt satt opp!

Du kan kjøre Umbraco uten problemer, men du vil ikke være i stand til å installere de anbefalte pakkene for å utnytte Umbraco fullt ut.]]>
- Hvordan løse problemet - Klikk her for å lese tekstversjonen - innføringsvideo om å sette opp rettigheter for Umbraco eller les tekstversjonen.]]> - Rettighetsinnstillingene kan være et problem!

Du kan kjøre Umbraco uten problemer, men du vil ikke være i stand til å installere de anbefalte pakkene for å utnytte Umbraco fullt ut.]]>
- Rettighetsinstillingene er ikke klargjort for Umbraco!

For å kunne kjøre Umbraco, må du oppdatere rettighetsinnstillingene dine.]]>
- Rettighetsinnstillingene er perfekt!

Du er klar for å kjøre Umbraco og installere pakker!]]>
- Løser mappeproblem - Følg denne linken for mer informasjon om problemer med ASP.NET og oppretting av mapper - Konfigurerer mappetillatelser - - Jeg ønsker å starte fra bunnen. - lær hvordan) Du kan fortsatt velge å installere Runway senere. Vennligst gå til Utvikler-seksjonen og velg Pakker.]]> - Du har akkurat satt opp en ren Umbraco plattform. Hva vil du gjøre nå? - Runway er installert - Dette er vår liste av anbefalte moduler- Kryss av de du ønsker å installere, eller se denfulle listen av moduler ]]> - Bare anbefalt for erfarne brukere - Jeg vil starte med en enkel webside - "Runway" er en enkel webside som utstyrer deg med noen grunnleggende dokumenttyper og maler. Veiviseren kan sette opp Runway for deg automatisk, men du kan enkelt endre, utvide eller slette den. Runway er ikke nødvendig, og du kan enkelt bruke Umbraco uten den. Imidlertidig tilbyr Runway et enkelt fundament basert på de beste metodene for å hjelpe deg i gang fortere enn noensinne. Hvis du velger å installere Runway, kan du også velge blant grunnleggende byggeklosser kalt Runway Moduler for å forøke dine Runway-sider.

Sider inkludert i Runway: Hjemmeside, Komme-i-gang, Installere moduler.
Valgfrie Moduler: Toppnavigasjon, Sidekart, Kontakt, Galleri.
]]>
- Hva er Runway - Steg 1/5 Godta lisens - Steg 2/5 Database konfigurasjon - Steg 3/5: Valider filrettigheter - Steg 4/5: Skjekk Umbraco sikkerheten - Steg 5/5: Umbraco er klar for deg til å starte! - Tusen takk for at du valgte Umbraco! - Se ditt nye nettsted Du har installert Runway, hvorfor ikke se hvordan ditt nettsted ser ut.]]> - Mer hjelp og info Få hjelp fra vårt prisbelønte samfunn, bla gjennom dokumentasjonen eller se noen gratis videoer på hvordan man bygger et enkelt nettsted, hvordan bruke pakker og en rask guide til Umbraco terminologi]]> - Umbraco %0% er installert og klar til bruk - web.config filen, og oppdatere AppSetting-nøkkelen UmbracoConfigurationStatus til verdien '%0%']]> - starte øyeblikkelig ved å klikke på "Start Umbraco" knappen nedenfor.
Hvis du er ny på Umbraco, kan du finne mange ressurser på våre komme-i-gang sider.]]>
- Start Umbraco For å administrere din webside, åpne Umbraco og begynn å legge til innhold, oppdatere maler og stilark eller utvide funksjonaliteten]]> - Tilkobling til databasen mislyktes. - Umbraco Versjon 3 - Umbraco Versjon 4 - Se - Umbraco %0% for en ny installasjon eller oppgradering fra versjon 3.0.

Trykk "neste" for å starte veiviseren.]]>
- - - Språkkode - Språk - - - Du har vært inaktiv og vil logges ut automatisk om - Forny innlogging for å lagre - - - Da er det søndag! - Smil, det er mandag! - Hurra, det er tirsdag! - For en herlig onsdag! - Gledelig torsdag! - Endelig fredag! - Gledelig lørdag - Logg på nedenfor - Logg på med - Din sesjon er utløpt - © 2001 - %0%
umbraco.com

]]>
- - - Skrivebord - Seksjoner - Innhold - - - Velg side over... - %0% er nå kopiert til %1% - Kopier til - %0% er nå flyttet til %1% - Flytt til - har blitt valgt som rot til ditt nye innhold, klikk 'ok' nedenfor. - Ingen node er valgt, vennligst velg en node i listen over før du klikker 'fortsett' - Gjeldende nodes type tillates ikke under valgt node - Gjeldende node kan ikke legges under en underordnet node - Denne noden kan ikke ligge på rotnivå - Handlingen tillates ikke. Du mangler tilgang til en eller flere underordnede noder. - Relater kopierte elementer til original(e) - - - Rediger dine varsler for %0% - - Hei %0%

- -

Dette er en automatisk mail for å informere om at handlingen '%1%' - er blitt utført på siden '%2%' - av brukeren '%3%' -

- -

-

Rettelser:

- - %6% -
-

- - - -

Ha en fin dag!

- Vennlig hilsen Umbraco roboten -

]]>
- [%0%] Varsling om %1% utført på %2% - Varslinger - - - Umbraco-pakker har vanligvis endelsen ".umb" eller ".zip".]]> - Utvikler - Demonstrasjon - Dokumentasjon - Metadata - Pakkenavn - Pakken inneholder ingen elementer -
Du kan trygt fjerne pakken fra systemet ved å klikke "avinstaller pakke" nedenfor.]]>
- Ingen oppdateringer tilgjengelig - Alternativer for pakke - Lesmeg for pakke - Pakkebrønn - Bekreft avinstallering - Pakken ble avinstallert - Pakken ble vellykket avinstallert - Avinstaller pakke - Advarsel: alle dokumenter, media, etc. som som er avhengig av elementene du sletter, vil slutte å virke, noe som kan føre til ustabilitet, så avinstaller med forsiktighet. Hvis du er i tvil, kontakt pakkeutvikleren.]]> - Last ned oppdatering fra pakkeregisteret - Oppgrader pakke - Oppgraderingsinstrukser - Det er en oppdatering tilgjengelig for denne pakken. Du kan laste den ned direkte fra pakkebrønnen. - Pakkeversjon - Pakkeversjonshistorie - Se pakkens nettsted - - - Lim inn med full formattering (Anbefales ikke) - Teksten du er i ferd med å lime inn, inneholder spesialtegn eller formattering. Dette kan skyldes at du kopierer fra f.eks. Microsoft Word. Umbraco kan fjerne denne spesialformatteringen automatisk slik at innholdet er mer velegnet for visning på en webside. - Lim inn som ren tekst, dvs. fjern al formattering - Lim inn og fjern uegnet formatering (anbefalt) - - - Avansert: Beskytt ved å velge hvilke brukergrupper som har tilgang til siden - ved å bruke Umbraco's medlems-grupper]]> - Du må opprette en medlemsgruppe før du kan bruke rollebasert autentikasjon. - Feilside - Brukt når personer logger på, men ikke har tilgang - Hvordan vil du beskytte siden din? - %0% er nå beskyttet - Beskyttelse fjernet fra %0% - Innloggingsside - Velg siden som har loginformularet - Fjern beskyttelse - Velg sidene som inneholder login-skjema og feilmelding ved feil innolgging. - Velg rollene som har tilgang til denne siden - Sett brukernavn og passord for denne siden - Enkelt: Beskytt ved hjelp av brukernavn og passord - Om du ønsker å bruke enkel autentisering via ett enkelt brukernavn og passord - - - %0% kunne ikke publiseres fordi den har planlagt utgivelsesdato. - %0% ble ikke publisert. Ett eller flere felter ble ikke godkjent av validering. - %0% kunne ikke publiseres fordi et tredjepartstillegg avbrøt handlingen. - %0% kan ikke publiseres fordi en overordnet side ikke er publisert. - Inkluder upubliserte undersider - Publiserer - vennligst vent... - %0% av %1% sider har blitt publisert... - %0% er nå publisert - %0% og alle undersider er nå publisert - Publiser alle undersider - ok for å publisere %0% og dermed gjøre innholdet synlig for alle.

Du kan publisere denne siden og alle dens undersider ved å krysse av Publiser alle undersider nedenfor.]]>
- - - Du har ikke konfigurert noen godkjente farger - - - skriv inn ekstern lenke - velg en intern side - Tittel - Lenke - Åpne i nytt vindu - Skriv inn en tekst - Skriv inn en lenke - - - Nullstill - - - Gjeldende versjon - Rød tekst vil ikke bli vist i den valgte versjonen. , grønn betyr lagt til]]> - Dokumentet er tilbakeført til en tidligere versjon - Dette viser den valgte versjonen som HTML, bruk avviksvisningen hvis du ønsker å se forksjellene mellom to versjoner samtidig. - Tilbakefør til - Velg versjon - Vis - - - Rediger scriptfilen - - - Concierge - Innhold - Courier - Utvikler - Umbraco konfigurasjonsveiviser - Mediaarkiv - Medlemmer - Nyhetsbrev - Innstillinger - Statistikk - Oversettelse - Brukere - Hjelp - Skjemaer - - - De beste Umbraco opplæringsvideoer - - - Standardmal - Ordboksnøkkel - For å importere en dokumenttype, finn ".udt" filen på datamaskinen din ved å klikke "Utforsk" knappen og klikk "Importer" (du vil bli spurt om bekreftelse i det neste skjermbildet) - Ny tittel på arkfane - Nodetype - Type - Stilark - Script - Stilark-egenskap - Arkfane - Tittel på arkfane - Arkfaner - Hovedinnholdstype aktivert - Denne dokumenttypen bruker - som hoveddokumenttype. Arkfaner fra hoveddokumenttyper vises ikke og kan kun endres på hoveddokumenttypen selv. - Ingen egenskaper definert i denne arkfanen. Klikk på "legg til ny egenskap" lenken i toppen for å opprette en ny egenskap. - - - Sort order - Creation date - Sortering ferdig. - Dra elementene opp eller ned for å arrangere dem. Du kan også klikke kolonneoverskriftene for å sortere alt på en gang. - - - - En feil oppsto - Utilstrekkelige brukertillatelser, kunne ikke fullføre operasjonen - Avbrutt - Handlingen ble avbrutt av et tredjepartstillegg - Publisering ble avbrutt av et tredjepartstillegg - Egenskaptypen finnes allerede - Egenskapstype opprettet - DataType: %1%]]> - Egenskapstype slettet - Innholdstype lagret - Du har opprettet en arkfane - Arkfane slettet - Arkfane med id: %0% slettet - Stilarket ble ikke lagret - Stilarket ble lagret - Stilark lagret uten feil - Datatype lagret - Ordbokelement lagret - Publiseringen feilet fordi den overliggende siden ikke er publisert - Innhold publisert - og er nå synlig for besøkende - Innhold lagret - Husk å publisere for å gjøre endringene synlig for besøkende - Sendt for godkjenning - Endringer har blitt sendt til godkjenning - Media lagret - Media lagret uten feil - Medlem lagret - Stilarksegenskap lagret - Stilark lagret - Mal lagret - Feil ved lagring av bruker (sjekk loggen) - Bruker lagret - Brukertypen lagret - Filen ble ikke lagret - Filen kunne ikke lagres. Vennligst sjekk filrettigheter - Filen ble lagret - Filen ble lagret uten feil - Språk lagret - Malen ble ikke lagret - Vennligst forviss deg om at du ikke har to maler med samme alias - Malen ble lagret - Malen ble lagret uten feil! - Innhold avpublisert - Delmal lagret - Delmal lagret uten feil - Delmal ble ikke lagret! - En feil oppsto ved lagring av delmal - - - Bruk CSS syntaks f.eks: h1, .redHeader, .blueText - Rediger stilark - Rediger egenskap for stilark - Navn for å identifisere stilarksegenskapen i rik-tekst editoren - Forhåndsvis - Stiler - - - Rediger mal - Sett inn innholdsområde - Sett inn plassholder for innholdsområde - Sett inn ordbokselement - Sett inn makro - Sett inn Umbraco sidefelt - Hovedmal - Hurtigguide til Umbraco sine maltagger - Mal - - - Image - Macro - Sett inn element - Velg layout - Legg til rad - Legg til innhold - Slipp innhold - Raden har tilpasset design - Innholdstypen er ikke tillatt her - Innholdstypen er tillatt her - Klikk for å bygge inn - Klikk for å sette inn et bilde - Bildetekst... - Skriv her... - Rutenettoppsett - Et oppsett er det overordnede arbeidsområdet til ditt rutenett - du vil typisk kun behøve ett eller to - Legg til rutenettoppsett - Juster oppsettet ved å konfigurere kolonnebredder og legge til ytterligere seksjoner - Radkonfigurasjoner - Rader er forhåndsdefinerte celler arrangert vannrett - Legg til radkonfigurasjon - Juster raden ved å sette cellebredder og legge til flere celler - Kolonner - Totalt antall kolonner i rutenettet - Innstillinger - Konfigurer hvilke innstillinger brukeren kan endre - Stiler - Konfigurer hvilke stiler redaktørene kan endre - Tillatt alle editorer - Tillat alle radkonfigurasjoner - Bruk som standard - Velg ekstra - Velg standard - er lagt til - - - Alternativt felt - Alternativ tekst - Store/små bokstaver - Encoding - Felt som skal settes inn - Konverter linjeskift - Erstatter et linjeskift med htmltaggen <br> - Egendefinerte felt - Ja, kun dato - Formatter som dato - HTML koding - Formater spesialtegn med tilsvarende HTML-tegn. - Denne teksten vil settes inn etter verdien av feltet - Denne teksten vil settes inn før verdien av feltet - Små bokstaver - Ingen - Sett inn etter felt - Sett inn før felt - Rekursivt - Standardfelter - Store bokstaver - URL koding - Dersom innholdet av feltene skal sendes til en URL skal spesialtegn formatteres - Denne teksten vil benyttes dersom feltene over er tomme - Dette feltet vil benyttes dersom feltet over er tomt - Ja, med klokkeslett. Dato/tid separator: - - - Oversettelses detaljer - Last ned XML DTD - Felt - Inkluder undersider - - Ingen oversettelses-bruker funnet. Vennligst opprett en oversettelses-bruker før du begynner å sende innhold til oversetting - Siden '%0%' har blitt sendt til oversetting - Send til oversetting - Antall ord - Oversett til - Oversetting fullført. - Du kan forhåndsvise sidene du nettopp har oversatt ved å klikke nedenfor. Hvis den originale siden finnes, vil du få en sammenligning av sidene. - Oversetting mislykkes, XML filen kan være korrupt - Alternativer for oversetting - Oversetter - Last opp XML med oversettelse - - - Hurtigbufferleser - Papirkurv - Opprettede pakker - Datatyper - Ordbok - Installerte pakker - Installer utseende - Installer startpakke - Språk - Installer lokal pakke - Makroer - Mediatyper - Medlemmer - Medlemsgrupper - Roller - Medlemstyper - Dokumenttyper - Pakker - Pakker - Installer fra pakkeregister - Installer Runway - Runway moduler - Skriptfiler - Skript - Stiler - Maler - Brukertillatelser - Brukertyper typer - Brukere - - - Ny oppdatering er klar - %0% er klar, klikk her for å laste ned - Ingen forbindelse til server - Kunne ikke sjekke etter ny oppdatering. Se trace for mere info. - - - Administrator - Kategorifelt - Bytt passord - Nytt passord - Bekreft nytt passord - Du kan endre passordet til Umbraco ved å fylle ut skjemaet under og klikke "Bytt passord" knappen. - Innholdskanal - Beskrivelsesfelt - Deaktiver bruker - Dokumenttype - Redaktør - Utdragsfelt - Språk - Brukernavn - Øverste nivå i Media - Moduler - Deaktiver tilgang til Umbraco - Passord - Nullstill passord - Passordet er endret - Bekreft nytt passord - Nytt passord - Nytt passord kan ikke være blankt - Gjeldende passord - Feil passord - Nytt og bekreftet passord må være like - Nytt og bekreftet passord må være like - Overskriv tillatelser på undernoder - Du redigerer for øyeblikket tillatelser for sidene: - Velg sider for å redigere deres tillatelser - Søk i alle undersider - Startnode - Navn - Brukertillatelser - Forfatter - Endre - Din profil - Din historikk - Sesjonen utløper om - -
diff --git a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml b/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml deleted file mode 100644 index 0a87631329..0000000000 --- a/src/Umbraco.Web.UI/umbraco/config/lang/zh_tw.xml +++ /dev/null @@ -1,1269 +0,0 @@ - - - - The Umbraco community - http://our.umbraco.org/documentation/Extending-Umbraco/Language-Files - - - 管理主機名稱 - 跟蹤審計 - 流覽節點 - 改變文檔類型 - 複製 - 創建 - 創建擴展包 - 刪除 - 禁用 - 清空回收站 - 匯出文檔類型 - 導入文檔類型 - 導入擴展包 - 即時編輯模式 - 退出 - 移動 - 提醒 - 公眾存取權限 - 發佈 - 取消發佈 - 重新載入節點 - 重新發佈整站 - 回復 - 許可權 - 回滾 - 提交至發佈者 - 發送給翻譯 - 排序 - 提交至發佈者 - 翻譯 - 更新 - 預設值 - - - 禁止訪問 - 添加功能變數名稱 - 移除 - 錯誤的節點 - 功能變數名稱錯誤 - 功能變數名稱重複 - 語言 - 功能變數名稱 - 新功能變數名稱 '%0%' 已創建 - 功能變數名稱 '%0%' 已刪除 - 功能變數名稱 '%0%' 已使用 - 功能變數名稱 '%0%' 已更新 - 編輯當前功能變數名稱 - - 繼承 - 語言 - 或從父節點繼承文化設定。
- 也會改變目前節點設定,除非下方網域有其他項目。]]>
- 功能變數名稱 - - - 查看 - - - 清除選擇 - 選擇 - 做別的事情 - 粗體 - 取消段落縮進 - 插入表單字段 - 插入圖片標題 - 編輯Html - 段落縮進 - 斜體 - 居中 - 左對齊 - 右對齊 - 插入連結 - 插入本地連結(錨點) - 圓點列表 - 數字清單 - 插入巨集 - 插入圖片 - 編輯關聯 - 回到清單 - 保存 - 保存並發佈 - 保存並提交審核 - 保存清單檢視 - 預覽 - 因未設置範本無法預覽 - 選擇樣式 - 顯示樣式 - 插入表格 - - - 要更改所選節點的文檔類型,先在列表中選擇合適的文檔類型。 - 然後設置當前文檔類型到新文檔類型的各欄位間的對應映射關係並保存。 - 內容已被重新發佈 - 當前屬性 - 當前類型 - 不能改變文檔類型,因為沒有可替代的類型。 - 文檔類型已更改 - 要映射的欄位 - 映射欄位 - 新範本 - 新類型 - - 內容 - 選擇新的文檔類型 - 選中文檔的類型已被成功更改為[new type],以下欄位被映射: - - 不能完成欄位映射,因為存在一個欄位映射至多欄位的問題。 - 僅顯示可作為替代的文檔類型。 - - - 已發表 - 關於本頁 - 別名 - (圖片的替代文本) - 替代連結 - 點擊編輯 - 創建者 - 創建者 - 更新者 - 創建時間 - 此文件創建的日期時間 - 文檔類型 - 編輯 - 過期於 - 該項發佈之後有更改 - 該項沒有發佈 - 最近發佈 - 沒有可供顯示的項目 - 此列表中沒有可供顯示的項目 - 媒體類型 - 媒體連結位址 - 會員組 - 角色 - 會員類型 - 沒有選擇時間 - 頁標題 - 屬性 - 該文檔不可見,因為其上級 '%0%' 未發佈。 - 糟糕:該文檔已發佈,但是沒有更新至緩存(內部錯誤) - 糟糕:沒辦法連結到此網址(內部錯誤-請參見記錄) - 糟糕:此文件已經發表,但是網址和其他內容相衝 %0% - 發佈 - 發佈狀態 - 發佈於 - 取消發表於 - 清空時間 - 排序完成 - 拖拽項目或按一下列頭即可排序,可以按住Shift多選。 - 統計 - 標題(可選) - 其他說明文字(可選) - 類型 - 取消發佈 - 最近編輯 - 本文件修改時間 - 移除文件 - 連結到文檔 - 會員組成員 - 非會員組成員 - 子項目 - 目標 - 預計發表的時間(伺服器端) - 這是什麼意思?]]> - - - 點選以便上傳 - 或按這裡選擇檔案 - 檔案大小上限為 - - - 新增一位會員 - 所有會員 - - - 您想在哪裡創建 %0% - 創建在 - 選擇類型和標題 - "文檔類型"處變更。]]> - "媒體類型"處變更。]]> - 文檔類型沒有相關範本 - 沒有資料夾 - 新資料類別 - - - 流覽您的網站 - - 隱藏 - 如果Umbraco沒有打開,您可能需要允許彈出式視窗。 - 已經在新視窗中打開 - 重啟 - 訪問 - 歡迎 - - - 留下 - 放棄變更 - 您有未存檔的變更 - 您確定要離開本頁? - 您有未存檔的變更 - - - 完成 - 刪除 %0% 個項目 - 刪除 %0% 個項目 - 刪除 %1% 個中的 %0% 個項目 - 刪除 %1% 個中的 %0% 個項目 - 已發佈 %0% 個項目 - 已發佈 %0% 個項目 - 已發佈 %1% 個中的 %0% 個項目 - 已發佈 %1% 個中的 %0% 個項目 - 取消發佈 %0% 個項目 - 取消發佈 %0% 個項目 - 取消發佈 %1 個中的 %0% 個項目 - 取消發佈 %1 個中的 %0% 個項目 - 移動 %0% 個項目 - 移動 %0% 個項目 - 移動 %1 個中的 %0% 個項目 - 移動 %1 個中的 %0% 個項目 - 複製 %0% 個項目 - 複製 %0% 個項目 - 複製 %1 個中的 %0% 個項目 - 複製 %1 個中的 %0% 個項目 - - - 錨點名稱 - 管理主機名稱 - 關閉窗口 - 您確定要刪除嗎 - 您確定要禁用嗎 - 您確定嗎? - 您確定嗎? - 剪切 - 編輯字典項 - 編輯語言 - 插入本地連結 - 插入字元 - 插入圖片標題 - 插入圖片 - 插入連結 - 插入巨集 - 插入表格 - 最近編輯 - 連結 - 內部連結: - 本地連結請用“#”號開頭 - 在新視窗中打開? - 巨集設置 - 本巨集沒有包含您可以編輯的屬性 - 粘貼 - 編輯許可權 - 正在清空回收站,請不要關閉窗口。 - 回收站已清空 - 從回收站刪除的項目將不可恢復 - regexlib.com的網站服務目前出現些狀況,而我們無能為力。我們對此不便感到十分抱歉。]]> - 查找規則運算式來驗證輸入,如: 'email、'zip-code'、'url'。 - 移除巨集 - 必填項目 - 網站已重建索引 - 網站緩存已刷新,所有已發佈的內容更新生效。 - 網站緩存將會刷新,所有已發佈的內容將會更新。 - 表格列數 - 表格行數 - 設定預留位置代碼 以便您要在子範本中插入內容到本範本時,填入此代碼到 <asp:content /> 裡面。]]> - 選擇預留位置代碼 於此清單中。您只能選擇目前父範本中的代碼。]]> - 點擊圖片查看完整大小 - 拾取項 - 查看緩存項 - 與原本相關 - 最友善的社群 - 頁面連結 - 打開此連結文檔至新視窗或標籤頁 - 媒體連結 - 選擇媒體 - 選擇圖示 - 選擇項目 - 選擇連結 - 選擇巨集 - 選擇內容 - 選擇會員 - 選擇會員群組 - 沒有找到任何圖示 - 本巨集沒有需要參數 - 外部登入提供者 - 例外細節 - 詳細記錄 - 內部例外 - 連結您的 - 取消連結您的 - 帳戶 - 選擇編輯器 - - - %0%' 編輯不同語言版本,
您可以在左方選單「語言」中增添新的語言 - ]]>
- 語言名稱 - - - 輸入您的使用者名稱 - 輸入您的密碼 - 確認您的密碼 - 命名此 %0%... - 輸入一個名稱 - 標籤... - 輸入一段描述... - 搜尋請輸入... - 過濾請輸入... - 增加標籤(每個標籤後請按輸入鍵)... - 輸入您的電子郵件 - - - 允許子項節點類型 - 創建 - 刪除選項卡 - 描述 - 新建選項卡 - 選項卡 - 縮略圖 - 新增自訂清單檢視 - 移除自訂清單檢視 - - - 添加預設值 - 資料庫資料類型 - 資料類型唯一標識 - 渲染控制項 - 按鈕 - 允許高級設置 - 允許快顯功能表 - 插入圖片預設最大 - 關聯的樣式表 - 顯示標籤 - 寬和高 - - - 資料已保存,但是發佈前您需要修正一些錯誤: - 當前成員提供程式不支援修改密碼(EnablePasswordRetrieval的值應該為true) - %0% 已存在 - 發現錯誤: - 發現錯誤: - 密碼最少%0%位元,且至少包含%1%位元非字母數位記號 - %0% 必須是整數 - %1% 中的 %0% 欄位是必填項 - %0% 是必填項 - %1% 中的 %0% 格式不正確 - %0% 格式不正確 - - - 收到伺服器傳來的錯誤 - 該檔案類型已被管理員禁用 - 注意,儘管配置中允許CodeMirror,但是它在IE上不夠穩定,所以無法在IE運行。 - 請為新的屬性類型填寫名稱和別名! - 許可權有問題,訪問指定文檔或資料夾失敗! - 讀取片段視圖腳本錯誤(檔案:%0%) - 讀取使用者控制項 %0% 錯誤 - 請輸入標題 - 請選擇類型 - 圖片尺寸大於原始尺寸不會提高圖片品質,您確定要把圖片尺寸變大嗎? - 預設打開頁面不存在,請聯繫管理員 - 請先選擇內容,再設置樣式。 - 沒有可用的樣式 - 請把游標放在您要合併的兩個儲存格中的左邊儲存格 - 非合併儲存格不能分離。 - - - 關於 - 操作 - 操作 - 添加 - 別名 - 所有 - 您確定嗎? - 回去 - 邊框 - - 取消 - 儲存格邊距 - 選擇 - 關閉 - 關閉窗口 - 備註 - 確認 - 強制屬性 - 繼續 - 複製 - 創建 - 資料庫 - 時間 - 默認 - 刪除 - 已刪除 - 正在刪除… - 設計 - 規格 - - 下載 - 編輯 - 已編輯 - 元素 - 郵箱 - 錯誤 - 查找文檔 - - 幫助 - 圖示 - 導入 - 內邊距 - 插入 - 安裝 - 不合格 - 對齊 - 語言 - 佈局 - 載入中 - 鎖定 - 登入 - 退出 - 登出 - 巨集 - 必要 - 移動 - 名稱 - 新的 - 下一步 - - 屬於 - 確定 - 打開 - - 密碼 - 路徑 - 預留位置代碼 - 請稍候… - 上一步 - 屬性 - 接收資料郵箱 - 回收站 - 保持狀態中 - 重命名 - 更新 - 必要 - 重試 - 許可權 - 搜索 - 伺服器 - 顯示 - 在發送時預覽 - 大小 - 排序 - 送出 - 類型 - 輸入內容開始搜尋… - - 更新 - 更新 - 上傳 - 連結位址 - 用戶 - 用戶名 - - 查看 - 歡迎… - - - 資料夾 - 搜尋結果 - 重新排列 - 我已經完成排列 - 預覽 - 更改密碼 - - 清單檢視 - 存檔中... - 目前 - 內嵌 - 選取的 - - - - - - 增加標籤頁 - 增加屬性 - 增加編輯器 - 增加範本 - 增加子節點 - 增加子項目 - 編輯資料類別 - 瀏覽區塊 - 捷徑 - 顯示捷徑 - 開關清單檢視 - 開關是否允許為根項目 - - - 背景色 - 粗體 - 前景色 - 字體 - 文本 - - - 頁面 - - - 無法連接到資料庫。 - 無法保存web.config檔,請手工修改。 - 發現資料庫 - 資料庫配置 - 安裝 按鈕來安裝Umbraco資料庫 %0% - ]]> - 下一步繼續。]]> - 沒有找到資料庫!請確認檔案"web.config"中的字串"connection string"是否正確。

-

請編輯檔案"web.config" (例如使用Visual Studio或您喜歡的編輯器),移動到檔案底部,並在名稱為"UmbracoDbDSN"的字串中設定資料庫連結資訊,並存檔。

-

- 點選重試按鈕當上述步驟完成。
- - 在此查詢更多編輯web.config的資訊。

]]>
- - 若需要時,請聯繫您的網路公司。如果您在本地機器或伺服器安裝的話,您也許需要聯絡系統管理者。]]> - - 點選升級按鈕來升級Umbraco資料庫 %0%

-

- 請別擔心 - 不會刪除任何資料而且馬上就會繼續運作! -

- ]]>
- 點選下一步繼續。]]> - 下一步繼續設定精靈。]]> - 預設使用者的密碼必須更改!]]> - 預設使用者已經被暫停或沒有Umbraco的使用權!

不需更多的操作步驟。點選下一步繼續。]]> - 安裝後預設使用者的密碼已經成功修改!

不需更多的操作步驟。點選下一步繼續。]]> - 密碼已更改 - 作為入門者,從視頻教程開始吧! - 點擊下一步 (或在Web.config中自行修改UmbracoConfigurationStatus),意味著您接受上述授權合約。 - 安裝失敗。 - 受影響的檔和資料夾 - 此處查看更多資訊 - 您需要對以下檔和資料夾授於ASP.NET用戶修改許可權 - 您的權限設定幾近完美!

- 您可以正常執行Umbraco沒有任何問題,只差您將沒有辦法安裝那些建議需要全部許可權的插件。]]>
- 如何解決 - 點擊閱讀文字版 - 影片教學來瞭解如何設定Umbraco的資料夾權限或閱讀文字版本。]]> - 您的權限可能有點小問題! -

- 您可以正常執行Umbraco沒有任何問題,然而您將無法新增資料夾或安裝那些可以讓Umbraco發揮全力的插件。]]>
- 您的權限設定尚未未完成! -

- 您需要更新權限設定才能執行Umbraco。]]>
- 您的權限設定完美無瑕!

- 您已經準備好執行Umbraco和安裝插件!]]>
- 解決資料夾問題 - 點此查看ASP.NET和創建資料夾的問題解決方案 - 設置資料夾許可權 - - 我要從頭開始 - 學習該怎麼做) - 您晚點仍可以選擇安裝Runway,請至開發者區域選擇安裝。 - ]]> - 您剛剛安裝了一個乾淨的系統,要繼續嗎? - “Runway”已安裝 - - 這是我們的模組推薦清單,選取您想要安裝的項目,或者至 查詢完整清單。 - ]]> - 僅推薦高級用戶使用 - 給我一個簡單的網站 - - "Runway"是一個提供基本檔案類別和範本的簡單網站。安裝程式會自動幫您設定Runway, - 但你仍可輕易編輯,擴充或移除它。它並非必要項目而且您可以在沒它的情況下完美執行Umbraco。然而, - Runway提供一個輕鬆簡便但基於寶貴經驗的平台讓您可以更快開始。 - 如果您安裝Runway,您還可以選擇名為「Runway模組」的基本區塊來加強Runway頁面。 -

- - 內含於Runway: 首頁,準備開始頁面,模組安裝頁面。
- 可選模組: 上方瀏覽列,網站地圖,聯絡,藝廊。 -
- ]]>
- “Runway”是什麼? - 步驟 1/5:接受授權合約 - 步驟 2/5:資料庫配置 - 步驟 3/5:文件許可權驗證 - 步驟 4/5:系統安全性 - 步驟 5/5:一切就緒,可以開始使用系統。 - 感謝選擇我們的產品 - 參觀您的新網站 -您剛安裝好Runway,何不瞧瞧它的模樣。]]> - 更多的幫忙與資訊 -從我們獲獎的社群得到幫助,瀏覽文件,或觀看免費影片來瞭解如何輕鬆架設網站,如何使用插件,和瞭解Umbraco項目名稱的快速上手指引。]]> - 系統 %0% 安裝完畢 - /web.config 檔案並且更新AppSetting中的字串UmbracoConfigurationStatus 內容為 '%0%'。]]> - 快速開始指引。
如果您是Umbraco的新成員, -您可以在其中找到相當多的資源。]]>
- 啟動Umbraco -想要管理您的網站時,只需開啟Umbraco後台便可增加內容,更新範本和樣式表,或增添新功能。]]> - 無法連接到資料庫。 - 系統版本 3 - 系統版本 4 - 觀看 -
- 點選"下一步"來啟動精靈。]]>
- - - 語言代碼 - 語言名稱 - - - 使用者在空閒狀態下將會自動登出 - 已更新,繼續工作。 - - - 超級星期天快樂 - 瘋狂星期一快樂 - 熱鬧星期二快樂 - 美妙星期三快樂 - 悅耳星期四快樂 - 時髦星期五快樂 - 喵喵星期六快樂 - 下方登入 - 登入使用 - 連線時間過了 - © 2001 - %0%
Umbraco.com

]]>
- 忘記密碼? - 一封內有重設密碼連結的電子郵件已經寄出給您 - 一封內有重設密碼連結的電子郵件已經寄到此信箱 - 回到登入畫面 - 請輸入新密碼 - 您的密碼已經更新 - 您點選的連結是無效或過期的 - Umbraco:重設密碼 - 您登入到後台的使用者名稱是:%0%

點選這裡來重設您的密碼或將此連結複製/貼上到您的瀏覽器:

%1%

]]>
- - - 儀錶板 - 區域 - 內容 - - - 選擇上面的頁面… - %0% 被複製到 %1% - 將 %0% 複製到 - %0% 已被移動到 %1% - 將 %0% 移動到 - 作為內容的根結點,點“確定”。 - 尚未選擇節點,請選擇一個節點點擊“確定”。 - 類型不符不允許選擇 - 該項不能移到其子項 - 當前節點不能建在根節點下 - 您在子項的許可權不夠,不允許該操作。 - 複本和原本建立關聯 - - - 為 %0% 編寫通知 - - 哈嘍 %0%

- -

這是一封自動產生的信件來通知您 %1% 工作 - 已經在頁面 %2% 上由使用者 %3% 執行完成 -

- -

-

更新摘要:

- - %6% -
-

- - - -

祝您有美好的一天!

- Umbraco機器人 謹上 -

]]>
- 在 %2%,[%0%] 關於 %1% 的通告已執行。 - 通知 - - - - 按鈕並點選該檔案。Umbraco擴展包通常有「.zip」的副檔名。 - ]]> - 作者 - 演示 - 文檔 - 中繼資料 - 名稱 - 擴展包不含任何項 -
- 您可以點選下方「移除擴展包」來安全地移除此項目。]]>
- 無可用更新 - 選項 - 說明 - 程式庫 - 確認卸載 - 已卸載 - 擴展包卸載成功 - 卸載 - - 注意: 任何文檔,媒體或需要這些項目才能運作的物件將會停止運作,並可能使得系統不穩定, - 請小心移除。若有疑慮,請聯絡擴展包作者。]]> - 從程式庫下載更新 - 更新擴展包 - 更新說明 - 擴展包有可用的更新,您可以從程式庫網站更新。 - 版本 - 版本歷史 - 訪問擴展包網站 - 擴展包已安裝 - 這個擴展包無法安裝,它需要Umbraco至少是版本 %0% - 移除中... - 下載中... - 匯入中... - 安裝中... - 重新啟動中,請稍後... - 都好了,您的瀏覽器將重新整理,請稍待... - - - 帶格式粘貼(不推薦) - 您所粘貼的文本含有特殊字元或格式,Umbraco將清除以適應網頁。 - 無格式粘貼 - 粘貼並移除格式(推薦) - - - 基於角色的保護 - 請使用Umbraco的會員群組。]]> - 使用基於角色的授權需要首先建立會員組。 - 錯誤頁 - 當用戶登錄後訪問沒有許可權的頁時顯示該頁 - 選擇限制訪問此頁的方式 - %0% 現在處於受保護狀態 - %0% 的保護被取消 - 登錄頁 - 選擇公開的登錄入口 - 取消保護 - 選擇一個包含登錄表單和提示資訊的頁 - 選擇訪問該頁的角色類型 - 為此頁設置帳號和密碼 - 單用戶保護 - 如果您只希望提供一個用戶名和密碼就能訪問 - - - - - - - - 包含未發佈的子項 - 正在發佈,請稍候… - %0% 中的 %1% 頁面已發佈… - %0% 已發佈 - %0% 及其子項已發佈 - 發佈 %0% 及其子項 - 發佈按鈕來將%0%的內容設定為公開。

- 您可以同時發佈本頁以及其子項目若您點選下面的包含子頁。 - ]]>
- - - 您尚未設定任何許可顏色 - - - 輸入外部連結 - 選擇內部連結 - 標題 - 連結 - 新視窗 - 輸入新標題 - 輸入連結 - - - 重設 - - - 當前版本 - 紅色 文字將不會顯示於所選版本,而綠色表示增加部分。]]> - 文檔已回滾 - 這顯示所選版本的HTML格式,如果您想要比較兩版本的差異,請使用比較檢視 - 回滾至 - 選擇版本 - 查看 - - - 編輯腳本 - - - Concierge - 內容 - Courier - 開發 - 設定精靈 - 媒體 - 會員 - 消息 - 設置 - 統計 - 翻譯 - 用戶 - 說明 - 表單 - - - 最好的Umbraco影片教學 - - - 預設範本 - 字典鍵 - 要導入文檔類型,請點擊“流覽”按鈕,再點擊“導入”,然後在您電腦上查找 ".udt"檔導入(下一頁中需要您再次確認) - 新建選項卡標題 - 節點類型 - 類型 - 樣式表 - 腳本 - 樣式表屬性 - 選項卡 - 選項卡標題 - 選項卡 - 主控文件類型啟動 - 該文檔類型使用 - 作為主控文件類型. 主控文件類型的標籤只能在主控文件類型裡修改。 - 沒有欄位設置在該標籤頁 - 增加圖示 - - - 排列順序 - 增添時間 - 排序完成。 - 上下拖拽項目或按一下列頭進行排序 - - - - 驗證 - 驗證錯誤一定要修正才能儲存項目 - 失敗 - 使用者權限不足,無法完成操作 - 已取消 - 操作被協力廠商外掛程式取消 - 發佈被協力廠商外掛程式取消 - 屬性類型已存在 - 屬性類型已創建 - 資料類別:%1%]]> - 屬性類型已刪除 - 內容類別型已保存 - 選項卡已創建 - 選項卡已刪除 - id為%0%的選項卡已刪除 - 樣式表未保存 - 樣式表已保存 - 樣式表保存,無錯誤。 - 資料類型已保存 - 字典項已保存 - 因為上級頁面未發佈導致發佈失敗! - 內容已發佈 - 公眾可見 - 內容已保存 - 請發佈以使更改生效 - 提交審核 - 更改已提交審核 - 媒體已保存 - 媒體已保存 - 會員已保存 - 樣式表屬性已保存 - 樣式表已保存 - 範本已保存 - 保存使用者出錯(請查看日誌) - 用戶已保存 - 用戶類型已保存 - 檔未保存 - 檔無法保存,請檢查許可權。 - 檔保存 - 檔保存,無錯誤。 - 語言已保存 - 媒體類別已儲存 - 會員類別已儲存 - 範本未保存 - 範本別名相同 - 範本已保存 - 範本保存,無錯誤。 - 內容已取消發佈 - 片段視圖已保存 - 片段視圖保存,無錯誤。 - 片段視圖未保存 - 片段視圖因為錯誤未能保存 - - - 使用CSS語法,如:h1、.redHeader、.blueTex。 - 編輯樣式表 - 編輯樣式屬性 - 編輯器中的樣式屬性名 - 預覽 - 樣式 - - - 編輯範本 - 插入內容區 - 插入內容預留位置 - 插入字典項 - 插入巨集 - 插入頁欄位 - 母版 - 範本標籤快速指南 - 範本 - - - Image - Macro - 選擇內容類別 - 選擇排列方式 - 新增一行 - 新增內容 - 放棄內容 - 設定已儲存 - 此處不允許有內容 - 此處允許有內容 - 點選來內嵌 - 點選來插入圖片 - 圖片標題... - 在此填寫... - 網格排列方式 - 排列是指網格編輯器的整體工作區域,通常您只需要一種或兩種排列方式 - 增加網格排列方式 - 藉由設定列寬以及增加新的區域來調整排列方式 - 行設定 - 行是預先水平排列的格子 - 增加行設定 - 藉由設定小格寬度和增添小格來調整此行 - - 網格排列方式的列總數 - 設定 - 調整設定編輯器可以改變的項目 - 樣式 - 調整樣式編輯器可以改變的項目 - 允許所有編輯器 - 允許所有行設定 - 定為預設 - 選擇額外 - 選擇預設 - 已增加 - - - 組合 - 您沒有增加任何選項卡 - 繼承的表格 - 增加屬性 - 必要標籤 - 允許清單檢視 - 允許內容項目顯示成可以排列及搜尋的清單,子項目不會被顯示 - 允許的範本 - 選擇哪些範本編輯器可以使用於此類別的內容 - 允許為根項目 - 允許編輯器新增此類別的內容為根項目 - 允許子節點種類 - 允許某些特定種類能夠成為此種類內容的子項目 - 選擇子節點 - 從已存在的文檔類別中繼承選項卡以及屬性。新選項卡將被新增至目前文檔種類或合併至已存在同名的選項卡中。 - 此內容種類已經用於集合中,因此不能重複添加本身。 - 沒有可用於集合的內容種類。 - 可用的編輯器 - 重複使用 - 編輯器設定 - 設定 - 是,刪除 - 已移至下層 - 已複製至下層 - 選擇要移動的資料夾 - 選擇要複製的資料夾 - 至下方樹狀結構 - 所有文檔種類 - 所有文檔 - 所有媒體項目 - 使用此文檔種類的將被永久刪除,請確認您也想要將它們刪除。 - 使用此媒體種類的將被永久刪除,請確認您也想要將它們刪除。 - 使用此會員種類的將被永久刪除,請確認您也想要將它們刪除。 - 以及所有使用此種類的文件項目 - 以及所有使用此種類的媒體項目 - 以及所有使用此種類的會員項目 - 會員可以編輯 - 顯示於會員資料 - - - 替代欄位 - 替代文本 - 大小寫 - 編碼 - 選取欄位 - 轉換分行符號 - 將換行符號取代成為HTML標籤 &lt;br&gt; - 自訂欄位 - 是,僅日期 - 格式化時間 - HTML編碼 - 將替換HTML中的特殊字元 - 將在欄位值後插入 - 將在欄位值前插入 - 小寫 - - 欄位後插入 - 欄位前插入 - 遞迴 - 標準欄位 - 大寫 - URL編碼 - 將格式化URL中的特殊字元 - 當上面欄位值為空時使用 - 該欄位僅在主欄位為空時使用 - 是,含時間,分隔符號為: - - - 翻譯詳情 - 下載 XML DTD - 欄位 - 包含子頁 - - 沒有翻譯員,請創建翻譯員角色的用戶。 - 頁面'%0%'已經發送給翻譯 - 發送頁面'%0%'以便翻譯 - 總字數 - 翻譯到 - 翻譯完成。 - 您可以流覽剛翻譯的頁面,如果原始頁存在,您將得到兩者的比較。 - 翻譯失敗,XML可能損壞了。 - 翻譯選項 - 翻譯員 - 上傳翻譯的xml - - - 緩存流覽 - 回收站 - 創建擴展包 - 資料類型 - 字典 - 已安裝的擴展包 - 安裝皮膚 - 安裝新手套件 - 語言 - 安裝本地擴展包 - 巨集 - 媒體類型 - 會員 - 會員組 - 角色 - 會員類型 - 文檔類型 - 相關類型 - 擴展包 - 擴展包 - 從線上程式庫安裝 - 安裝Runway - Runway模組 - Scripting文件 - 腳本 - 樣式表 - 範本 - - - 有可用更新 - %0%已就緒,點擊這裡下載 - 無到伺服器的連接 - 檢查更新失敗 - - - 管理員 - 分類欄位 - 更改密碼 - 更改密碼 - 確認新密碼 - 要改變密碼,請在框中輸入新密碼,然後按一下“更改密碼”。 - 內容頻道 - 描述欄位 - 禁用用戶 - 文檔類型 - 編輯 - 排除欄位 - 語言 - 登錄 - 默認打開媒體項 - 區域 - 禁用後臺管理介面 - 舊的密碼 - 密碼 - 重設密碼 - 您的密碼已更改! - 重輸密碼 - 輸入新密碼 - 新密碼不能為空! - 當前密碼 - 密碼錯誤 - 新密碼和重輸入的密碼不一致,請重試! - 重輸的密碼和原密碼不一致! - 替換子項許可權設置 - 您正在修改存取權限的頁面: - 選擇要修改許可權的頁 - 搜索子物件 - 預設打開內容項 - 用戶名 - 用戶許可權 - 撰稿人 - 改變 - 您的個人檔案 - 您的歷程記錄 - 連線到期於 - - - 驗證 - 以電子郵件驗證 - 以數字驗證 - 以網址驗證 - ...或輸入自訂驗證 - 必要欄位 - - - - 數值已設為推薦值:%0% - 在設定檔 %3% 中XPath %2% 的數值設為 %1% 。 - 在設定檔 %3% 中XPath %2% 的預期值設為 %1% ,但卻是 %0%。 - 在設定檔 %3% 中XPath %2% 的值為非預期值 %0%。 - - 自訂錯誤設定為 %0% - 自訂錯誤設定為 %0。建議在上線前改為 %1%。 - 自訂錯誤成功設定為 %0% - 巨集錯誤設為 %0% - 巨集錯誤設為 %0%,如此一來,當巨集有任何錯誤時會阻止某些或全部頁面正常載入。改正會將此設定 %1%。 - 巨集錯誤已設為 %0% - - 嘗試略過IIS自訂錯誤目前設為 %0%,而且您使用的IIS版本為 %1%。 - 嘗試略過IIS自訂錯誤目前設為 %0%,然而在您使用的IIS版本為 %2% 時,建議設定是 %1%。 - 嘗試略過IIS自訂錯誤已成功設為 %0%。 - - 檔案不存在:%0%。 - '%1%'中無法找到'%0%'。]]> - 有錯誤產生,請參閱下列錯誤的紀錄:%0%。 - 憑證驗證錯誤:%0% - 網址探查錯誤:%0% - '%1%' - 您目前使用HTTPS瀏覽本站:%0% - 在您的web.config檔案中,appSetting的umbracoUseSSL是設為false。當您開始使用HTTPS時,應將其改為 true。 - 在您的web.config檔案中,appSetting的umbracoUseSSL是設為 %0%,您的cookies %0% 標成安全。 - 無法在您的web.config檔案中,更新appSetting的umbracoUseSSL設定,錯誤訊息:%0% - - 開啟HTTPS - 在web.config檔案中,將appSetting的umbracoUseSSL設true。 - 在您的web.config檔案中,appSetting的umbracoUseSSL已設為 true,您的cookies 將被標成安全。 - 修正 - 無法修正比較種類檢查為'ShouldNotEqual'。 - 用提供的數值無法修正比較種類檢查為'ShouldEqual'。 - 沒有提供要修正檢查的數值。 - 偵錯編輯模式關閉。 - 偵錯編輯模式目前已開啟。上線前建議將其關閉。 - 偵錯編輯模式已成功關閉。 - 詳細記錄模式已關閉。 - 詳細記錄模式目前已開啟。上線前建議將其關閉。 - 詳細記錄模式已成功關閉。 - 所有資料夾已有正確權限設定。 - - %0%。]]> - %0%。如果無須寫入,不需採取行動。]]> - 所有檔案已有正確權限設定。 - - %0%。]]> - %0%。如果無須寫入,不需採取行動。]]> - X-Frame-Options 設定能控制網站是否可以被其他人IFRAMEd已找到。]]> - X-Frame-Options 設定能控制網站是否可以被其他人IFRAMEd沒有找到。]]> - 調整設定的標頭 - 在 web.config 的 httpProtocol/customHeaders 區域增加設定來防止本站被別的網站IFRAMEd。 - 在 web.config 的 httpProtocol/customHeaders 區域已經增加設定來防止本站被別的網站IFRAMEd。 - 無法更新web.config檔案,錯誤:%0% - - %0%。]]> - 在標頭中沒有找到揭露網站技術的資訊。 - 在 Web.config 檔案中,找不到 system.net/mailsettings。 - 在 Web.config 檔案中的 system.net/mailsettings,沒有設定 host 。 - SMTP設定正確,而且服務正常運作。 - SMTP伺服器 %0% : %1% 無法連接。請確認在Web.config 檔案中 system.net/mailsettings 設定正確。 - %0%。]]> - %0%。]]> - - - 停止網址追蹤器 - 啟動網址追蹤器 - 原本網址 - 轉址成 - 沒有任何轉址 - 當發佈後的頁面改名或移動時,會自動轉址至新網頁。 - 移除 - 您確定要移除從 %0% 到 %1% 的轉址嗎? - 轉址已移除。 - 移除轉址錯誤。 - - 您確定要停止轉址追蹤器? - 轉址追蹤器已停止。 - 停止轉址追蹤器錯誤,更多資訊請參閱您的紀錄檔。 - 轉址追蹤器已開啟。 - 啟動轉址追蹤器錯誤,更多資訊請參閱您的紀錄檔。 - -
diff --git a/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index 956c17fe4a..0000000000 --- a/src/Umbraco.Web.UI/umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs index 22f1554269..b0192e9e75 100644 --- a/src/Umbraco.Web/Cache/ContentCacheRefresher.cs +++ b/src/Umbraco.Web/Cache/ContentCacheRefresher.cs @@ -44,16 +44,14 @@ namespace Umbraco.Web.Cache public override void Refresh(JsonPayload[] payloads) { - var runtimeCache = Current.ApplicationCache.RuntimeCache; - - runtimeCache.ClearCacheObjectTypes(); + CacheHelper.RuntimeCache.ClearCacheObjectTypes(); var idsRemoved = new HashSet(); + var isolatedCache = CacheHelper.IsolatedRuntimeCache.GetOrCreateCache(); foreach (var payload in payloads) { - // remove that one - runtimeCache.ClearCacheItem(RepositoryCacheKeys.GetKey(payload.Id)); + isolatedCache.ClearCacheItem(RepositoryCacheKeys.GetKey(payload.Id)); _idkMap.ClearCache(payload.Id); @@ -61,7 +59,7 @@ namespace Umbraco.Web.Cache if (payload.ChangeTypes.HasTypesAny(TreeChangeTypes.RefreshBranch | TreeChangeTypes.Remove)) { var pathid = "," + payload.Id + ","; - runtimeCache.ClearCacheObjectTypes((k, v) => v.Path.Contains(pathid)); + isolatedCache.ClearCacheObjectTypes((k, v) => v.Path.Contains(pathid)); } //if the item is being completely removed, we need to refresh the domains cache if any domain was assigned to the content diff --git a/src/Umbraco.Web/Composing/Current.cs b/src/Umbraco.Web/Composing/Current.cs index ffe07393e6..1e8f3d17f7 100644 --- a/src/Umbraco.Web/Composing/Current.cs +++ b/src/Umbraco.Web/Composing/Current.cs @@ -11,6 +11,7 @@ using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Persistence; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; +using Umbraco.Core.Packaging; using Umbraco.Core.PropertyEditors; using Umbraco.Core.Scoping; using Umbraco.Core.Services; @@ -158,41 +159,6 @@ namespace Umbraco.Web.Composing #endregion - #region Web Actions - - public static void RestartAppPool() - { - // see notes in overload - - var httpContext = HttpContext.Current; - if (httpContext != null) - { - httpContext.Application.Add("AppPoolRestarting", true); - httpContext.User = null; - } - Thread.CurrentPrincipal = null; - HttpRuntime.UnloadAppDomain(); - } - - public static void RestartAppPool(HttpContextBase httpContext) - { - // we're going to put an application wide flag to show that the application is about to restart. - // we're doing this because if there is a script checking if the app pool is fully restarted, then - // it can check if this flag exists... if it does it means the app pool isn't restarted yet. - httpContext.Application.Add("AppPoolRestarting", true); - - // unload app domain - we must null out all identities otherwise we get serialization errors - // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue - httpContext.User = null; - if (HttpContext.Current != null) - HttpContext.Current.User = null; - Thread.CurrentPrincipal = null; - - HttpRuntime.UnloadAppDomain(); - } - - #endregion - #region Core Getters // proxy Core for convenience @@ -215,6 +181,8 @@ namespace Umbraco.Web.Composing internal static ManifestValueValidatorCollection ManifestValidators => CoreCurrent.ManifestValidators; + internal static IPackageActionRunner PackageActionRunner => CoreCurrent.PackageActionRunner; + internal static PackageActionCollection PackageActions => CoreCurrent.PackageActions; internal static PropertyValueConverterCollection PropertyValueConverters => CoreCurrent.PropertyValueConverters; diff --git a/src/Umbraco.Web/Composing/ModuleInjector.cs b/src/Umbraco.Web/Composing/ModuleInjector.cs index 01930d55fd..57ef766dea 100644 --- a/src/Umbraco.Web/Composing/ModuleInjector.cs +++ b/src/Umbraco.Web/Composing/ModuleInjector.cs @@ -1,5 +1,4 @@ -using System; -using System.Web; +using System.Web; using Umbraco.Core; using Umbraco.Core.Composing; using Umbraco.Core.Exceptions; @@ -11,7 +10,7 @@ namespace Umbraco.Web.Composing ///
/// The type of the injected module. public abstract class ModuleInjector : IHttpModule - where TModule : IHttpModule + where TModule : class, IHttpModule { protected TModule Module { get; private set; } diff --git a/src/Umbraco.Web/CompositionExtensions.cs b/src/Umbraco.Web/CompositionExtensions.cs index f72c876a61..d33b1addf5 100644 --- a/src/Umbraco.Web/CompositionExtensions.cs +++ b/src/Umbraco.Web/CompositionExtensions.cs @@ -81,7 +81,7 @@ namespace Umbraco.Core.Components /// Gets the url providers collection builder. ///
/// The composition. - internal static UrlProviderCollectionBuilder UrlProviders(this Composition composition) + public static UrlProviderCollectionBuilder UrlProviders(this Composition composition) => composition.WithCollectionBuilder(); #endregion diff --git a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs index 71ceb6f4b4..ee973a4b58 100644 --- a/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs +++ b/src/Umbraco.Web/Editors/BackOfficeServerVariables.cs @@ -208,6 +208,10 @@ namespace Umbraco.Web.Editors "packageInstallApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.Fetch(string.Empty)) }, + { + "packageApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( + controller => controller.GetCreatedPackages()) + }, { "relationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl( controller => controller.GetById(0)) diff --git a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs index 8c3edd915d..dd6d22a967 100644 --- a/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs +++ b/src/Umbraco.Web/Editors/Binders/ContentModelBinderHelper.cs @@ -3,6 +3,7 @@ using System.Net.Http; using System.Web.Http; using System.Web.Http.Controllers; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Core.Models; using Umbraco.Core.Models.Editors; using Umbraco.Web.Models.ContentEditing; @@ -19,7 +20,7 @@ namespace Umbraco.Web.Editors.Binders public TModelSave BindModelFromMultipartRequest(HttpActionContext actionContext, ModelBindingContext bindingContext) where TModelSave : IHaveUploadedFiles { - var result = actionContext.ReadAsMultipart("~/App_Data/TEMP/FileUploads"); + var result = actionContext.ReadAsMultipart(SystemDirectories.TempFileUploads); var model = actionContext.GetModelFromMultipartRequest(result, "contentItem"); diff --git a/src/Umbraco.Web/Editors/ContentTypeController.cs b/src/Umbraco.Web/Editors/ContentTypeController.cs index 397f6b3e9d..670d37e7a7 100644 --- a/src/Umbraco.Web/Editors/ContentTypeController.cs +++ b/src/Umbraco.Web/Editors/ContentTypeController.cs @@ -11,10 +11,18 @@ using System.Web.Http; using System.Xml; using System.Xml.Linq; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.Packaging; +using Umbraco.Core.Persistence; +using Umbraco.Core.PropertyEditors; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; using Umbraco.Web.Models; using Umbraco.Web.Models.ContentEditing; @@ -40,6 +48,22 @@ namespace Umbraco.Web.Editors [EnableOverrideAuthorization] public class ContentTypeController : ContentTypeControllerBase { + private readonly IEntityXmlSerializer _serializer; + private readonly PropertyEditorCollection _propertyEditors; + + public ContentTypeController(IEntityXmlSerializer serializer, + ICultureDictionaryFactory cultureDictionaryFactory, + IGlobalSettings globalSettings, + IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, PropertyEditorCollection propertyEditors, + ServiceContext services, CacheHelper applicationCache, + IProfilingLogger logger, IRuntimeState runtimeState) + : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + _serializer = serializer; + _propertyEditors = propertyEditors; + } + public int GetCount() { return Services.ContentTypeService.Count(); @@ -150,8 +174,8 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.NotFound); } - var configuration = Current.Services.DataTypeService.GetDataType(id).Configuration; - var editor = Current.PropertyEditors[dataTypeDiff.EditorAlias]; + var configuration = Services.DataTypeService.GetDataType(id).Configuration; + var editor = _propertyEditors[dataTypeDiff.EditorAlias]; return new ContentPropertyDisplay() { @@ -444,11 +468,7 @@ namespace Umbraco.Web.Editors var contentType = Services.ContentTypeService.Get(id); if (contentType == null) throw new NullReferenceException("No content type found with id " + id); - var serializer = new EntityXmlSerializer(); - var xml = serializer.Serialize( - Services.DataTypeService, - Services.ContentTypeService, - contentType); + var xml = _serializer.Serialize(contentType); var response = new HttpResponseMessage { @@ -480,14 +500,16 @@ namespace Umbraco.Web.Editors { return Request.CreateResponse(HttpStatusCode.NotFound); } + + var dataInstaller = new PackageDataInstallation(Logger, Services.FileService, Services.MacroService, Services.LocalizationService, + Services.DataTypeService, Services.EntityService, Services.ContentTypeService, Services.ContentService, _propertyEditors); - var xd = new XmlDocument(); - xd.XmlResolver = null; + var xd = new XmlDocument {XmlResolver = null}; xd.Load(filePath); - var userId = Security.GetUserId(); + var userId = Security.GetUserId().ResultOr(0); var element = XElement.Parse(xd.InnerXml); - Current.Services.PackagingService.ImportContentTypes(element, userId); + dataInstaller.ImportDocumentType(element, userId); // Try to clean up the temporary file. try @@ -496,7 +518,7 @@ namespace Umbraco.Web.Editors } catch (Exception ex) { - Current.Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); + Logger.Error(ex, "Error cleaning up temporary udt file in App_Data: {File}", filePath); } return Request.CreateResponse(HttpStatusCode.OK); @@ -511,7 +533,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "FileUploads"); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -524,26 +546,24 @@ namespace Umbraco.Web.Editors } var model = new ContentTypeImportModel(); + var file = result.FileData[0]; var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); if (ext.InvariantEquals("udt")) { - //TODO: Currently it has to be here, it's not ideal but that's the way it is right now - var tempDir = IOHelper.MapPath(SystemDirectories.Data); + model.TempFileName = Path.Combine(root, model.TempFileName); - //ensure it's there - Directory.CreateDirectory(tempDir); - - model.TempFileName = "justDelete_" + Guid.NewGuid() + ".udt"; - var tempFileLocation = Path.Combine(tempDir, model.TempFileName); - System.IO.File.Copy(file.LocalFileName, tempFileLocation, true); + model.UploadedFiles.Add(new ContentPropertyFile + { + TempFilePath = model.TempFileName + }); var xd = new XmlDocument { XmlResolver = null }; - xd.Load(tempFileLocation); + xd.Load(model.TempFileName); model.Alias = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Alias")?.FirstChild.Value; model.Name = xd.DocumentElement?.SelectSingleNode("//DocumentType/Info/Name")?.FirstChild.Value; @@ -559,5 +579,7 @@ namespace Umbraco.Web.Editors return model; } + + } } diff --git a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs index 2572bed816..898208319a 100644 --- a/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs +++ b/src/Umbraco.Web/Editors/ContentTypeControllerBase.cs @@ -7,9 +7,13 @@ using System.Text; using System.Web.Http; using AutoMapper; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; using Umbraco.Core.Dictionary; using Umbraco.Core.Exceptions; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Models.ContentEditing; @@ -26,8 +30,16 @@ namespace Umbraco.Web.Editors public abstract class ContentTypeControllerBase : UmbracoAuthorizedJsonController where TContentType : class, IContentTypeComposition { + private readonly ICultureDictionaryFactory _cultureDictionaryFactory; private ICultureDictionary _cultureDictionary; + protected ContentTypeControllerBase(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + _cultureDictionaryFactory = cultureDictionaryFactory; + } + + + /// /// Returns the available composite content types for a given content type /// @@ -536,6 +548,6 @@ namespace Umbraco.Web.Editors } private ICultureDictionary CultureDictionary - => _cultureDictionary ?? (_cultureDictionary = Current.CultureDictionaryFactory.CreateDictionary()); + => _cultureDictionary ?? (_cultureDictionary = _cultureDictionaryFactory.CreateDictionary()); } } diff --git a/src/Umbraco.Web/Editors/MediaController.cs b/src/Umbraco.Web/Editors/MediaController.cs index 2c6ba06dc2..ce088e0caa 100644 --- a/src/Umbraco.Web/Editors/MediaController.cs +++ b/src/Umbraco.Web/Editors/MediaController.cs @@ -626,7 +626,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); diff --git a/src/Umbraco.Web/Editors/MediaTypeController.cs b/src/Umbraco.Web/Editors/MediaTypeController.cs index f8455fc01d..f2b8fd3dda 100644 --- a/src/Umbraco.Web/Editors/MediaTypeController.cs +++ b/src/Umbraco.Web/Editors/MediaTypeController.cs @@ -15,6 +15,11 @@ using System; using System.ComponentModel; using System.Web.Http.Controllers; using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; using Umbraco.Web.Composing; namespace Umbraco.Web.Editors @@ -32,6 +37,10 @@ namespace Umbraco.Web.Editors [MediaTypeControllerControllerConfiguration] public class MediaTypeController : ContentTypeControllerBase { + public MediaTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + } + /// /// Configures this controller with a custom action selector /// @@ -302,5 +311,7 @@ namespace Umbraco.Web.Editors getContentType: i => Services.MediaTypeService.Get(i), doCopy: (type, i) => Services.MediaTypeService.Copy(type, i)); } + + } } diff --git a/src/Umbraco.Web/Editors/MemberTypeController.cs b/src/Umbraco.Web/Editors/MemberTypeController.cs index 77ff974aaa..3abc0035d3 100644 --- a/src/Umbraco.Web/Editors/MemberTypeController.cs +++ b/src/Umbraco.Web/Editors/MemberTypeController.cs @@ -6,7 +6,13 @@ using System.Net.Http; using System.Web.Http; using System.Web.Security; using AutoMapper; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Dictionary; +using Umbraco.Core.Logging; using Umbraco.Core.Models; +using Umbraco.Core.Persistence; using Umbraco.Core.Security; using Umbraco.Core.Services; using Umbraco.Web.Models.ContentEditing; @@ -24,6 +30,10 @@ namespace Umbraco.Web.Editors [UmbracoTreeAuthorize(new string[] { Constants.Trees.MemberTypes, Constants.Trees.Members})] public class MemberTypeController : ContentTypeControllerBase { + public MemberTypeController(ICultureDictionaryFactory cultureDictionaryFactory, IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(cultureDictionaryFactory, globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + } + private readonly MembershipProvider _provider = Core.Security.MembershipProviderExtensions.GetMembersMembershipProvider(); [UmbracoTreeAuthorize(Constants.Trees.MemberTypes)] @@ -166,5 +176,7 @@ namespace Umbraco.Web.Editors return display; } + + } } diff --git a/src/Umbraco.Web/Editors/PackageController.cs b/src/Umbraco.Web/Editors/PackageController.cs new file mode 100644 index 0000000000..4814a8000a --- /dev/null +++ b/src/Umbraco.Web/Editors/PackageController.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Web.Http; +using Semver; +using Umbraco.Core.IO; +using Umbraco.Core.Models.Packaging; +using Umbraco.Web.Models.ContentEditing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; +using Umbraco.Web.WebApi.Filters; + +namespace Umbraco.Web.Editors +{ + /// + /// A controller used for managing packages in the back office + /// + [PluginController("UmbracoApi")] + [SerializeVersion] + [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] + public class PackageController : UmbracoAuthorizedJsonController + { + public IEnumerable GetCreatedPackages() + { + return Services.PackagingService.GetAllCreatedPackages(); + } + + public PackageDefinition GetCreatedPackageById(int id) + { + var package = Services.PackagingService.GetCreatedPackageById(id); + if (package == null) + throw new HttpResponseException(HttpStatusCode.NotFound); + + return package; + } + + public PackageDefinition GetEmpty() + { + return new PackageDefinition(); + } + + /// + /// Creates or updates a package + /// + /// + /// + public PackageDefinition PostSavePackage(PackageDefinition model) + { + if (ModelState.IsValid == false) + throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState)); + + //save it + if (!Services.PackagingService.SaveCreatedPackage(model)) + throw new HttpResponseException( + Request.CreateNotificationValidationErrorResponse( + model.Id == default + ? $"A package with the name {model.Name} already exists" + : $"The package with id {model.Id} was not found")); + + Services.PackagingService.ExportCreatedPackage(model); + + //the packagePath will be on the model + return model; + } + + /// + /// Deletes a created package + /// + /// + /// + [HttpPost] + [HttpDelete] + public IHttpActionResult DeleteCreatedPackage(int packageId) + { + Services.PackagingService.DeleteCreatedPackage(packageId, Security.GetUserId().ResultOr(0)); + + return Ok(); + } + + [HttpGet] + public HttpResponseMessage DownloadCreatedPackage(int id) + { + var package = Services.PackagingService.GetCreatedPackageById(id); + if (package == null) + return Request.CreateResponse(HttpStatusCode.NotFound); + + var fullPath = IOHelper.MapPath(package.PackagePath); + if (!File.Exists(fullPath)) + return Request.CreateNotificationValidationErrorResponse("No file found for path " + package.PackagePath); + + var fileName = Path.GetFileName(package.PackagePath); + + var response = new HttpResponseMessage + { + Content = new StreamContent(File.OpenRead(fullPath)) + { + Headers = + { + ContentDisposition = new ContentDispositionHeaderValue("attachment") + { + FileName = fileName + }, + ContentType = new MediaTypeHeaderValue( "application/octet-stream") + } + } + }; + + // Set custom header so umbRequestHelper.downloadFile can save the correct filename + response.Headers.Add("x-filename", fileName); + + return response; + } + + /// + /// Returns all installed packages - only shows their latest versions + /// + /// + public IEnumerable GetInstalled() + { + return Services.PackagingService.GetAllInstalledPackages() + .GroupBy( + //group by name + x => x.Name, + //select the package with a parsed version + pck => SemVersion.TryParse(pck.Version, out var pckVersion) + ? new { package = pck, version = pckVersion } + : new { package = pck, version = new SemVersion(0, 0, 0) }) + .Select(grouping => + { + //get the max version for the package + var maxVersion = grouping.Max(x => x.version); + //only return the first package with this version + return grouping.First(x => x.version == maxVersion).package; + }) + .Select(pack => new InstalledPackageModel + { + Name = pack.Name, + Id = pack.Id, + Author = pack.Author, + Version = pack.Version, + Url = pack.Url, + License = pack.License, + LicenseUrl = pack.LicenseUrl, + Files = pack.Files, + IconUrl = pack.IconUrl, + Readme = pack.Readme + }) + .ToList(); + } + } +} diff --git a/src/Umbraco.Web/Editors/PackageInstallController.cs b/src/Umbraco.Web/Editors/PackageInstallController.cs index aee74551ea..05d1e2a7a3 100644 --- a/src/Umbraco.Web/Editors/PackageInstallController.cs +++ b/src/Umbraco.Web/Editors/PackageInstallController.cs @@ -8,13 +8,19 @@ using System.Net.Http; using System.Threading.Tasks; using System.Web.Http; using System.Xml; +using System.Xml.Linq; +using Semver; using Umbraco.Core; +using Umbraco.Core.Cache; using Umbraco.Core.Configuration; using Umbraco.Core.Events; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.Models; -using Umbraco.Core.Packaging.Models; +using Umbraco.Core.Models.Editors; +using Umbraco.Core.Models.Packaging; +using Umbraco.Core.Packaging; +using Umbraco.Core.Persistence; using Umbraco.Core.Services; using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; @@ -25,8 +31,6 @@ using Umbraco.Web.UI; using Umbraco.Web.UI.JavaScript; using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; -using Umbraco.Web._Legacy.Packager; -using Umbraco.Web._Legacy.Packager.PackageInstance; using File = System.IO.File; using Notification = Umbraco.Web.Models.ContentEditing.Notification; using Version = System.Version; @@ -40,6 +44,13 @@ namespace Umbraco.Web.Editors [UmbracoApplicationAuthorize(Core.Constants.Applications.Packages)] public class PackageInstallController : UmbracoAuthorizedJsonController { + public PackageInstallController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, + ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, + IProfilingLogger logger, IRuntimeState runtimeState) + : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + } + /// /// This checks if this package & version is alraedy installed /// @@ -49,9 +60,11 @@ namespace Umbraco.Web.Editors [HttpPost] public IHttpActionResult ValidateInstalled(string name, string version) { - var validate = ValidateInstalledInternal(name, version); - if (validate == false) + var installType = Services.PackagingService.GetPackageInstallType(name, SemVersion.Parse(version), out _); + + if (installType == PackageInstallType.AlreadyInstalled) return BadRequest(); + return Ok(); } @@ -60,17 +73,17 @@ namespace Umbraco.Web.Editors { try { - var pack = InstalledPackage.GetById(packageId); - if (pack == null) return NotFound(); + var package = Services.PackagingService.GetInstalledPackageById(packageId); + if (package == null) return NotFound(); - PerformUninstall(pack); + var summary = Services.PackagingService.UninstallPackage(package.Name, Security.GetUserId().ResultOr(0)); //now get all other packages by this name since we'll uninstall all versions - foreach (var installed in InstalledPackage.GetAllInstalledPackages() - .Where(x => x.Data.Name == pack.Data.Name && x.Data.Id != pack.Data.Id)) + foreach (var installed in Services.PackagingService.GetAllInstalledPackages() + .Where(x => x.Name == package.Name && x.Id != package.Id)) { - //remove from teh xml - installed.Delete(Security.GetUserId().ResultOr(0)); + //remove from the xml + Services.PackagingService.DeleteInstalledPackage(installed.Id, Security.GetUserId().ResultOr(0)); } } catch (Exception ex) @@ -82,267 +95,37 @@ namespace Umbraco.Web.Editors return Ok(); } - /// - /// SORRY :( I didn't have time to put this in a service somewhere - the old packager did this all manually too - /// - /// - protected void PerformUninstall(InstalledPackage pack) - { - if (pack == null) throw new ArgumentNullException("pack"); - - var removedTemplates = new List(); - var removedMacros = new List(); - var removedContentTypes = new List(); - var removedDictionaryItems = new List(); - var removedDataTypes = new List(); - var removedFiles = new List(); - - //Uninstall templates - foreach (var item in pack.Data.Templates.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var found = Services.FileService.GetTemplate(nId); - if (found != null) - { - removedTemplates.Add(found); - Services.FileService.DeleteTemplate(found.Alias, Security.GetUserId().ResultOr(0)); - } - pack.Data.Templates.Remove(nId.ToString()); - } - - //Uninstall macros - foreach (var item in pack.Data.Macros.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var macro = Services.MacroService.GetById(nId); - if (macro != null) - { - removedMacros.Add(macro); - Services.MacroService.Delete(macro); - } - pack.Data.Macros.Remove(nId.ToString()); - } - - //Remove Document Types - var contentTypes = new List(); - var contentTypeService = Services.ContentTypeService; - foreach (var item in pack.Data.Documenttypes.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var contentType = contentTypeService.Get(nId); - if (contentType == null) continue; - contentTypes.Add(contentType); - pack.Data.Documenttypes.Remove(nId.ToString(CultureInfo.InvariantCulture)); - } - - //Order the DocumentTypes before removing them - if (contentTypes.Any()) - { - //TODO: I don't think this ordering is necessary - var orderedTypes = from contentType in contentTypes - orderby contentType.ParentId descending, contentType.Id descending - select contentType; - removedContentTypes.AddRange(orderedTypes); - contentTypeService.Delete(orderedTypes); - } - - //Remove Dictionary items - foreach (var item in pack.Data.DictionaryItems.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var di = Services.LocalizationService.GetDictionaryItemById(nId); - if (di != null) - { - removedDictionaryItems.Add(di); - Services.LocalizationService.Delete(di); - } - pack.Data.DictionaryItems.Remove(nId.ToString()); - } - - //Remove Data types - foreach (var item in pack.Data.DataTypes.ToArray()) - { - int nId; - if (int.TryParse(item, out nId) == false) continue; - var dtd = Services.DataTypeService.GetDataType(nId); - if (dtd != null) - { - removedDataTypes.Add(dtd); - Services.DataTypeService.Delete(dtd); - } - pack.Data.DataTypes.Remove(nId.ToString()); - } - - pack.Save(); - - // uninstall actions - //TODO: We should probably report errors to the UI!! - // This never happened before though, but we should do something now - if (pack.Data.Actions.IsNullOrWhiteSpace() == false) - { - try - { - var actionsXml = new XmlDocument(); - actionsXml.LoadXml("" + pack.Data.Actions + ""); - - Logger.Debug("Executing undo actions: {UndoActionsXml}", actionsXml.OuterXml); - - foreach (XmlNode n in actionsXml.DocumentElement.SelectNodes("//Action")) - { - try - { - global::Umbraco.Web._Legacy.Packager.PackageInstance.PackageAction - .UndoPackageAction(pack.Data.Name, n.Attributes["alias"].Value, n); - } - catch (Exception ex) - { - Logger.Error(ex, "An error occurred running undo actions"); - } - } - } - catch (Exception ex) - { - Logger.Error(ex, "An error occurred running undo actions"); - } - } - - //moved remove of files here so custom package actions can still undo - //Remove files - foreach (var item in pack.Data.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) - { - if (file.StartsWith("/") == false) - file = string.Format("/{0}", file); - var filePath = IOHelper.MapPath(file); - - if (File.Exists(filePath)) - File.Delete(filePath); - - } - pack.Data.Files.Remove(file); - } - pack.Save(); - pack.Delete(Security.GetUserId().ResultOr(0)); - - // create a summary of what was actually removed, for PackagingService.UninstalledPackage - var summary = new UninstallationSummary - { - MetaData = pack.GetMetaData(), - TemplatesUninstalled = removedTemplates, - MacrosUninstalled = removedMacros, - ContentTypesUninstalled = removedContentTypes, - DictionaryItemsUninstalled = removedDictionaryItems, - DataTypesUninstalled = removedDataTypes, - FilesUninstalled = removedFiles, - PackageUninstalled = true - }; - - // trigger the UninstalledPackage event - PackagingService.OnUninstalledPackage(new UninstallPackageEventArgs(summary, false)); - - } - - /// - /// Returns all installed packages - only shows their latest versions - /// - /// - public IEnumerable GetInstalled() - { - return InstalledPackage.GetAllInstalledPackages() - .GroupBy( - //group by name - x => x.Data.Name, - //select the package with a parsed version - pck => - { - Version pckVersion; - return Version.TryParse(pck.Data.Version, out pckVersion) - ? new { package = pck, version = pckVersion } - : new { package = pck, version = new Version(0, 0, 0) }; - }) - .Select(grouping => - { - //get the max version for the package - var maxVersion = grouping.Max(x => x.version); - //only return the first package with this version - return grouping.First(x => x.version == maxVersion).package; - }) - .Select(pack => new InstalledPackageModel - { - Name = pack.Data.Name, - Id = pack.Data.Id, - Author = pack.Data.Author, - Version = pack.Data.Version, - Url = pack.Data.Url, - License = pack.Data.License, - LicenseUrl = pack.Data.LicenseUrl, - Files = pack.Data.Files, - IconUrl = pack.Data.IconUrl - }) - .ToList(); - } - - /// - /// Deletes a created package - /// - /// - /// - [HttpPost] - [HttpDelete] - public IHttpActionResult DeleteCreatedPackage(int packageId) - { - var package = CreatedPackage.GetById(packageId); - if (package == null) - return NotFound(); - - package.Delete(); - - return Ok(); - } - + + private void PopulateFromPackageData(LocalPackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - //this will load in all the metadata too - var tempDir = ins.Import(model.ZipFilePath, false); + var zipFile = new FileInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), model.ZipFileName)); + + var ins = Services.PackagingService.GetCompiledPackageInfo(zipFile); - model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempDir); model.Name = ins.Name; model.Author = ins.Author; model.AuthorUrl = ins.AuthorUrl; model.IconUrl = ins.IconUrl; model.License = ins.License; model.LicenseUrl = ins.LicenseUrl; - model.ReadMe = ins.ReadMe; - model.ConflictingMacroAliases = ins.ConflictingMacroAliases; - model.ConflictingStyleSheetNames = ins.ConflictingStyleSheetNames; - model.ConflictingTemplateAliases = ins.ConflictingTemplateAliases; - model.ContainsBinaryFileErrors = ins.ContainsBinaryFileErrors; - model.ContainsMacroConflict = ins.ContainsMacroConflict; - model.ContainsStyleSheetConflicts = ins.ContainsStyleSheeConflicts; - model.ContainsTemplateConflicts = ins.ContainsTemplateConflicts; - model.ContainsUnsecureFiles = ins.ContainsUnsecureFiles; + model.Readme = ins.Readme; + model.ConflictingMacroAliases = ins.Warnings.ConflictingMacros.ToDictionary(x => x.Name, x => x.Alias); + model.ConflictingStyleSheetNames = ins.Warnings.ConflictingStylesheets.ToDictionary(x => x.Name, x => x.Alias); ; + model.ConflictingTemplateAliases = ins.Warnings.ConflictingTemplates.ToDictionary(x => x.Name, x => x.Alias); ; + model.ContainsUnsecureFiles = ins.Warnings.UnsecureFiles.Any(); model.Url = ins.Url; model.Version = ins.Version; - model.UmbracoVersion = ins.RequirementsType == RequirementsType.Strict - ? string.Format("{0}.{1}.{2}", ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch) + model.UmbracoVersion = ins.UmbracoVersionRequirementsType == RequirementsType.Strict + ? ins.UmbracoVersion.ToString(3) : string.Empty; //now we need to check for version comparison model.IsCompatible = true; - if (ins.RequirementsType == RequirementsType.Strict) + if (ins.UmbracoVersionRequirementsType == RequirementsType.Strict) { - var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch); + var packageMinVersion = ins.UmbracoVersion; if (UmbracoVersion.Current < packageMinVersion) { model.IsCompatible = false; @@ -350,44 +133,14 @@ namespace Umbraco.Web.Editors } } - private bool ValidateInstalledInternal(string name, string version) - { - var allInstalled = InstalledPackage.GetAllInstalledPackages(); - var found = allInstalled.FirstOrDefault(x => - { - if (x.Data.Name != name) return false; - //match the exact version - if (x.Data.Version == version) - { - return true; - } - //now try to compare the versions - Version installed; - Version selected; - if (Version.TryParse(x.Data.Version, out installed) && Version.TryParse(version, out selected)) - { - if (installed >= selected) return true; - } - return false; - }); - if (found != null) - { - //this package is already installed - return false; - } - return true; - } - [HttpPost] [FileUploadCleanupFilter(false)] public async Task UploadLocalPackage() { if (Request.Content.IsMimeMultipartContent() == false) - { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); - } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); @@ -396,51 +149,51 @@ namespace Umbraco.Web.Editors //must have a file if (result.FileData.Count == 0) - { throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotFound)); - } - //TODO: App/Tree Permissions? var model = new LocalPackageInstallModel { + //Generate a new package Id for this, we'll use this later for tracking, when persisting, saving the file, etc... PackageGuid = Guid.NewGuid() }; //get the files foreach (var file in result.FileData) { - var fileName = file.Headers.ContentDisposition.FileName.Trim(new[] { '\"' }); + var fileName = file.Headers.ContentDisposition.FileName.Trim('\"'); var ext = fileName.Substring(fileName.LastIndexOf('.') + 1).ToLower(); - //TODO: Only allow .zip if (ext.InvariantEquals("zip") || ext.InvariantEquals("umb")) { - //TODO: Currently it has to be here, it's not ideal but that's the way it is right now - var packageTempDir = IOHelper.MapPath(SystemDirectories.Data); + //we always save package files to /App_Data/packages/package-guid.umb for processing as a standard so lets copy. + + var packagesFolder = IOHelper.MapPath(SystemDirectories.Packages); + Directory.CreateDirectory(packagesFolder); + var packageFile = Path.Combine(packagesFolder, model.PackageGuid + ".umb"); + File.Copy(file.LocalFileName, packageFile); - //ensure it's there - Directory.CreateDirectory(packageTempDir); + model.ZipFileName = Path.GetFileName(packageFile); - //copy it - must always be '.umb' for the installer thing to work - //the file name must be a GUID - this is what the packager expects (strange yes) - //because essentially this is creating a temporary package Id that will be used - //for unpacking/installing/etc... - model.ZipFilePath = model.PackageGuid + ".umb"; - var packageTempFileLocation = Path.Combine(packageTempDir, model.ZipFilePath); - File.Copy(file.LocalFileName, packageTempFileLocation, true); + //add to the outgoing model so that all temp files are cleaned up + model.UploadedFiles.Add(new ContentPropertyFile + { + TempFilePath = file.LocalFileName + }); //Populate the model from the metadata in the package file (zip file) PopulateFromPackageData(model); - var validate = ValidateInstalledInternal(model.Name, model.Version); + var installType = Services.PackagingService.GetPackageInstallType(model.Name, SemVersion.Parse(model.Version), out var alreadyInstalled); - if (validate == false) + if (installType == PackageInstallType.AlreadyInstalled) { //this package is already installed throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( Services.TextService.Localize("packager/packageAlreadyInstalled"))); } + model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null; + } else { @@ -462,34 +215,39 @@ namespace Umbraco.Web.Editors /// /// [HttpGet] - public LocalPackageInstallModel Fetch(string packageGuid) + public async Task Fetch(string packageGuid) { //Default path - string path = Path.Combine("packages", packageGuid + ".umb"); - if (File.Exists(IOHelper.MapPath(Path.Combine(SystemDirectories.Data, path))) == false) + string fileName = packageGuid + ".umb"; + if (File.Exists(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), fileName)) == false) { - path = Services.PackagingService.FetchPackageFile(Guid.Parse(packageGuid), UmbracoVersion.Current, Security.GetUserId().ResultOr(0)); + var packageFile = await Services.PackagingService.FetchPackageFileAsync( + Guid.Parse(packageGuid), + UmbracoVersion.Current, + Security.GetUserId().ResultOr(0)); + + fileName = packageFile.Name; } var model = new LocalPackageInstallModel { PackageGuid = Guid.Parse(packageGuid), - RepositoryGuid = Guid.Parse("65194810-1f85-11dd-bd0b-0800200c9a66"), - ZipFilePath = path + ZipFileName = fileName }; //Populate the model from the metadata in the package file (zip file) PopulateFromPackageData(model); - var validate = ValidateInstalledInternal(model.Name, model.Version); + var installType = Services.PackagingService.GetPackageInstallType(model.Name, SemVersion.Parse(model.Version), out var alreadyInstalled); - if (validate == false) + if (installType == PackageInstallType.AlreadyInstalled) { - //this package is already installed throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( Services.TextService.Localize("packager/packageAlreadyInstalled"))); } + model.OriginalVersion = installType == PackageInstallType.Upgrade ? alreadyInstalled.Version : null; + return model; } @@ -501,22 +259,42 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel Import(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); + var zipFile = new FileInfo(Path.Combine(IOHelper.MapPath(SystemDirectories.Packages), model.ZipFileName)); + + var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile); - var tempPath = ins.Import(model.ZipFilePath); //now we need to check for version comparison - if (ins.RequirementsType == RequirementsType.Strict) + if (packageInfo.UmbracoVersionRequirementsType == RequirementsType.Strict) { - var packageMinVersion = new System.Version(ins.RequirementsMajor, ins.RequirementsMinor, ins.RequirementsPatch); + var packageMinVersion = packageInfo.UmbracoVersion; if (UmbracoVersion.Current < packageMinVersion) - { throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse( - Services.TextService.Localize("packager/targetVersionMismatch", new[] { packageMinVersion.ToString() }))); - } + Services.TextService.Localize("packager/targetVersionMismatch", new[] {packageMinVersion.ToString()}))); } - model.TemporaryDirectoryPath = Path.Combine(SystemDirectories.Data, tempPath); - model.Id = ins.CreateManifest(IOHelper.MapPath(model.TemporaryDirectoryPath), model.PackageGuid.ToString(), model.RepositoryGuid.ToString()); + var installType = Services.PackagingService.GetPackageInstallType(packageInfo.Name, SemVersion.Parse(packageInfo.Version), out var alreadyInstalled); + + var packageDefinition = PackageDefinition.FromCompiledPackage(packageInfo); + packageDefinition.PackagePath = zipFile.FullName; + packageDefinition.PackageId = model.PackageGuid; //We must re-map the original package GUID that was generated + + switch (installType) + { + case PackageInstallType.AlreadyInstalled: + throw new InvalidOperationException("The package is already installed"); + case PackageInstallType.NewInstall: + case PackageInstallType.Upgrade: + + //save to the installedPackages.config, this will create a new entry with a new Id + if (!Services.PackagingService.SaveInstalledPackage(packageDefinition)) + throw new HttpResponseException(Request.CreateNotificationValidationErrorResponse("Could not save the package")); + + model.Id = packageDefinition.Id; + break; + default: + throw new ArgumentOutOfRangeException(); + } + return model; } @@ -529,12 +307,15 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallFiles(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallFiles(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var definition = Services.PackagingService.GetInstalledPackageById(model.Id); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); + + var zipFile = new FileInfo(definition.PackagePath); + + var installedFiles = Services.PackagingService.InstallCompiledPackageFiles(definition, zipFile, Security.GetUserId().ResultOr(0)); //set a restarting marker and reset the app pool - Current.RestartAppPool(Request.TryGetHttpContext().Result); + UmbracoApplication.Restart(Request.TryGetHttpContext().Result); model.IsRestarting = true; @@ -563,9 +344,13 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallModel InstallData(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallBusinessLogic(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var definition = Services.PackagingService.GetInstalledPackageById(model.Id); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); + + var zipFile = new FileInfo(definition.PackagePath); + + var installSummary = Services.PackagingService.InstallCompiledPackageData(definition, zipFile, Security.GetUserId().ResultOr(0)); + return model; } @@ -577,31 +362,33 @@ namespace Umbraco.Web.Editors [HttpPost] public PackageInstallResult CleanUp(PackageInstallModel model) { - var ins = new global::Umbraco.Web._Legacy.Packager.Installer(Security.CurrentUser.Id); - var tempDir = IOHelper.MapPath(model.TemporaryDirectoryPath); - ins.LoadConfig(IOHelper.MapPath(model.TemporaryDirectoryPath)); - ins.InstallCleanUp(model.Id, IOHelper.MapPath(model.TemporaryDirectoryPath)); + var definition = Services.PackagingService.GetInstalledPackageById(model.Id); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + model.Id); + + var zipFile = new FileInfo(definition.PackagePath); + + var packageInfo = Services.PackagingService.GetCompiledPackageInfo(zipFile); var clientDependencyConfig = new ClientDependencyConfiguration(Logger); var clientDependencyUpdated = clientDependencyConfig.UpdateVersionNumber( UmbracoVersion.SemanticVersion, DateTime.UtcNow, "yyyyMMdd"); + zipFile.Delete(); var redirectUrl = ""; - if (ins.Control.IsNullOrWhiteSpace() == false) + if (packageInfo.Control.IsNullOrWhiteSpace() == false) { - redirectUrl = string.Format("/developer/framed/{0}", - Uri.EscapeDataString( - string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url))); + //fixme: this needs to be replaced with an angular view the installer.aspx no longer exists. + //redirectUrl = string.Format("/developer/framed/{0}", + // Uri.EscapeDataString( + // string.Format("/umbraco/developer/Packages/installer.aspx?installing=custominstaller&dir={0}&pId={1}&customControl={2}&customUrl={3}", tempDir, model.Id, ins.Control, ins.Url))); } return new PackageInstallResult { Id = model.Id, - ZipFilePath = model.ZipFilePath, + ZipFileName = model.ZipFileName, PackageGuid = model.PackageGuid, - RepositoryGuid = model.RepositoryGuid, - TemporaryDirectoryPath = model.TemporaryDirectoryPath, PostInstallationPath = redirectUrl }; diff --git a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs index 2b1fce884d..3baa5e85ff 100644 --- a/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs +++ b/src/Umbraco.Web/Editors/UmbracoAuthorizedJsonController.cs @@ -1,4 +1,10 @@ -using Umbraco.Web.WebApi; +using Umbraco.Core; +using Umbraco.Core.Cache; +using Umbraco.Core.Configuration; +using Umbraco.Core.Logging; +using Umbraco.Core.Persistence; +using Umbraco.Core.Services; +using Umbraco.Web.WebApi; using Umbraco.Web.WebApi.Filters; namespace Umbraco.Web.Editors @@ -13,5 +19,26 @@ namespace Umbraco.Web.Editors [ValidateAngularAntiForgeryToken] [AngularJsonOnlyConfiguration] public abstract class UmbracoAuthorizedJsonController : UmbracoAuthorizedApiController - { } + { + /// + /// Initializes a new instance of the with auto dependencies. + /// + protected UmbracoAuthorizedJsonController() + { + } + + /// + /// Initializes a new instance of the class with all its dependencies. + /// + /// + /// + /// + /// + /// + /// + /// + protected UmbracoAuthorizedJsonController(IGlobalSettings globalSettings, IUmbracoContextAccessor umbracoContextAccessor, ISqlContext sqlContext, ServiceContext services, CacheHelper applicationCache, IProfilingLogger logger, IRuntimeState runtimeState) : base(globalSettings, umbracoContextAccessor, sqlContext, services, applicationCache, logger, runtimeState) + { + } + } } diff --git a/src/Umbraco.Web/Editors/UsersController.cs b/src/Umbraco.Web/Editors/UsersController.cs index 7577624621..e46e83c6e4 100644 --- a/src/Umbraco.Web/Editors/UsersController.cs +++ b/src/Umbraco.Web/Editors/UsersController.cs @@ -70,7 +70,7 @@ namespace Umbraco.Web.Editors throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); } - var root = IOHelper.MapPath("~/App_Data/TEMP/FileUploads"); + var root = IOHelper.MapPath(SystemDirectories.TempFileUploads); //ensure it exists Directory.CreateDirectory(root); var provider = new MultipartFormDataStreamProvider(root); diff --git a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs index a63484fb39..c8e862abcb 100644 --- a/src/Umbraco.Web/Install/Controllers/InstallApiController.cs +++ b/src/Umbraco.Web/Install/Controllers/InstallApiController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.Threading.Tasks; using System.Web.Http; using Newtonsoft.Json.Linq; using Umbraco.Core; @@ -75,7 +76,7 @@ namespace Umbraco.Web.Install.Controllers /// /// Installs. /// - public InstallProgressResultModel PostPerformInstall(InstallInstructions installModel) + public async Task PostPerformInstall(InstallInstructions installModel) { if (installModel == null) throw new ArgumentNullException(nameof(installModel)); @@ -94,8 +95,7 @@ namespace Umbraco.Web.Install.Controllers var step = _installSteps.GetAllSteps().Single(x => x.Name == item.Name); // if this step has any instructions then extract them - JToken instruction; - installModel.Instructions.TryGetValue(item.Name, out instruction); // else null + installModel.Instructions.TryGetValue(item.Name, out var instruction); // else null // if this step doesn't require execution then continue to the next one, this is just a fail-safe check. if (StepRequiresExecution(step, instruction) == false) @@ -107,7 +107,7 @@ namespace Umbraco.Web.Install.Controllers try { - var setupData = ExecuteStep(step, instruction); + var setupData = await ExecuteStepAsync(step, instruction); // update the status InstallStatusTracker.SetComplete(installModel.InstallId, step.Name, setupData?.SavedStepData); @@ -222,7 +222,7 @@ namespace Umbraco.Web.Install.Controllers } // executes the step - internal InstallSetupResult ExecuteStep(InstallSetupStep step, JToken instruction) + internal async Task ExecuteStepAsync(InstallSetupStep step, JToken instruction) { using (_proflog.TraceDuration($"Executing installation step: '{step.Name}'.", "Step completed")) { @@ -232,8 +232,9 @@ namespace Umbraco.Web.Install.Controllers var typedStepType = genericStepType.MakeGenericType(typeArgs); try { - var method = typedStepType.GetMethods().Single(x => x.Name == "Execute"); - return (InstallSetupResult) method.Invoke(step, new[] { model }); + var method = typedStepType.GetMethods().Single(x => x.Name == "ExecuteAsync"); + var task = (Task) method.Invoke(step, new[] { model }); + return await task; } catch (Exception ex) { diff --git a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs b/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs deleted file mode 100644 index 1fd2ac27bb..0000000000 --- a/src/Umbraco.Web/Install/Controllers/InstallPackageController.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Web; -using System.Web.Http; -using Newtonsoft.Json.Linq; -using umbraco; -using Umbraco.Core; -using Umbraco.Web.Cache; -using Umbraco.Core.Configuration; -using Umbraco.Web.Composing; -using Umbraco.Web.Install.Models; -using Umbraco.Web.WebApi; - -namespace Umbraco.Web.Install.Controllers -{ - /// - /// A controller for the installation process regarding packages - /// - /// - /// Currently this is used for web services however we should/could eventually migrate the whole installer to MVC as it - /// is a bit of a mess currently. - /// - [HttpInstallAuthorize] - [AngularJsonOnlyConfiguration] - [Obsolete("This is only used for the legacy way of installing starter kits in the back office")] - public class InstallPackageController : ApiController - { - public InstallPackageController() - { } - - private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; - - /// - /// Empty action, useful for retrieving the base url for this controller - /// - /// - [HttpGet] - public HttpResponseMessage Index() - { - throw new NotImplementedException(); - } - - /// - /// Connects to the repo, downloads the package and creates the manifest - /// - /// - /// - [HttpPost] - public HttpResponseMessage DownloadPackageFiles(InstallPackageModel model) - { - var packageFile = Current.Services.PackagingService.FetchPackageFile( - model.KitGuid, - UmbracoVersion.Current, - UmbracoContext.Current.Security.CurrentUser.Id); - - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - - var tempFile = installer.Import(packageFile); - installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, model.KitGuid.ToString(), RepoGuid); - return Json(new - { - success = true, - manifestId = pId, - packageFile = tempFile, - percentage = 10, - message = "Downloading starter kit files..." - }, HttpStatusCode.OK); - } - - /// - /// Installs the files in the package - /// - /// - [HttpPost] - public HttpResponseMessage InstallPackageFiles(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallFiles(model.ManifestId, model.PackageFile); - return Json(new - { - success = true, - model.ManifestId, - model.PackageFile, - percentage = 20, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } - - /// - /// Ensures the app pool is restarted - /// - /// - [HttpPost] - public HttpResponseMessage RestartAppPool() - { - Current.RestartAppPool(Request.TryGetHttpContext().Result); - return Json(new - { - success = true, - percentage = 25, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } - - /// - /// Checks if the app pool has completed restarted - /// - /// - [HttpPost] - public HttpResponseMessage CheckAppPoolRestart() - { - if (Request.TryGetHttpContext().Result.Application.AllKeys.Contains("AppPoolRestarting")) - { - return Request.CreateResponse(HttpStatusCode.BadRequest); - } - - return Json(new - { - percentage = 30, - success = true, - }, HttpStatusCode.OK); - } - - /// - /// Installs the business logic portion of the package after app restart - /// - /// - [HttpPost] - public HttpResponseMessage InstallBusinessLogic(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallBusinessLogic(model.ManifestId, model.PackageFile); - return Json(new - { - success = true, - model.ManifestId, - model.PackageFile, - percentage = 70, - message = "Installing starter kit files" - }, HttpStatusCode.OK); - } - - /// - /// Cleans up the package installation - /// - /// - [HttpPost] - public HttpResponseMessage CleanupInstallation(InstallPackageModel model) - { - model.PackageFile = HttpUtility.UrlDecode(model.PackageFile); - var installer = new global::Umbraco.Web._Legacy.Packager.Installer(UmbracoContext.Current.Security.CurrentUser.Id); - installer.LoadConfig(model.PackageFile); - installer.InstallCleanUp(model.ManifestId, model.PackageFile); - - // library.RefreshContent is obsolete, would need to RefreshAll... snapshot, - // but it should be managed automatically by services and caches! - //DistributedCache.Instance.RefreshAll...(); - - return Json(new - { - success = true, - model.ManifestId, - model.PackageFile, - percentage = 100, - message = "Starter kit has been installed" - }, HttpStatusCode.OK); - } - - private HttpResponseMessage Json(object jsonObject, HttpStatusCode status) - { - var response = Request.CreateResponse(status); - var json = JObject.FromObject(jsonObject); - response.Content = new StringContent(json.ToString(), Encoding.UTF8, "application/json"); - return response; - } - } - -} diff --git a/src/Umbraco.Web/Install/InstallHelper.cs b/src/Umbraco.Web/Install/InstallHelper.cs index 08a552d4d4..36fb384655 100644 --- a/src/Umbraco.Web/Install/InstallHelper.cs +++ b/src/Umbraco.Web/Install/InstallHelper.cs @@ -41,31 +41,6 @@ namespace Umbraco.Web.Install return _installationType ?? (_installationType = IsBrandNewInstall ? InstallationType.NewInstall : InstallationType.Upgrade).Value; } - internal static void DeleteLegacyInstaller() - { - if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Install))) - { - if (Directory.Exists(IOHelper.MapPath("~/app_data/temp/install_backup"))) - { - //this means the backup already exists with files but there's no files in it, so we'll delete the backup and re-run it - if (Directory.GetFiles(IOHelper.MapPath("~/app_data/temp/install_backup")).Any() == false) - { - Directory.Delete(IOHelper.MapPath("~/app_data/temp/install_backup"), true); - Directory.Move(IOHelper.MapPath(SystemDirectories.Install), IOHelper.MapPath("~/app_data/temp/install_backup")); - } - } - else - { - Directory.Move(IOHelper.MapPath(SystemDirectories.Install), IOHelper.MapPath("~/app_data/temp/install_backup")); - } - } - - if (Directory.Exists(IOHelper.MapPath("~/Areas/UmbracoInstall"))) - { - Directory.Delete(IOHelper.MapPath("~/Areas/UmbracoInstall"), true); - } - } - internal void InstallStatus(bool isCompleted, string errorMsg) { try diff --git a/src/Umbraco.Web/Install/InstallStatusTracker.cs b/src/Umbraco.Web/Install/InstallStatusTracker.cs index 7944100648..d93eb9a06c 100644 --- a/src/Umbraco.Web/Install/InstallStatusTracker.cs +++ b/src/Umbraco.Web/Install/InstallStatusTracker.cs @@ -24,7 +24,7 @@ namespace Umbraco.Web.Install private static string GetFile(Guid installId) { - var file = IOHelper.MapPath("~/App_Data/TEMP/Install/" + var file = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "Install/" + "install_" + installId.ToString("N") + ".txt"); @@ -39,7 +39,7 @@ namespace Umbraco.Web.Install public static void ClearFiles() { - var dir = IOHelper.MapPath("~/App_Data/TEMP/Install/"); + var dir = IOHelper.MapPath(SystemDirectories.TempData.EnsureEndsWith('/') + "Install/"); if (Directory.Exists(dir)) { var files = Directory.GetFiles(dir); diff --git a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs index de53fd60cf..f71d486f5a 100644 --- a/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs +++ b/src/Umbraco.Web/Install/InstallSteps/ConfigureMachineKey.cs @@ -1,4 +1,5 @@ using System.Linq; +using System.Threading.Tasks; using System.Web.Configuration; using System.Xml.Linq; using Umbraco.Core.IO; @@ -30,7 +31,7 @@ namespace Umbraco.Web.Install.InstallSteps ///
/// /// - public override InstallSetupResult Execute(bool? model) + public override Task ExecuteAsync(bool? model) { if (model.HasValue && model.Value == false) return null; @@ -50,7 +51,7 @@ namespace Umbraco.Web.Install.InstallSteps xml.Save(fileName, SaveOptions.DisableFormatting); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(bool? model) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs index 2fe6c0ceda..9d1aec1c71 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseConfigureStep.cs @@ -1,5 +1,6 @@ using System; using System.Configuration; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; @@ -20,7 +21,7 @@ namespace Umbraco.Web.Install.InstallSteps _databaseBuilder = databaseBuilder; } - public override InstallSetupResult Execute(DatabaseModel database) + public override Task ExecuteAsync(DatabaseModel database) { //if the database model is null then we will apply the defaults if (database == null) @@ -33,7 +34,7 @@ namespace Umbraco.Web.Install.InstallSteps throw new InstallException("Could not connect to the database"); } ConfigureConnection(database); - return null; + return Task.FromResult(null); } private void ConfigureConnection(DatabaseModel database) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs index a9daee6e95..812889b977 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseInstallStep.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -24,7 +25,7 @@ namespace Umbraco.Web.Install.InstallSteps _logger = logger; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { if (_runtime.Level == RuntimeLevel.Run) throw new Exception("Umbraco is already configured!"); @@ -43,10 +44,10 @@ namespace Umbraco.Web.Install.InstallSteps } //upgrade is required so set the flag for the next step - return new InstallSetupResult(new Dictionary + return Task.FromResult(new InstallSetupResult(new Dictionary { {"upgrade", true} - }); + })); } internal static void HandleConnectionStrings(ILogger logger) diff --git a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs index 8283eb6bef..e8046bd196 100644 --- a/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/DatabaseUpgradeStep.cs @@ -1,6 +1,7 @@ using System; using System.Configuration; using System.Linq; +using System.Threading.Tasks; using Umbraco.Core; using Umbraco.Core.Logging; using Umbraco.Core.Migrations.Install; @@ -23,7 +24,7 @@ namespace Umbraco.Web.Install.InstallSteps _logger = logger; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "DatabaseInstall"); @@ -43,7 +44,7 @@ namespace Umbraco.Web.Install.InstallSteps DatabaseInstallStep.HandleConnectionStrings(_logger); } - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs index 6d07190b4f..c5bc9ac047 100644 --- a/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/FilePermissionsStep.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using umbraco; using Umbraco.Core; using Umbraco.Core.IO; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Install.InstallSteps PerformsAppRestart = true)] internal class FilePermissionsStep : InstallSetupStep { - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { // validate file permissions Dictionary> report; @@ -22,7 +23,7 @@ namespace Umbraco.Web.Install.InstallSteps if (permissionsOk == false) throw new InstallException("Permission check failed", "permissionsreport", new { errors = report }); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs index 64b8568fe5..9132f5cd49 100644 --- a/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/NewInstallStep.cs @@ -4,6 +4,7 @@ using System.Configuration; using System.Net; using System.Net.Http; using System.Text; +using System.Threading.Tasks; using System.Web; using System.Web.Security; using Newtonsoft.Json; @@ -51,7 +52,7 @@ namespace Umbraco.Web.Install.InstallSteps } } - public override InstallSetupResult Execute(UserModel user) + public override Task ExecuteAsync(UserModel user) { var admin = _userService.GetUserById(Constants.Security.SuperUserId); if (admin == null) @@ -99,7 +100,7 @@ namespace Umbraco.Web.Install.InstallSteps catch { /* fail in silence */ } } - return null; + return Task.FromResult(null); } /// diff --git a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs b/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs index 0cac3fe91f..3e836397fd 100644 --- a/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/SetUmbracoVersionStep.cs @@ -1,4 +1,5 @@ -using System.Web; +using System.Threading.Tasks; +using System.Web; using Umbraco.Core; using Umbraco.Core.Configuration; using Umbraco.Core.Logging; @@ -31,7 +32,7 @@ namespace Umbraco.Web.Install.InstallSteps _distributedCache = distributedCache; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { //During a new install we'll log the default user in (which is id = 0). // During an upgrade, the user will already need to be logged in in order to run the installer. @@ -56,7 +57,7 @@ namespace Umbraco.Web.Install.InstallSteps //reports the ended install _installHelper.InstallStatus(true, ""); - return null; + return Task.FromResult(null); } public override bool RequiresExecution(object model) diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs index 9345a0fc96..8c168f7230 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitCleanupStep.cs @@ -1,8 +1,8 @@ using System; using System.Linq; +using System.Threading.Tasks; using System.Web; using Umbraco.Web.Install.Models; -using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { @@ -10,35 +10,30 @@ namespace Umbraco.Web.Install.InstallSteps "StarterKitCleanup", 32, "Almost done")] internal class StarterKitCleanupStep : InstallSetupStep { - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); - var manifestId = Convert.ToInt32(previousStep.AdditionalData["manifestId"]); + var packageId = Convert.ToInt32(previousStep.AdditionalData["packageId"]); var packageFile = (string)previousStep.AdditionalData["packageFile"]; - CleanupInstallation(manifestId, packageFile); + CleanupInstallation(packageId, packageFile); - return null; + return Task.FromResult(null); } - private void CleanupInstallation(int manifestId, string packageFile) + private void CleanupInstallation(int packageId, string packageFile) { packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallCleanUp(manifestId, packageFile); - // library.RefreshContent is obsolete, would need to RefreshAll... snapshot, - // but it should be managed automatically by services and caches! - //DistributedCache.Instance.RefreshAll...(); + //fixme: When does the zip file get deleted? } public override bool RequiresExecution(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); //this step relies on the preious one completed - because it has stored some information we need - if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("manifestId")) == false) + if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("packageId")) == false) { return false; } diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs index 8f9f9242d7..641146fb4a 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitDownloadStep.cs @@ -1,14 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Web; using Umbraco.Core.Services; using Umbraco.Core.Configuration; +using Umbraco.Core.Models.Packaging; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web.Security; -using Umbraco.Web._Legacy.Packager; -using Umbraco.Web._Legacy.Packager.PackageInstance; namespace Umbraco.Web.Install.InstallSteps { @@ -20,17 +20,19 @@ namespace Umbraco.Web.Install.InstallSteps private readonly InstallHelper _installHelper; private readonly UmbracoContext _umbracoContext; private readonly IContentService _contentService; + private readonly IPackagingService _packageService; - public StarterKitDownloadStep(IContentService contentService, InstallHelper installHelper, UmbracoContext umbracoContext) + public StarterKitDownloadStep(IContentService contentService, IPackagingService packageService, InstallHelper installHelper, UmbracoContext umbracoContext) { _installHelper = installHelper; _umbracoContext = umbracoContext; _contentService = contentService; + _packageService = packageService; } - private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; + //private const string RepoGuid = "65194810-1f85-11dd-bd0b-0800200c9a66"; - public override InstallSetupResult Execute(Guid? starterKitId) + public override async Task ExecuteAsync(Guid? starterKitId) { //if there is no value assigned then use the default starter kit if (starterKitId.HasValue == false) @@ -48,46 +50,41 @@ namespace Umbraco.Web.Install.InstallSteps return null; } - var result = DownloadPackageFiles(starterKitId.Value); + var (packageFile, packageId) = await DownloadPackageFilesAsync(starterKitId.Value); - Current.RestartAppPool(); + UmbracoApplication.Restart(); return new InstallSetupResult(new Dictionary { - {"manifestId", result.Item2}, - {"packageFile", result.Item1} + {"packageId", packageId}, + {"packageFile", packageFile} }); } - private Tuple DownloadPackageFiles(Guid kitGuid) + private async Task<(string packageFile, int packageId)> DownloadPackageFilesAsync(Guid kitGuid) { - var installer = new Installer(); - //Go get the package file from the package repo - var packageFile = Current.Services.PackagingService.FetchPackageFile(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + var packageFile = await _packageService.FetchPackageFileAsync(kitGuid, UmbracoVersion.Current, _umbracoContext.Security.GetUserId().ResultOr(0)); + if (packageFile == null) throw new InvalidOperationException("Could not fetch package file " + kitGuid); - var tempFile = installer.Import(packageFile); - installer.LoadConfig(tempFile); - var pId = installer.CreateManifest(tempFile, kitGuid.ToString(), RepoGuid); + //add an entry to the installedPackages.config + var compiledPackage = _packageService.GetCompiledPackageInfo(packageFile); + var packageDefinition = PackageDefinition.FromCompiledPackage(compiledPackage); + _packageService.SaveInstalledPackage(packageDefinition); - InstallPackageFiles(pId, tempFile); + InstallPackageFiles(packageDefinition, compiledPackage.PackageFile); - return new Tuple(tempFile, pId); + return (compiledPackage.PackageFile.Name, packageDefinition.Id); } - private void InstallPackageFiles(int manifestId, string packageFile) + private void InstallPackageFiles(PackageDefinition packageDefinition, FileInfo packageFile) { - packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallFiles(manifestId, packageFile); + if (packageDefinition == null) throw new ArgumentNullException(nameof(packageDefinition)); + _packageService.InstallCompiledPackageData(packageDefinition, packageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); } - public override string View - { - get { return (InstalledPackage.GetAllInstalledPackages().Count > 0) ? string.Empty : base.View; } - } + public override string View => _packageService.GetAllInstalledPackages().Any() ? string.Empty : base.View; public override bool RequiresExecution(Guid? model) { @@ -97,7 +94,7 @@ namespace Umbraco.Web.Install.InstallSteps return false; } - if (InstalledPackage.GetAllInstalledPackages().Count > 0) + if (_packageService.GetAllInstalledPackages().Any()) return false; if (_contentService.GetRootContent().Any()) diff --git a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs index 8bec4ca199..8bd699f293 100644 --- a/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/StarterKitInstallStep.cs @@ -1,9 +1,11 @@ using System; +using System.IO; using System.Linq; +using System.Threading.Tasks; using System.Web; +using Umbraco.Core.Services; using Umbraco.Web.Composing; using Umbraco.Web.Install.Models; -using Umbraco.Web._Legacy.Packager; namespace Umbraco.Web.Install.InstallSteps { @@ -13,40 +15,45 @@ namespace Umbraco.Web.Install.InstallSteps internal class StarterKitInstallStep : InstallSetupStep { private readonly HttpContextBase _httContext; + private readonly UmbracoContext _umbracoContext; + private readonly IPackagingService _packagingService; - public StarterKitInstallStep(HttpContextBase httContext) + public StarterKitInstallStep(HttpContextBase httContext, UmbracoContext umbracoContext, IPackagingService packagingService) { _httContext = httContext; + _umbracoContext = umbracoContext; + _packagingService = packagingService; } - public override InstallSetupResult Execute(object model) + public override Task ExecuteAsync(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); var previousStep = installSteps.Single(x => x.Name == "StarterKitDownload"); - var manifestId = Convert.ToInt32(previousStep.AdditionalData["manifestId"]); - var packageFile = (string)previousStep.AdditionalData["packageFile"]; + var packageId = Convert.ToInt32(previousStep.AdditionalData["packageId"]); - InstallBusinessLogic(manifestId, packageFile); + InstallBusinessLogic(packageId); - Current.RestartAppPool(_httContext); + UmbracoApplication.Restart(_httContext); - return null; + return Task.FromResult(null); } - private void InstallBusinessLogic(int manifestId, string packageFile) + private void InstallBusinessLogic(int packageId) { - packageFile = HttpUtility.UrlDecode(packageFile); - var installer = new Installer(); - installer.LoadConfig(packageFile); - installer.InstallBusinessLogic(manifestId, packageFile); + var definition = _packagingService.GetInstalledPackageById(packageId); + if (definition == null) throw new InvalidOperationException("Not package definition found with id " + packageId); + + var packageFile = new FileInfo(definition.PackagePath); + + _packagingService.InstallCompiledPackageData(definition, packageFile, _umbracoContext.Security.GetUserId().ResultOr(0)); } public override bool RequiresExecution(object model) { var installSteps = InstallStatusTracker.GetStatus().ToArray(); //this step relies on the preious one completed - because it has stored some information we need - if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("manifestId")) == false) + if (installSteps.Any(x => x.Name == "StarterKitDownload" && x.AdditionalData.ContainsKey("packageId")) == false) { return false; } diff --git a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs index 7ed3de6471..5f44555092 100644 --- a/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs +++ b/src/Umbraco.Web/Install/InstallSteps/UpgradeStep.cs @@ -1,4 +1,5 @@ using System; +using System.Threading.Tasks; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Web.Install.Models; @@ -13,7 +14,7 @@ namespace Umbraco.Web.Install.InstallSteps { public override bool RequiresExecution(object model) => true; - public override InstallSetupResult Execute(object model) => null; + public override Task ExecuteAsync(object model) => Task.FromResult(null); public override object ViewModel { diff --git a/src/Umbraco.Web/Install/Models/InstallPackageModel.cs b/src/Umbraco.Web/Install/Models/InstallPackageModel.cs index 01eb368b6a..3ab74fa5e4 100644 --- a/src/Umbraco.Web/Install/Models/InstallPackageModel.cs +++ b/src/Umbraco.Web/Install/Models/InstallPackageModel.cs @@ -7,15 +7,15 @@ using System.Threading.Tasks; namespace Umbraco.Web.Install.Models { - + //fixme: do we need this? [Obsolete("This is only used for the obsolete controller InstallPackageController")] [DataContract(Name = "installPackage", Namespace = "")] public class InstallPackageModel { [DataMember(Name = "kitGuid")] public Guid KitGuid { get; set; } - [DataMember(Name = "manifestId")] - public int ManifestId { get; set; } + [DataMember(Name = "packageId")] + public int PackageId { get; set; } [DataMember(Name = "packageFile")] public string PackageFile { get; set; } diff --git a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs index 36cf838a82..fd50d7855c 100644 --- a/src/Umbraco.Web/Install/Models/InstallSetupStep.cs +++ b/src/Umbraco.Web/Install/Models/InstallSetupStep.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.Serialization; +using System.Threading.Tasks; using Umbraco.Core; namespace Umbraco.Web.Install.Models @@ -14,17 +15,14 @@ namespace Umbraco.Web.Install.Models /// Defines the step model type on the server side so we can bind it /// [IgnoreDataMember] - public override Type StepType - { - get { return typeof(T); } - } + public override Type StepType => typeof(T); /// /// The step execution method /// /// /// - public abstract InstallSetupResult Execute(T model); + public abstract Task ExecuteAsync(T model); /// /// Determines if this step needs to execute based on the current state of the application and/or install process @@ -84,12 +82,6 @@ namespace Umbraco.Web.Install.Models /// [IgnoreDataMember] public abstract Type StepType { get; } - - [IgnoreDataMember] - public bool HasUIElement - { - get { return View.IsNullOrWhiteSpace() == false; } - } - + } } diff --git a/src/Umbraco.Web/Install/UmbracoInstallArea.cs b/src/Umbraco.Web/Install/UmbracoInstallArea.cs index 2b44abd4ab..398ed7b245 100644 --- a/src/Umbraco.Web/Install/UmbracoInstallArea.cs +++ b/src/Umbraco.Web/Install/UmbracoInstallArea.cs @@ -32,14 +32,6 @@ namespace Umbraco.Web.Install new { controller = "Install", action = "Index", id = UrlParameter.Optional }, new[] { typeof(InstallController).Namespace }); - //TODO: We can remove this when we re-build the back office package installer - //Create the install routes - context.MapHttpRoute( - "Umbraco_install_packages", - "Install/PackageInstaller/{action}/{id}", - new { controller = "InstallPackage", action = "Index", id = UrlParameter.Optional }, - new[] { typeof(InstallPackageController).Namespace }); - context.MapHttpRoute( "umbraco-install-api", "install/api/{action}/{id}", diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs index 4908025351..80358bfc7a 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentItemDisplay.cs @@ -86,6 +86,12 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "isContainer")] public bool IsContainer { get; set; } + /// + /// Indicates if the content is configured as an element + /// + [DataMember(Name = "isElement")] + public bool IsElement { get; set; } + /// /// Property indicating if this item is part of a list view parent /// @@ -117,7 +123,7 @@ namespace Umbraco.Web.Models.ContentEditing /// [DataMember(Name = "updateDate")] public DateTime UpdateDate { get; set; } - + [DataMember(Name = "template")] public string TemplateAlias { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs index 7211ddbf61..e5e74c2749 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeCompositionDisplay.cs @@ -26,6 +26,10 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "isContainer")] public bool IsContainer { get; set; } + //Element + [DataMember(Name = "isElement")] + public bool IsElement { get; set; } + [DataMember(Name = "listViewEditorName")] [ReadOnly(true)] public string ListViewEditorName { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs index c2ec70d3dc..b1d24c5fd2 100644 --- a/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs +++ b/src/Umbraco.Web/Models/ContentEditing/ContentTypeSave.cs @@ -25,6 +25,9 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "isContainer")] public bool IsContainer { get; set; } + [DataMember(Name = "isElement")] + public bool IsElement { get; set; } + [DataMember(Name = "allowAsRoot")] public bool AllowAsRoot { get; set; } diff --git a/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs b/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs index 9161f972bd..67bfe6fe53 100644 --- a/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs +++ b/src/Umbraco.Web/Models/ContentEditing/InstalledPackageModel.cs @@ -32,5 +32,8 @@ namespace Umbraco.Web.Models.ContentEditing [DataMember(Name = "iconUrl")] public string IconUrl { get; set; } + + [DataMember(Name = "readme")] + public string Readme { get; set; } } } diff --git a/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs b/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs index 74e82ecfe9..04d06845d9 100644 --- a/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs +++ b/src/Umbraco.Web/Models/ContentEditing/UmbracoEntityTypes.cs @@ -87,5 +87,7 @@ namespace Umbraco.Web.Models.ContentEditing /// Property Group /// PropertyGroup + + //TODO: Dictionary? } } diff --git a/src/Umbraco.Web/Models/ContentTypeImportModel.cs b/src/Umbraco.Web/Models/ContentTypeImportModel.cs index 961476e5f0..f6f9a5926d 100644 --- a/src/Umbraco.Web/Models/ContentTypeImportModel.cs +++ b/src/Umbraco.Web/Models/ContentTypeImportModel.cs @@ -1,17 +1,13 @@ using System.Collections.Generic; using System.Runtime.Serialization; +using Umbraco.Core.Models.Editors; using Umbraco.Web.Models.ContentEditing; namespace Umbraco.Web.Models { [DataContract(Name = "contentTypeImportModel")] - public class ContentTypeImportModel : INotificationModel + public class ContentTypeImportModel : INotificationModel, IHaveUploadedFiles { - public ContentTypeImportModel() - { - Notifications = new List(); - } - [DataMember(Name = "alias")] public string Alias { get; set; } @@ -19,9 +15,11 @@ namespace Umbraco.Web.Models public string Name { get; set; } [DataMember(Name = "notifications")] - public List Notifications { get; } + public List Notifications { get; } = new List(); [DataMember(Name = "tempFileName")] public string TempFileName { get; set; } + + public List UploadedFiles => new List(); } } diff --git a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs index 06216597cb..5a3788be5e 100644 --- a/src/Umbraco.Web/Models/LocalPackageInstallModel.cs +++ b/src/Umbraco.Web/Models/LocalPackageInstallModel.cs @@ -11,16 +11,10 @@ namespace Umbraco.Web.Models [DataContract(Name = "localPackageInstallModel")] public class LocalPackageInstallModel : PackageInstallModel, IHaveUploadedFiles, INotificationModel { - public LocalPackageInstallModel() - { - UploadedFiles = new List(); - Notifications = new List(); - } - - public List UploadedFiles { get; } + public List UploadedFiles { get; } = new List(); [DataMember(Name = "notifications")] - public List Notifications { get; } + public List Notifications { get; } = new List(); /// /// A flag to determine if this package is compatible to be installed @@ -43,32 +37,44 @@ namespace Umbraco.Web.Models [DataMember(Name = "version")] public string Version { get; set; } + /// + /// If this is not null then it means the package is being from this version + /// + [DataMember(Name = "originalVersion")] + public string OriginalVersion { get; set; } + [DataMember(Name = "containsUnsecureFiles")] public bool ContainsUnsecureFiles { get; set; } [DataMember(Name = "containsTemplateConflicts")] - public bool ContainsTemplateConflicts { get; set; } + public bool ContainsTemplateConflicts => ConflictingTemplateAliases != null && ConflictingTemplateAliases.Count > 0; [DataMember(Name = "containsStyleSheetConflicts")] - public bool ContainsStyleSheetConflicts { get; set; } + public bool ContainsStyleSheetConflicts => ConflictingStyleSheetNames != null && ConflictingStyleSheetNames.Count > 0; [DataMember(Name = "containsMacroConflict")] - public bool ContainsMacroConflict { get; set; } - - [DataMember(Name = "containsBinaryFileErrors")] - public bool ContainsBinaryFileErrors { get; set; } + public bool ContainsMacroConflict => ConflictingMacroAliases != null && ConflictingMacroAliases.Count > 0; + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingTemplateAliases")] public IDictionary ConflictingTemplateAliases { get; set; } + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingStyleSheetNames")] public IDictionary ConflictingStyleSheetNames { get; set; } + /// + /// Key value of name + alias + /// [DataMember(Name = "conflictingMacroAliases")] public IDictionary ConflictingMacroAliases { get; set; } - [DataMember(Name = "readMe")] - public string ReadMe { get; set; } + [DataMember(Name = "readme")] + public string Readme { get; set; } [DataMember(Name = "licenseUrl")] public string LicenseUrl { get; set; } diff --git a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs index 6de3bdc02c..1caf81a1eb 100644 --- a/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs +++ b/src/Umbraco.Web/Models/Mapping/ContentMapperProfile.cs @@ -46,6 +46,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.ContentTypeAlias, opt => opt.MapFrom(src => src.ContentType.Alias)) .ForMember(dest => dest.ContentTypeName, opt => opt.MapFrom(src => src.ContentType.Name)) .ForMember(dest => dest.IsContainer, opt => opt.MapFrom(src => src.ContentType.IsContainer)) + .ForMember(dest => dest.IsElement, opt => opt.MapFrom(src => src.ContentType.IsElement)) .ForMember(dest => dest.IsBlueprint, opt => opt.MapFrom(src => src.Blueprint)) .ForMember(dest => dest.IsChildOfListView, opt => opt.ResolveUsing(childOfListViewResolver)) .ForMember(dest => dest.Trashed, opt => opt.MapFrom(src => src.Trashed)) @@ -59,7 +60,7 @@ namespace Umbraco.Web.Models.Mapping .ForMember(dest => dest.AllowedTemplates, opt => opt.MapFrom(content => content.ContentType.AllowedTemplates .Where(t => t.Alias.IsNullOrWhiteSpace() == false && t.Name.IsNullOrWhiteSpace() == false) - .ToDictionary(t => t.Alias, t => t.Name))) + .ToDictionary(t => t.Alias, t => t.Name))) .ForMember(dest => dest.AllowedActions, opt => opt.ResolveUsing(src => actionButtonsResolver.Resolve(src))) .ForMember(dest => dest.AdditionalData, opt => opt.Ignore()); @@ -140,5 +141,5 @@ namespace Umbraco.Web.Models.Mapping return source.CultureInfos.TryGetValue(culture, out var name) && !name.Name.IsNullOrWhiteSpace() ? name.Name : $"(({source.Name}))"; } } - } + } } diff --git a/src/Umbraco.Web/Models/PackageInstallModel.cs b/src/Umbraco.Web/Models/PackageInstallModel.cs index 7748129a40..c4dbbfde2a 100644 --- a/src/Umbraco.Web/Models/PackageInstallModel.cs +++ b/src/Umbraco.Web/Models/PackageInstallModel.cs @@ -15,19 +15,14 @@ namespace Umbraco.Web.Models [DataMember(Name = "packageGuid")] public Guid PackageGuid { get; set; } - [DataMember(Name = "repositoryGuid")] - public Guid RepositoryGuid { get; set; } - - [DataMember(Name = "temporaryDirectoryPath")] - public string TemporaryDirectoryPath { get; set; } - - [DataMember(Name = "zipFilePath")] - public string ZipFilePath { get; set; } + [DataMember(Name = "zipFileName")] + public string ZipFileName { get; set; } /// /// During installation this can be used to track any pending appdomain restarts /// [DataMember(Name = "isRestarting")] public bool IsRestarting { get; set; } + } } diff --git a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs b/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs index 70672e3796..0007b346c5 100644 --- a/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs +++ b/src/Umbraco.Web/Models/PublishedContent/HttpContextVariationContextAccessor.cs @@ -21,7 +21,7 @@ namespace Umbraco.Web.Models.PublishedContent /// public VariationContext VariationContext { - get => (VariationContext) HttpContextAccessor.HttpContext.Items[ContextKey]; + get => (VariationContext) HttpContextAccessor.HttpContext?.Items[ContextKey]; set => HttpContextAccessor.HttpContext.Items[ContextKey] = value; } } diff --git a/src/Umbraco.Web/Mvc/ContentModelBinder.cs b/src/Umbraco.Web/Mvc/ContentModelBinder.cs index b68d5a36f7..c6df52e007 100644 --- a/src/Umbraco.Web/Mvc/ContentModelBinder.cs +++ b/src/Umbraco.Web/Mvc/ContentModelBinder.cs @@ -150,7 +150,7 @@ namespace Umbraco.Web.Mvc if (context == null) AppDomain.Unload(AppDomain.CurrentDomain); else - Current.RestartAppPool(new HttpContextWrapper(context)); + UmbracoApplication.Restart(new HttpContextWrapper(context)); } throw new ModelBindingException(msg.ToString()); diff --git a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs index ce8b5835e2..f2392c9c3d 100644 --- a/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/MemberCache.cs @@ -8,30 +8,29 @@ using Umbraco.Core.Models; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Security; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Core.Xml.XPath; using Umbraco.Web.PublishedCache.NuCache.Navigable; namespace Umbraco.Web.PublishedCache.NuCache { - class MemberCache : IPublishedMemberCache, INavigableData + internal class MemberCache : IPublishedMemberCache, INavigableData { private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; public readonly IVariationContextAccessor VariationContextAccessor; + private readonly IEntityXmlSerializer _entitySerializer; private readonly ICacheProvider _snapshotCache; private readonly IMemberService _memberService; - private readonly IDataTypeService _dataTypeService; - private readonly ILocalizationService _localizationService; private readonly PublishedContentTypeCache _contentTypeCache; private readonly bool _previewDefault; - public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, IDataTypeService dataTypeService, ILocalizationService localizationService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor) + public MemberCache(bool previewDefault, ICacheProvider snapshotCache, IMemberService memberService, PublishedContentTypeCache contentTypeCache, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IEntityXmlSerializer entitySerializer) { _snapshotCache = snapshotCache; _publishedSnapshotAccessor = publishedSnapshotAccessor; VariationContextAccessor = variationContextAccessor; + _entitySerializer = entitySerializer; _memberService = memberService; - _dataTypeService = dataTypeService; - _localizationService = localizationService; _previewDefault = previewDefault; _contentTypeCache = contentTypeCache; } @@ -141,7 +140,7 @@ namespace Umbraco.Web.PublishedCache.NuCache var result = _memberService.GetById(id); if (result == null) return null; - var s = EntityXmlSerializer.Serialize(_dataTypeService, _localizationService, result); + var s = _entitySerializer.Serialize(result); var n = s.GetXmlNode(); return n.CreateNavigator(); } diff --git a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs index 0647e1c806..36f3472c31 100755 --- a/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/NuCache/PublishedSnapshotService.cs @@ -40,6 +40,7 @@ namespace Umbraco.Web.PublishedCache.NuCache private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IEntityXmlSerializer _entitySerializer; private readonly IDefaultCultureAccessor _defaultCultureAccessor; // volatile because we read it with no lock @@ -82,7 +83,8 @@ namespace Umbraco.Web.PublishedCache.NuCache ILogger logger, IScopeProvider scopeProvider, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, - IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper) + IDataSource dataSource, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer) : base(publishedSnapshotAccessor, variationContextAccessor) { //if (Interlocked.Increment(ref _singletonCheck) > 1) @@ -100,6 +102,10 @@ namespace Umbraco.Web.PublishedCache.NuCache _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + // we need an Xml serializer here so that the member cache can support XPath, + // for members this is done by navigating the serialized-to-xml member + _entitySerializer = entitySerializer; + // we always want to handle repository events, configured or not // assuming no repository event will trigger before the whole db is ready // (ideally we'd have Upgrading.App vs Upgrading.Data application states...) @@ -1008,7 +1014,7 @@ namespace Umbraco.Web.PublishedCache.NuCache { ContentCache = new ContentCache(previewDefault, contentSnap, snapshotCache, elementsCache, domainHelper, _globalSettings, _serviceContext.LocalizationService), MediaCache = new MediaCache(previewDefault, mediaSnap, snapshotCache, elementsCache), - MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, _serviceContext.DataTypeService, _serviceContext.LocalizationService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor), + MemberCache = new MemberCache(previewDefault, snapshotCache, _serviceContext.MemberService, memberTypeCache, PublishedSnapshotAccessor, VariationContextAccessor, _entitySerializer), DomainCache = domainCache, SnapshotCache = snapshotCache, ElementsCache = elementsCache diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs index 9e45bc17c5..09b76b5c2e 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Xml; using Umbraco.Examine; using Umbraco.Core.Cache; using Umbraco.Core.Services; +using Umbraco.Core.Services.Implement; using Umbraco.Web.Composing; namespace Umbraco.Web.PublishedCache.XmlPublishedCache @@ -37,14 +38,14 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache // method GetExamineManagerSafe(). // private readonly ISearcher _searchProvider; - private readonly IIndex _indexProvider; private readonly XmlStore _xmlStore; private readonly PublishedContentTypeCache _contentTypeCache; + private readonly IEntityXmlSerializer _entitySerializer; // must be specified by the ctor private readonly ICacheProvider _cacheProvider; - public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + public PublishedMediaCache(XmlStore xmlStore, IMediaService mediaService, IUserService userService, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer) : base(false) { _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); @@ -53,6 +54,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _cacheProvider = cacheProvider; _xmlStore = xmlStore; _contentTypeCache = contentTypeCache; + _entitySerializer = entitySerializer; } /// @@ -61,18 +63,18 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// /// /// - /// /// /// - internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, BaseIndexProvider indexProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache) + /// + internal PublishedMediaCache(IMediaService mediaService, IUserService userService, ISearcher searchProvider, ICacheProvider cacheProvider, PublishedContentTypeCache contentTypeCache, IEntityXmlSerializer entitySerializer) : base(false) { _mediaService = mediaService ?? throw new ArgumentNullException(nameof(mediaService)); _userService = userService ?? throw new ArgumentNullException(nameof(userService)); _searchProvider = searchProvider ?? throw new ArgumentNullException(nameof(searchProvider)); - _indexProvider = indexProvider ?? throw new ArgumentNullException(nameof(indexProvider)); _cacheProvider = cacheProvider; _contentTypeCache = contentTypeCache; + _entitySerializer = entitySerializer; } static PublishedMediaCache() @@ -553,14 +555,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return Enumerable.Empty(); } - var serialized = EntityXmlSerializer.Serialize( - Current.Services.MediaService, - Current.Services.DataTypeService, - Current.Services.UserService, - Current.Services.LocalizationService, - Current.UrlSegmentProviders, - media, - true); + var serialized = _entitySerializer.Serialize(media, true); var mediaIterator = serialized.CreateNavigator().Select("/"); diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs index 78585ba2e2..e286e9d95c 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedSnapshotService.cs @@ -34,6 +34,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IGlobalSettings _globalSettings; private readonly IDefaultCultureAccessor _defaultCultureAccessor; private readonly ISiteDomainHelper _siteDomainHelper; + private readonly IEntityXmlSerializer _entitySerializer; #region Constructors @@ -42,20 +43,20 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache IPublishedContentTypeFactory publishedContentTypeFactory, IScopeProvider scopeProvider, ICacheProvider requestCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IDefaultCultureAccessor defaultCultureAccessor, ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer, MainDom mainDom, bool testing = false, bool enableRepositoryEvents = true) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, segmentProviders, + : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, publishedSnapshotAccessor, variationContextAccessor, documentRepository, mediaRepository, memberRepository, defaultCultureAccessor, - logger, globalSettings, siteDomainHelper, null, mainDom, testing, enableRepositoryEvents) + logger, globalSettings, siteDomainHelper, entitySerializer, null, mainDom, testing, enableRepositoryEvents) { } // used in some tests @@ -69,27 +70,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache ILogger logger, IGlobalSettings globalSettings, ISiteDomainHelper siteDomainHelper, - PublishedContentTypeCache contentTypeCache, - MainDom mainDom, - bool testing, bool enableRepositoryEvents) - : this(serviceContext, publishedContentTypeFactory, scopeProvider, requestCache, Enumerable.Empty(), - publishedSnapshotAccessor, variationContextAccessor, - documentRepository, mediaRepository, memberRepository, - defaultCultureAccessor, - logger, globalSettings, siteDomainHelper, contentTypeCache, mainDom, testing, enableRepositoryEvents) - { } - - private PublishedSnapshotService(ServiceContext serviceContext, - IPublishedContentTypeFactory publishedContentTypeFactory, - IScopeProvider scopeProvider, - ICacheProvider requestCache, - IEnumerable segmentProviders, - IPublishedSnapshotAccessor publishedSnapshotAccessor, IVariationContextAccessor variationContextAccessor, - IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, - IDefaultCultureAccessor defaultCultureAccessor, - ILogger logger, - IGlobalSettings globalSettings, - ISiteDomainHelper siteDomainHelper, + IEntityXmlSerializer entitySerializer, PublishedContentTypeCache contentTypeCache, MainDom mainDom, bool testing, bool enableRepositoryEvents) @@ -100,9 +81,9 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _contentTypeCache = contentTypeCache ?? new PublishedContentTypeCache(serviceContext.ContentTypeService, serviceContext.MediaTypeService, serviceContext.MemberTypeService, publishedContentTypeFactory, logger); - _xmlStore = new XmlStore(serviceContext, scopeProvider, _routesCache, - _contentTypeCache, segmentProviders, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, - documentRepository, mediaRepository, memberRepository, globalSettings); + _xmlStore = new XmlStore(serviceContext.ContentTypeService, serviceContext.ContentService, scopeProvider, _routesCache, + _contentTypeCache, publishedSnapshotAccessor, mainDom, testing, enableRepositoryEvents, + documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer); _domainService = serviceContext.DomainService; _memberService = serviceContext.MemberService; @@ -113,6 +94,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _requestCache = requestCache; _globalSettings = globalSettings; _siteDomainHelper = siteDomainHelper; + _entitySerializer = entitySerializer; } public override void Dispose() @@ -157,7 +139,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache return new PublishedSnapshot( new PublishedContentCache(_xmlStore, domainCache, _requestCache, _globalSettings, _siteDomainHelper, _contentTypeCache, _routesCache, previewToken), - new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache), + new PublishedMediaCache(_xmlStore, _mediaService, _userService, _requestCache, _contentTypeCache, _entitySerializer), new PublishedMemberCache(_xmlStore, _requestCache, _memberService, _contentTypeCache), domainCache); } diff --git a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs index fef5039579..614515e433 100644 --- a/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs +++ b/src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlStore.cs @@ -44,15 +44,16 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache private readonly IMediaRepository _mediaRepository; private readonly IMemberRepository _memberRepository; private readonly IGlobalSettings _globalSettings; + private readonly IEntityXmlSerializer _entitySerializer; private XmlStoreFilePersister _persisterTask; private volatile bool _released; private bool _withRepositoryEvents; private readonly IPublishedSnapshotAccessor _publishedSnapshotAccessor; private readonly PublishedContentTypeCache _contentTypeCache; - private readonly IEnumerable _segmentProviders; private readonly RoutesCache _routesCache; - private readonly ServiceContext _serviceContext; // fixme WHY + private readonly IContentTypeService _contentTypeService; + private readonly IContentService _contentService; private readonly IScopeProvider _scopeProvider; #region Constructors @@ -61,22 +62,23 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache /// Initializes a new instance of the class. /// /// The default constructor will boot the cache, load data from file or database, /// wire events in order to manage changes, etc. - public XmlStore(ServiceContext serviceContext, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings) - : this(serviceContext, scopeProvider, routesCache, contentTypeCache, segmentProviders, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings) + public XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer) + : this(contentTypeService, contentService, scopeProvider, routesCache, contentTypeCache, publishedSnapshotAccessor, mainDom, false, false, documentRepository, mediaRepository, memberRepository, globalSettings, entitySerializer) { } // internal for unit tests // no file nor db, no config check // fixme - er, we DO have a DB? - internal XmlStore(ServiceContext serviceContext, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, - IEnumerable segmentProviders, IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, - bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings) + internal XmlStore(IContentTypeService contentTypeService, IContentService contentService, IScopeProvider scopeProvider, RoutesCache routesCache, PublishedContentTypeCache contentTypeCache, + IPublishedSnapshotAccessor publishedSnapshotAccessor, MainDom mainDom, + bool testing, bool enableRepositoryEvents, IDocumentRepository documentRepository, IMediaRepository mediaRepository, IMemberRepository memberRepository, IGlobalSettings globalSettings, IEntityXmlSerializer entitySerializer) { if (testing == false) EnsureConfigurationIsValid(); - _serviceContext = serviceContext; + _contentTypeService = contentTypeService; + _contentService = contentService; _scopeProvider = scopeProvider; _routesCache = routesCache; _contentTypeCache = contentTypeCache; @@ -85,8 +87,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache _mediaRepository = mediaRepository; _memberRepository = memberRepository; _globalSettings = globalSettings; + _entitySerializer = entitySerializer; _xmlFileName = IOHelper.MapPath(SystemFiles.GetContentCacheXml(_globalSettings)); - _segmentProviders = segmentProviders; if (testing) { @@ -399,7 +401,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache try { var dtdInner = new StringBuilder(); - var contentTypes = _serviceContext.ContentTypeService.GetAll(); + var contentTypes = _contentTypeService.GetAll(); // though aliases should be safe and non null already? var aliases = contentTypes.Select(x => x.Alias.ToSafeAlias()).WhereNotNull(); foreach (var alias in aliases) @@ -556,7 +558,7 @@ AND (umbracoNode.id=@id)"; public XmlDocument GetPreviewXml(int contentId, bool includeSubs) { - var content = _serviceContext.ContentService.GetById(contentId); + var content = _contentService.GetById(contentId); var doc = (XmlDocument)Xml.Clone(); if (content == null) return doc; @@ -1065,7 +1067,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; continue; } - var content = _serviceContext.ContentService.GetById(payload.Id); + var content = _contentService.GetById(payload.Id); var current = safeXml.Xml.GetElementById(payload.Id.ToInvariantString()); if (content == null || content.Published == false || content.Trashed) @@ -1536,7 +1538,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; var entity = args.Entity; // serialize edit values for preview - var editXml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity, false).ToDataString(); + var editXml = _entitySerializer.Serialize(entity, false).ToDataString(); // change below to write only one row - not one per version var dto1 = new PreviewXmlDto @@ -1565,7 +1567,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; return; // serialize published values for content cache - var publishedXml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity, true).ToDataString(); + var publishedXml = _entitySerializer.Serialize(entity, true).ToDataString(); var dto2 = new ContentXmlDto { NodeId = entity.Id, Xml = publishedXml }; OnRepositoryRefreshed(db, dto2); @@ -1581,7 +1583,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; if (entity.Trashed) db.Execute("DELETE FROM cmsContentXml WHERE nodeId=@id", new { id = entity.Id }); - var xml = EntityXmlSerializer.Serialize(_serviceContext.MediaService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, entity).ToDataString(); + var xml = _entitySerializer.Serialize(entity).ToDataString(); var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; OnRepositoryRefreshed(db, dto1); @@ -1592,7 +1594,7 @@ ORDER BY umbracoNode.level, umbracoNode.sortOrder"; var db = args.Scope.Database; var entity = args.Entity; - var xml = EntityXmlSerializer.Serialize(_serviceContext.DataTypeService, _serviceContext.LocalizationService, entity).ToDataString(); + var xml = _entitySerializer.Serialize(entity).ToDataString(); var dto1 = new ContentXmlDto { NodeId = entity.Id, Xml = xml }; OnRepositoryRefreshed(db, dto1); @@ -1749,7 +1751,7 @@ WHERE cmsContentXml.nodeId IN ( var descendants = _documentRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); const bool published = true; // contentXml contains published content! var items = descendants.Select(c => new ContentXmlDto { NodeId = c.Id, Xml = - EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, c, published).ToDataString() }).ToArray(); + _entitySerializer.Serialize(c, published).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); @@ -1824,7 +1826,7 @@ WHERE cmsPreviewXml.nodeId IN ( var items = descendants.Select(c => new PreviewXmlDto { NodeId = c.Id, - Xml = EntityXmlSerializer.Serialize(_serviceContext.ContentService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, c, published).ToDataString() + Xml = _entitySerializer.Serialize(c, published).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; @@ -1894,7 +1896,7 @@ WHERE cmsContentXml.nodeId IN ( { var descendants = _mediaRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = - EntityXmlSerializer.Serialize(_serviceContext.MediaService, _serviceContext.DataTypeService, _serviceContext.UserService, _serviceContext.LocalizationService, _segmentProviders, m).ToDataString() }).ToArray(); + _entitySerializer.Serialize(m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); @@ -1962,7 +1964,7 @@ WHERE cmsContentXml.nodeId IN ( do { var descendants = _memberRepository.GetPage(query, pageIndex++, groupSize, out total, null, Ordering.By("Path")); - var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = EntityXmlSerializer.Serialize(_serviceContext.DataTypeService, _serviceContext.LocalizationService, m).ToDataString() }).ToArray(); + var items = descendants.Select(m => new ContentXmlDto { NodeId = m.Id, Xml = _entitySerializer.Serialize(m).ToDataString() }).ToArray(); db.BulkInsertRecords(items); processed += items.Length; } while (processed < total); diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 0199a34579..422b502f80 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -17,6 +17,7 @@ using Umbraco.Core.Components; using Umbraco.Core.Composing; using Umbraco.Core.Configuration; using Umbraco.Core.Configuration.UmbracoSettings; +using Umbraco.Core.IO; using Umbraco.Core.Models.PublishedContent; using Umbraco.Core.Profiling; using Umbraco.Core.Services; @@ -77,8 +78,6 @@ namespace Umbraco.Web.Runtime // Disable the X-AspNetMvc-Version HTTP Header MvcHandler.DisableMvcResponseHeader = true; - InstallHelper.DeleteLegacyInstaller(); - // wrap view engines in the profiling engine WrapViewEngines(ViewEngines.Engines); @@ -245,7 +244,7 @@ namespace Umbraco.Web.Runtime private static void ConfigureClientDependency(IGlobalSettings globalSettings) { // Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK] - XmlFileMapper.FileMapDefaultFolder = "~/App_Data/TEMP/ClientDependency"; + XmlFileMapper.FileMapDefaultFolder = SystemDirectories.TempData.EnsureEndsWith('/') + "ClientDependency"; BaseCompositeFileProcessingProvider.UrlTypeDefault = CompositeUrlType.Base64QueryStrings; // Now we need to detect if we are running umbracoLocalTempStorage as EnvironmentTemp and in that case we want to change the CDF file diff --git a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs index e4bc74b7f2..cd4f5ac933 100644 --- a/src/Umbraco.Web/Scheduling/SchedulerComponent.cs +++ b/src/Umbraco.Web/Scheduling/SchedulerComponent.cs @@ -149,6 +149,7 @@ namespace Umbraco.Web.Scheduling var periodInMilliseconds = healthCheckConfig.NotificationSettings.PeriodInHours * 60 * 60 * 1000; var task = new HealthCheckNotifier(_healthCheckRunner, delayInMilliseconds, periodInMilliseconds, healthChecks, notifications, _runtime, logger); + _healthCheckRunner.TryAdd(task); return task; } diff --git a/src/Umbraco.Web/Search/ExamineComponent.cs b/src/Umbraco.Web/Search/ExamineComponent.cs index 09d0c555da..05a1258441 100644 --- a/src/Umbraco.Web/Search/ExamineComponent.cs +++ b/src/Umbraco.Web/Search/ExamineComponent.cs @@ -446,13 +446,14 @@ namespace Umbraco.Web.Search while (page * pageSize < total) { //paging with examine, see https://shazwazza.com/post/paging-with-examine/ - var results = searcher.CreateQuery().Field("nodeType", id).Execute(maxResults: pageSize * (page + 1)); + var results = searcher.CreateQuery().Field("nodeType", id.ToInvariantString()).Execute(maxResults: pageSize * (page + 1)); total = results.TotalItemCount; var paged = results.Skip(page * pageSize); foreach (var item in paged) if (int.TryParse(item.Id, out var contentId)) DeleteIndexForEntity(contentId, false); + page++; } } } diff --git a/src/Umbraco.Web/Search/ExamineComposer.cs b/src/Umbraco.Web/Search/ExamineComposer.cs index 8ee2caedee..7aab3cfd8f 100644 --- a/src/Umbraco.Web/Search/ExamineComposer.cs +++ b/src/Umbraco.Web/Search/ExamineComposer.cs @@ -33,13 +33,13 @@ namespace Umbraco.Web.Search composition.RegisterUnique(factory => new ContentValueSetBuilder( factory.GetInstance(), - factory.GetInstance>(), + factory.GetInstance(), factory.GetInstance(), true)); composition.RegisterUnique(factory => new ContentValueSetBuilder( factory.GetInstance(), - factory.GetInstance>(), + factory.GetInstance(), factory.GetInstance(), false)); composition.RegisterUnique, MediaValueSetBuilder>(); diff --git a/src/Umbraco.Web/Services/SectionService.cs b/src/Umbraco.Web/Services/SectionService.cs index 5d013d7e79..6337db67f9 100644 --- a/src/Umbraco.Web/Services/SectionService.cs +++ b/src/Umbraco.Web/Services/SectionService.cs @@ -298,7 +298,7 @@ namespace Umbraco.Web.Services //we need to interrogate the attributes for the data. Would be better to have a base class that contains //metadata populated by the attribute. Oh well i guess. var attrs = types.Select(x => x.GetCustomAttributes(false).Single()); - return Enumerable.ToArray
(attrs.Select(x => new Section(x.Name, x.Alias, x.SortOrder))); + return attrs.Select(x => new Section(x.Name, x.Alias, x.SortOrder)).ToArray(); }); } diff --git a/src/Umbraco.Web/Trees/FileSystemTreeController.cs b/src/Umbraco.Web/Trees/FileSystemTreeController.cs index 9b7d078e13..528e57773c 100644 --- a/src/Umbraco.Web/Trees/FileSystemTreeController.cs +++ b/src/Umbraco.Web/Trees/FileSystemTreeController.cs @@ -55,6 +55,10 @@ namespace Umbraco.Web.Trees var files = FileSystem.GetFiles(path).Where(x => { var extension = Path.GetExtension(x); + + if (Extensions.Contains("*")) + return true; + return extension != null && Extensions.Contains(extension.Trim('.'), StringComparer.InvariantCultureIgnoreCase); }); diff --git a/src/Umbraco.Web/Trees/FilesTreeController.cs b/src/Umbraco.Web/Trees/FilesTreeController.cs new file mode 100644 index 0000000000..947522747d --- /dev/null +++ b/src/Umbraco.Web/Trees/FilesTreeController.cs @@ -0,0 +1,25 @@ +using Umbraco.Core; +using Umbraco.Core.IO; +using Umbraco.Web.Models.Trees; + +namespace Umbraco.Web.Trees +{ + [CoreTree(TreeGroup = Constants.Trees.Groups.Templating)] + [Tree(Constants.Applications.Settings, "files", "Files", "icon-folder", "icon-folder", sortOrder: 13, initialize: false)] + public class FilesTreeController : FileSystemTreeController + { + protected override IFileSystem FileSystem => new PhysicalFileSystem("~/"); // fixme inject + + private static readonly string[] ExtensionsStatic = { "*" }; + + protected override string[] Extensions => ExtensionsStatic; + + protected override string FileIcon => "icon-document"; + + protected override void OnRenderFolderNode(ref TreeNode treeNode) + { + //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. + treeNode.AdditionalData["jsClickCallback"] = "javascript:void(0);"; + } + } +} diff --git a/src/Umbraco.Web/Trees/PackagesTreeController.cs b/src/Umbraco.Web/Trees/PackagesTreeController.cs index c56ed716a1..fdc5294aa2 100644 --- a/src/Umbraco.Web/Trees/PackagesTreeController.cs +++ b/src/Umbraco.Web/Trees/PackagesTreeController.cs @@ -1,18 +1,14 @@ -using System; -using System.Linq; -using System.Net.Http.Formatting; +using System.Net.Http.Formatting; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; using Umbraco.Web.WebApi.Filters; -using Umbraco.Core.Services; -using Umbraco.Web.Actions; -using Umbraco.Web._Legacy.Packager.PackageInstance; + using Constants = Umbraco.Core.Constants; namespace Umbraco.Web.Trees { [UmbracoTreeAuthorize(Constants.Trees.Packages)] - [Tree(Constants.Applications.Packages, Constants.Trees.Packages, null, sortOrder: 0)] + [Tree(Constants.Applications.Packages, Constants.Trees.Packages, null, sortOrder: 0, isSingleNodeTree: true)] [PluginController("UmbracoTrees")] [CoreTree] public class PackagesTreeController : TreeController @@ -24,77 +20,26 @@ namespace Umbraco.Web.Trees protected override TreeNode CreateRootNode(FormDataCollection queryStrings) { var root = base.CreateRootNode(queryStrings); + + //this will load in a custom UI instead of the dashboard for the root node root.RoutePath = $"{Constants.Applications.Packages}/{Constants.Trees.Packages}/overview"; root.Icon = "icon-box"; + + root.HasChildren = false; return root; } + protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings) { - var nodes = new TreeNodeCollection(); - - var createdPackages = CreatedPackage.GetAllCreatedPackages(); - - if (id == "created") - { - nodes.AddRange( - createdPackages - .OrderBy(entity => entity.Data.Name) - .Select(dt => - { - var node = CreateTreeNode(dt.Data.Id.ToString(), id, queryStrings, dt.Data.Name, - "icon-inbox", false, - $"/{queryStrings.GetValue("application")}/framed/{Uri.EscapeDataString("developer/Packages/EditPackage.aspx?id=" + dt.Data.Id)}"); - return node; - })); - } - else - { - //must be root - var node = CreateTreeNode( - "created", - id, - queryStrings, - Services.TextService.Localize("treeHeaders/createdPackages"), - "icon-folder", - createdPackages.Count > 0, - string.Empty); - - //TODO: This isn't the best way to ensure a noop process for clicking a node but it works for now. - node.AdditionalData["jsClickCallback"] = "javascript:void(0);"; - - nodes.Add(node); - } - - return nodes; + //full screen app without tree nodes + return TreeNodeCollection.Empty; } protected override MenuItemCollection GetMenuForNode(string id, FormDataCollection queryStrings) { - var menu = new MenuItemCollection(); - - // Root actions - if (id == "-1") - { - menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, - queryStrings.GetValue("application")); - } - else if (id == "created") - { - menu.Items.Add(Services.TextService, opensDialog: true) - .ConvertLegacyMenuItem(null, Constants.Trees.Packages, - queryStrings.GetValue("application")); - - menu.Items.Add(new RefreshNode(Services.TextService, true)); - } - else - { - //it's a package node - menu.Items.Add(Services.TextService, opensDialog: true); - } - - return menu; + //doesn't have a menu, this is a full screen app without tree nodes + return MenuItemCollection.Empty; } } } diff --git a/src/Umbraco.Web/Umbraco.Web.csproj b/src/Umbraco.Web/Umbraco.Web.csproj index 7f452b9933..b0142771d2 100755 --- a/src/Umbraco.Web/Umbraco.Web.csproj +++ b/src/Umbraco.Web/Umbraco.Web.csproj @@ -123,6 +123,7 @@ + @@ -215,11 +216,13 @@ + + @@ -727,7 +730,6 @@ - @@ -1123,30 +1125,15 @@ - - - - - - - - - - - - - - - @@ -1227,8 +1214,6 @@ Code - - FeedProxy.aspx @@ -1237,13 +1222,6 @@ FeedProxy.aspx - - editPackage.aspx - ASPXCodeBehind - - - editPackage.aspx - @@ -1289,9 +1267,6 @@ - - ASPXCodeBehind - diff --git a/src/Umbraco.Web/UmbracoApplication.cs b/src/Umbraco.Web/UmbracoApplication.cs index 136f09f83d..191fb9dcd6 100644 --- a/src/Umbraco.Web/UmbracoApplication.cs +++ b/src/Umbraco.Web/UmbracoApplication.cs @@ -1,4 +1,6 @@ -using Umbraco.Core; +using System.Threading; +using System.Web; +using Umbraco.Core; using Umbraco.Web.Runtime; namespace Umbraco.Web @@ -12,5 +14,46 @@ namespace Umbraco.Web { return new WebRuntime(this); } + + /// + /// Restarts the Umbraco application. + /// + public static void Restart() + { + // see notes in overload + + var httpContext = HttpContext.Current; + if (httpContext != null) + { + httpContext.Application.Add("AppPoolRestarting", true); + httpContext.User = null; + } + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } + + /// + /// Restarts the Umbraco application. + /// + public static void Restart(HttpContextBase httpContext) + { + if (httpContext != null) + { + // we're going to put an application wide flag to show that the application is about to restart. + // we're doing this because if there is a script checking if the app pool is fully restarted, then + // it can check if this flag exists... if it does it means the app pool isn't restarted yet. + httpContext.Application.Add("AppPoolRestarting", true); + + // unload app domain - we must null out all identities otherwise we get serialization errors + // http://www.zpqrtbnk.net/posts/custom-iidentity-serialization-issue + httpContext.User = null; + } + + if (HttpContext.Current != null) + HttpContext.Current.User = null; + + Thread.CurrentPrincipal = null; + HttpRuntime.UnloadAppDomain(); + } } } diff --git a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs index b60545e3cb..233ce39e52 100644 --- a/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs +++ b/src/Umbraco.Web/WebApi/Filters/FileUploadCleanupFilterAttribute.cs @@ -14,7 +14,7 @@ using File = System.IO.File; namespace Umbraco.Web.WebApi.Filters { /// - /// Checks if the parameter is ContentItemSave and then deletes any temporary saved files from file uploads associated with the request + /// Checks if the parameter is IHaveUploadedFiles and then deletes any temporary saved files from file uploads associated with the request /// internal sealed class FileUploadCleanupFilterAttribute : ActionFilterAttribute { @@ -29,14 +29,6 @@ namespace Umbraco.Web.WebApi.Filters _incomingModel = incomingModel; } - /// - /// Returns true so that other filters can execute along with this one - /// - public override bool AllowMultiple - { - get { return true; } - } - public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); @@ -47,8 +39,7 @@ namespace Umbraco.Web.WebApi.Filters { if (actionExecutedContext.ActionContext.ActionArguments.Any()) { - var contentItem = actionExecutedContext.ActionContext.ActionArguments.First().Value as IHaveUploadedFiles; - if (contentItem != null) + if (actionExecutedContext.ActionContext.ActionArguments.First().Value is IHaveUploadedFiles contentItem) { //cleanup any files associated foreach (var f in contentItem.UploadedFiles) @@ -104,8 +95,7 @@ namespace Umbraco.Web.WebApi.Filters if (objectContent != null) { - var uploadedFiles = objectContent.Value as IHaveUploadedFiles; - if (uploadedFiles != null) + if (objectContent.Value is IHaveUploadedFiles uploadedFiles) { if (uploadedFiles.UploadedFiles != null) { diff --git a/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs b/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs new file mode 100644 index 0000000000..ea285434b3 --- /dev/null +++ b/src/Umbraco.Web/WebApi/SerializeVersionAttribute.cs @@ -0,0 +1,15 @@ +using Newtonsoft.Json.Converters; +using System; +using System.Web.Http.Controllers; + +namespace Umbraco.Web.WebApi +{ + internal class SerializeVersionAttribute : Attribute, IControllerConfiguration + { + public void Initialize(HttpControllerSettings controllerSettings, HttpControllerDescriptor controllerDescriptor) + { + var formatter = controllerSettings.Formatters.JsonFormatter; + formatter.SerializerSettings.Converters.Add(new VersionConverter()); + } + } +} diff --git a/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs b/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs deleted file mode 100644 index c2298e4906..0000000000 --- a/src/Umbraco.Web/_Legacy/Controls/BaseTreePicker.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System; -using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.HtmlControls; -using Umbraco.Core.Composing; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Controls -{ - [ValidationProperty("Value")] - public abstract class BaseTreePicker : Control, INamingContainer - { - - protected HiddenField ItemIdValue; - protected HtmlAnchor DeleteLink; - protected HtmlAnchor ChooseLink; - protected HtmlGenericControl ItemTitle; - protected HtmlGenericControl ButtonContainer; - protected HtmlGenericControl RootContainer; - - public BaseTreePicker() - { - ShowDelete = true; - ModalHeight = 400; - ModalWidth = 300; - ShowHeader = true; - } - - /// - /// Wraps the hidden vield value - /// - public string Value - { - get - { - EnsureChildControls(); - return ItemIdValue.Value; - } - set - { - EnsureChildControls(); - ItemIdValue.Value = value; - } - } - - public int ModalWidth { get; set; } - public int ModalHeight { get; set; } - public bool ShowDelete { get; set; } - public bool ShowHeader { get; set; } - - /// - /// Need to specify the tree picker url (iframe) - /// - public abstract string TreePickerUrl { get; } - - /// - /// The title to specify for the picker window - /// - public abstract string ModalWindowTitle { get; } - - /// - /// If item has been selected or stored, this will query the db for its title - /// - protected virtual string GetItemTitle() - { - if (!string.IsNullOrEmpty(ItemIdValue.Value)) - { - try - { - return Current.Services.EntityService.Get(int.Parse(ItemIdValue.Value)).Name; - } - catch (ArgumentException) { /*the node does not exist! we will ignore*/ } - } - return ""; - } - - /// - /// Just like GetItemTitle, except returns the full path (breadcrumbs) of the node - /// - protected virtual string GetItemBreadcrumbs() - { - //TODO: Shouldn't this use the same/similar logic as the EntityController.GetResultForAncestors ? - - if (!string.IsNullOrEmpty(ItemIdValue.Value)) - { - try - { - int nodeId = -1; - if (int.TryParse(ItemIdValue.Value, out nodeId)) - { - var n = Current.Services.EntityService.Get(nodeId); - string title = n.Name; - string separator = " > "; - while (n.Level > 1) - { - n = Current.Services.EntityService.Get(n.ParentId); - title = n.Name + separator + title; - } - return title; - } - else - { - return ItemIdValue.Value; - } - - } - catch (ArgumentException) { /*the node does not exist! we will ignore*/ } - } - return ""; - } - - /// - /// Outputs the JavaScript instances used to make this control work - /// - protected virtual string GetJSScript() - { - /* 0 = this control's client id - * 1 = label - * 2 = itemIdValueClientID - * 3 = itemTitleClientID - * 4 = itemPickerUrl - * 5 = popup width - * 6 = popup height - * 7 = show header - * 8 = umbraco path - */ - return string.Format(@" - var mc_{0} = new Umbraco.Controls.TreePicker('{0}','{1}','{2}','{3}','{4}',{5},{6},{7},'{8}');", - new string[] - { - this.ClientID, - ModalWindowTitle, - ItemIdValue.ClientID, - ItemTitle.ClientID, - TreePickerUrl, - ModalWidth.ToString(), - ModalHeight.ToString(), - ShowHeader.ToString().ToLower(), - Umbraco.Core.IO.IOHelper.ResolveUrl(Umbraco.Core.IO.SystemDirectories.Umbraco).TrimEnd('/') - }); - } - - /// - /// Registers the required JS classes required to make this control work - /// - protected virtual void RenderJSComponents() - { - const string BaseTreePickerScriptJs = @"/// -(function ($) { - $(document).ready(function () { - // Tooltip only Text - $('.umb-tree-picker a.choose').click(function () { - var that = this; - var s = $(that).data(""section""); - UmbClientMgr.openAngularModalWindow({ - template: 'views/common/dialogs/treepicker.html', - section: s, - callback: function (data) { - //this returns the content object picked - var p = jQuery(that).parent(); - p.find("".buttons"").show(); - - p.find(""input"").val(data.id); - p.find("".treePickerTitle"").text(data.name).show(); - p.find("".clear"").show(); - } - }); - - return false; - }); - - $('.umb-tree-picker a.clear').click(function () { - jQuery(this).parent().parent().find(""input"").val(""-1""); - jQuery(this).parent().parent().find("".treePickerTitle"").text("""").hide(); - jQuery(this).hide(); - }); - }); -})(jQuery); -"; - - const string scriptKey = "BaseTreePickerScripts"; - if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack) - { - ScriptManager.RegisterStartupScript(this, this.GetType(), scriptKey, BaseTreePickerScriptJs, true); - } - else - { - Page.ClientScript.RegisterClientScriptBlock(typeof(BaseTreePicker), scriptKey, BaseTreePickerScriptJs, true); - } - } - - protected override void OnInit(EventArgs e) - { - base.OnInit(e); - - EnsureChildControls(); - - //disable view state for this control - this.EnableViewState = false; - } - - /// - /// Create the native .net child controls for this control - /// - protected override void CreateChildControls() - { - base.CreateChildControls(); - - RootContainer = new HtmlGenericControl("span"); - RootContainer.Attributes.Add("class", "umb-tree-picker"); - this.Controls.Add(RootContainer); - - //create the hidden field - ItemIdValue = new HiddenField(); - ItemIdValue.ID = "ContentIdValue"; - RootContainer.Controls.Add(ItemIdValue); - - - ButtonContainer = new HtmlGenericControl("span"); - ButtonContainer.ID = "btns"; - - //add item title with padding - ItemTitle = new HtmlGenericControl("span"); - ItemTitle.ID = "title"; - ItemTitle.Style.Add(HtmlTextWriterStyle.FontWeight, "bold"); - ItemTitle.Attributes.Add("class", "treePickerTitle"); // solely for styling, e.g. with an underline or dotted border, etc. - ButtonContainer.Controls.Add(ItemTitle); - ButtonContainer.Attributes.Add("class", "buttons"); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - - //add the delete link with padding - DeleteLink = new HtmlAnchor(); - DeleteLink.HRef = "#"; //set on pre-render - DeleteLink.Style.Add(HtmlTextWriterStyle.Color, "red"); - DeleteLink.Title = Current.Services.TextService.Localize("delete"); - DeleteLink.InnerText = Current.Services.TextService.Localize("delete"); - DeleteLink.Attributes.Add("class", "clear"); - - ButtonContainer.Controls.Add(DeleteLink); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - ButtonContainer.Controls.Add(new LiteralControl(" ")); - if (!ShowDelete) - { - DeleteLink.Style.Add(HtmlTextWriterStyle.Display, "none"); - } - - RootContainer.Controls.Add(ButtonContainer); - - //add choose link with padding - ChooseLink = new HtmlAnchor(); - ChooseLink.HRef = "#"; //filled in on pre-render - ChooseLink.InnerText = Current.Services.TextService.Localize("choose") + "..."; - ChooseLink.Attributes.Add("data-section", this.TreePickerUrl); - ChooseLink.Attributes.Add("class", "choose"); - - RootContainer.Controls.Add(ChooseLink); - } - - /// - /// Registers the JavaScript required for the control to function and hides/shows controls depending on it's properties - /// - /// - protected override void OnPreRender(EventArgs e) - { - base.OnPreRender(e); - - //hide the buttons if no item, otherwise get the item title - if (string.IsNullOrEmpty(ItemIdValue.Value)) - { - ButtonContainer.Style.Add(HtmlTextWriterStyle.Display, "none"); - } - else - { - ItemTitle.InnerText = GetItemTitle(); - ItemTitle.Attributes.Add("title", GetItemBreadcrumbs()); // Adding full path/meta info (Issue U4-192) - } - /* - ChooseLink.HRef = string.Format("javascript:mc_{0}.LaunchPicker();", this.ClientID); - DeleteLink.HRef = string.Format("javascript:mc_{0}.ClearSelection();", this.ClientID); - */ - - RenderJSComponents(); - - /* - if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack) - { - ScriptManager.RegisterStartupScript(this, this.GetType(), this.ClientID + "TreePicker", GetJSScript(), true); - } - else - { - Page.ClientScript.RegisterStartupScript(this.GetType(), this.ClientID + "TreePicker", GetJSScript(), true); - }*/ - - } - - - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs b/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs deleted file mode 100644 index 55171c2643..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/PackageHelper.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Xml; - -namespace Umbraco.Web._Legacy.PackageActions -{ - internal class PackageHelper - { - //Helper method to replace umbraco tags that breaks the xml format.. - public static string ParseToValidXml(ITemplate templateObj, ref bool hasAspNetContentBeginning, string template, bool toValid) - { - string retVal = template; - if (toValid) - { - // test for asp:content as the first part of the design - if (retVal.StartsWith("") + 1); - retVal = retVal.Substring(0, retVal.Length - 14); - } - //shorten empty macro tags.. - retVal = retVal.Replace(">", " />"); - retVal = retVal.Replace(">", " />"); - - retVal = retVal.Replace("", ""); - retVal = retVal.Replace("", ""); - retVal = retVal.Replace("", ""); - - // add asp content element - if (hasAspNetContentBeginning) - { - retVal = MasterPageHelper.GetMasterContentElement(templateObj/*templateObj.MasterTemplate*/) + retVal + ""; - } - } - - return retVal; - } - - public static XmlNode ParseStringToXmlNode(string value) - { - XmlDocument doc = new XmlDocument(); - XmlNode node = XmlHelper.AddTextNode(doc, "error", ""); - - try - { - doc.LoadXml(value); - return doc.SelectSingleNode("."); - } - catch - { - return node; - } - - } - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs index 09c6df23c4..26116820e6 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addApplication.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -10,7 +11,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. ///
- public class addApplication : IPackageAction + public class AddApplication : IPackageAction { #region IPackageAction Members @@ -24,20 +25,20 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// true if successfull - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string name = xmlData.Attributes["appName"].Value; - string alias = xmlData.Attributes["appAlias"].Value; - string icon = xmlData.Attributes["appIcon"].Value; + string name = xmlData.AttributeValue("appName"); + string alias = xmlData.AttributeValue("appAlias"); + string icon = xmlData.AttributeValue("appIcon"); Current.Services.SectionService.MakeNew(name, alias, icon); return true; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { - string alias = xmlData.Attributes["appAlias"].Value; + string alias = xmlData.AttributeValue("appAlias"); var section = Current.Services.SectionService.GetByAlias(alias); if (section != null) { @@ -55,11 +56,6 @@ namespace Umbraco.Web._Legacy.PackageActions } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs b/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs deleted file mode 100644 index 9b3bc5829b..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/addDashboardSection.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Core.Xml; -using Umbraco.Core._Legacy.PackageActions; - -namespace Umbraco.Web._Legacy.PackageActions -{ - /// - /// - /// - public class addDashboardSection : IPackageAction - { - #region IPackageAction Members - - /// - /// Installs a dashboard section. This action reuses the action XML, so it has to be valid dashboard markup. - /// - /// Name of the package. - /// The XML data. - /// true if successfull - /// - /// - /// - ///
- /// - /// default - /// content - /// - /// - /// /usercontrols/dashboard/latestEdits.ascx - /// /usercontrols/umbracoBlog/dashboardBlogPostCreate.ascx - /// - /// - /// /usercontrols/umbracoBlog/dashboardBlogPostCreate.ascx - /// - ///
- ///
- ///
- ///
- public bool Execute(string packageName, XmlNode xmlData) - { - //this will need a complete section node to work... - - if (xmlData.HasChildNodes) - { - string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; - string dbConfig = SystemFiles.DashboardConfig; - - XmlNode section = xmlData.SelectSingleNode("./section"); - XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); - - //don't continue if it already exists - var found = dashboardFile.SelectNodes("//section[@alias='" + sectionAlias + "']"); - if (found == null || found.Count <= 0) - { - - XmlNode importedSection = dashboardFile.ImportNode(section, true); - - XmlAttribute alias = XmlHelper.AddAttribute(dashboardFile, "alias", sectionAlias); - importedSection.Attributes.Append(alias); - - dashboardFile.DocumentElement.AppendChild(importedSection); - - dashboardFile.Save(IOHelper.MapPath(dbConfig)); - } - - return true; - } - - return false; - } - - - public string Alias() - { - return "addDashboardSection"; - } - - public bool Undo(string packageName, XmlNode xmlData) - { - - string sectionAlias = xmlData.Attributes["dashboardAlias"].Value; - string dbConfig = SystemFiles.DashboardConfig; - XmlDocument dashboardFile = XmlHelper.OpenAsXmlDocument(dbConfig); - - XmlNode section = dashboardFile.SelectSingleNode("//section [@alias = '" + sectionAlias + "']"); - - if (section != null) - { - - dashboardFile.SelectSingleNode("/dashBoard").RemoveChild(section); - dashboardFile.Save(IOHelper.MapPath(dbConfig)); - } - - return true; - } - - #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs index ac02bc0c33..f53adb3be0 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/addProxyFeedHost.cs @@ -1,4 +1,5 @@ using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core.IO; using Umbraco.Core.Xml; @@ -6,46 +7,36 @@ using Umbraco.Core._Legacy.PackageActions; namespace Umbraco.Web._Legacy.PackageActions { - public class addProxyFeedHost : IPackageAction + public class AddProxyFeedHost : IPackageAction { #region IPackageAction Members - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - var hostname = xmlData.Attributes["host"].Value; + var hostname = xmlData.AttributeValue("host"); if (string.IsNullOrEmpty(hostname)) return false; - var xdoc = XmlHelper.OpenAsXmlDocument(SystemFiles.FeedProxyConfig); + var xdoc = XDocument.Load(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - xdoc.PreserveWhitespace = true; + var insert = true; - var xn = xdoc.SelectSingleNode("//feedProxy"); - if (xn != null) + if (xdoc.Root.HasElements) { - var insert = true; - - if (xn.HasChildNodes) + foreach (var node in xdoc.Root.Elements("allow")) { - foreach (XmlNode node in xn.SelectNodes("//allow")) - { - if (node.Attributes["host"] != null && node.Attributes["host"].Value == hostname) - insert = false; - } - } - - if (insert) - { - var newHostname = XmlHelper.AddTextNode(xdoc, "allow", string.Empty); - newHostname.Attributes.Append(XmlHelper.AddAttribute(xdoc, "host", hostname)); - xn.AppendChild(newHostname); - - xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - - return true; + if (node.AttributeValue("host") != null && node.AttributeValue("host") == hostname) + insert = false; } } + if (insert) + { + xdoc.Root.Add(new XElement("allow", new XAttribute("host", hostname))); + xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); + + return true; + } return false; } @@ -54,47 +45,37 @@ namespace Umbraco.Web._Legacy.PackageActions return "addProxyFeedHost"; } - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { - var hostname = xmlData.Attributes["host"].Value; + var hostname = xmlData.AttributeValue("host"); if (string.IsNullOrEmpty(hostname)) return false; - var xdoc = XmlHelper.OpenAsXmlDocument(SystemFiles.FeedProxyConfig); - xdoc.PreserveWhitespace = true; + var xdoc = XDocument.Load(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - var xn = xdoc.SelectSingleNode("//feedProxy"); - if (xn != null) + bool inserted = false; + if (xdoc.Root.HasElements) { - bool inserted = false; - if (xn.HasChildNodes) + foreach (var node in xdoc.Root.Elements("allow")) { - foreach (XmlNode node in xn.SelectNodes("//allow")) + if (node.AttributeValue("host") != null && node.AttributeValue("host") == hostname) { - if (node.Attributes["host"] != null && node.Attributes["host"].Value == hostname) - { - xn.RemoveChild(node); - inserted = true; - } + node.Remove(); + inserted = true; } } + } - if (inserted) - { - xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); - return true; - } + if (inserted) + { + xdoc.Save(IOHelper.MapPath(SystemFiles.FeedProxyConfig)); + return true; } return false; } #endregion - - public XmlNode SampleXml() - { - string sample = ""; - return PackageHelper.ParseStringToXmlNode(sample); - } + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs b/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs deleted file mode 100644 index 56ebb76980..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/addStringToHtmlElement.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Xml; -using Umbraco.Core._Legacy.PackageActions; -using Umbraco.Web.Composing; - -namespace Umbraco.Web._Legacy.PackageActions -{ - /// - /// This class implements the IPackageAction Interface, used to execute code when packages are installed. - /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. - /// addStringToHtmlElement adds a string to specific HTML element in a specific template, and can either append or prepend it. - /// It uses the action xml node to do this, exemple action xml node: - /// - /// The above will add the string "hello world!" to the first html element with the id "newsSection" in the template "news" - /// - public class addStringToHtmlElement : IPackageAction - { - #region IPackageAction Members - - /// - /// Executes the specified package action. - /// - /// Name of the package. - /// The XML data. - /// - /// - /// - /// True if executed successfully - public bool Execute(string packageName, XmlNode xmlData) - { - - - string templateAlias = xmlData.Attributes["templateAlias"].Value; - string htmlElementId = xmlData.Attributes["htmlElementId"].Value; - string position = xmlData.Attributes["position"].Value; - string value = XmlHelper.GetNodeValue(xmlData); - var tmp = Current.Services.FileService.GetTemplate(templateAlias); - - value = MasterPageHelper.EnsureMasterPageSyntax(templateAlias, value); - - _addStringToHtmlElement(tmp, value, htmlElementId, position); - - return true; - } - - - /// - /// Undoes the addStringToHtml Execute() method, by removing the same string from the same template. - /// - /// Name of the package. - /// The XML data. - /// - public bool Undo(string packageName, XmlNode xmlData) - { - string templateAlias = xmlData.Attributes["templateAlias"].Value; - string htmlElementId = xmlData.Attributes["htmlElementId"].Value; - string value = XmlHelper.GetNodeValue(xmlData); - var tmp = Current.Services.FileService.GetTemplate(templateAlias); - - value = MasterPageHelper.EnsureMasterPageSyntax(templateAlias, value); - - _removeStringFromHtmlElement(tmp, value, htmlElementId); - return true; - } - - /// - /// Action alias. - /// - /// - public string Alias() - { - return "addStringToHtmlElement"; - } - - private void _addStringToHtmlElement(ITemplate tmp, string value, string htmlElementId, string position) - { - bool hasAspNetContentBeginning = false; - string design = ""; - string directive = ""; - - if (tmp != null) - { - try - { - XmlDocument templateXml = new XmlDocument(); - templateXml.PreserveWhitespace = true; - - //Make sure that directive is remove before hacked non html4 compatiple replacement action... - design = tmp.Content; - - - splitDesignAndDirective(ref design, ref directive); - - //making sure that the template xml has a root node... - if (string.IsNullOrWhiteSpace(tmp.MasterTemplateAlias) == false) - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, "" + design + "", true)); - else - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, design, true)); - - XmlNode xmlElement = templateXml.SelectSingleNode("//* [@id = '" + htmlElementId + "']"); - - if (xmlElement != null) - { - - if (position == "beginning") - { - xmlElement.InnerXml = "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true) + "\n" + xmlElement.InnerXml; - } - else - { - xmlElement.InnerXml = xmlElement.InnerXml + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true) + "\n"; - } - } - - tmp.Content = directive + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, templateXml.OuterXml, false); - Current.Services.FileService.SaveTemplate(tmp); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred"); - } - } - else - { - Current.Logger.Debug("template not found"); - } - } - - private void _removeStringFromHtmlElement(ITemplate tmp, string value, string htmlElementId) - { - bool hasAspNetContentBeginning = false; - string design = ""; - string directive = ""; - - - if (tmp != null) - { - try - { - XmlDocument templateXml = new XmlDocument(); - templateXml.PreserveWhitespace = true; - - //Make sure that directive is remove before hacked non html4 compatiple replacement action... - design = tmp.Content; - splitDesignAndDirective(ref design, ref directive); - - //making sure that the template xml has a root node... - if (string.IsNullOrWhiteSpace(tmp.MasterTemplateAlias) == false) - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, "" + design + "", true)); - else - templateXml.LoadXml(PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, design, true)); - - XmlNode xmlElement = templateXml.SelectSingleNode("//* [@id = '" + htmlElementId + "']"); - - - - if (xmlElement != null) - { - string repValue = PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, value, true); - xmlElement.InnerXml = xmlElement.InnerXml.Replace(repValue, ""); - } - - tmp.Content = directive + "\n" + PackageHelper.ParseToValidXml(tmp, ref hasAspNetContentBeginning, templateXml.OuterXml, false); - Current.Services.FileService.SaveTemplate(tmp); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred"); - } - } - else - { - Current.Logger.Debug("template not found"); - } - } - - - - private void splitDesignAndDirective(ref string design, ref string directive) - { - if (design.StartsWith("<%@")) - { - directive = design.Substring(0, design.IndexOf("%>") + 2).Trim(Environment.NewLine.ToCharArray()); - design = design.Substring(design.IndexOf("%>") + 3).Trim(Environment.NewLine.ToCharArray()); - } - } - - #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - - } -} diff --git a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs index 146925b85f..f4b206a9ad 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/allowDoctype.cs @@ -2,6 +2,8 @@ using System.Collections; using System.Linq; using System.Xml; +using System.Xml.Linq; +using Umbraco.Core; using Umbraco.Core.Models; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -12,7 +14,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. /// - public class allowDoctype : IPackageAction + public class AllowDoctype : IPackageAction { #region IPackageAction Members @@ -26,10 +28,10 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// Returns true on success - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string doctypeName = xmlData.Attributes["documentTypeAlias"].Value; - string parentDoctypeName = xmlData.Attributes["parentDocumentTypeAlias"].Value; + string doctypeName = xmlData.AttributeValue("documentTypeAlias"); + string parentDoctypeName = xmlData.AttributeValue("parentDocumentTypeAlias"); //global::umbraco.cms.businesslogic.ContentType ct = global::umbraco.cms.businesslogic.ContentType.GetByAlias(doctypeName); //global::umbraco.cms.businesslogic.ContentType parentct = global::umbraco.cms.businesslogic.ContentType.GetByAlias(parentDoctypeName); @@ -66,14 +68,13 @@ namespace Umbraco.Web._Legacy.PackageActions return false; } - //this has no undo. /// /// This action has no undo. /// /// Name of the package. /// The XML data. /// - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { return true; } @@ -88,11 +89,6 @@ namespace Umbraco.Web._Legacy.PackageActions } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs index 32147342f6..f4bd7dd4fc 100644 --- a/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs +++ b/src/Umbraco.Web/_Legacy/PackageActions/publishRootDocument.cs @@ -1,5 +1,6 @@ using System; using System.Xml; +using System.Xml.Linq; using Umbraco.Core; using Umbraco.Core._Legacy.PackageActions; using Umbraco.Web.Composing; @@ -10,7 +11,7 @@ namespace Umbraco.Web._Legacy.PackageActions /// This class implements the IPackageAction Interface, used to execute code when packages are installed. /// All IPackageActions only takes a PackageName and a XmlNode as input, and executes based on the data in the xmlnode. /// - public class publishRootDocument : IPackageAction + public class PublishRootDocument : IPackageAction { #region IPackageAction Members @@ -23,17 +24,16 @@ namespace Umbraco.Web._Legacy.PackageActions /// /// /// True if executed succesfully - public bool Execute(string packageName, XmlNode xmlData) + public bool Execute(string packageName, XElement xmlData) { - string documentName = xmlData.Attributes["documentName"].Value; + string documentName = xmlData.AttributeValue("documentName"); - //global::umbraco.cms.businesslogic.web.Document[] rootDocs = global::umbraco.cms.businesslogic.web.Document.GetRootDocuments(); var rootDocs = Current.Services.ContentService.GetRootContent(); foreach (var rootDoc in rootDocs) { - if (rootDoc.Name.Trim() == documentName.Trim() && rootDoc != null && rootDoc.ContentType != null) + if (rootDoc.Name.Trim() == documentName.Trim() && rootDoc.ContentType != null) { // fixme variants? Current.Services.ContentService.SaveAndPublishBranch(rootDoc, true); @@ -43,14 +43,13 @@ namespace Umbraco.Web._Legacy.PackageActions return true; } - //this has no undo. /// /// This action has no undo. /// /// Name of the package. /// The XML data. /// - public bool Undo(string packageName, XmlNode xmlData) + public bool Undo(string packageName, XElement xmlData) { return true; } @@ -64,11 +63,6 @@ namespace Umbraco.Web._Legacy.PackageActions return "publishRootDocument"; } #endregion - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - + } } diff --git a/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs b/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs deleted file mode 100644 index fd2f40178b..0000000000 --- a/src/Umbraco.Web/_Legacy/PackageActions/removeStringFromTemplate.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core._Legacy.PackageActions; - -namespace Umbraco.Web._Legacy.PackageActions -{ - public class removeStringFromTemplate : IPackageAction - { - #region IPackageAction Members - - public bool Execute(string packageName, XmlNode xmlData) - { - addStringToHtmlElement ast = new addStringToHtmlElement(); - return ast.Undo(packageName, xmlData); - } - - public string Alias() - { - return "removeStringFromHtmlElement"; - } - - public bool Undo(string packageName, XmlNode xmlData) - { - return true; - } - - public XmlNode SampleXml() - { - throw new NotImplementedException(); - } - - #endregion - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/Installer.cs b/src/Umbraco.Web/_Legacy/Packager/Installer.cs deleted file mode 100644 index 795aa9ead7..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/Installer.cs +++ /dev/null @@ -1,841 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Xml; -using ICSharpCode.SharpZipLib.Zip; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Events; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Services.Implement; -using Umbraco.Core.Xml; -using Umbraco.Web._Legacy.Packager.PackageInstance; -using File = System.IO.File; -using PackageAction = Umbraco.Web._Legacy.Packager.PackageInstance.PackageAction; - -namespace Umbraco.Web._Legacy.Packager -{ - /// - /// The packager is a component which enables sharing of both data and functionality components between different umbraco installations. - /// - /// The output is a .umb (a zip compressed file) which contains the exported documents/medias/macroes/documenttypes (etc.) - /// in a Xml document, along with the physical files used (images/usercontrols/xsl documents etc.) - /// - /// Partly implemented, import of packages is done, the export is *under construction*. - /// - /// - /// Ruben Verborgh 31/12/2007: I had to change some code, I marked my changes with "DATALAYER". - /// Reason: @@IDENTITY can't be used with the new datalayer. - /// I wasn't able to test the code, since I'm not aware how the code functions. - /// - public class Installer - { - private const string PackageServer = "packages.umbraco.org"; - - private readonly List _unsecureFiles = new List(); - private readonly Dictionary _conflictingMacroAliases = new Dictionary(); - private readonly Dictionary _conflictingTemplateAliases = new Dictionary(); - private readonly Dictionary _conflictingStyleSheetNames = new Dictionary(); - - private readonly List _binaryFileErrors = new List(); - private int _currentUserId = -1; - private static WebClient _webClient; - - - public string Name { get; private set; } - public string Version { get; private set; } - public string Url { get; private set; } - public string License { get; private set; } - public string LicenseUrl { get; private set; } - public string Author { get; private set; } - public string AuthorUrl { get; private set; } - public string ReadMe { get; private set; } - public string Control { get; private set; } - - public bool ContainsMacroConflict { get; private set; } - public IDictionary ConflictingMacroAliases { get { return _conflictingMacroAliases; } } - - public bool ContainsUnsecureFiles { get; private set; } - public List UnsecureFiles { get { return _unsecureFiles; } } - - public bool ContainsTemplateConflicts { get; private set; } - public IDictionary ConflictingTemplateAliases { get { return _conflictingTemplateAliases; } } - - /// - /// Indicates that the package contains assembly reference errors - /// - public bool ContainsBinaryFileErrors { get; private set; } - - /// - /// List each assembly reference error - /// - public List BinaryFileErrors { get { return _binaryFileErrors; } } - - public bool ContainsStyleSheeConflicts { get; private set; } - public IDictionary ConflictingStyleSheetNames { get { return _conflictingStyleSheetNames; } } - - public int RequirementsMajor { get; private set; } - public int RequirementsMinor { get; private set; } - public int RequirementsPatch { get; private set; } - - public RequirementsType RequirementsType { get; private set; } - - public string IconUrl { get; private set; } - - /// - /// The xmldocument, describing the contents of a package. - /// - public XmlDocument Config { get; private set; } - - /// - /// Constructor - /// - public Installer() - { - Initialize(); - } - - public Installer(int currentUserId) - { - Initialize(); - _currentUserId = currentUserId; - } - - private void Initialize() - { - ContainsBinaryFileErrors = false; - ContainsTemplateConflicts = false; - ContainsUnsecureFiles = false; - ContainsMacroConflict = false; - ContainsStyleSheeConflicts = false; - } - - - - /// - /// Constructor - /// - /// The name of the package - /// The version of the package - /// The url to a descriptionpage - /// The license under which the package is released (preferably GPL ;)) - /// The url to a licensedescription - /// The original author of the package - /// The url to the Authors website - /// Umbraco version major - /// Umbraco version minor - /// Umbraco version patch - /// The readme text - /// The name of the usercontrol used to configure the package after install - /// - /// - public Installer(string name, string version, string url, string license, string licenseUrl, string author, string authorUrl, int requirementsMajor, int requirementsMinor, int requirementsPatch, string readme, string control, RequirementsType requirementsType, string iconUrl) - { - ContainsBinaryFileErrors = false; - ContainsTemplateConflicts = false; - ContainsUnsecureFiles = false; - ContainsMacroConflict = false; - ContainsStyleSheeConflicts = false; - this.Name = name; - this.Version = version; - this.Url = url; - this.License = license; - this.LicenseUrl = licenseUrl; - this.RequirementsMajor = requirementsMajor; - this.RequirementsMinor = requirementsMinor; - this.RequirementsPatch = requirementsPatch; - this.RequirementsType = requirementsType; - this.Author = author; - this.AuthorUrl = authorUrl; - this.IconUrl = iconUrl; - ReadMe = readme; - this.Control = control; - } - - #region Public Methods - - /// - /// Imports the specified package - /// - /// Filename of the umbracopackage - /// true if the input file should be deleted after import - /// - public string Import(string inputFile, bool deleteFile) - { - using (Current.ProfilingLogger.DebugDuration( - $"Importing package file {inputFile}.", - $"Package file {inputFile} imported.")) - { - var tempDir = ""; - if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile))) - { - var fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile)); - // Check if the file is a valid package - if (fi.Extension.ToLower() == ".umb") - { - try - { - tempDir = UnPack(fi.FullName, deleteFile); - LoadConfig(tempDir); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error importing file {FileName}", fi.FullName); - throw; - } - } - else - throw new Exception("Error - file isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); - } - else - throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + inputFile) + "'"); - return tempDir; - } - - } - - /// - /// Imports the specified package - /// - /// Filename of the umbracopackage - /// - public string Import(string inputFile) - { - return Import(inputFile, true); - } - - public int CreateManifest(string tempDir, string guid, string repoGuid) - { - //This is the new improved install rutine, which chops up the process into 3 steps, creating the manifest, moving files, and finally handling umb objects - var packName = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/name")); - var packAuthor = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/name")); - var packAuthorUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/website")); - var packVersion = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/version")); - var packReadme = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/readme")); - var packLicense = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license ")); - var packUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/url ")); - var iconUrl = XmlHelper.GetNodeValue(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/iconUrl")); - - var enableSkins = false; - var skinRepoGuid = ""; - - if (Config.DocumentElement.SelectSingleNode("/umbPackage/enableSkins") != null) - { - var skinNode = Config.DocumentElement.SelectSingleNode("/umbPackage/enableSkins"); - enableSkins = bool.Parse(XmlHelper.GetNodeValue(skinNode)); - if (skinNode.Attributes["repository"] != null && string.IsNullOrEmpty(skinNode.Attributes["repository"].Value) == false) - skinRepoGuid = skinNode.Attributes["repository"].Value; - } - - //Create a new package instance to record all the installed package adds - this is the same format as the created packages has. - //save the package meta data - var insPack = InstalledPackage.MakeNew(packName); - insPack.Data.Author = packAuthor; - insPack.Data.AuthorUrl = packAuthorUrl; - insPack.Data.Version = packVersion; - insPack.Data.Readme = packReadme; - insPack.Data.License = packLicense; - insPack.Data.Url = packUrl; - insPack.Data.IconUrl = iconUrl; - - insPack.Data.PackageGuid = guid; //the package unique key. - insPack.Data.RepositoryGuid = repoGuid; //the repository unique key, if the package is a file install, the repository will not get logged. - insPack.Save(); - - return insPack.Data.Id; - } - - public void InstallFiles(int packageId, string tempDir) - { - using (Current.ProfilingLogger.DebugDuration( - "Installing package files for package id " + packageId + " into temp folder " + tempDir, - "Package file installation complete for package id " + packageId)) - { - //retrieve the manifest to continue installation - var insPack = InstalledPackage.GetById(packageId); - - //TODO: Depending on some files, some files should be installed differently. - //i.e. if stylsheets should probably be installed via business logic, media items should probably use the media IFileSystem! - - // Move files - //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; - string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - - try - { - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) - { - var destPath = GetFileName(basePath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); - var sourceFile = GetFileName(tempDir, XmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); - var destFile = GetFileName(destPath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - - // Create the destination directory if it doesn't exist - if (Directory.Exists(destPath) == false) - Directory.CreateDirectory(destPath); - //If a file with this name exists, delete it - else if (File.Exists(destFile)) - File.Delete(destFile); - - // Copy the file - // SJ: Note - this used to do a move but some packages included the same file to be - // copied to multiple locations like so: - // - // - // my-icon.png - // /umbraco/Images/ - // my-icon.png - // - // - // my-icon.png - // /App_Plugins/MyPlugin/Images - // my-icon.png - // - // - // Since this file unzips as a flat list of files, moving the file the first time means - // that when you try to do that a second time, it would result in a FileNotFoundException - File.Copy(sourceFile, destFile); - - //PPH log file install - insPack.Data.Files.Add(XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath")) + "/" + XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - - } - - // Once we're done copying, remove all the files - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) - { - var sourceFile = GetFileName(tempDir, XmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); - if (File.Exists(sourceFile)) - File.Delete(sourceFile); - } - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Package install error"); - throw; - } - - // log that a user has install files - if (_currentUserId > -1) - { - Current.Services.AuditService.Add(AuditType.PackagerInstall, - _currentUserId, - -1, "Package", string.Format("Package '{0}' installed. Package guid: {1}", insPack.Data.Name, insPack.Data.PackageGuid)); - } - - insPack.Save(); - } - } - - public void InstallBusinessLogic(int packageId, string tempDir) - { - using (Current.ProfilingLogger.DebugDuration( - "Installing business logic for package id " + packageId + " into temp folder " + tempDir, - "Package business logic installation complete for package id " + packageId)) - { - InstalledPackage insPack; - try - { - //retrieve the manifest to continue installation - insPack = InstalledPackage.GetById(packageId); - //bool saveNeeded = false; - - // Get current user, with a fallback - var currentUser = Current.Services.UserService.GetUserById(Constants.Security.SuperUserId); - - //TODO: Get rid of this entire class! Until then all packages will be installed by the admin user - - - //Xml as XElement which is used with the new PackagingService - var rootElement = Config.DocumentElement.GetXElement(); - var packagingService = Current.Services.PackagingService; - - //Perhaps it would have been a good idea to put the following into methods eh?!? - - #region DataTypes - var dataTypeElement = rootElement.Descendants("DataTypes").FirstOrDefault(); - if (dataTypeElement != null) - { - var dataTypeDefinitions = packagingService.ImportDataTypeDefinitions(dataTypeElement, currentUser.Id); - foreach (var dataTypeDefinition in dataTypeDefinitions) - { - insPack.Data.DataTypes.Add(dataTypeDefinition.Id.ToString(CultureInfo.InvariantCulture)); - } - } - #endregion - - #region Languages - var languageItemsElement = rootElement.Descendants("Languages").FirstOrDefault(); - if (languageItemsElement != null) - { - var insertedLanguages = packagingService.ImportLanguages(languageItemsElement); - insPack.Data.Languages.AddRange(insertedLanguages.Select(l => l.Id.ToString(CultureInfo.InvariantCulture))); - } - - #endregion - - #region Dictionary items - var dictionaryItemsElement = rootElement.Descendants("DictionaryItems").FirstOrDefault(); - if (dictionaryItemsElement != null) - { - var insertedDictionaryItems = packagingService.ImportDictionaryItems(dictionaryItemsElement); - insPack.Data.DictionaryItems.AddRange(insertedDictionaryItems.Select(d => d.Id.ToString(CultureInfo.InvariantCulture))); - } - #endregion - - #region Macros - var macroItemsElement = rootElement.Descendants("Macros").FirstOrDefault(); - if (macroItemsElement != null) - { - var insertedMacros = packagingService.ImportMacros(macroItemsElement); - insPack.Data.Macros.AddRange(insertedMacros.Select(m => m.Id.ToString(CultureInfo.InvariantCulture))); - } - #endregion - - #region Templates - var templateElement = rootElement.Descendants("Templates").FirstOrDefault(); - if (templateElement != null) - { - var templates = packagingService.ImportTemplates(templateElement, currentUser.Id); - foreach (var template in templates) - { - insPack.Data.Templates.Add(template.Id.ToString(CultureInfo.InvariantCulture)); - } - } - #endregion - - #region DocumentTypes - //Check whether the root element is a doc type rather then a complete package - var docTypeElement = rootElement.Name.LocalName.Equals("DocumentType") || - rootElement.Name.LocalName.Equals("DocumentTypes") - ? rootElement - : rootElement.Descendants("DocumentTypes").FirstOrDefault(); - - if (docTypeElement != null) - { - var contentTypes = packagingService.ImportContentTypes(docTypeElement, currentUser.Id); - foreach (var contentType in contentTypes) - { - insPack.Data.Documenttypes.Add(contentType.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } - } - #endregion - - #region Stylesheets - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) - { - //StyleSheet s = StyleSheet.Import(n, currentUser); - - - string stylesheetName = XmlHelper.GetNodeValue(n.SelectSingleNode("Name")); - //StyleSheet s = GetByName(stylesheetName); - var s = Current.Services.FileService.GetStylesheetByName(stylesheetName); - if (s == null) - { - s = new Stylesheet(XmlHelper.GetNodeValue(n.SelectSingleNode("FileName"))) { Content = XmlHelper.GetNodeValue(n.SelectSingleNode("Content")) }; - Current.Services.FileService.SaveStylesheet(s); - } - - foreach (XmlNode prop in n.SelectNodes("Properties/Property")) - { - string alias = XmlHelper.GetNodeValue(prop.SelectSingleNode("Alias")); - var sp = s.Properties.SingleOrDefault(p => p != null && p.Alias == alias); - string name = XmlHelper.GetNodeValue(prop.SelectSingleNode("Name")); - if (sp == null) - { - //sp = StylesheetProperty.MakeNew( - // name, - // s, - // u); - - sp = new StylesheetProperty(name, "#" + name.ToSafeAlias(), ""); - s.AddProperty(sp); - } - else - { - //sp.Text = name; - //Changing the name requires removing the current property and then adding another new one - if (sp.Name != name) - { - s.RemoveProperty(sp.Name); - var newProp = new StylesheetProperty(name, sp.Alias, sp.Value); - s.AddProperty(newProp); - sp = newProp; - } - } - sp.Alias = alias; - sp.Value = XmlHelper.GetNodeValue(prop.SelectSingleNode("Value")); - } - //s.saveCssToFile(); - Current.Services.FileService.SaveStylesheet(s); - - - - - insPack.Data.Stylesheets.Add(s.Id.ToString(CultureInfo.InvariantCulture)); - //saveNeeded = true; - } - - //if (saveNeeded) { insPack.Save(); saveNeeded = false; } - #endregion - - #region Documents - var documentElement = rootElement.Descendants("DocumentSet").FirstOrDefault(); - if (documentElement != null) - { - var content = packagingService.ImportContent(documentElement, -1, currentUser.Id); - var firstContentItem = content.First(); - insPack.Data.ContentNodeId = firstContentItem.Id.ToString(CultureInfo.InvariantCulture); - } - #endregion - - #region Package Actions - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Actions/Action")) - { - if (n.Attributes["undo"] == null || n.Attributes["undo"].Value == "true") - { - insPack.Data.Actions += n.OuterXml; - } - - //Run the actions tagged only for 'install' - - if (n.Attributes["runat"] != null && n.Attributes["runat"].Value == "install") - { - var alias = n.Attributes["alias"] != null ? n.Attributes["alias"].Value : ""; - - if (alias.IsNullOrWhiteSpace() == false) - { - PackageAction.RunPackageAction(insPack.Data.Name, alias, n); - } - } - } - #endregion - - insPack.Save(); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error installing businesslogic"); - throw; - } - - OnPackageBusinessLogicInstalled(insPack); - OnPackageInstalled(insPack); - } - } - - /// - /// Remove the temp installation folder - /// - /// - /// - public void InstallCleanUp(int packageId, string tempDir) - { - if (Directory.Exists(tempDir)) - { - Directory.Delete(tempDir, true); - } - } - - /// - /// Reads the configuration of the package from the configuration xmldocument - /// - /// The folder to which the contents of the package is extracted - public void LoadConfig(string tempDir) - { - Config = new XmlDocument(); - Config.Load(tempDir + Path.DirectorySeparatorChar + "package.xml"); - - Name = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/name").FirstChild.Value; - Version = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/version").FirstChild.Value; - Url = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/url").FirstChild.Value; - License = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license").FirstChild.Value; - LicenseUrl = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/license").Attributes.GetNamedItem("url").Value; - - RequirementsMajor = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/major").FirstChild.Value); - RequirementsMinor = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/minor").FirstChild.Value); - RequirementsPatch = int.Parse(Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/patch").FirstChild.Value); - - var reqNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements"); - RequirementsType = reqNode != null && reqNode.Attributes != null && reqNode.Attributes["type"] != null - ? Enum.Parse(reqNode.Attributes["type"].Value, true) - : RequirementsType.Legacy; - var iconNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/package/iconUrl"); - if (iconNode != null && iconNode.FirstChild != null) - { - IconUrl = iconNode.FirstChild.Value; - } - - Author = Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/name").FirstChild.Value; - AuthorUrl = Config.DocumentElement.SelectSingleNode("/umbPackage/info/author/website").FirstChild.Value; - - var basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - var dllBinFiles = new List(); - - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//file")) - { - var badFile = false; - var destPath = GetFileName(basePath, XmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); - var orgName = XmlHelper.GetNodeValue(n.SelectSingleNode("orgName")); - var destFile = GetFileName(destPath, orgName); - - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) - { - badFile = true; - } - - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) - { - badFile = true; - } - - if (destFile.ToLower().EndsWith(".dll")) - { - badFile = true; - dllBinFiles.Add(Path.Combine(tempDir, orgName)); - } - - if (badFile) - { - ContainsUnsecureFiles = true; - _unsecureFiles.Add(XmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - } - } - - - - //this will check for existing macros with the same alias - //since we will not overwrite on import it's a good idea to inform the user what will be overwritten - foreach (XmlNode n in Config.DocumentElement.SelectNodes("//macro")) - { - var alias = n.SelectSingleNode("alias").InnerText; - if (!string.IsNullOrEmpty(alias)) - { - var m = Current.Services.MacroService.GetByAlias(alias); - if (m != null) - { - ContainsMacroConflict = true; - if (_conflictingMacroAliases.ContainsKey(m.Name) == false) - { - _conflictingMacroAliases.Add(m.Name, alias); - } - } - } - } - - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Templates/Template")) - { - var alias = n.SelectSingleNode("Alias").InnerText; - if (!string.IsNullOrEmpty(alias)) - { - var t = Current.Services.FileService.GetTemplate(alias); - if (t != null) - { - ContainsTemplateConflicts = true; - if (_conflictingTemplateAliases.ContainsKey(t.Alias) == false) - { - _conflictingTemplateAliases.Add(t.Alias, alias); - } - } - } - } - - foreach (XmlNode n in Config.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) - { - var alias = n.SelectSingleNode("Name").InnerText; - if (!string.IsNullOrEmpty(alias)) - { - var s = Current.Services.FileService.GetStylesheetByName(alias); - if (s != null) - { - ContainsStyleSheeConflicts = true; - if (_conflictingStyleSheetNames.ContainsKey(s.Alias) == false) - { - _conflictingStyleSheetNames.Add(s.Alias, alias); - } - } - } - } - - var readmeNode = Config.DocumentElement.SelectSingleNode("/umbPackage/info/readme"); - if (readmeNode != null) - { - ReadMe = XmlHelper.GetNodeValue(readmeNode); - } - - var controlNode = Config.DocumentElement.SelectSingleNode("/umbPackage/control"); - if (controlNode != null) - { - Control = XmlHelper.GetNodeValue(controlNode); - } - } - - /// - /// This uses the old method of fetching and only supports the packages.umbraco.org repository. - /// - /// - /// - public string Fetch(Guid Package) - { - // Check for package directory - if (Directory.Exists(IOHelper.MapPath(SystemDirectories.Packages)) == false) - Directory.CreateDirectory(IOHelper.MapPath(SystemDirectories.Packages)); - - if (_webClient == null) - _webClient = new WebClient(); - - _webClient.DownloadFile( - "http://" + PackageServer + "/fetch?package=" + Package.ToString(), - IOHelper.MapPath(SystemDirectories.Packages + "/" + Package + ".umb")); - - return "packages\\" + Package + ".umb"; - } - - #endregion - - #region Private Methods - - /// - /// Gets the name of the file in the specified path. - /// Corrects possible problems with slashes that would result from a simple concatenation. - /// Can also be used to concatenate paths. - /// - /// The path. - /// Name of the file. - /// The name of the file in the specified path. - private static string GetFileName(string path, string fileName) - { - // virtual dir support - fileName = IOHelper.FindFile(fileName); - - 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); - } - - //to support virtual dirs we try to lookup the file... - path = IOHelper.FindFile(path); - - - - Debug.Assert(path != null && path.Length >= 1); - Debug.Assert(fileName != null && fileName.Length >= 1); - - path = path.Replace('/', '\\'); - fileName = fileName.Replace('/', '\\'); - - // Does filename start with a slash? Does path end with one? - bool fileNameStartsWithSlash = (fileName[0] == Path.DirectorySeparatorChar); - bool pathEndsWithSlash = (path[path.Length - 1] == Path.DirectorySeparatorChar); - - // Path ends with a slash - if (pathEndsWithSlash) - { - if (!fileNameStartsWithSlash) - // No double slash, just concatenate - return path + fileName; - return path + fileName.Substring(1); - } - if (fileNameStartsWithSlash) - // Required slash specified, just concatenate - return path + fileName; - return path + Path.DirectorySeparatorChar + fileName; - } - - private static string UnPack(string zipName, bool deleteFile) - { - // Unzip - - //the temp directory will be the package GUID - this keeps it consistent! - //the zipName is always the package Guid.umb - - var packageFileName = Path.GetFileNameWithoutExtension(zipName); - var packageId = Guid.NewGuid(); - Guid.TryParse(packageFileName, out packageId); - - string tempDir = IOHelper.MapPath(SystemDirectories.Data) + Path.DirectorySeparatorChar + packageId.ToString(); - //clear the directory if it exists - if (Directory.Exists(tempDir)) Directory.Delete(tempDir, true); - Directory.CreateDirectory(tempDir); - - var s = new ZipInputStream(File.OpenRead(zipName)); - - ZipEntry theEntry; - while ((theEntry = s.GetNextEntry()) != null) - { - string fileName = Path.GetFileName(theEntry.Name); - - if (fileName != String.Empty) - { - FileStream streamWriter = File.Create(tempDir + Path.DirectorySeparatorChar + fileName); - - int size = 2048; - byte[] data = new byte[2048]; - while (true) - { - size = s.Read(data, 0, data.Length); - if (size > 0) - { - streamWriter.Write(data, 0, size); - } - else - { - break; - } - } - - streamWriter.Close(); - - } - } - - // Clean up - s.Close(); - - if (deleteFile) - { - File.Delete(zipName); - } - - - return tempDir; - - } - - #endregion - - internal static event EventHandler PackageBusinessLogicInstalled; - - private static void OnPackageBusinessLogicInstalled(InstalledPackage e) - { - EventHandler handler = PackageBusinessLogicInstalled; - if (handler != null) handler(null, e); - } - - private void OnPackageInstalled(InstalledPackage insPack) - { - // getting an InstallationSummary for sending to the PackagingService.ImportedPackage event - var fileService = Current.Services.FileService; - var macroService = Current.Services.MacroService; - var contentTypeService = Current.Services.ContentTypeService; - var dataTypeService = Current.Services.DataTypeService; - var localizationService = Current.Services.LocalizationService; - - var installationSummary = insPack.GetInstallationSummary(contentTypeService, dataTypeService, fileService, localizationService, macroService); - installationSummary.PackageInstalled = true; - - var args = new ImportPackageEventArgs(installationSummary, false); - PackagingService.OnImportedPackage(args); - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs deleted file mode 100644 index 3bfcdd3f86..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/CreatedPackage.cs +++ /dev/null @@ -1,388 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Models; -using Umbraco.Core.Services; -using File = System.IO.File; - - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - public class CreatedPackage - { - - public static CreatedPackage GetById(int id) - { - var pack = new CreatedPackage(); - pack.Data = data.Package(id, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - return pack; - } - - public static CreatedPackage MakeNew(string name) - { - var pack = new CreatedPackage - { - Data = data.MakeNew(name, IOHelper.MapPath(Settings.CreatedPackagesSettings)) - }; - - - return pack; - } - - public void Save() - { - data.Save(this.Data, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - } - - public void Delete() - { - data.Delete(this.Data.Id, IOHelper.MapPath(Settings.CreatedPackagesSettings)); - } - - public PackageInstance Data { get; set; } - - public static List GetAllCreatedPackages() - { - var val = new List(); - - foreach (var pack in data.GetAllPackages(IOHelper.MapPath(Settings.CreatedPackagesSettings))) - { - var crPack = new CreatedPackage(); - crPack.Data = pack; - val.Add(crPack); - } - - return val; - } - - private static XmlDocument _packageManifest; - private static void CreatePackageManifest() - { - _packageManifest = new XmlDocument(); - var xmldecl = _packageManifest.CreateXmlDeclaration("1.0", "UTF-8", "no"); - - _packageManifest.AppendChild(xmldecl); - - //root node - XmlNode umbPackage = _packageManifest.CreateElement("umbPackage"); - _packageManifest.AppendChild(umbPackage); - //Files node - umbPackage.AppendChild(_packageManifest.CreateElement("files")); - } - - private static void AppendElement(XmlNode node) - { - var root = _packageManifest.SelectSingleNode("/umbPackage"); - root.AppendChild(node); - } - - - public void Publish() - { - - var package = this; - var pack = package.Data; - - var outInt = 0; - - //Path checking... - var localPath = IOHelper.MapPath(SystemDirectories.Media + "/" + pack.Folder); - - if (Directory.Exists(localPath) == false) - Directory.CreateDirectory(localPath); - - //Init package file... - CreatePackageManifest(); - //Info section.. - AppendElement(PackagerUtility.PackageInfo(pack, _packageManifest)); - - //Documents and tags... - var contentNodeId = 0; - if (string.IsNullOrEmpty(pack.ContentNodeId) == false && int.TryParse(pack.ContentNodeId, out contentNodeId)) - { - if (contentNodeId > 0) - { - //Create the Documents/DocumentSet node - XmlNode documents = _packageManifest.CreateElement("Documents"); - XmlNode documentSet = _packageManifest.CreateElement("DocumentSet"); - XmlAttribute importMode = _packageManifest.CreateAttribute("importMode", ""); - importMode.Value = "root"; - documentSet.Attributes.Append(importMode); - documents.AppendChild(documentSet); - - //load content from umbraco. - //var umbDocument = new Document(contentNodeId); - //var x = umbDocument.ToXml(_packageManifest, pack.ContentLoadChildNodes); - var udoc = Current.Services.ContentService.GetById(contentNodeId); - var xe = pack.ContentLoadChildNodes ? udoc.ToDeepXml(Current.Services.PackagingService) : udoc.ToXml(Current.Services.PackagingService); - var x = xe.GetXmlNode(_packageManifest); - documentSet.AppendChild(x); - - AppendElement(documents); - - ////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 = _packageManifest.CreateElement("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() - // .Select(x => x.Value.TryConvertTo()) - // .Where(x => x.Success) - // .Select(x => x.Result) - // .ToArray(); - //var allContentTags = new List(); - //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(); - //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 = _packageManifest.CreateElement("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); - // } - //} - - //AppendElement(tagProps); - - } - } - - //Document types.. - var dtl = new List(); - var docTypes = _packageManifest.CreateElement("DocumentTypes"); - foreach (var dtId in pack.Documenttypes) - { - if (int.TryParse(dtId, out outInt)) - { - var docT = Current.Services.ContentTypeService.Get(outInt); - //DocumentType docT = new DocumentType(outInt); - - AddDocumentType(docT, ref dtl); - - } - } - - var exporter = new EntityXmlSerializer(); - - foreach (var d in dtl) - { - var xml = exporter.Serialize(Current.Services.DataTypeService, Current.Services.ContentTypeService, d); - var xNode = xml.GetXmlNode(); - var n = (XmlElement) _packageManifest.ImportNode(xNode, true); - docTypes.AppendChild(n); - } - - AppendElement(docTypes); - - //Templates - var templates = _packageManifest.CreateElement("Templates"); - foreach (var templateId in pack.Templates) - { - if (int.TryParse(templateId, out outInt)) - { - var t = Current.Services.FileService.GetTemplate(outInt); - - var serializer = new EntityXmlSerializer(); - var serialized = serializer.Serialize(t); - var n = serialized.GetXmlNode(_packageManifest); - - - templates.AppendChild(n); - } - } - AppendElement(templates); - - //Stylesheets - var stylesheets = _packageManifest.CreateElement("Stylesheets"); - foreach (var stylesheetName in pack.Stylesheets) - { - if (stylesheetName.IsNullOrWhiteSpace()) continue; - var stylesheetXmlNode = PackagerUtility.Stylesheet(stylesheetName, true, _packageManifest); - if (stylesheetXmlNode != null) - stylesheets.AppendChild(stylesheetXmlNode); - } - AppendElement(stylesheets); - - //Macros - var macros = _packageManifest.CreateElement("Macros"); - foreach (var macroId in pack.Macros) - { - if (int.TryParse(macroId, out outInt)) - { - macros.AppendChild(PackagerUtility.Macro(int.Parse(macroId), true, localPath, _packageManifest)); - } - } - AppendElement(macros); - - //Dictionary Items - var dictionaryItems = _packageManifest.CreateElement("DictionaryItems"); - foreach (var dictionaryId in pack.DictionaryItems) - { - if (int.TryParse(dictionaryId, out outInt)) - { - var di = Current.Services.LocalizationService.GetDictionaryItemById(outInt); - var entitySerializer = new EntityXmlSerializer(); - var xmlNode = entitySerializer.Serialize(di).GetXmlNode(_packageManifest); - dictionaryItems.AppendChild(xmlNode); - } - } - AppendElement(dictionaryItems); - - //Languages - var languages = _packageManifest.CreateElement("Languages"); - foreach (var langId in pack.Languages) - { - if (int.TryParse(langId, out outInt)) - { - var lang = Current.Services.LocalizationService.GetLanguageById(outInt); - - var serializer = new EntityXmlSerializer(); - var xml = serializer.Serialize(lang); - var n = xml.GetXmlNode(_packageManifest); - - languages.AppendChild(n); - } - } - AppendElement(languages); - - //TODO: Fix this! ... actually once we use the new packager we don't need to - - ////Datatypes - //var dataTypes = _packageManifest.CreateElement("DataTypes"); - //foreach (var dtId in pack.DataTypes) - //{ - // if (int.TryParse(dtId, out outInt)) - // { - // datatype.DataTypeDefinition dtd = new datatype.DataTypeDefinition(outInt); - // dataTypes.AppendChild(dtd.ToXml(_packageManifest)); - // } - //} - //AppendElement(dataTypes); - - //Files - foreach (var fileName in pack.Files) - { - PackagerUtility.AppendFileToManifest(fileName, localPath, _packageManifest); - } - - //Load control on install... - if (string.IsNullOrEmpty(pack.LoadControl) == false) - { - XmlNode control = _packageManifest.CreateElement("control"); - control.InnerText = pack.LoadControl; - PackagerUtility.AppendFileToManifest(pack.LoadControl, localPath, _packageManifest); - AppendElement(control); - } - - //Actions - if (string.IsNullOrEmpty(pack.Actions) == false) - { - try - { - var xdActions = new XmlDocument(); - xdActions.LoadXml("" + pack.Actions + ""); - var actions = xdActions.DocumentElement.SelectSingleNode("."); - - - if (actions != null) - { - actions = _packageManifest.ImportNode(actions, true).Clone(); - AppendElement(actions); - } - } - catch { } - } - - var manifestFileName = localPath + "/package.xml"; - - if (File.Exists(manifestFileName)) - File.Delete(manifestFileName); - - _packageManifest.Save(manifestFileName); - _packageManifest = null; - - - //string packPath = Settings.PackagerRoot.Replace(System.IO.Path.DirectorySeparatorChar.ToString(), "/") + "/" + pack.Name.Replace(' ', '_') + "_" + pack.Version.Replace(' ', '_') + "." + Settings.PackageFileExtension; - - // check if there's a packages directory below media - var packagesDirectory = SystemDirectories.Media + "/created-packages"; - if (Directory.Exists(IOHelper.MapPath(packagesDirectory)) == false) - { - Directory.CreateDirectory(IOHelper.MapPath(packagesDirectory)); - } - - - var packPath = packagesDirectory + "/" + (pack.Name + "_" + pack.Version).Replace(' ', '_') + "." + Settings.PackageFileExtension; - PackagerUtility.ZipPackage(localPath, IOHelper.MapPath(packPath)); - - pack.PackagePath = packPath; - - if (pack.PackageGuid.Trim() == "") - pack.PackageGuid = Guid.NewGuid().ToString(); - - package.Save(); - - //Clean up.. - File.Delete(localPath + "/package.xml"); - Directory.Delete(localPath, true); - } - - private void AddDocumentType(IContentType dt, ref List dtl) - { - if (dt.ParentId > 0) - { - var parent = Current.Services.ContentTypeService.Get(dt.ParentId); - if (parent != null) // could be a container - { - AddDocumentType(parent, ref dtl); - } - } - - if (dtl.Contains(dt) == false) - { - dtl.Add(dt); - } - } - - - - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs deleted file mode 100644 index b920c85a9f..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/IPackageInstance.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace Umbraco.Web._Legacy.Packager.PackageInstance{ - public interface IPackageInstance { - string Actions { get; set; } - string Author { get; set; } - string AuthorUrl { get; set; } - bool ContentLoadChildNodes { get; set; } - string ContentNodeId { get; set; } - System.Collections.Generic.List Documenttypes { get; set; } - System.Collections.Generic.List Files { get; set; } - string Folder { get; set; } - bool HasUpdate { get; set; } - int Id { get; set; } - string License { get; set; } - string LicenseUrl { get; set; } - string LoadControl { get; set; } - System.Collections.Generic.List Macros { get; set; } - string Name { get; set; } - string PackageGuid { get; set; } - string PackagePath { get; set; } - string Readme { get; set; } - string RepositoryGuid { get; set; } - System.Collections.Generic.List Stylesheets { get; set; } - System.Collections.Generic.List Templates { get; set; } - string Url { get; set; } - string Version { get; set; } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs deleted file mode 100644 index 3fca97759b..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/InstalledPackage.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Models; -using Umbraco.Core.Models.Packaging; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - public class InstalledPackage - { - - private int _saveHitCount = 0; - - public static InstalledPackage GetById(int id) { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.Package(id, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - return pack; - } - - public static InstalledPackage GetByGuid(string packageGuid) { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.Package(packageGuid, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - return pack; - } - - public static InstalledPackage MakeNew(string name) { - InstalledPackage pack = new InstalledPackage(); - pack.Data = data.MakeNew(name, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - pack.OnNew(EventArgs.Empty); - return pack; - } - - public void Save() - { -#if DEBUG - _saveHitCount++; - Current.Logger.Info("The InstalledPackage class save method has been hit {Total} times.", _saveHitCount); -#endif - this.FireBeforeSave(EventArgs.Empty); - data.Save(this.Data, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - this.FireAfterSave(EventArgs.Empty); - } - - public static List GetAllInstalledPackages() { - - List val = new List(); - - foreach (PackageInstance pack in data.GetAllPackages(IOHelper.MapPath(Settings.InstalledPackagesSettings))) - { - InstalledPackage insPackage = new InstalledPackage(); - insPackage.Data = pack; - val.Add(insPackage); - } - - return val; - } - - private PackageInstance m_data; - public PackageInstance Data { - get { return m_data; } - set { m_data = value; } - } - - public void Delete(int userId) - { - Current.Services.AuditService.Add(AuditType.PackagerUninstall, userId, -1, "Package", string.Format("Package '{0}' uninstalled. Package guid: {1}", Data.Name, Data.PackageGuid)); - Delete(); - } - - public void Delete() { - this.FireBeforeDelete(EventArgs.Empty); - data.Delete(this.Data.Id, IOHelper.MapPath(Settings.InstalledPackagesSettings)); - this.FireAfterDelete(EventArgs.Empty); - } - - public static bool isPackageInstalled(string packageGuid) { - try - { - if (data.GetFromGuid(packageGuid, IOHelper.MapPath(Settings.InstalledPackagesSettings), true) == null) - return false; - else - return true; - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occured in isPackagedInstalled"); - return false; - } - } - - //EVENTS - public delegate void SaveEventHandler(InstalledPackage sender, EventArgs e); - public delegate void NewEventHandler(InstalledPackage sender, EventArgs e); - public delegate void DeleteEventHandler(InstalledPackage sender, EventArgs e); - - /// - /// Occurs when a macro is saved. - /// - public static event SaveEventHandler BeforeSave; - protected virtual void FireBeforeSave(EventArgs e) { - if (BeforeSave != null) - BeforeSave(this, e); - } - - public static event SaveEventHandler AfterSave; - protected virtual void FireAfterSave(EventArgs e) { - if (AfterSave != null) - AfterSave(this, e); - } - - public static event NewEventHandler New; - protected virtual void OnNew(EventArgs e) { - if (New != null) - New(this, e); - } - - public static event DeleteEventHandler BeforeDelete; - protected virtual void FireBeforeDelete(EventArgs e) { - if (BeforeDelete != null) - BeforeDelete(this, e); - } - - public static event DeleteEventHandler AfterDelete; - protected virtual void FireAfterDelete(EventArgs e) { - if (AfterDelete != null) - AfterDelete(this, e); - } - - - /// - /// Used internally for creating an InstallationSummary (used in new PackagingService) representation of this InstalledPackage object. - /// - /// - /// - /// - /// - /// - /// - internal InstallationSummary GetInstallationSummary(IContentTypeService contentTypeService, IDataTypeService dataTypeService, IFileService fileService, ILocalizationService localizationService, IMacroService macroService) - { - var macros = TryGetIntegerIds(Data.Macros).Select(macroService.GetById).ToList(); - var templates = TryGetIntegerIds(Data.Templates).Select(fileService.GetTemplate).ToList(); - var contentTypes = TryGetIntegerIds(Data.Documenttypes).Select(contentTypeService.Get).ToList(); // fixme - media types? - var dataTypes = TryGetIntegerIds(Data.DataTypes).Select(dataTypeService.GetDataType).ToList(); - var dictionaryItems = TryGetIntegerIds(Data.DictionaryItems).Select(localizationService.GetDictionaryItemById).ToList(); - var languages = TryGetIntegerIds(Data.Languages).Select(localizationService.GetLanguageById).ToList(); - - for (var i = 0; i < Data.Files.Count; i++) - { - var filePath = Data.Files[i]; - Data.Files[i] = filePath.GetRelativePath(); - } - - return new InstallationSummary - { - ContentTypesInstalled = contentTypes, - DataTypesInstalled = dataTypes, - DictionaryItemsInstalled = dictionaryItems, - FilesInstalled = Data.Files, - LanguagesInstalled = languages, - MacrosInstalled = macros, - MetaData = GetMetaData(), - TemplatesInstalled = templates, - }; - } - - internal MetaData GetMetaData() - { - return new MetaData() - { - AuthorName = Data.Author, - AuthorUrl = Data.AuthorUrl, - Control = Data.LoadControl, - License = Data.License, - LicenseUrl = Data.LicenseUrl, - Name = Data.Name, - Readme = Data.Readme, - Url = Data.Url, - Version = Data.Version - }; - } - - private static IEnumerable TryGetIntegerIds(IEnumerable ids) - { - var intIds = new List(); - foreach (var id in ids) - { - int parsed; - if (int.TryParse(id, out parsed)) - intIds.Add(parsed); - } - return intIds; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs deleted file mode 100644 index bfd1030d85..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageActions.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Xml; -using Umbraco.Core.Composing; -using Umbraco.Core.Logging; -using Umbraco.Core._Legacy.PackageActions; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - - /// - /// Package actions are executed on packge install / uninstall. - /// - public class PackageAction - { - - /// - /// Runs the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public static void RunPackageAction(string packageName, string actionAlias, XmlNode actionXml) - { - - foreach (var ipa in Current.PackageActions) - { - try - { - if (ipa.Alias() == actionAlias) - { - - ipa.Execute(packageName, actionXml); - } - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error loading package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); - } - } - } - - /// - /// Undos the package action with the specified action alias. - /// - /// Name of the package. - /// The action alias. - /// The action XML. - public static void UndoPackageAction(string packageName, string actionAlias, System.Xml.XmlNode actionXml) - { - - foreach (IPackageAction ipa in Current.PackageActions) - { - try - { - if (ipa.Alias() == actionAlias) - { - - ipa.Undo(packageName, actionXml); - } - } - catch (Exception ex) - { - Current.Logger.Error(ex, "Error undoing package action '{PackageActionAlias}' for package {PackageName}", ipa.Alias(), packageName); - } - } - } - - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs deleted file mode 100644 index fe32633ccd..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackageInstance.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - public class PackageInstance - { - public int Id { get; set; } - - public string RepositoryGuid { get; set; } - - public string PackageGuid { get; set; } - - public bool HasUpdate { get; set; } - - public string Name { get; set; } - - public string Url { get; set; } - - public string Folder { get; set; } - - public string PackagePath { get; set; } - - public string Version { get; set; } - - /// - /// The minimum umbraco version that this package requires - /// - public Version UmbracoVersion { get; set; } - - public string Author { get; set; } - - public string AuthorUrl { get; set; } - - public string License { get; set; } - - public string LicenseUrl { get; set; } - - public string Readme { get; set; } - - public bool ContentLoadChildNodes { get; set; } - - public string ContentNodeId { get; set; } - - public List Macros { get; set; } - - public List Languages { get; set; } - - public List DictionaryItems { get; set; } - - public List Templates { get; set; } - - public List Documenttypes { get; set; } - - public List Stylesheets { get; set; } - - public List Files { get; set; } - - public string LoadControl { get; set; } - - public string Actions { get; set; } - - public List DataTypes { get; set; } - - public string IconUrl { get; set; } - - public PackageInstance() - { - Name = string.Empty; - Url = string.Empty; - Folder = string.Empty; - PackagePath = string.Empty; - Version = string.Empty; - UmbracoVersion = null; - Author = string.Empty; - AuthorUrl = string.Empty; - License = string.Empty; - LicenseUrl = string.Empty; - Readme = string.Empty; - ContentNodeId = string.Empty; - IconUrl = string.Empty; - Macros = new List(); - Languages = new List(); - DictionaryItems = new List(); - Templates = new List(); - Documenttypes = new List(); - Stylesheets = new List(); - Files = new List(); - LoadControl = string.Empty; - DataTypes = new List(); - ContentLoadChildNodes = false; - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs b/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs deleted file mode 100644 index d972977bad..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/PackageInstance/PackagerUtility.cs +++ /dev/null @@ -1,326 +0,0 @@ -using System; -using System.Collections; -using System.IO; -using System.Xml; -using ICSharpCode.SharpZipLib.Zip; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.IO; -using Umbraco.Core.Services; - -namespace Umbraco.Web._Legacy.Packager.PackageInstance -{ - /// - /// A utillity class for working with packager data. - /// It provides basic methods for adding new items to a package manifest, moving files and other misc. - /// - public class PackagerUtility - { - /// - /// Creates a package manifest containing name, license, version and other meta data. - /// - /// The packinstance. - /// The xml document. - /// - public static XmlNode PackageInfo(PackageInstance pack, XmlDocument doc) - { - XmlNode info = doc.CreateElement("info"); - - //Package info - XmlNode package = doc.CreateElement("package"); - package.AppendChild(CreateNode("name", pack.Name, doc)); - package.AppendChild(CreateNode("version", pack.Version, doc)); - package.AppendChild(CreateNode("iconUrl", pack.IconUrl, doc)); - - XmlNode license = CreateNode("license", pack.License, doc); - license.Attributes.Append(CreateAttribute("url", pack.LicenseUrl, doc)); - package.AppendChild(license); - - package.AppendChild(CreateNode("url", pack.Url, doc)); - - XmlNode requirements = doc.CreateElement("requirements"); - //NOTE: The defaults are 3.0.0 - I'm just leaving that here since that's the way it's been //SD - requirements.AppendChild(CreateNode("major", pack.UmbracoVersion == null ? "3" : pack.UmbracoVersion.Major.ToInvariantString(), doc)); - requirements.AppendChild(CreateNode("minor", pack.UmbracoVersion == null ? "0" : pack.UmbracoVersion.Minor.ToInvariantString(), doc)); - requirements.AppendChild(CreateNode("patch", pack.UmbracoVersion == null ? "0" : pack.UmbracoVersion.Build.ToInvariantString(), doc)); - if (pack.UmbracoVersion != null) - requirements.Attributes.Append(CreateAttribute("type", "strict", doc)); - - package.AppendChild(requirements); - info.AppendChild(package); - - //Author - XmlNode author = CreateNode("author", "", doc); - author.AppendChild(CreateNode("name", pack.Author, doc)); - author.AppendChild(CreateNode("website", pack.AuthorUrl, doc)); - info.AppendChild(author); - - info.AppendChild(CreateNode("readme", "", doc)); - - return info; - } - - - /// - /// Converts an umbraco template to a package xml node - /// - /// The template id. - /// The xml doc. - /// - public static XmlNode Template(int templateId, XmlDocument doc) - { - var tmpl = Current.Services.FileService.GetTemplate(templateId); - //Template tmpl = new Template(templateId); - - XmlNode template = doc.CreateElement("Template"); - template.AppendChild(CreateNode("Name", tmpl.Name, doc)); - template.AppendChild(CreateNode("Alias", tmpl.Alias, doc)); - - //if (tmpl.MasterTemplate != 0) - if (string.IsNullOrWhiteSpace(tmpl.MasterTemplateAlias) == false) - template.AppendChild(CreateNode("Master", tmpl.MasterTemplateAlias, doc)); - - template.AppendChild(CreateNode("Design", "", doc)); - - return template; - } - - /// - /// Converts a umbraco stylesheet to a package xml node - /// - /// The name of the stylesheet. - /// if set to true [incluce properties]. - /// The doc. - /// - public static XmlNode Stylesheet(string name, bool includeProperties, XmlDocument doc) - { - if (doc == null) throw new ArgumentNullException("doc"); - if (string.IsNullOrWhiteSpace(name)) throw new ArgumentException("Value cannot be null or whitespace.", "name"); - - var fileService = Current.Services.FileService; - var sts = fileService.GetStylesheetByName(name); - var stylesheet = doc.CreateElement("Stylesheet"); - stylesheet.AppendChild(CreateNode("Name", sts.Alias, doc)); - stylesheet.AppendChild(CreateNode("FileName", sts.Name, doc)); - stylesheet.AppendChild(CreateNode("Content", "", doc)); - if (includeProperties) - { - var properties = doc.CreateElement("Properties"); - foreach (var ssP in sts.Properties) - { - var property = doc.CreateElement("Property"); - property.AppendChild(CreateNode("Name", ssP.Name, doc)); - property.AppendChild(CreateNode("Alias", ssP.Alias, doc)); - property.AppendChild(CreateNode("Value", ssP.Value, doc)); - } - stylesheet.AppendChild(properties); - } - return stylesheet; - } - - /// - /// Converts a macro to a package xml node - /// - /// The macro id. - /// if set to true [append file]. - /// The package directory. - /// The doc. - /// - public static XmlNode Macro(int macroId, bool appendFile, string packageDirectory, XmlDocument doc) - { - var mcr = Current.Services.MacroService.GetById(macroId); - - if (appendFile) - { - if (!string.IsNullOrEmpty(mcr.MacroSource)) - AppendFileToManifest(mcr.MacroSource, packageDirectory, doc); - } - - var serializer = new EntityXmlSerializer(); - var xml = serializer.Serialize(mcr); - return xml.GetXmlNode(doc); - } - - - /// - /// Appends a file to package manifest and copies the file to the correct folder. - /// - /// The path. - /// The package directory. - /// The doc. - public static void AppendFileToManifest(string path, string packageDirectory, XmlDocument doc) - { - if (!path.StartsWith("~/") && !path.StartsWith("/")) - path = "~/" + path; - - string serverPath = IOHelper.MapPath(path); - - if (System.IO.File.Exists(serverPath)) - - AppendFileXml(path, packageDirectory, doc); - else if (System.IO.Directory.Exists(serverPath)) - ProcessDirectory(path, packageDirectory, doc); - } - - - - //Process files in directory and add them to package - private static void ProcessDirectory(string path, string packageDirectory, XmlDocument doc) - { - string serverPath = IOHelper.MapPath(path); - if (System.IO.Directory.Exists(serverPath)) - { - System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(serverPath); - - foreach (System.IO.FileInfo file in di.GetFiles()) - AppendFileXml(path + "/" + file.Name, packageDirectory, doc); - - foreach (System.IO.DirectoryInfo dir in di.GetDirectories()) - ProcessDirectory(path + "/" + dir.Name, packageDirectory, doc); - } - } - - private static void AppendFileXml(string path, string packageDirectory, XmlDocument doc) - { - - string serverPath = IOHelper.MapPath(path); - - string orgPath = path.Substring(0, (path.LastIndexOf('/'))); - string orgName = path.Substring((path.LastIndexOf('/') + 1)); - string newFileName = orgName; - - if (System.IO.File.Exists(packageDirectory + "/" + orgName)) - { - string fileGuid = System.Guid.NewGuid().ToString(); - newFileName = fileGuid + "_" + newFileName; - } - - //Copy file to directory for zipping... - System.IO.File.Copy(serverPath, packageDirectory + "/" + newFileName, true); - - //Append file info to files xml node - XmlNode files = doc.SelectSingleNode("/umbPackage/files"); - - XmlNode file = doc.CreateElement("file"); - file.AppendChild(CreateNode("guid", newFileName, doc)); - file.AppendChild(CreateNode("orgPath", orgPath == "" ? "/" : orgPath, doc)); - file.AppendChild(CreateNode("orgName", orgName, doc)); - - files.AppendChild(file); - } - - /// - /// Determines whether the file is in the package manifest - /// - /// The GUID. - /// The doc. - /// - /// true if [is file in manifest]; otherwise, false. - /// - public static bool IsFileInManifest(string guid, XmlDocument doc) - { - return false; - } - - private static XmlNode CreateNode(string name, string value, XmlDocument doc) - { - var node = doc.CreateElement(name); - node.InnerXml = value; - return node; - } - - private static XmlAttribute CreateAttribute(string name, string value, XmlDocument doc) - { - var attribute = doc.CreateAttribute(name); - attribute.Value = value; - return attribute; - } - - /// - /// Zips the package. - /// - /// The path. - /// The save path. - public static void ZipPackage(string Path, string savePath) - { - string OutPath = savePath; - - ArrayList ar = GenerateFileList(Path); - // generate file list - // find number of chars to remove from orginal file path - int TrimLength = (Directory.GetParent(Path)).ToString().Length; - - TrimLength += 1; - - //remove '\' - FileStream ostream; - - byte[] obuffer; - - ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(OutPath)); - // create zip stream - - - oZipStream.SetLevel(9); - // 9 = maximum compression level - ZipEntry oZipEntry; - - foreach (string Fil in ar) // for each file, generate a zipentry - { - oZipEntry = new ZipEntry(Fil.Remove(0, TrimLength)); - oZipStream.PutNextEntry(oZipEntry); - - - if (!Fil.EndsWith(@"/")) // if a file ends with '/' its a directory - { - ostream = File.OpenRead(Fil); - - obuffer = new byte[ostream.Length]; - - // byte buffer - ostream.Read(obuffer, 0, obuffer.Length); - - oZipStream.Write(obuffer, 0, obuffer.Length); - ostream.Close(); - } - } - oZipStream.Finish(); - oZipStream.Close(); - oZipStream.Dispose(); - oZipStream = null; - - oZipEntry = null; - - - ostream = null; - ar.Clear(); - ar = null; - } - - private static ArrayList GenerateFileList(string Dir) - { - ArrayList mid = new ArrayList(); - - bool Empty = true; - - // add each file in directory - foreach (string file in Directory.GetFiles(Dir)) - { - mid.Add(file); - Empty = false; - } - - // if directory is completely empty, add it - if (Empty && Directory.GetDirectories(Dir).Length == 0) - mid.Add(Dir + @"/"); - - // do this recursively - foreach (string dirs in Directory.GetDirectories(Dir)) - { - foreach (object obj in GenerateFileList(dirs)) - mid.Add(obj); - } - return mid; // return file list - } - } -} diff --git a/src/Umbraco.Web/_Legacy/Packager/Settings.cs b/src/Umbraco.Web/_Legacy/Packager/Settings.cs deleted file mode 100644 index 57ab4b9ea4..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/Settings.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.IO; -using Umbraco.Core.IO; - -namespace Umbraco.Web._Legacy.Packager -{ - public class Settings - { - - public static string PackagerRoot - { - get { return SystemDirectories.Packages; } - } - - public static string PackagesStorage - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "created"; } - } - - public static string InstalledPackagesStorage - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "installed"; } - } - - public static string InstalledPackagesSettings - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "installed" + IOHelper.DirSepChar + "installedPackages.config"; } - } - - public static string CreatedPackagesSettings - { - get { return SystemDirectories.Packages + IOHelper.DirSepChar + "created" + IOHelper.DirSepChar + "createdPackages.config"; } - } - - public static string PackageFileExtension - { - get { return "zip"; } - } - - public static bool HasFileAccess(ref Exception exp) - { - bool hasAccess = false; - StreamWriter sw1 = null; - StreamWriter sw2 = null; - try - { - sw1 = System.IO.File.AppendText(IOHelper.MapPath(InstalledPackagesSettings)); - sw1.Close(); - - sw2 = System.IO.File.AppendText(IOHelper.MapPath(CreatedPackagesSettings)); - sw1.Close(); - - System.IO.Directory.CreateDirectory(IOHelper.MapPath(PackagesStorage) + IOHelper.DirSepChar + "__testFolder__"); - System.IO.Directory.CreateDirectory(IOHelper.MapPath(InstalledPackagesStorage) + IOHelper.DirSepChar + "__testFolder__"); - - System.IO.Directory.Delete(IOHelper.MapPath(PackagesStorage) + IOHelper.DirSepChar + "__testFolder__", true); - System.IO.Directory.Delete(IOHelper.MapPath(InstalledPackagesStorage) + IOHelper.DirSepChar + "__testFolder__", true); - - hasAccess = true; - } - finally - { - if (sw1 != null) - sw1.Close(); - if (sw2 != null) - sw2.Close(); - } - - return hasAccess; - } - - - } - -} diff --git a/src/Umbraco.Web/_Legacy/Packager/data.cs b/src/Umbraco.Web/_Legacy/Packager/data.cs deleted file mode 100644 index 962b7d5ed4..0000000000 --- a/src/Umbraco.Web/_Legacy/Packager/data.cs +++ /dev/null @@ -1,386 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Xml; -using Umbraco.Core; -using Umbraco.Core.Composing; -using Umbraco.Core.Configuration; -using Umbraco.Core.IO; -using Umbraco.Core.Logging; -using Umbraco.Core.Xml; - -namespace Umbraco.Web._Legacy.Packager -{ - /// - /// This is the xml data for installed packages. This is not the same xml as a pckage format! - /// - public class data - { - public static XmlDocument Source { get; private set; } - - public static void Reload(string dataSource) - { - //do some error checking and create the folders/files if they don't exist - if (!File.Exists(dataSource)) - { - if (!Directory.Exists(IOHelper.MapPath(Settings.PackagerRoot))) - { - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagerRoot)); - } - if (!Directory.Exists(IOHelper.MapPath(Settings.PackagesStorage))) - { - Directory.CreateDirectory(IOHelper.MapPath(Settings.PackagesStorage)); - } - if (!Directory.Exists(IOHelper.MapPath(Settings.InstalledPackagesStorage))) - { - Directory.CreateDirectory(IOHelper.MapPath(Settings.InstalledPackagesStorage)); - } - - using (StreamWriter sw = File.CreateText(dataSource)) - { - sw.Write($@"{Environment.NewLine}{Environment.NewLine}"); - sw.Flush(); - } - - } - - if (Source == null) - { - Source = new XmlDocument(); - } - - //error checking here - if (File.Exists(dataSource)) - { - var isEmpty = false; - using (var sr = new StreamReader(dataSource)) - { - if (sr.ReadToEnd().IsNullOrWhiteSpace()) - { - isEmpty = true; - } - } - if (isEmpty) - { - File.WriteAllText(dataSource, @""); - } - } - - Source.Load(dataSource); - } - - public static XmlNode GetFromId(int Id, string dataSource, bool reload) - { - if (reload) - Reload(dataSource); - - return Source.SelectSingleNode("/packages/package [@id = '" + Id.ToString().ToUpper() + "']"); - } - - public static XmlNode GetFromGuid(string guid, string dataSource, bool reload) - { - if (reload) - Reload(dataSource); - - return Source.SelectSingleNode("/packages/package [@packageGuid = '" + guid + "']"); - } - - public static PackageInstance.PackageInstance MakeNew(string Name, string dataSource) - { - Reload(dataSource); - - int maxId = 1; - // Find max id - foreach (XmlNode n in Source.SelectNodes("packages/package")) - { - if (int.Parse(n.Attributes.GetNamedItem("id").Value) >= maxId) - maxId = int.Parse(n.Attributes.GetNamedItem("id").Value) + 1; - } - - XmlElement instance = Source.CreateElement("package"); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "id", maxId.ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "version", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "name", Name)); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "folder", Guid.NewGuid().ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packagepath", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "repositoryGuid", "")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "iconUrl", "")); - //set to current version - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "umbVersion", UmbracoVersion.Current.ToString(3))); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "packageGuid", Guid.NewGuid().ToString())); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "hasUpdate", "false")); - - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "enableSkins", "false")); - instance.Attributes.Append(XmlHelper.AddAttribute(Source, "skinRepoGuid", "")); - - XmlElement license = Source.CreateElement("license"); - license.InnerText = "MIT License"; - license.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "http://opensource.org/licenses/MIT")); - instance.AppendChild(license); - - XmlElement author = Source.CreateElement("author"); - author.InnerText = ""; - author.Attributes.Append(XmlHelper.AddAttribute(Source, "url", "")); - instance.AppendChild(author); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "readme", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "actions", "")); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "datatypes", "")); - - XmlElement content = Source.CreateElement("content"); - content.InnerText = ""; - content.Attributes.Append(XmlHelper.AddAttribute(Source, "nodeId", "")); - content.Attributes.Append(XmlHelper.AddAttribute(Source, "loadChildNodes", "false")); - instance.AppendChild(content); - - instance.AppendChild(XmlHelper.AddTextNode(Source, "templates", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "stylesheets", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "documenttypes", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "macros", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "files", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "languages", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "dictionaryitems", "")); - instance.AppendChild(XmlHelper.AddTextNode(Source, "loadcontrol", "")); - - Source.SelectSingleNode("packages").AppendChild(instance); - Source.Save(dataSource); - var retVal = data.Package(maxId, dataSource); - - return retVal; - } - - public static PackageInstance.PackageInstance Package(int id, string datasource) - { - return ConvertXmlToPackage(GetFromId(id, datasource, true)); - } - - public static PackageInstance.PackageInstance Package(string guid, string datasource) - { - XmlNode node = GetFromGuid(guid, datasource, true); - if (node != null) - return ConvertXmlToPackage(node); - else - return new PackageInstance.PackageInstance(); - } - - public static List GetAllPackages(string dataSource) - { - Reload(dataSource); - XmlNodeList nList = data.Source.SelectNodes("packages/package"); - - List retVal = new List(); - - for (int i = 0; i < nList.Count; i++) - { - try - { - retVal.Add(ConvertXmlToPackage(nList[i])); - } - catch (Exception ex) - { - Current.Logger.Error(ex, "An error occurred in GetAllPackages"); - } - } - - return retVal; - } - - private static PackageInstance.PackageInstance ConvertXmlToPackage(XmlNode n) - { - PackageInstance.PackageInstance retVal = new PackageInstance.PackageInstance(); - - if (n != null) - { - retVal.Id = int.Parse(SafeAttribute("id", n)); - retVal.Name = SafeAttribute("name", n); - retVal.Folder = SafeAttribute("folder", n); - retVal.PackagePath = SafeAttribute("packagepath", n); - retVal.Version = SafeAttribute("version", n); - retVal.Url = SafeAttribute("url", n); - retVal.RepositoryGuid = SafeAttribute("repositoryGuid", n); - retVal.PackageGuid = SafeAttribute("packageGuid", n); - retVal.HasUpdate = bool.Parse(SafeAttribute("hasUpdate", n)); - - retVal.IconUrl = SafeAttribute("iconUrl", n); - var umbVersion = SafeAttribute("umbVersion", n); - Version parsedVersion; - if (umbVersion.IsNullOrWhiteSpace() == false && Version.TryParse(umbVersion, out parsedVersion)) - { - retVal.UmbracoVersion = parsedVersion; - } - - retVal.License = SafeNodeValue(n.SelectSingleNode("license")); - retVal.LicenseUrl = n.SelectSingleNode("license").Attributes.GetNamedItem("url").Value; - - retVal.Author = SafeNodeValue(n.SelectSingleNode("author")); - retVal.AuthorUrl = SafeAttribute("url", n.SelectSingleNode("author")); - - retVal.Readme = SafeNodeValue(n.SelectSingleNode("readme")); - retVal.Actions = SafeNodeInnerXml(n.SelectSingleNode("actions")); - - retVal.ContentNodeId = SafeAttribute("nodeId", n.SelectSingleNode("content")); - retVal.ContentLoadChildNodes = bool.Parse(SafeAttribute("loadChildNodes", n.SelectSingleNode("content"))); - - retVal.Macros = new List(SafeNodeValue(n.SelectSingleNode("macros")).Trim(',').Split(',')); - retVal.Macros = new List(SafeNodeValue(n.SelectSingleNode("macros")).Trim(',').Split(',')); - retVal.Templates = new List(SafeNodeValue(n.SelectSingleNode("templates")).Trim(',').Split(',')); - retVal.Stylesheets = new List(SafeNodeValue(n.SelectSingleNode("stylesheets")).Trim(',').Split(',')); - retVal.Documenttypes = new List(SafeNodeValue(n.SelectSingleNode("documenttypes")).Trim(',').Split(',')); - retVal.Languages = new List(SafeNodeValue(n.SelectSingleNode("languages")).Trim(',').Split(',')); - retVal.DictionaryItems = new List(SafeNodeValue(n.SelectSingleNode("dictionaryitems")).Trim(',').Split(',')); - retVal.DataTypes = new List(SafeNodeValue(n.SelectSingleNode("datatypes")).Trim(',').Split(',')); - - XmlNodeList xmlFiles = n.SelectNodes("files/file"); - retVal.Files = new List(); - - for (int i = 0; i < xmlFiles.Count; i++) - retVal.Files.Add(xmlFiles[i].InnerText); - - retVal.LoadControl = SafeNodeValue(n.SelectSingleNode("loadcontrol")); - } - - return retVal; - } - - public static void Delete(int Id, string dataSource) - { - Reload(dataSource); - // Remove physical xml file if any - //PackageInstance p = new PackageInstance(Id); - - //TODO DELETE PACKAGE FOLDER... - //p.Folder - - XmlNode n = data.GetFromId(Id, dataSource, true); - if (n != null) - { - data.Source.SelectSingleNode("/packages").RemoveChild(n); - data.Source.Save(dataSource); - } - - } - - - public static void Save(PackageInstance.PackageInstance package, string dataSource) - { - Reload(dataSource); - var xmlDef = GetFromId(package.Id, dataSource, false); - XmlHelper.SetAttribute(Source, xmlDef, "name", package.Name); - XmlHelper.SetAttribute(Source, xmlDef, "version", package.Version); - XmlHelper.SetAttribute(Source, xmlDef, "url", package.Url); - XmlHelper.SetAttribute(Source, xmlDef, "packagepath", package.PackagePath); - XmlHelper.SetAttribute(Source, xmlDef, "repositoryGuid", package.RepositoryGuid); - XmlHelper.SetAttribute(Source, xmlDef, "packageGuid", package.PackageGuid); - XmlHelper.SetAttribute(Source, xmlDef, "hasUpdate", package.HasUpdate.ToString()); - XmlHelper.SetAttribute(Source, xmlDef, "iconUrl", package.IconUrl); - if (package.UmbracoVersion != null) - XmlHelper.SetAttribute(Source, xmlDef, "umbVersion", package.UmbracoVersion.ToString(3)); - - var licenseNode = xmlDef.SelectSingleNode("license"); - if (licenseNode == null) - { - licenseNode = Source.CreateElement("license"); - xmlDef.AppendChild(licenseNode); - } - licenseNode.InnerText = package.License; - XmlHelper.SetAttribute(Source, licenseNode, "url", package.LicenseUrl); - - var authorNode = xmlDef.SelectSingleNode("author"); - if (authorNode == null) - { - authorNode = Source.CreateElement("author"); - xmlDef.AppendChild(authorNode); - } - authorNode.InnerText = package.Author; - XmlHelper.SetAttribute(Source, authorNode, "url", package.AuthorUrl); - - XmlHelper.SetCDataNode(Source, xmlDef, "readme", package.Readme); - XmlHelper.SetInnerXmlNode(Source, xmlDef, "actions", package.Actions); - - var contentNode = xmlDef.SelectSingleNode("content"); - if (contentNode == null) - { - contentNode = Source.CreateElement("content"); - xmlDef.AppendChild(contentNode); - } - XmlHelper.SetAttribute(Source, contentNode, "nodeId", package.ContentNodeId); - XmlHelper.SetAttribute(Source, contentNode, "loadChildNodes", package.ContentLoadChildNodes.ToString()); - - XmlHelper.SetTextNode(Source, xmlDef, "macros", JoinList(package.Macros, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "templates", JoinList(package.Templates, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "stylesheets", JoinList(package.Stylesheets, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "documenttypes", JoinList(package.Documenttypes, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "languages", JoinList(package.Languages, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "dictionaryitems", JoinList(package.DictionaryItems, ',')); - XmlHelper.SetTextNode(Source, xmlDef, "datatypes", JoinList(package.DataTypes, ',')); - - var filesNode = xmlDef.SelectSingleNode("files"); - if (filesNode == null) - { - filesNode = Source.CreateElement("files"); - xmlDef.AppendChild(filesNode); - } - filesNode.InnerXml = ""; - - foreach (var fileStr in package.Files) - { - if (string.IsNullOrWhiteSpace(fileStr) == false) - filesNode.AppendChild(XmlHelper.AddTextNode(Source, "file", fileStr)); - } - - XmlHelper.SetTextNode(Source, xmlDef, "loadcontrol", package.LoadControl); - - Source.Save(dataSource); - } - - - - private static string SafeAttribute(string name, XmlNode n) - { - return n.Attributes == null || n.Attributes[name] == null ? string.Empty : n.Attributes[name].Value; - } - - private static string SafeNodeValue(XmlNode n) - { - try - { - return XmlHelper.GetNodeValue(n); - } - catch - { - return string.Empty; - } - } - - private static string SafeNodeInnerXml(XmlNode n) - { - try - { - return n.InnerXml; - } - catch - { - return string.Empty; - } - } - - - private static string JoinList(List list, char seperator) - { - string retVal = ""; - foreach (string str in list) - { - retVal += str + seperator; - } - - return retVal.Trim(seperator); - } - - public data() - { - - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs deleted file mode 100644 index 018c49a249..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/controls/ContentPicker.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using Umbraco.Web._Legacy.Controls; -using Umbraco.Core; -using Umbraco.Core.Services; -using Umbraco.Web; -using Umbraco.Web.Composing; - -namespace umbraco.controls -{ - - public class ContentPicker : BaseTreePicker - { - - public ContentPicker() - { - AppAlias = Constants.Applications.Content; - TreeAlias = "content"; - } - - - public string AppAlias { get; set; } - public string TreeAlias { get; set; } - - public override string TreePickerUrl - { - get - { - return AppAlias; - } - } - - public override string ModalWindowTitle - { - get - { - return Current.Services.TextService.Localize("general/choose") + " " + Current.Services.TextService.Localize("sections/" + TreeAlias.ToLower()); - } - } - - protected override string GetItemTitle() - { - string tempTitle = ""; - try - { - if (Value != "" && Value != "-1") - { - //tempTitle = new cms.businesslogic.CMSNode(int.Parse(Value)).Text; - tempTitle = Current.Services.EntityService.Get(int.Parse(Value)).Name; - } - else - { - tempTitle = (!string.IsNullOrEmpty(TreeAlias) ? Current.Services.TextService.Localize(TreeAlias) : Current.Services.TextService.Localize(AppAlias)); - - } - } - catch { } - return tempTitle; - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs deleted file mode 100644 index 47980f2808..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/create/CreatedPackageTasks.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Umbraco.Core.Logging; -using Umbraco.Web.UI; -using Umbraco.Core; -using Umbraco.Web; -using Umbraco.Web.Composing; -using Umbraco.Web._Legacy.Packager.PackageInstance; -using Umbraco.Web._Legacy.UI; - -namespace Umbraco.Web -{ - public class CreatedPackageTasks : LegacyDialogTask - { - - public override bool PerformSave() - { - Current.Logger.Info("Xml save started"); - int id = CreatedPackage.MakeNew(Alias).Data.Id; - _returnUrl = string.Format("developer/packages/editPackage.aspx?id={0}", id); - return true; - } - - public override bool PerformDelete() - { - // we need to grab the id from the alias as the new tree needs to prefix the NodeID with "package_" - if (ParentID == 0) - { - ParentID = int.Parse(Alias.Substring("package_".Length)); - } - CreatedPackage.GetById(ParentID).Delete(); - return true; - } - - private string _returnUrl = ""; - - public override string ReturnUrl - { - get { return _returnUrl; } - } - - public override string AssignedApp - { - get { return Constants.Applications.Packages.ToString(); } - } - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx deleted file mode 100644 index d7b71ecde1..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx +++ /dev/null @@ -1,232 +0,0 @@ -<%@ Page Language="C#" ValidateRequest="false" AutoEventWireup="true" MasterPageFile="../../masterpages/umbracoPage.Master" - Title="Package and export content" CodeBehind="editPackage.aspx.cs" Inherits="umbraco.presentation.developer.packages._Default" %> - -<%@ Register TagPrefix="cc2" Namespace="Umbraco.Web._Legacy.Controls" Assembly="Umbraco.Web" %> - - - - - - - - - * - - - - * - - - - * - - - - - - - - - - - - - - * - Invalid version number (eg. 7.5.0) - - - - - - - * - - - - * - - - - - - - * - - - - * - - - - - - - - - - -
- - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Remember: .xslt and .ascx files for your macros - will be added automaticly, but you will still need to add assemblies, - images and script files manually to the list below. -
-
- - - - - - - - - - - - - - - - - -
- Absolute path to file (ie: /bin/umbraco.bin) - -
- - - -
- - - - - - -
-
- - - - - - - - -
- Load control after installation (ex: /usercontrols/installer.ascx) -
- - - - -
-
- - - - - - - - - - - -
-

- Here you can add custom installer / uninstaller events to perform certain tasks - during installation and uninstallation. -
- All actions are formed as a xml node, containing data for the action to be performed. - Package actions documentation -

- -
- Actions: -
- -
-
- -
diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs deleted file mode 100644 index 645aa088f2..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.cs +++ /dev/null @@ -1,451 +0,0 @@ -using Umbraco.Core.Services; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Web.UI; -using System.Web.UI.WebControls; - -using System.Xml; -using umbraco.controls; -using Umbraco.Core; -using Umbraco.Core.IO; -using Umbraco.Web.UI; -using Umbraco.Web.UI.Pages; -using Umbraco.Web._Legacy.Packager.PackageInstance; - -namespace umbraco.presentation.developer.packages -{ - public partial class _Default : UmbracoEnsuredPage - { - - public _Default() - { - CurrentApp = Constants.Applications.Packages.ToString(); - - } - public Umbraco.Web._Legacy.Controls.TabPage packageInfo; - public Umbraco.Web._Legacy.Controls.TabPage packageContents; - public Umbraco.Web._Legacy.Controls.TabPage packageFiles; - public Umbraco.Web._Legacy.Controls.TabPage packageOutput; - public Umbraco.Web._Legacy.Controls.TabPage packageAbout; - public Umbraco.Web._Legacy.Controls.TabPage packageActions; - - protected ContentPicker cp; - private PackageInstance pack; - private CreatedPackage createdPackage; - - protected void Page_Load(object sender, EventArgs e) - { - if (Request.QueryString["id"] != null) - { - createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - pack = createdPackage.Data; - - /* CONTENT */ - - cp = new ContentPicker(); - content.Controls.Add(cp); - - if (string.IsNullOrEmpty(pack.PackagePath) == false) - { - packageUmbFile.Text = "   Download"; - } - else - { - packageUmbFile.Text = "This package is not published"; - } - - if (Page.IsPostBack == false) - { - ClientTools - .SyncTree("-1,created," + createdPackage.Data.Id, false); - - packageAuthorName.Text = pack.Author; - packageAuthorUrl.Text = pack.AuthorUrl; - packageLicenseName.Text = pack.License; - packageLicenseUrl.Text = pack.LicenseUrl; - packageName.Text = pack.Name; - packageReadme.Text = pack.Readme; - packageVersion.Text = pack.Version; - packageUrl.Text = pack.Url; - iconUrl.Text = pack.IconUrl; - umbracoVersion.Text = pack.UmbracoVersion != null ? pack.UmbracoVersion.ToString(3) : string.Empty; - - /*ACTIONS XML*/ - tb_actions.Text = pack.Actions; - - cp.Value = pack.ContentNodeId.ToString(); - - //startNode.Value = pack.ContentNodeId.ToString(); - - packageContentSubdirs.Checked = pack.ContentLoadChildNodes; - - - /*TEMPLATES */ - var nTemplates = Services.FileService.GetTemplates(); - //Template[] umbTemplates = Template.GetAllAsList().ToArray(); - foreach (var tmp in nTemplates) - { - ListItem li = new ListItem(tmp.Name, tmp.Id.ToString()); - - if (pack.Templates.Contains(tmp.Id.ToString())) - li.Selected = true; - - templates.Items.Add(li); - } - - /* DOC TYPES */ - // fixme - media types? member types? - var nContentTypes = Services.ContentTypeService.GetAll(); - //DocumentType[] docs = DocumentType.GetAllAsList().ToArray(); - foreach (var dc in nContentTypes) - { - ListItem li = new ListItem(dc.Name, dc.Id.ToString()); - if (pack.Documenttypes.Contains(dc.Id.ToString())) - li.Selected = true; - - documentTypes.Items.Add(li); - } - - /*Stylesheets */ - var sheets = Services.FileService.GetStylesheets(); - foreach (var st in sheets) - { - if (string.IsNullOrEmpty(st.Name) == false) - { - var li = new ListItem(st.Alias, st.Name); - if (pack.Stylesheets.Contains(st.Name)) - li.Selected = true; - stylesheets.Items.Add(li); - } - } - - /* MACROS */ - var nMacros = Services.MacroService.GetAll(); - //Macro[] umbMacros = Macro.GetAll(); - foreach (var m in nMacros) - { - ListItem li = new ListItem(m.Name, m.Id.ToString()); - if (pack.Macros.Contains(m.Id.ToString())) - li.Selected = true; - - macros.Items.Add(li); - } - - /*Langauges */ - var nLanguages = Services.LocalizationService.GetAllLanguages(); - //Language[] umbLanguages = Language.getAll; - foreach (var l in nLanguages) - { - ListItem li = new ListItem(l.CultureName, l.Id.ToString()); - if (pack.Languages.Contains(l.Id.ToString())) - li.Selected = true; - - languages.Items.Add(li); - } - - /*Dictionary Items*/ - var umbDictionary = Services.LocalizationService.GetRootDictionaryItems(); - foreach (var d in umbDictionary) - { - - string liName = d.ItemKey; - var children = Services.LocalizationService.GetDictionaryItemChildren(d.Key); - if (children.Any()) - liName += " (Including all child items)"; - - var li = new ListItem(liName, d.Id.ToString()); - - if (pack.DictionaryItems.Contains(d.Id.ToString())) - li.Selected = true; - - dictionary.Items.Add(li); - } - - //TODO: Fix this with the new services and apis! and then remove since this should all be in angular - - ///*Data types */ - //cms.businesslogic.datatype.DataTypeDefinition[] umbDataType = cms.businesslogic.datatype.DataTypeDefinition.GetAll(); - - // sort array by name - //Array.Sort(umbDataType, delegate(cms.businesslogic.datatype.DataTypeDefinition umbDataType1, cms.businesslogic.datatype.DataTypeDefinition umbDataType2) - //{ - // return umbDataType1.Text.CompareTo(umbDataType2.Text); - //}); - - //foreach (cms.businesslogic.datatype.DataTypeDefinition umbDtd in umbDataType) - //{ - - // ListItem li = new ListItem(umbDtd.Text, umbDtd.Id.ToString()); - - // if (pack.DataTypes.Contains(umbDtd.Id.ToString())) - // li.Selected = true; - - // cbl_datatypes.Items.Add(li); - //} - - /* FILES */ - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - - packageControlPath.Text = pack.LoadControl; - } - else - { - ClientTools - .SyncTree("-1,created," + createdPackage.Data.Id, true); - } - } - } - - protected void validateActions(object sender, ServerValidateEventArgs e) - { - string actions = tb_actions.Text; - if (!string.IsNullOrEmpty(actions)) - { - - actions = "" + actions + ""; - - try - { - //we try to load an xml document with the potential malformed xml to ensure that this is actual action xml... - XmlDocument xd = new XmlDocument(); - xd.LoadXml(actions); - e.IsValid = true; - } - catch - { - e.IsValid = false; - } - } - else - e.IsValid = true; - } - - protected void saveOrPublish(object sender, CommandEventArgs e) - { - - if (!Page.IsValid) - { - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Saved failed.", "Some fields have not been filled-out correctly"); - } - else - { - if (e.CommandName == "save") - SavePackage(true); - - if (e.CommandName == "publish") - { - SavePackage(false); - int packageID = int.Parse(Request.QueryString["id"]); - //string packFileName = cms.businesslogic.packager. Publish.publishPackage(packageID); - - createdPackage.Publish(); - - - if (!string.IsNullOrEmpty(pack.PackagePath)) - { - - packageUmbFile.Text = "   Download"; - - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Success, "Package saved and published", ""); - } - else - { - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Error, "Save failed", "check your umbraco log."); - } - } - } - } - - - private void SavePackage(bool showNotification) - { - pack.Author = packageAuthorName.Text; - pack.AuthorUrl = packageAuthorUrl.Text; - - pack.License = packageLicenseName.Text; - pack.LicenseUrl = packageLicenseUrl.Text; - - pack.Readme = packageReadme.Text; - pack.Actions = tb_actions.Text; - - pack.Name = packageName.Text; - pack.Url = packageUrl.Text; - pack.Version = packageVersion.Text; - pack.IconUrl = iconUrl.Text; - pack.UmbracoVersion = Version.Parse(umbracoVersion.Text); - - pack.ContentLoadChildNodes = packageContentSubdirs.Checked; - - if (string.IsNullOrEmpty(cp.Value) == false) - pack.ContentNodeId = cp.Value; - else - pack.ContentNodeId = ""; - - - string tmpStylesheets = ""; - foreach (ListItem li in stylesheets.Items) - { - if (li.Selected) - tmpStylesheets += li.Value + ","; - } - pack.Stylesheets = new List(tmpStylesheets.Trim(',').Split(',')); - - - string tmpDoctypes = ""; - foreach (ListItem li in documentTypes.Items) - { - if (li.Selected) - tmpDoctypes += li.Value + ","; - } - pack.Documenttypes = new List(tmpDoctypes.Trim(',').Split(',')); - - - string tmpMacros = ""; - foreach (ListItem li in macros.Items) - { - if (li.Selected) - tmpMacros += li.Value + ","; - } - pack.Macros = new List(tmpMacros.Trim(',').Split(',')); - - - string tmpLanguages = ""; - foreach (ListItem li in languages.Items) - { - if (li.Selected) - tmpLanguages += li.Value + ","; - } - pack.Languages = new List(tmpLanguages.Trim(',').Split(',')); - - string tmpDictionaries = ""; - foreach (ListItem li in dictionary.Items) - { - if (li.Selected) - tmpDictionaries += li.Value + ","; - } - pack.DictionaryItems = new List(tmpDictionaries.Trim(',').Split(',')); - - - string tmpTemplates = ""; - foreach (ListItem li in templates.Items) - { - if (li.Selected) - tmpTemplates += li.Value + ","; - } - pack.Templates = new List(tmpTemplates.Trim(',').Split(',')); - - string tmpDataTypes = ""; - foreach (ListItem li in cbl_datatypes.Items) - { - if (li.Selected) - tmpDataTypes += li.Value + ","; - } - pack.DataTypes = new List(tmpDataTypes.Trim(',').Split(',')); - - pack.LoadControl = packageControlPath.Text; - - - createdPackage.Save(); - - if (showNotification) - this.ClientTools.ShowSpeechBubble(SpeechBubbleIcon.Save, "Package Saved", ""); - } - - protected void addFileToPackage(object sender, EventArgs e) - { - string newPath = packageFilePathNew.Text; - - if (newPath.Trim() != "") - { - CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageInstance pack = createdPackage.Data; - - pack.Files.Add(newPath); - - createdPackage.Save(); - - packageFilePathNew.Text = ""; - - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - } - } - - protected void deleteFileFromPackage(object sender, EventArgs e) - { - TextBox filePathControl = (TextBox)((Control)sender).Parent.FindControl("packageFilePath"); - filePathControl.Text = ""; - - string tmpFilePathString = ""; - foreach (RepeaterItem rItem in packageFilesRepeater.Items) - { - string tmpFFFF = ((TextBox)rItem.FindControl("packageFilePath")).Text; - if (tmpFFFF.Trim() != "") - tmpFilePathString += tmpFFFF + "�"; - } - - CreatedPackage createdPackage = CreatedPackage.GetById(int.Parse(Request.QueryString["id"])); - PackageInstance pack = createdPackage.Data; - - pack.Files = new List(tmpFilePathString.Trim('�').Split('�')); - pack.Files.TrimExcess(); - - createdPackage.Save(); - - packageFilesRepeater.DataSource = pack.Files; - packageFilesRepeater.DataBind(); - } - - protected override void OnInit(EventArgs e) - { - // Tab setup - packageInfo = TabView1.NewTabPage("Package Properties"); - packageInfo.Controls.Add(Pane1); - packageInfo.Controls.Add(Pane5); - packageInfo.Controls.Add(Pane1_1); - packageInfo.Controls.Add(Pane1_2); - packageInfo.Controls.Add(Pane1_3); - - - packageContents = TabView1.NewTabPage("Package Contents"); - packageContents.Controls.Add(Pane2); - packageContents.Controls.Add(Pane2_1); - packageContents.Controls.Add(Pane2_2); - packageContents.Controls.Add(Pane2_3); - packageContents.Controls.Add(Pane2_4); - packageContents.Controls.Add(Pane2_5); - packageContents.Controls.Add(Pane2_6); - packageContents.Controls.Add(Pane2_7); - - packageFiles = TabView1.NewTabPage("Package Files"); - packageFiles.Controls.Add(Pane3); - packageFiles.Controls.Add(Pane3_1); - packageFiles.Controls.Add(Pane3_2); - - packageActions = TabView1.NewTabPage("Package Actions"); - packageActions.Controls.Add(Pane4); - - //var pubs = TabView1.Menu.NewButton(); - //pubs.Text = Services.TextService.Localize("publish"); - //pubs.CommandName = "publish"; - //pubs.Command += new CommandEventHandler(saveOrPublish); - //pubs.ID = "saveAndPublish"; - - //var saves = TabView1.Menu.NewButton(); - //saves.Text = Services.TextService.Localize("save"); - //saves.CommandName = "save"; - //saves.Command += new CommandEventHandler(saveOrPublish); - //saves.ButtonType = Umbraco.Web._Legacy.Controls.MenuButtonType.Primary; - //saves.ID = "save"; - - - - - base.OnInit(e); - } - - } -} diff --git a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs b/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs deleted file mode 100644 index d41967444a..0000000000 --- a/src/Umbraco.Web/umbraco.presentation/umbraco/developer/Packages/editPackage.aspx.designer.cs +++ /dev/null @@ -1,609 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace umbraco.presentation.developer.packages { - - - public partial class _Default { - - /// - /// TabView1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.TabView TabView1; - - /// - /// Pane1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1; - - /// - /// pp_name control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_name; - - /// - /// packageName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageName; - - /// - /// RequiredFieldValidator0 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator0; - - /// - /// pp_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_url; - - /// - /// packageUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageUrl; - - /// - /// RequiredFieldValidator1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator1; - - /// - /// pp_version control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_version; - - /// - /// packageVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageVersion; - - /// - /// RequiredFieldValidator2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator2; - - /// - /// pp_icon control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_icon; - - /// - /// iconUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox iconUrl; - - /// - /// pp_file control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_file; - - /// - /// packageUmbFile control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Literal packageUmbFile; - - /// - /// Pane5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane5; - - /// - /// pp_umbracoVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_umbracoVersion; - - /// - /// umbracoVersion control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox umbracoVersion; - - /// - /// RequiredFieldValidator7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator7; - - /// - /// VersionValidator control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RegularExpressionValidator VersionValidator; - - /// - /// Pane1_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_1; - - /// - /// pp_author control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_author; - - /// - /// packageAuthorName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageAuthorName; - - /// - /// RequiredFieldValidator3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator3; - - /// - /// pp_author_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_author_url; - - /// - /// packageAuthorUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageAuthorUrl; - - /// - /// RequiredFieldValidator4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator4; - - /// - /// Pane1_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_2; - - /// - /// pp_licens control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_licens; - - /// - /// packageLicenseName control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageLicenseName; - - /// - /// RequiredFieldValidator5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator5; - - /// - /// pp_license_url control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_license_url; - - /// - /// packageLicenseUrl control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageLicenseUrl; - - /// - /// RequiredFieldValidator6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.RequiredFieldValidator RequiredFieldValidator6; - - /// - /// Pane1_3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane1_3; - - /// - /// pp_readme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_readme; - - /// - /// packageReadme control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageReadme; - - /// - /// Pane2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2; - - /// - /// pp_content control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.PropertyPanel pp_content; - - /// - /// content control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.PlaceHolder content; - - /// - /// packageContentSubdirs control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBox packageContentSubdirs; - - /// - /// packageContentSubdirsLabel control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Label packageContentSubdirsLabel; - - /// - /// Pane2_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_1; - - /// - /// documentTypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList documentTypes; - - /// - /// Pane2_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_2; - - /// - /// templates control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList templates; - - /// - /// Pane2_3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_3; - - /// - /// stylesheets control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList stylesheets; - - /// - /// Pane2_4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_4; - - /// - /// macros control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList macros; - - /// - /// Pane2_5 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_5; - - /// - /// languages control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList languages; - - /// - /// Pane2_6 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_6; - - /// - /// dictionary control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList dictionary; - - /// - /// Pane2_7 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane2_7; - - /// - /// cbl_datatypes control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CheckBoxList cbl_datatypes; - - /// - /// Pane3 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3; - - /// - /// Pane3_1 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3_1; - - /// - /// packageFilesRepeater control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Repeater packageFilesRepeater; - - /// - /// packageFilePathNew control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageFilePathNew; - - /// - /// createNewFilePath control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.Button createNewFilePath; - - /// - /// Pane3_2 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane3_2; - - /// - /// packageControlPath control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox packageControlPath; - - /// - /// Pane4 control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::Umbraco.Web._Legacy.Controls.Pane Pane4; - - /// - /// actionsVal control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.CustomValidator actionsVal; - - /// - /// tb_actions control. - /// - /// - /// Auto-generated field. - /// To modify move field declaration from designer file to code-behind file. - /// - protected global::System.Web.UI.WebControls.TextBox tb_actions; - } -}