From ab9c9df7d613f617c7b73e2b0b72690aed1f9b3c Mon Sep 17 00:00:00 2001 From: Sebastiaan Janssen Date: Tue, 27 Nov 2012 13:27:33 -0100 Subject: [PATCH] Reverting last minute hacks around the pluginmanager and adding Shannon's fixes from changeset 85f9e5879e60 --- src/Umbraco.Core/DisposableTimer.cs | 231 +-- src/Umbraco.Core/PluginManager.cs | 1275 +++++++++-------- src/Umbraco.Tests/PluginManagerTests.cs | 546 +++---- .../businesslogic/Packager/Installer.cs | 593 ++++---- 4 files changed, 1418 insertions(+), 1227 deletions(-) diff --git a/src/Umbraco.Core/DisposableTimer.cs b/src/Umbraco.Core/DisposableTimer.cs index 26a7875559..07af39995e 100644 --- a/src/Umbraco.Core/DisposableTimer.cs +++ b/src/Umbraco.Core/DisposableTimer.cs @@ -4,124 +4,135 @@ using Umbraco.Core.Logging; namespace Umbraco.Core { - /// - /// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a using (C#) statement. - /// - /// - /// - /// Console.WriteLine("Testing Stopwatchdisposable, should be 567:"); - // using (var timer = new DisposableTimer(result => Console.WriteLine("Took {0}ms", result))) - // { - // Thread.Sleep(567); - // } - /// - /// - public class DisposableTimer : DisposableObject - { - private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); - private readonly Action _callback; + /// + /// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a using (C#) statement. + /// + /// + /// + /// Console.WriteLine("Testing Stopwatchdisposable, should be 567:"); + // using (var timer = new DisposableTimer(result => Console.WriteLine("Took {0}ms", result))) + // { + // Thread.Sleep(567); + // } + /// + /// + public class DisposableTimer : DisposableObject + { + private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); + private readonly Action _callback; - protected DisposableTimer(Action callback) - { - _callback = callback; - } + protected DisposableTimer(Action callback) + { + _callback = callback; + } - public Stopwatch Stopwatch - { - get { return _stopwatch; } - } + public Stopwatch Stopwatch + { + get { return _stopwatch; } + } - /// - /// Starts the timer and invokes the specified callback upon disposal. - /// - /// The callback. - /// - public static DisposableTimer Start(Action callback) - { - return new DisposableTimer(callback); - } + /// + /// Starts the timer and invokes the specified callback upon disposal. + /// + /// The callback. + /// + public static DisposableTimer Start(Action callback) + { + return new DisposableTimer(callback); + } - /// - /// Adds a start and end log entry as Info and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer TraceDuration(string startMessage, string completeMessage) - { - return TraceDuration(typeof(T), startMessage, completeMessage); - } + public static DisposableTimer TraceDuration(Func startMessage, Func completeMessage) + { + return TraceDuration(typeof(T), startMessage, completeMessage); + } - /// - /// Adds a start and end log entry as Info and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage) - { - LogHelper.Info(loggerType, () => startMessage); - return new DisposableTimer(x => LogHelper.Info(loggerType, () => completeMessage + " (took " + x + "ms)")); - } + public static DisposableTimer TraceDuration(Type loggerType, Func startMessage, Func completeMessage) + { + LogHelper.Debug(loggerType, startMessage); + return new DisposableTimer(x => LogHelper.Info(loggerType, () => completeMessage() + " (took " + x + "ms)")); + } - /// - /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer DebugDuration(string startMessage, string completeMessage) - { - return DebugDuration(typeof(T), startMessage, completeMessage); - } + /// + /// Adds a start and end log entry as Info and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer TraceDuration(string startMessage, string completeMessage) + { + return TraceDuration(typeof(T), startMessage, completeMessage); + } - /// - /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer DebugDuration(Func startMessage, Func completeMessage) - { - return DebugDuration(typeof(T), startMessage, completeMessage); - } + /// + /// Adds a start and end log entry as Info and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage) + { + LogHelper.Info(loggerType, () => startMessage); + return new DisposableTimer(x => LogHelper.Info(loggerType, () => completeMessage + " (took " + x + "ms)")); + } - /// - /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage) - { - LogHelper.Debug(loggerType, () => startMessage); - return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage + " (took " + x + "ms)")); - } + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(string startMessage, string completeMessage) + { + return DebugDuration(typeof(T), startMessage, completeMessage); + } - /// - /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. - /// - /// - /// - /// - /// - public static DisposableTimer DebugDuration(Type loggerType, Func startMessage, Func completeMessage) - { - LogHelper.Debug(loggerType, startMessage); - return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage() + " (took " + x + "ms)")); - } + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(Func startMessage, Func completeMessage) + { + return DebugDuration(typeof(T), startMessage, completeMessage); + } - /// - /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic. - /// - protected override void DisposeResources() - { - _callback.Invoke(Stopwatch.ElapsedMilliseconds); - } - } + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage) + { + LogHelper.Debug(loggerType, () => startMessage); + return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage + " (took " + x + "ms)")); + } + + /// + /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. + /// + /// + /// + /// + /// + public static DisposableTimer DebugDuration(Type loggerType, Func startMessage, Func completeMessage) + { + LogHelper.Debug(loggerType, startMessage); + return new DisposableTimer(x => LogHelper.Debug(loggerType, () => completeMessage() + " (took " + x + "ms)")); + } + + /// + /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic. + /// + protected override void DisposeResources() + { + _callback.Invoke(Stopwatch.ElapsedMilliseconds); + } + } } \ No newline at end of file diff --git a/src/Umbraco.Core/PluginManager.cs b/src/Umbraco.Core/PluginManager.cs index 65cf377e5f..5b458e6a15 100644 --- a/src/Umbraco.Core/PluginManager.cs +++ b/src/Umbraco.Core/PluginManager.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -7,162 +6,169 @@ using System.Linq; using System.Reflection; using System.Text; using System.Threading; -using System.Web.Caching; using System.Web.Compilation; -using System.Xml; using System.Xml.Linq; -using Umbraco.Core.Configuration; using Umbraco.Core.IO; using Umbraco.Core.Logging; using Umbraco.Core.PropertyEditors; using umbraco.interfaces; +using File = System.IO.File; namespace Umbraco.Core { - /// - /// Used to resolve all plugin types and cache them and is also used to instantiate plugin types - /// - /// - /// - /// This class should be used to resolve all plugin types, the TypeFinder should not be used directly! - /// - /// This class can expose extension methods to resolve custom plugins - /// - /// Before this class resolves any plugins it checks if the hash has changed for the DLLs in the /bin folder, if it hasn't - /// it will use the cached resolved plugins that it has already found which means that no assembly scanning is necessary. This leads - /// to much faster startup times. - /// - internal class PluginManager - { - private readonly ApplicationContext _appContext; + /// + /// Used to resolve all plugin types and cache them and is also used to instantiate plugin types + /// + /// + /// + /// This class should be used to resolve all plugin types, the TypeFinder should not be used directly! + /// + /// This class can expose extension methods to resolve custom plugins + /// + /// Before this class resolves any plugins it checks if the hash has changed for the DLLs in the /bin folder, if it hasn't + /// it will use the cached resolved plugins that it has already found which means that no assembly scanning is necessary. This leads + /// to much faster startup times. + /// + internal class PluginManager + { + private readonly ApplicationContext _appContext; - /// - /// Creates a new PluginManager with an ApplicationContext instance which ensures that the plugin xml - /// file is cached temporarily until app startup completes. - /// - /// - /// - internal PluginManager(ApplicationContext appContext, bool detectBinChanges = true) - : this(detectBinChanges) - { - if (appContext == null) throw new ArgumentNullException("appContext"); - _appContext = appContext; - } + /// + /// Creates a new PluginManager with an ApplicationContext instance which ensures that the plugin xml + /// file is cached temporarily until app startup completes. + /// + /// + /// + internal PluginManager(ApplicationContext appContext, bool detectBinChanges = true) + : this(detectBinChanges) + { + if (appContext == null) throw new ArgumentNullException("appContext"); + _appContext = appContext; + } - /// - /// Creates a new PluginManager - /// - /// - /// If true will detect changes in the /bin folder and therefor load plugins from the - /// cached plugins file if one is found. If false will never use the cache file for plugins - /// - internal PluginManager(bool detectCodeChanges = true) - { - _tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); - //create the folder if it doesn't exist - if (!Directory.Exists(_tempFolder)) - { - Directory.CreateDirectory(_tempFolder); - } + /// + /// Creates a new PluginManager + /// + /// + /// If true will detect changes in the /bin folder and therefor load plugins from the + /// cached plugins file if one is found. If false will never use the cache file for plugins + /// + internal PluginManager(bool detectCodeChanges = true) + { + _tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + //create the folder if it doesn't exist + if (!Directory.Exists(_tempFolder)) + { + Directory.CreateDirectory(_tempFolder); + } - if (detectCodeChanges) - { - //first check if the cached hash is 0, if it is then we ne - //do the check if they've changed - HaveAssembliesChanged = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0; - //if they have changed, we need to write the new file - if (HaveAssembliesChanged) - { - WriteCachePluginsHash(); - } - } - else - { - //always set to true if we're not detecting (generally only for testing) - HaveAssembliesChanged = true; - } - - } + //this is a check for legacy changes, before we didn't store the TypeResolutionKind in the file which was a mistake, + //so we need to detect if the old file is there without this attribute, if it is then we delete it + if (DetectLegacyPluginListFile()) + { + var filePath = GetPluginListFilePath(); + File.Delete(filePath); + } - static PluginManager _resolver; - static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); - private readonly string _tempFolder; - private long _cachedAssembliesHash = -1; - private long _currentAssembliesHash = -1; + if (detectCodeChanges) + { + //first check if the cached hash is 0, if it is then we ne + //do the check if they've changed + HaveAssembliesChanged = (CachedAssembliesHash != CurrentAssembliesHash) || CachedAssembliesHash == 0; + //if they have changed, we need to write the new file + if (HaveAssembliesChanged) + { + WriteCachePluginsHash(); + } + } + else + { + //always set to true if we're not detecting (generally only for testing) + HaveAssembliesChanged = true; + } - /// - /// We will ensure that no matter what, only one of these is created, this is to ensure that caching always takes place - /// - /// - /// The setter is generally only used for unit tests - /// - internal static PluginManager Current - { - get - { - using (var l = new UpgradeableReadLock(Lock)) - { - if (_resolver == null) - { - l.UpgradeToWriteLock(); - _resolver = ApplicationContext.Current == null - ? new PluginManager() - : new PluginManager(ApplicationContext.Current); - } - return _resolver; - } - } - set { _resolver = value; } - } + } - #region Hash checking methods + static PluginManager _resolver; + static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(); + private readonly string _tempFolder; + private long _cachedAssembliesHash = -1; + private long _currentAssembliesHash = -1; - /// - /// Returns a bool if the assemblies in the /bin have changed since they were last hashed. - /// - internal bool HaveAssembliesChanged { get; private set; } + /// + /// We will ensure that no matter what, only one of these is created, this is to ensure that caching always takes place + /// + /// + /// The setter is generally only used for unit tests + /// + internal static PluginManager Current + { + get + { + using (var l = new UpgradeableReadLock(Lock)) + { + if (_resolver == null) + { + l.UpgradeToWriteLock(); + _resolver = ApplicationContext.Current == null + ? new PluginManager() + : new PluginManager(ApplicationContext.Current); + } + return _resolver; + } + } + set { _resolver = value; } + } - /// - /// Returns the currently cached hash value of the scanned assemblies in the /bin folder. Returns 0 - /// if no cache is found. - /// - /// - internal long CachedAssembliesHash - { - get - { - if (_cachedAssembliesHash != -1) - return _cachedAssembliesHash; + #region Hash checking methods - var filePath = Path.Combine(_tempFolder, "umbraco-plugins.hash"); - if (!File.Exists(filePath)) - return 0; - var hash = File.ReadAllText(filePath, Encoding.UTF8); - Int64 val; - if (Int64.TryParse(hash, out val)) - { - _cachedAssembliesHash = val; - return _cachedAssembliesHash; - } - //it could not parse for some reason so we'll return 0. - return 0; - } - } - /// - /// Returns the current assemblies hash based on creating a hash from the assemblies in the /bin - /// - /// - internal long CurrentAssembliesHash - { - get - { - if (_currentAssembliesHash != -1) - return _currentAssembliesHash; + /// + /// Returns a bool if the assemblies in the /bin have changed since they were last hashed. + /// + internal bool HaveAssembliesChanged { get; private set; } - _currentAssembliesHash = GetAssembliesHash( - new FileSystemInfo[] + /// + /// Returns the currently cached hash value of the scanned assemblies in the /bin folder. Returns 0 + /// if no cache is found. + /// + /// + internal long CachedAssembliesHash + { + get + { + if (_cachedAssembliesHash != -1) + return _cachedAssembliesHash; + + var filePath = Path.Combine(_tempFolder, "umbraco-plugins.hash"); + if (!File.Exists(filePath)) + return 0; + var hash = File.ReadAllText(filePath, Encoding.UTF8); + Int64 val; + if (Int64.TryParse(hash, out val)) + { + _cachedAssembliesHash = val; + return _cachedAssembliesHash; + } + //it could not parse for some reason so we'll return 0. + return 0; + } + } + + /// + /// Returns the current assemblies hash based on creating a hash from the assemblies in the /bin + /// + /// + internal long CurrentAssembliesHash + { + get + { + if (_currentAssembliesHash != -1) + return _currentAssembliesHash; + + _currentAssembliesHash = GetAssembliesHash( + new FileSystemInfo[] { //add the bin folder and everything in it new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), @@ -171,507 +177,582 @@ namespace Umbraco.Core //add the global.asax (the app domain also monitors this, if it changes will do a full restart) new FileInfo(IOHelper.MapPath("~/global.asax")) } - ); - return _currentAssembliesHash; - } - } + ); + return _currentAssembliesHash; + } + } - /// - /// Writes the assembly hash file - /// - private void WriteCachePluginsHash() - { - var filePath = Path.Combine(_tempFolder, "umbraco-plugins.hash"); - File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8); - } + /// + /// Writes the assembly hash file + /// + private void WriteCachePluginsHash() + { + var filePath = Path.Combine(_tempFolder, "umbraco-plugins.hash"); + File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8); + } - /// - /// Returns a unique hash for the combination of FileInfo objects passed in - /// - /// - /// - internal static long GetAssembliesHash(IEnumerable filesAndFolders) - { - using (DisposableTimer.TraceDuration("Determining hash of code files on disk", "Hash determined")) - { - var hashCombiner = new HashCodeCombiner(); - //add each unique folder to the hash - foreach (var i in filesAndFolders.DistinctBy(x => x.FullName)) - { - hashCombiner.AddFileSystemItem(i); - } - return ConvertPluginsHashFromHex(hashCombiner.GetCombinedHashCode()); - } - } + /// + /// Returns a unique hash for the combination of FileInfo objects passed in + /// + /// + /// + internal static long GetAssembliesHash(IEnumerable filesAndFolders) + { + using (DisposableTimer.TraceDuration("Determining hash of code files on disk", "Hash determined")) + { + var hashCombiner = new HashCodeCombiner(); + //add each unique folder to the hash + foreach (var i in filesAndFolders.DistinctBy(x => x.FullName)) + { + hashCombiner.AddFileSystemItem(i); + } + return ConvertPluginsHashFromHex(hashCombiner.GetCombinedHashCode()); + } + } - /// - /// Converts the hash value of current plugins to long from string - /// - /// - /// - internal static long ConvertPluginsHashFromHex(string val) - { - long outVal; - if (Int64.TryParse(val, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out outVal)) - { - return outVal; - } - return 0; - } + /// + /// Converts the hash value of current plugins to long from string + /// + /// + /// + internal static long ConvertPluginsHashFromHex(string val) + { + long outVal; + if (Int64.TryParse(val, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out outVal)) + { + return outVal; + } + return 0; + } - /// - /// Attempts to resolve the list of plugin + assemblies found in the runtime for the base type 'T' passed in. - /// If the cache file doesn't exist, fails to load, is corrupt or the type 'T' element is not found then - /// a false attempt is returned. - /// - /// - /// - internal Attempt> TryGetCachedPluginsFromFile() - { - var filePath = Path.Combine(_tempFolder, "umbraco-plugins.list"); - if (!File.Exists(filePath)) - return Attempt>.False; + /// + /// Attempts to resolve the list of plugin + assemblies found in the runtime for the base type 'T' passed in. + /// If the cache file doesn't exist, fails to load, is corrupt or the type 'T' element is not found then + /// a false attempt is returned. + /// + /// + /// + internal Attempt> TryGetCachedPluginsFromFile(TypeResolutionKind resolutionType) + { + var filePath = GetPluginListFilePath(); + if (!File.Exists(filePath)) + return Attempt>.False; - try - { - //we will load the xml document, if the app context exist, we will load it from the cache (which is only around for 5 minutes) - //while the app boots up, this should save some IO time on app startup when the app context is there (which is always unless in unit tests) - XDocument xml; - if (_appContext != null) - { - xml = _appContext.ApplicationCache.GetCacheItem("umbraco-plugins.list", - new TimeSpan(0, 0, 5, 0), - () => XDocument.Load(filePath)); - } - else - { - xml = XDocument.Load(filePath); - } - - - if (xml.Root == null) - return Attempt>.False; - - var typeElement = xml.Root.Elements() - .SingleOrDefault(x => - x.Name.LocalName == "baseType" - && ((string) x.Attribute("type")) == typeof (T).FullName); - if (typeElement == null) - return Attempt>.False; - - //return success - return new Attempt>( - true, - typeElement.Elements("add") - .Select(x => (string) x.Attribute("type"))); - } - catch (Exception) - { - //if the file is corrupted, etc... return false - return Attempt>.False; - } - } - - /// - /// Adds/Updates the type list for the base type 'T' in the cached file - /// - /// - /// - /// - /// THIS METHOD IS NOT THREAD SAFE - /// - /// - /// - /// - /// - /// - /// - /// - /// ]]> - /// - internal void UpdateCachedPluginsFile(IEnumerable typesFound) - { - var filePath = Path.Combine(_tempFolder, "umbraco-plugins.list"); - XDocument xml; - try - { - xml = XDocument.Load(filePath); - } - catch - { - //if there's an exception loading then this is somehow corrupt, we'll just replace it. - File.Delete(filePath); - //create the document and the root - xml = new XDocument(new XElement("plugins")); - } - if (xml.Root == null) - { - //if for some reason there is no root, create it - xml.Add(new XElement("plugins")); - } - //find the type 'T' element to add or update - var typeElement = xml.Root.Elements() - .SingleOrDefault(x => - x.Name.LocalName == "baseType" - && ((string)x.Attribute("type")) == typeof(T).FullName); - if (typeElement == null) - { - //create the type element - typeElement = new XElement("baseType", new XAttribute("type", typeof(T).FullName)); - //then add it to the root - xml.Root.Add(typeElement); - } + try + { + //we will load the xml document, if the app context exist, we will load it from the cache (which is only around for 5 minutes) + //while the app boots up, this should save some IO time on app startup when the app context is there (which is always unless in unit tests) + XDocument xml; + if (_appContext != null) + { + xml = _appContext.ApplicationCache.GetCacheItem("umbraco-plugins.list", + new TimeSpan(0, 0, 5, 0), + () => XDocument.Load(filePath)); + } + else + { + xml = XDocument.Load(filePath); + } - //now we have the type element, we need to clear any previous types as children and add/update it with new ones - typeElement.ReplaceNodes(typesFound - .Select(x => - new XElement("add", - new XAttribute("type", x.AssemblyQualifiedName)))); - //save the xml file - xml.Save(filePath); - } + if (xml.Root == null) + return Attempt>.False; - #endregion + var typeElement = xml.Root.Elements() + .SingleOrDefault(x => + x.Name.LocalName == "baseType" + && ((string)x.Attribute("type")) == typeof(T).FullName + && ((string)x.Attribute("resolutionType")) == resolutionType.ToString()); - private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); - private readonly HashSet _types = new HashSet(); - private IEnumerable _assemblies; + //return false but specify this exception type so we can detect it + if (typeElement == null) + return new Attempt>(new CachedPluginNotFoundInFile()); - /// - /// Returns all classes attributed with XsltExtensionAttribute attribute - /// - /// - internal IEnumerable ResolveCacheRefreshers() - { - return ResolveTypes(); - } + //return success + return new Attempt>( + true, + typeElement.Elements("add") + .Select(x => (string)x.Attribute("type"))); + } + catch (Exception ex) + { + //if the file is corrupted, etc... return false + return new Attempt>(ex); + } + } - /// - /// Returns all available IPropertyEditorValueConverter - /// - /// - internal IEnumerable ResolvePropertyEditorValueConverters() - { - return ResolveTypes(); - } + private string GetPluginListFilePath() + { + return Path.Combine(_tempFolder, "umbraco-plugins.list"); + } - /// - /// Returns all available IDataType in application - /// - /// - internal IEnumerable ResolveDataTypes() - { - return ResolveTypes(); - } + /// + /// This will return true if the plugin list file is a legacy one + /// + /// + /// + /// This method exists purely due to an error in 4.11. We were writing the plugin list file without the + /// type resolution kind which will have caused some problems. Now we detect this legacy file and if it is detected + /// we remove it so it can be recreated properly. + /// + internal bool DetectLegacyPluginListFile() + { + var filePath = GetPluginListFilePath(); + if (!File.Exists(filePath)) + return false; - /// - /// Returns all available IMacroGuiRendering in application - /// - /// - internal IEnumerable ResolveMacroRenderings() - { - return ResolveTypes(); - } + try + { + var xml = XDocument.Load(filePath); + if (xml.Root == null) + return false; - /// - /// Returns all available IPackageAction in application - /// - /// - internal IEnumerable ResolvePackageActions() - { - return ResolveTypes(); - } + var typeElement = xml.Root.Elements() + .FirstOrDefault(x => x.Name.LocalName == "baseType"); - /// - /// Returns all available IAction in application - /// - /// - internal IEnumerable ResolveActions() - { - return ResolveTypes(); - } + if (typeElement == null) + return false; - /// - /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set - /// this will search all assemblies known to have plugins and exclude ones known to not have them. - /// - internal IEnumerable AssembliesToScan - { - get { return _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); } - set { _assemblies = value; } - } + //now check if the typeElement is missing the resolutionType attribute + return typeElement.Attributes().All(x => x.Name.LocalName != "resolutionType"); + } + catch (Exception) + { + //if the file is corrupted, etc... return true so it is removed + return true; + } + } - /// - /// Used to resolve and create instances of the specified type based on the resolved/cached plugin types - /// - /// - /// set to true if an exception is to be thrown if there is an error during instantiation - /// - internal IEnumerable FindAndCreateInstances(bool throwException = false) - { - var types = ResolveTypes(); - return CreateInstances(types, throwException); - } + /// + /// Adds/Updates the type list for the base type 'T' in the cached file + /// + /// + /// + /// + /// + /// THIS METHOD IS NOT THREAD SAFE + /// + /// + /// + /// + /// + /// + /// + /// + /// ]]> + /// + internal void UpdateCachedPluginsFile(IEnumerable typesFound, TypeResolutionKind resolutionType) + { + var filePath = GetPluginListFilePath(); + XDocument xml; + try + { + xml = XDocument.Load(filePath); + } + catch + { + //if there's an exception loading then this is somehow corrupt, we'll just replace it. + File.Delete(filePath); + //create the document and the root + xml = new XDocument(new XElement("plugins")); + } + if (xml.Root == null) + { + //if for some reason there is no root, create it + xml.Add(new XElement("plugins")); + } + //find the type 'T' element to add or update + var typeElement = xml.Root.Elements() + .SingleOrDefault(x => + x.Name.LocalName == "baseType" + && ((string)x.Attribute("type")) == typeof(T).FullName + && ((string)x.Attribute("resolutionType")) == resolutionType.ToString()); - /// - /// Used to create instances of the specified type based on the resolved/cached plugin types - /// - /// - /// - /// set to true if an exception is to be thrown if there is an error during instantiation - /// - internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false) - { - //Have removed logging because it doesn't really need to be logged since the time taken is generally 0ms. - //we want to know if it fails ever, not how long it took if it is only 0. + if (typeElement == null) + { + //create the type element + typeElement = new XElement("baseType", + new XAttribute("type", typeof(T).FullName), + new XAttribute("resolutionType", resolutionType.ToString())); + //then add it to the root + xml.Root.Add(typeElement); + } - var typesAsArray = types.ToArray(); - //using (DisposableTimer.DebugDuration( - // String.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName), - // String.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName))) - //{ - var instances = new List(); - foreach (var t in typesAsArray) - { - try - { - var typeInstance = (T)Activator.CreateInstance(t); - instances.Add(typeInstance); - } - catch (Exception ex) - { - LogHelper.Error(String.Format("Error creating type {0}", t.FullName), ex); + //now we have the type element, we need to clear any previous types as children and add/update it with new ones + typeElement.ReplaceNodes(typesFound.Select(x => new XElement("add", new XAttribute("type", x.AssemblyQualifiedName)))); + //save the xml file + xml.Save(filePath); + } - if (throwException) - { - throw ex; - } - } - } - return instances; - //} - } + #endregion - /// - /// Used to create an instance of the specified type based on the resolved/cached plugin types - /// - /// - /// - /// - /// - internal T CreateInstance(Type type, bool throwException = false) - { - var instances = CreateInstances(new[] { type }, throwException); - return instances.FirstOrDefault(); - } + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + private readonly HashSet _types = new HashSet(); + private IEnumerable _assemblies; - private IEnumerable ResolveTypes( - Func> finder, - TypeResolutionKind resolutionType, - bool cacheResult) - { - using (var readLock = new UpgradeableReadLock(_lock)) - { - using (DisposableTimer.TraceDuration( - String.Format("Starting resolution types of {0}", typeof(T).FullName), - String.Format("Completed resolution of types of {0}", typeof(T).FullName))) - { - //check if the TypeList already exists, if so return it, if not we'll create it - var typeList = _types.SingleOrDefault(x => x.IsTypeList(resolutionType)); - //if we're not caching the result then proceed, or if the type list doesn't exist then proceed - if (!cacheResult || typeList == null) - { - //upgrade to a write lock since we're adding to the collection - readLock.UpgradeToWriteLock(); + /// + /// Returns all classes attributed with XsltExtensionAttribute attribute + /// + /// + internal IEnumerable ResolveCacheRefreshers() + { + return ResolveTypes(); + } - typeList = new TypeList(resolutionType); + /// + /// Returns all available IPropertyEditorValueConverter + /// + /// + internal IEnumerable ResolvePropertyEditorValueConverters() + { + return ResolveTypes(); + } - //we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory). - if (!HaveAssembliesChanged) - { - var fileCacheResult = TryGetCachedPluginsFromFile(); - if (fileCacheResult.Success) - { - var successfullyLoadedFromCache = true; - //we have a previous cache for this so we don't need to scan we just load what has been found in the file - foreach(var t in fileCacheResult.Result) - { - try - { - //we use the build manager to ensure we get all types loaded, this is slightly slower than - //Type.GetType but if the types in the assembly aren't loaded yet then we have problems with that. - var type = BuildManager.GetType(t, true); - typeList.AddType(type); - } - catch (Exception ex) - { - //if there are any exceptions loading types, we have to exist, this should never happen so - //we will need to revert to scanning for types. - successfullyLoadedFromCache = false; - LogHelper.Error("Could not load a cached plugin type: " + t + " now reverting to re-scanning assemblies for the base type: " + typeof (T).FullName, ex); - break; - } - } - if (!successfullyLoadedFromCache ) - { - //we need to manually load by scanning if loading from the file was not successful. - LoadViaScanningAndUpdateCacheFile(typeList, finder); - } - else - { - LogHelper.Debug("Loaded plugin types " + typeof(T).FullName + " from persisted cache"); - } - } - } - else - { - //we don't have a cache for this so proceed to look them up by scanning - LoadViaScanningAndUpdateCacheFile(typeList, finder); - } + /// + /// Returns all available IDataType in application + /// + /// + internal IEnumerable ResolveDataTypes() + { + return ResolveTypes(); + } - //only add the cache if we are to cache the results - if (cacheResult) - { - //add the type list to the collection - _types.Add(typeList); - } - } - return typeList.GetTypes(); - } - } - } + /// + /// Returns all available IMacroGuiRendering in application + /// + /// + internal IEnumerable ResolveMacroRenderings() + { + return ResolveTypes(); + } - /// - /// This method invokes the finder which scans the assemblies for the types and then loads the result into the type finder. - /// Once the results are loaded, we update the cached type xml file - /// - /// - /// - /// - /// THIS METHODS IS NOT THREAD SAFE - /// - private void LoadViaScanningAndUpdateCacheFile(TypeList typeList, Func> finder) - { - //we don't have a cache for this so proceed to look them up by scanning - foreach (var t in finder()) - { - typeList.AddType(t); - } - UpdateCachedPluginsFile(typeList.GetTypes()); - } + /// + /// Returns all available IPackageAction in application + /// + /// + internal IEnumerable ResolvePackageActions() + { + return ResolveTypes(); + } - /// - /// Generic method to find the specified type and cache the result - /// - /// - /// - internal IEnumerable ResolveTypes(bool cacheResult = true) - { - return ResolveTypes( - () => TypeFinder.FindClassesOfType(AssembliesToScan), - TypeResolutionKind.FindAllTypes, - cacheResult); - } + /// + /// Returns all available IAction in application + /// + /// + internal IEnumerable ResolveActions() + { + return ResolveTypes(); + } - /// - /// Generic method to find the specified type that has an attribute and cache the result - /// - /// - /// - /// - internal IEnumerable ResolveTypesWithAttribute(bool cacheResult = true) - where TAttribute : Attribute - { - return ResolveTypes( - () => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan), - TypeResolutionKind.FindTypesWithAttribute, - cacheResult); - } + /// + /// Gets/sets which assemblies to scan when type finding, generally used for unit testing, if not explicitly set + /// this will search all assemblies known to have plugins and exclude ones known to not have them. + /// + internal IEnumerable AssembliesToScan + { + get { return _assemblies ?? (_assemblies = TypeFinder.GetAssembliesWithKnownExclusions()); } + set { _assemblies = value; } + } - /// - /// Generic method to find any type that has the specified attribute - /// - /// - /// - internal IEnumerable ResolveAttributedTypes(bool cacheResult = true) - where TAttribute : Attribute - { - return ResolveTypes( - () => TypeFinder.FindClassesWithAttribute(AssembliesToScan), - TypeResolutionKind.FindAttributedTypes, - cacheResult); - } + /// + /// Used to resolve and create instances of the specified type based on the resolved/cached plugin types + /// + /// + /// set to true if an exception is to be thrown if there is an error during instantiation + /// + internal IEnumerable FindAndCreateInstances(bool throwException = false) + { + var types = ResolveTypes(); + return CreateInstances(types, throwException); + } - /// - /// Used for unit tests - /// - /// - internal HashSet GetTypeLists() - { - return _types; - } + /// + /// Used to create instances of the specified type based on the resolved/cached plugin types + /// + /// + /// + /// set to true if an exception is to be thrown if there is an error during instantiation + /// + internal IEnumerable CreateInstances(IEnumerable types, bool throwException = false) + { + //Have removed logging because it doesn't really need to be logged since the time taken is generally 0ms. + //we want to know if it fails ever, not how long it took if it is only 0. + + var typesAsArray = types.ToArray(); + //using (DisposableTimer.DebugDuration( + // String.Format("Starting instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName), + // String.Format("Completed instantiation of {0} objects of type {1}", typesAsArray.Length, typeof(T).FullName))) + //{ + var instances = new List(); + foreach (var t in typesAsArray) + { + try + { + var typeInstance = (T)Activator.CreateInstance(t); + instances.Add(typeInstance); + } + catch (Exception ex) + { + + LogHelper.Error(String.Format("Error creating type {0}", t.FullName), ex); + + if (throwException) + { + throw ex; + } + } + } + return instances; + //} + } + + /// + /// Used to create an instance of the specified type based on the resolved/cached plugin types + /// + /// + /// + /// + /// + internal T CreateInstance(Type type, bool throwException = false) + { + var instances = CreateInstances(new[] { type }, throwException); + return instances.FirstOrDefault(); + } + + private IEnumerable ResolveTypes( + Func> finder, + TypeResolutionKind resolutionType, + bool cacheResult) + { + using (var readLock = new UpgradeableReadLock(_lock)) + { + var typesFound = new List(); + + using (DisposableTimer.TraceDuration( + () => String.Format("Starting resolution types of {0}", typeof(T).FullName), + () => String.Format("Completed resolution of types of {0}, found {1}", typeof(T).FullName, typesFound.Count))) + { + //check if the TypeList already exists, if so return it, if not we'll create it + var typeList = _types.SingleOrDefault(x => x.IsTypeList(resolutionType)); + //if we're not caching the result then proceed, or if the type list doesn't exist then proceed + if (!cacheResult || typeList == null) + { + //upgrade to a write lock since we're adding to the collection + readLock.UpgradeToWriteLock(); + + typeList = new TypeList(resolutionType); + + //we first need to look into our cache file (this has nothing to do with the 'cacheResult' parameter which caches in memory). + //if assemblies have not changed and the cache file actually exists, then proceed to try to lookup by the cache file. + if (!HaveAssembliesChanged && File.Exists(GetPluginListFilePath())) + { + var fileCacheResult = TryGetCachedPluginsFromFile(resolutionType); + + //here we need to identify if the CachedPluginNotFoundInFile was the exception, if it was then we need to re-scan + //in some cases the plugin will not have been scanned for on application startup, but the assemblies haven't changed + //so in this instance there will never be a result. + if (fileCacheResult.Error != null && fileCacheResult.Error is CachedPluginNotFoundInFile) + { + //we don't have a cache for this so proceed to look them up by scanning + LoadViaScanningAndUpdateCacheFile(typeList, resolutionType, finder); + } + else + { + if (fileCacheResult.Success) + { + var successfullyLoadedFromCache = true; + //we have a previous cache for this so we don't need to scan we just load what has been found in the file + foreach (var t in fileCacheResult.Result) + { + try + { + //we use the build manager to ensure we get all types loaded, this is slightly slower than + //Type.GetType but if the types in the assembly aren't loaded yet then we have problems with that. + var type = BuildManager.GetType(t, true); + typeList.AddType(type); + } + catch (Exception ex) + { + //if there are any exceptions loading types, we have to exist, this should never happen so + //we will need to revert to scanning for types. + successfullyLoadedFromCache = false; + LogHelper.Error("Could not load a cached plugin type: " + t + " now reverting to re-scanning assemblies for the base type: " + typeof(T).FullName, ex); + break; + } + } + if (!successfullyLoadedFromCache) + { + //we need to manually load by scanning if loading from the file was not successful. + LoadViaScanningAndUpdateCacheFile(typeList, resolutionType, finder); + } + else + { + LogHelper.Debug("Loaded plugin types " + typeof(T).FullName + " from persisted cache"); + } + } + } + } + else + { + //we don't have a cache for this so proceed to look them up by scanning + LoadViaScanningAndUpdateCacheFile(typeList, resolutionType, finder); + } + + //only add the cache if we are to cache the results + if (cacheResult) + { + //add the type list to the collection + _types.Add(typeList); + } + } + typesFound = typeList.GetTypes().ToList(); + } + + return typesFound; + } + } + + /// + /// This method invokes the finder which scans the assemblies for the types and then loads the result into the type finder. + /// Once the results are loaded, we update the cached type xml file + /// + /// + /// + /// + /// + /// THIS METHODS IS NOT THREAD SAFE + /// + private void LoadViaScanningAndUpdateCacheFile(TypeList typeList, TypeResolutionKind resolutionKind, Func> finder) + { + //we don't have a cache for this so proceed to look them up by scanning + foreach (var t in finder()) + { + typeList.AddType(t); + } + UpdateCachedPluginsFile(typeList.GetTypes(), resolutionKind); + } + + /// + /// Generic method to find the specified type and cache the result + /// + /// + /// + internal IEnumerable ResolveTypes(bool cacheResult = true) + { + return ResolveTypes( + () => TypeFinder.FindClassesOfType(AssembliesToScan), + TypeResolutionKind.FindAllTypes, + cacheResult); + } + + /// + /// Generic method to find the specified type that has an attribute and cache the result + /// + /// + /// + /// + internal IEnumerable ResolveTypesWithAttribute(bool cacheResult = true) + where TAttribute : Attribute + { + return ResolveTypes( + () => TypeFinder.FindClassesOfTypeWithAttribute(AssembliesToScan), + TypeResolutionKind.FindTypesWithAttribute, + cacheResult); + } + + /// + /// Generic method to find any type that has the specified attribute + /// + /// + /// + internal IEnumerable ResolveAttributedTypes(bool cacheResult = true) + where TAttribute : Attribute + { + return ResolveTypes( + () => TypeFinder.FindClassesWithAttribute(AssembliesToScan), + TypeResolutionKind.FindAttributedTypes, + cacheResult); + } + + /// + /// Used for unit tests + /// + /// + internal HashSet GetTypeLists() + { + return _types; + } - #region Private classes/Enums + #region Private classes/Enums - /// - /// The type of resolution being invoked - /// - internal enum TypeResolutionKind - { - FindAllTypes, - FindAttributedTypes, - FindTypesWithAttribute - } + /// + /// The type of resolution being invoked + /// + internal enum TypeResolutionKind + { + FindAllTypes, + FindAttributedTypes, + FindTypesWithAttribute + } - internal abstract class TypeList - { - public abstract void AddType(Type t); - public abstract bool IsTypeList(TypeResolutionKind resolutionType); - public abstract IEnumerable GetTypes(); - } + internal abstract class TypeList + { + public abstract void AddType(Type t); + public abstract bool IsTypeList(TypeResolutionKind resolutionType); + public abstract IEnumerable GetTypes(); + } - internal class TypeList : TypeList - { - private readonly TypeResolutionKind _resolutionType; + internal class TypeList : TypeList + { + private readonly TypeResolutionKind _resolutionType; - public TypeList(TypeResolutionKind resolutionType) - { - _resolutionType = resolutionType; - } + public TypeList(TypeResolutionKind resolutionType) + { + _resolutionType = resolutionType; + } - private readonly List _types = new List(); + private readonly List _types = new List(); - public override void AddType(Type t) - { - //if the type is an attribute type we won't do the type check because typeof is going to be the - //attribute type whereas the 't' type is the object type found with the attribute. - if (_resolutionType == TypeResolutionKind.FindAttributedTypes || t.IsType()) - { - _types.Add(t); - } - } + public override void AddType(Type t) + { + //if the type is an attribute type we won't do the type check because typeof is going to be the + //attribute type whereas the 't' type is the object type found with the attribute. + if (_resolutionType == TypeResolutionKind.FindAttributedTypes || t.IsType()) + { + _types.Add(t); + } + } - /// - /// Returns true if the current TypeList is of the same type and of the same type - /// - /// - /// - /// - public override bool IsTypeList(TypeResolutionKind resolutionType) - { - return _resolutionType == resolutionType && (typeof (T)).IsType(); - } + /// + /// Returns true if the current TypeList is of the same type and of the same type + /// + /// + /// + /// + public override bool IsTypeList(TypeResolutionKind resolutionType) + { + return _resolutionType == resolutionType && (typeof(T)).IsType(); + } - public override IEnumerable GetTypes() - { - return _types; - } - } - #endregion - } + public override IEnumerable GetTypes() + { + return _types; + } + } + + /// + /// This class is used simply to determine that a plugin was not found in the cache plugin list with the specified + /// TypeResolutionKind. + /// + internal class CachedPluginNotFoundInFile : Exception + { + + } + + #endregion + } } diff --git a/src/Umbraco.Tests/PluginManagerTests.cs b/src/Umbraco.Tests/PluginManagerTests.cs index f7262c8814..4b690d6271 100644 --- a/src/Umbraco.Tests/PluginManagerTests.cs +++ b/src/Umbraco.Tests/PluginManagerTests.cs @@ -8,12 +8,12 @@ using System.Web.Compilation; using NUnit.Framework; using SqlCE4Umbraco; using Umbraco.Core; +using Umbraco.Core.IO; using Umbraco.Tests.TestHelpers; using Umbraco.Web; using umbraco; using umbraco.DataLayer; using umbraco.MacroEngines; -using umbraco.MacroEngines.Iron; using umbraco.businesslogic; using umbraco.cms.businesslogic; using umbraco.editorControls; @@ -24,21 +24,21 @@ using umbraco.cms; namespace Umbraco.Tests { - [TestFixture] - public class PluginManagerTests - { + [TestFixture] + public class PluginManagerTests + { - [SetUp] - public void Initialize() - { - TestHelper.SetupLog4NetForTests(); + [SetUp] + public void Initialize() + { + TestHelper.SetupLog4NetForTests(); - //this ensures its reset - PluginManager.Current = new PluginManager(false); + //this ensures its reset + PluginManager.Current = new PluginManager(false); - //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver - //TODO: Should probably update this so it only searches this assembly and add custom types to be found - PluginManager.Current.AssembliesToScan = new[] + //for testing, we'll specify which assemblies are scanned for the PluginTypeResolver + //TODO: Should probably update this so it only searches this assembly and add custom types to be found + PluginManager.Current.AssembliesToScan = new[] { this.GetType().Assembly, typeof(ApplicationStartupHandler).Assembly, @@ -54,286 +54,318 @@ namespace Umbraco.Tests typeof(System.Web.Mvc.ActionResult).Assembly, typeof(TypeFinder).Assembly, typeof(ISqlHelper).Assembly, - typeof(DLRScriptingEngine).Assembly, typeof(ICultureDictionary).Assembly, typeof(UmbracoContext).Assembly, typeof(BaseDataType).Assembly }; - } + } - private DirectoryInfo PrepareFolder() - { - var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; - var dir = Directory.CreateDirectory(Path.Combine(assDir.FullName, "PluginManager", Guid.NewGuid().ToString("N"))); - foreach (var f in dir.GetFiles()) - { - f.Delete(); - } - return dir; - } + private DirectoryInfo PrepareFolder() + { + var assDir = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory; + var dir = Directory.CreateDirectory(Path.Combine(assDir.FullName, "PluginManager", Guid.NewGuid().ToString("N"))); + foreach (var f in dir.GetFiles()) + { + f.Delete(); + } + return dir; + } - //[Test] - //public void Scan_Vs_Load_Benchmark() - //{ - // var pluginManager = new PluginManager(false); - // var watch = new Stopwatch(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var refreshers = pluginManager.ResolveTypes(false); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); - //} + //[Test] + //public void Scan_Vs_Load_Benchmark() + //{ + // var pluginManager = new PluginManager(false); + // var watch = new Stopwatch(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var refreshers = pluginManager.ResolveTypes(false); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); + //} - ////NOTE: This test shows that Type.GetType is 100% faster than Assembly.Load(..).GetType(...) so we'll use that :) - //[Test] - //public void Load_Type_Benchmark() - //{ - // var watch = new Stopwatch(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.macroCacheRefresh"); - // var type3 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.templateCacheRefresh"); - // var type4 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.presentation.cache.MediaLibraryRefreshers"); - // var type5 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") - // .GetType("umbraco.presentation.cache.pageRefresher"); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); - // watch.Reset(); - // watch.Start(); - // for (var i = 0; i < 1000; i++) - // { - // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); - // } - // watch.Stop(); - // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); - //} + ////NOTE: This test shows that Type.GetType is 100% faster than Assembly.Load(..).GetType(...) so we'll use that :) + //[Test] + //public void Load_Type_Benchmark() + //{ + // var watch = new Stopwatch(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Type.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type3 = Type.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type4 = Type.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // var type5 = Type.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.macroCacheRefresh"); + // var type3 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.templateCacheRefresh"); + // var type4 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.presentation.cache.MediaLibraryRefreshers"); + // var type5 = Assembly.Load("umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null") + // .GetType("umbraco.presentation.cache.pageRefresher"); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (2nd round): " + watch.ElapsedMilliseconds); + // watch.Reset(); + // watch.Start(); + // for (var i = 0; i < 1000; i++) + // { + // var type2 = BuildManager.GetType("umbraco.macroCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type3 = BuildManager.GetType("umbraco.templateCacheRefresh, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type4 = BuildManager.GetType("umbraco.presentation.cache.MediaLibraryRefreshers, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // var type5 = BuildManager.GetType("umbraco.presentation.cache.pageRefresher, umbraco, Version=1.0.4698.259, Culture=neutral, PublicKeyToken=null", true); + // } + // watch.Stop(); + // Debug.WriteLine("TOTAL TIME (1st round): " + watch.ElapsedMilliseconds); + //} - [Test] - public void Create_Cached_Plugin_File() - { - var types = new[] {typeof (PluginManager), typeof (PluginManagerTests), typeof (UmbracoContext)}; + [Test] + public void Detect_Legacy_Plugin_File_List() + { + var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache"); + var manager = new PluginManager(false); + var filePath = Path.Combine(tempFolder, "umbraco-plugins.list"); + File.WriteAllText(filePath, @" + + + + +"); - var manager = new PluginManager(false); - //yes this is silly, none of these types inherit from string, but this is just to test the xml file format - manager.UpdateCachedPluginsFile(types); + Assert.IsTrue(manager.DetectLegacyPluginListFile()); - var plugins = manager.TryGetCachedPluginsFromFile(); - Assert.IsTrue(plugins.Success); - Assert.AreEqual(3, plugins.Result.Count()); - var shouldContain = types.Select(x => x.AssemblyQualifiedName); - //ensure they are all found - Assert.IsTrue(plugins.Result.ContainsAll(shouldContain)); - } + File.Delete(filePath); - [Test] - public void PluginHash_From_String() - { - var s = "hello my name is someone".GetHashCode().ToString("x", CultureInfo.InvariantCulture); - var output = PluginManager.ConvertPluginsHashFromHex(s); - Assert.AreNotEqual(0, output); - } + //now create a valid one + File.WriteAllText(filePath, @" + + + + +"); - [Test] - public void Get_Plugins_Hash() - { - //Arrange - var dir = PrepareFolder(); - var d1 = dir.CreateSubdirectory("1"); - var d2 = dir.CreateSubdirectory("2"); - var d3 = dir.CreateSubdirectory("3"); - var d4 = dir.CreateSubdirectory("4"); - var f1 = new FileInfo(Path.Combine(d1.FullName, "test1.dll")); - var f2 = new FileInfo(Path.Combine(d1.FullName, "test2.dll")); - var f3 = new FileInfo(Path.Combine(d2.FullName, "test1.dll")); - var f4 = new FileInfo(Path.Combine(d2.FullName, "test2.dll")); - var f5 = new FileInfo(Path.Combine(d3.FullName, "test1.dll")); - var f6 = new FileInfo(Path.Combine(d3.FullName, "test2.dll")); - var f7 = new FileInfo(Path.Combine(d4.FullName, "test1.dll")); - f1.CreateText().Close(); - f2.CreateText().Close(); - f3.CreateText().Close(); - f4.CreateText().Close(); - f5.CreateText().Close(); - f6.CreateText().Close(); - f7.CreateText().Close(); - var list1 = new[] { f1, f2, f3, f4, f5, f6 }; - var list2 = new[] { f1, f3, f5 }; - var list3 = new[] { f1, f3, f5, f7 }; + Assert.IsFalse(manager.DetectLegacyPluginListFile()); + } - //Act - var hash1 = PluginManager.GetAssembliesHash(list1); - var hash2 = PluginManager.GetAssembliesHash(list2); - var hash3 = PluginManager.GetAssembliesHash(list3); + [Test] + public void Create_Cached_Plugin_File() + { + var types = new[] { typeof(PluginManager), typeof(PluginManagerTests), typeof(UmbracoContext) }; - //Assert + var manager = new PluginManager(false); + //yes this is silly, none of these types inherit from string, but this is just to test the xml file format + manager.UpdateCachedPluginsFile(types, PluginManager.TypeResolutionKind.FindAllTypes); - //both should be the same since we only create the hash based on the unique folder of the list passed in, yet - //all files will exist in those folders still - Assert.AreEqual(hash1, hash2); - Assert.AreNotEqual(hash1, hash3); - } + var plugins = manager.TryGetCachedPluginsFromFile(PluginManager.TypeResolutionKind.FindAllTypes); + var diffType = manager.TryGetCachedPluginsFromFile(PluginManager.TypeResolutionKind.FindAttributedTypes); - [Test] - public void Ensure_Only_One_Type_List_Created() - { - var foundTypes1 = PluginManager.Current.ResolveFindMeTypes(); - var foundTypes2 = PluginManager.Current.ResolveFindMeTypes(); - Assert.AreEqual(1, - PluginManager.Current.GetTypeLists() - .Count(x => x.IsTypeList(PluginManager.TypeResolutionKind.FindAllTypes))); - } + Assert.IsTrue(plugins.Success); + //this will be false since there is no cache of that type resolution kind + Assert.IsFalse(diffType.Success); - [Test] - public void Resolves_Types() - { - var foundTypes1 = PluginManager.Current.ResolveFindMeTypes(); - Assert.AreEqual(2, foundTypes1.Count()); - } + Assert.AreEqual(3, plugins.Result.Count()); + var shouldContain = types.Select(x => x.AssemblyQualifiedName); + //ensure they are all found + Assert.IsTrue(plugins.Result.ContainsAll(shouldContain)); + } - [Test] - public void Resolves_Attributed_Trees() - { - var trees = PluginManager.Current.ResolveAttributedTrees(); - Assert.AreEqual(27, trees.Count()); - } + [Test] + public void PluginHash_From_String() + { + var s = "hello my name is someone".GetHashCode().ToString("x", CultureInfo.InvariantCulture); + var output = PluginManager.ConvertPluginsHashFromHex(s); + Assert.AreNotEqual(0, output); + } - [Test] - public void Resolves_Actions() - { - var actions = PluginManager.Current.ResolveActions(); - Assert.AreEqual(37, actions.Count()); - } + [Test] + public void Get_Plugins_Hash() + { + //Arrange + var dir = PrepareFolder(); + var d1 = dir.CreateSubdirectory("1"); + var d2 = dir.CreateSubdirectory("2"); + var d3 = dir.CreateSubdirectory("3"); + var d4 = dir.CreateSubdirectory("4"); + var f1 = new FileInfo(Path.Combine(d1.FullName, "test1.dll")); + var f2 = new FileInfo(Path.Combine(d1.FullName, "test2.dll")); + var f3 = new FileInfo(Path.Combine(d2.FullName, "test1.dll")); + var f4 = new FileInfo(Path.Combine(d2.FullName, "test2.dll")); + var f5 = new FileInfo(Path.Combine(d3.FullName, "test1.dll")); + var f6 = new FileInfo(Path.Combine(d3.FullName, "test2.dll")); + var f7 = new FileInfo(Path.Combine(d4.FullName, "test1.dll")); + f1.CreateText().Close(); + f2.CreateText().Close(); + f3.CreateText().Close(); + f4.CreateText().Close(); + f5.CreateText().Close(); + f6.CreateText().Close(); + f7.CreateText().Close(); + var list1 = new[] { f1, f2, f3, f4, f5, f6 }; + var list2 = new[] { f1, f3, f5 }; + var list3 = new[] { f1, f3, f5, f7 }; - [Test] - public void Resolves_Trees() - { - var trees = PluginManager.Current.ResolveTrees(); - Assert.AreEqual(36, trees.Count()); - } + //Act + var hash1 = PluginManager.GetAssembliesHash(list1); + var hash2 = PluginManager.GetAssembliesHash(list2); + var hash3 = PluginManager.GetAssembliesHash(list3); - [Test] - public void Resolves_Applications() - { - var apps = PluginManager.Current.ResolveApplications(); - Assert.AreEqual(7, apps.Count()); - } + //Assert + Assert.AreNotEqual(hash1, hash2); + Assert.AreNotEqual(hash1, hash3); + Assert.AreNotEqual(hash2, hash3); - [Test] - public void Resolves_Action_Handlers() - { - var types = PluginManager.Current.ResolveActionHandlers(); - Assert.AreEqual(1, types.Count()); - } + Assert.AreEqual(hash1, PluginManager.GetAssembliesHash(list1)); + } - [Test] - public void Resolves_DataTypes() - { - var types = PluginManager.Current.ResolveDataTypes(); - Assert.AreEqual(37, types.Count()); - } + [Test] + public void Ensure_Only_One_Type_List_Created() + { + var foundTypes1 = PluginManager.Current.ResolveFindMeTypes(); + var foundTypes2 = PluginManager.Current.ResolveFindMeTypes(); + Assert.AreEqual(1, + PluginManager.Current.GetTypeLists() + .Count(x => x.IsTypeList(PluginManager.TypeResolutionKind.FindAllTypes))); + } - [Test] - public void Resolves_RazorDataTypeModels() - { - var types = PluginManager.Current.ResolveRazorDataTypeModels(); - Assert.AreEqual(2, types.Count()); - } + [Test] + public void Resolves_Types() + { + var foundTypes1 = PluginManager.Current.ResolveFindMeTypes(); + Assert.AreEqual(2, foundTypes1.Count()); + } - [Test] - public void Resolves_RestExtensions() - { - var types = PluginManager.Current.ResolveRestExtensions(); - Assert.AreEqual(2, types.Count()); - } + [Test] + public void Resolves_Attributed_Trees() + { + var trees = PluginManager.Current.ResolveAttributedTrees(); + Assert.AreEqual(27, trees.Count()); + } - [Test] - public void Resolves_LegacyRestExtensions() - { - var types = PluginManager.Current.ResolveLegacyRestExtensions(); - Assert.AreEqual(1, types.Count()); - } + [Test] + public void Resolves_Actions() + { + var actions = PluginManager.Current.ResolveActions(); + Assert.AreEqual(37, actions.Count()); + } - [Test] - public void Resolves_XsltExtensions() - { - var types = PluginManager.Current.ResolveXsltExtensions(); - Assert.AreEqual(1, types.Count()); - } + [Test] + public void Resolves_Trees() + { + var trees = PluginManager.Current.ResolveTrees(); + Assert.AreEqual(36, trees.Count()); + } - [XsltExtension("Blah.Blah")] - public class MyXsltExtension - { + [Test] + public void Resolves_Applications() + { + var apps = PluginManager.Current.ResolveApplications(); + Assert.AreEqual(7, apps.Count()); + } - } + [Test] + public void Resolves_Action_Handlers() + { + var types = PluginManager.Current.ResolveActionHandlers(); + Assert.AreEqual(1, types.Count()); + } - [umbraco.presentation.umbracobase.RestExtension("Blah")] - public class MyLegacyRestExtension - { - - } + [Test] + public void Resolves_DataTypes() + { + var types = PluginManager.Current.ResolveDataTypes(); + Assert.AreEqual(37, types.Count()); + } - [Umbraco.Web.BaseRest.RestExtension("Blah")] - public class MyRestExtesion - { - - } + [Test] + public void Resolves_RazorDataTypeModels() + { + var types = PluginManager.Current.ResolveRazorDataTypeModels(); + Assert.AreEqual(2, types.Count()); + } - public interface IFindMe - { + [Test] + public void Resolves_RestExtensions() + { + var types = PluginManager.Current.ResolveRestExtensions(); + Assert.AreEqual(2, types.Count()); + } - } + [Test] + public void Resolves_LegacyRestExtensions() + { + var types = PluginManager.Current.ResolveLegacyRestExtensions(); + Assert.AreEqual(1, types.Count()); + } - public class FindMe1 : IFindMe - { + [Test] + public void Resolves_XsltExtensions() + { + var types = PluginManager.Current.ResolveXsltExtensions(); + Assert.AreEqual(1, types.Count()); + } - } + [XsltExtension("Blah.Blah")] + public class MyXsltExtension + { - public class FindMe2 : IFindMe - { + } - } + [umbraco.presentation.umbracobase.RestExtension("Blah")] + public class MyLegacyRestExtension + { - } + } + + [Umbraco.Web.BaseRest.RestExtension("Blah")] + public class MyRestExtesion + { + + } + + public interface IFindMe + { + + } + + public class FindMe1 : IFindMe + { + + } + + public class FindMe2 : IFindMe + { + + } + + } } \ No newline at end of file diff --git a/src/umbraco.cms/businesslogic/Packager/Installer.cs b/src/umbraco.cms/businesslogic/Packager/Installer.cs index b58f20290b..ba8419448d 100644 --- a/src/umbraco.cms/businesslogic/Packager/Installer.cs +++ b/src/umbraco.cms/businesslogic/Packager/Installer.cs @@ -13,7 +13,8 @@ using ICSharpCode.SharpZipLib; using ICSharpCode.SharpZipLib.Zip; using ICSharpCode.SharpZipLib.Zip.Compression; using ICSharpCode.SharpZipLib.Zip.Compression.Streams; - +using Umbraco.Core; +using Umbraco.Core.Logging; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.propertytype; using umbraco.BusinessLogic; @@ -76,7 +77,7 @@ namespace umbraco.cms.businesslogic.packager public bool ContainsMacroConflict { get { return _containsMacroConflict; } } public IDictionary ConflictingMacroAliases { get { return _conflictingMacroAliases; } } - + public bool ContainsUnsecureFiles { get { return _containUnsecureFiles; } } public List UnsecureFiles { get { return _unsecureFiles; } } @@ -145,7 +146,7 @@ namespace umbraco.cms.businesslogic.packager _macros.Add(MacroToAdd); } - + /// /// Imports the specified package @@ -154,33 +155,39 @@ namespace umbraco.cms.businesslogic.packager /// public string Import(string InputFile) { - string tempDir = ""; - if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile))) + using (DisposableTimer.DebugDuration( + () => "Importing package file " + InputFile, + () => "Package file " + InputFile + "imported")) { - FileInfo fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile)); - // Check if the file is a valid package - if (fi.Extension.ToLower() == ".umb") + string tempDir = ""; + if (File.Exists(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile))) { - try + FileInfo fi = new FileInfo(IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile)); + // Check if the file is a valid package + if (fi.Extension.ToLower() == ".umb") { - tempDir = unPack(fi.FullName); - LoadConfig(tempDir); - } - catch (Exception unpackE) - { - throw new Exception("Error unpacking extension...", unpackE); + try + { + tempDir = unPack(fi.FullName); + LoadConfig(tempDir); + } + catch (Exception unpackE) + { + throw new Exception("Error unpacking extension...", unpackE); + } } + 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 isn't a package (doesn't have a .umb extension). Check if the file automatically got named '.zip' upon download."); + throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile) + "'"); + return tempDir; } - else - throw new Exception("Error - file not found. Could find file named '" + IOHelper.MapPath(SystemDirectories.Data + Path.DirectorySeparatorChar + InputFile) + "'"); - return tempDir; } - public int CreateManifest(string tempDir, string guid, string repoGuid) { + 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 string _packName = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/package/name")); string _packAuthor = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/name")); @@ -216,225 +223,268 @@ namespace umbraco.cms.businesslogic.packager 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) { + public void InstallFiles(int packageId, string tempDir) + { + using (DisposableTimer.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 + packager.InstalledPackage insPack = packager.InstalledPackage.GetById(packageId); - //retrieve the manifest to continue installation - packager.InstalledPackage insPack = packager.InstalledPackage.GetById(packageId); + // Move files + //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; + string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - // Move files - //string virtualBasePath = System.Web.HttpContext.Current.Request.ApplicationPath; - string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//file")) + { + //we enclose the whole file-moving to ensure that the entire installer doesn't crash + try + { + String destPath = getFileName(basePath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); + String sourceFile = getFileName(tempDir, xmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); + String destFile = getFileName(destPath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//file")) { - //we enclose the whole file-moving to ensure that the entire installer doesn't crash - try { - String destPath = getFileName(basePath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); - String sourceFile = getFileName(tempDir, xmlHelper.GetNodeValue(n.SelectSingleNode("guid"))); - String destFile = getFileName(destPath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + // Create the destination directory if it doesn't exist + if (!Directory.Exists(destPath)) + Directory.CreateDirectory(destPath); + //If a file with this name exists, delete it + else if (File.Exists(destFile)) + File.Delete(destFile); - // Create the destination directory if it doesn't exist - if (!Directory.Exists(destPath)) - Directory.CreateDirectory(destPath); - //If a file with this name exists, delete it - else if (File.Exists(destFile)) - File.Delete(destFile); + // Move the file + File.Move(sourceFile, destFile); - // Move the file - File.Move(sourceFile, destFile); - - //PPH log file install - insPack.Data.Files.Add(xmlHelper.GetNodeValue(n.SelectSingleNode("orgPath")) + "/" + xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - } catch (Exception ex) { - Log.Add(LogTypes.Error, -1, "Package install error: " + ex.ToString()); + //PPH log file install + insPack.Data.Files.Add(xmlHelper.GetNodeValue(n.SelectSingleNode("orgPath")) + "/" + xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + } + catch (Exception ex) + { + Log.Add(LogTypes.Error, -1, "Package install error: " + ex.ToString()); + } } - } - insPack.Save(); + insPack.Save(); + } } - public void InstallBusinessLogic(int packageId, string tempDir) { - - //retrieve the manifest to continue installation - packager.InstalledPackage insPack = packager.InstalledPackage.GetById(packageId); - bool saveNeeded = false; + public void InstallBusinessLogic(int packageId, string tempDir) + { - //Install DataTypes - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//DataType")) { - cms.businesslogic.datatype.DataTypeDefinition newDtd = cms.businesslogic.datatype.DataTypeDefinition.Import(n); + using (DisposableTimer.DebugDuration( + () => "Installing business logic for package id " + packageId + " into temp folder " + tempDir, + () => "Package business logic installation complete for package id " + packageId)) + { + //retrieve the manifest to continue installation + packager.InstalledPackage insPack = packager.InstalledPackage.GetById(packageId); + bool saveNeeded = false; - if (newDtd != null) { - insPack.Data.DataTypes.Add(newDtd.Id.ToString()); - saveNeeded = true; - } - } + //Install DataTypes + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//DataType")) + { + cms.businesslogic.datatype.DataTypeDefinition newDtd = cms.businesslogic.datatype.DataTypeDefinition.Import(n); - if (saveNeeded) {insPack.Save(); saveNeeded = false;} - - //Install languages - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//Language")) { - language.Language newLang = language.Language.Import(n); - - if (newLang != null) { - insPack.Data.Languages.Add(newLang.id.ToString()); - saveNeeded = true; - } - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - //Install dictionary items - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) { - Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); - - if (newDi != null) { - insPack.Data.DictionaryItems.Add(newDi.id.ToString()); - saveNeeded = true; - } - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - // Install macros - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//macro")) { - cms.businesslogic.macro.Macro m = cms.businesslogic.macro.Macro.Import(n); - - if (m != null) { - insPack.Data.Macros.Add(m.Id.ToString()); - saveNeeded = true; - } - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - // Get current user, with a fallback - User u = new User(0); - if (!string.IsNullOrEmpty(BasePages.UmbracoEnsuredPage.umbracoUserContextID)) { - if (BasePages.UmbracoEnsuredPage.ValidateUserContextID(BasePages.UmbracoEnsuredPage.umbracoUserContextID)) { - u = User.GetCurrent(); - } - } - - - // Add Templates - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) { - var t = Template.Import(n, u); - - insPack.Data.Templates.Add(t.Id.ToString()); - - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - - //NOTE: SD: I'm pretty sure the only thing the below script does is ensure that the Master template Id is set - // in the database, but this is also duplicating the saving of the design content since the above Template.Import - // already does this. I've left this for now because I'm not sure the reprocussions of removing it but seems there - // is a lot of excess database calls happening here. - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) { - string master = xmlHelper.GetNodeValue(n.SelectSingleNode("Master")); - template.Template t = template.Template.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Alias"))); - if (master.Trim() != "") { - template.Template masterTemplate = template.Template.GetByAlias(master); - if (masterTemplate != null) { - t.MasterTemplate = template.Template.GetByAlias(master).Id; - //SD: This appears to always just save an empty template because the design isn't set yet - // this fixes an issue now that we have MVC because if there is an empty template and MVC is - // the default, it will create a View not a master page and then the system will try to route via - // MVC which means that the package will not work anymore. - // The code below that imports the templates should suffice because it's actually importing - // template data not just blank data. - - //if (UmbracoSettings.UseAspNetMasterPages) - // t.SaveMasterPageFile(t.Design); + if (newDtd != null) + { + insPack.Data.DataTypes.Add(newDtd.Id.ToString()); + saveNeeded = true; } } - // Master templates can only be generated when their master is known - if (UmbracoSettings.UseAspNetMasterPages) { - t.ImportDesign(xmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); - t.SaveMasterPageFile(t.Design); - } - } - // Add documenttypes - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) { - ImportDocumentType(n, u, false); - saveNeeded = true; - } + if (saveNeeded) { insPack.Save(); saveNeeded = false; } - if (saveNeeded) { insPack.Save(); saveNeeded = false; } + //Install languages + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//Language")) + { + language.Language newLang = language.Language.Import(n); - - // Add documenttype structure - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) { - DocumentType dt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias"))); - if (dt != null) { - ArrayList allowed = new ArrayList(); - foreach (XmlNode structure in n.SelectNodes("Structure/DocumentType")) { - DocumentType dtt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(structure)); - if(dtt != null) - allowed.Add(dtt.Id); + if (newLang != null) + { + insPack.Data.Languages.Add(newLang.id.ToString()); + saveNeeded = true; } + } - int[] adt = new int[allowed.Count]; - for (int i = 0; i < allowed.Count; i++) - adt[i] = (int)allowed[i]; - dt.AllowedChildContentTypeIDs = adt; + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + //Install dictionary items + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) + { + Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); + + if (newDi != null) + { + insPack.Data.DictionaryItems.Add(newDi.id.ToString()); + saveNeeded = true; + } + } + + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + // Install macros + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//macro")) + { + cms.businesslogic.macro.Macro m = cms.businesslogic.macro.Macro.Import(n); + + if (m != null) + { + insPack.Data.Macros.Add(m.Id.ToString()); + saveNeeded = true; + } + } + + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + // Get current user, with a fallback + User u = new User(0); + if (!string.IsNullOrEmpty(BasePages.UmbracoEnsuredPage.umbracoUserContextID)) + { + if (BasePages.UmbracoEnsuredPage.ValidateUserContextID(BasePages.UmbracoEnsuredPage.umbracoUserContextID)) + { + u = User.GetCurrent(); + } + } + + + // Add Templates + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) + { + var t = Template.Import(n, u); + + insPack.Data.Templates.Add(t.Id.ToString()); - //PPH we log the document type install here. - insPack.Data.Documenttypes.Add(dt.Id.ToString()); saveNeeded = true; } - } - if (saveNeeded) { insPack.Save(); saveNeeded = false; } + if (saveNeeded) { insPack.Save(); saveNeeded = false; } - // Stylesheets - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) { - StyleSheet s = StyleSheet.Import(n, u); - insPack.Data.Stylesheets.Add(s.Id.ToString()); - saveNeeded = true; - } - - if (saveNeeded) { insPack.Save(); saveNeeded = false; } - - // Documents - foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) { - insPack.Data.ContentNodeId = cms.businesslogic.web.Document.Import(-1, u, n).ToString(); - } - - //Package Actions - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action")) { - - if (n.Attributes["undo"] == null || n.Attributes["undo"].Value == "true") { - insPack.Data.Actions += n.OuterXml; - Log.Add(LogTypes.Debug, -1, HttpUtility.HtmlEncode(n.OuterXml)); - } - - if (n.Attributes["runat"] != null && n.Attributes["runat"].Value == "install") { - try { - packager.PackageAction.RunPackageAction(insPack.Data.Name, n.Attributes["alias"].Value, n); - } catch { + //NOTE: SD: I'm pretty sure the only thing the below script does is ensure that the Master template Id is set + // in the database, but this is also duplicating the saving of the design content since the above Template.Import + // already does this. I've left this for now because I'm not sure the reprocussions of removing it but seems there + // is a lot of excess database calls happening here. + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Templates/Template")) + { + string master = xmlHelper.GetNodeValue(n.SelectSingleNode("Master")); + template.Template t = template.Template.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Alias"))); + if (master.Trim() != "") + { + template.Template masterTemplate = template.Template.GetByAlias(master); + if (masterTemplate != null) + { + t.MasterTemplate = template.Template.GetByAlias(master).Id; + //SD: This appears to always just save an empty template because the design isn't set yet + // this fixes an issue now that we have MVC because if there is an empty template and MVC is + // the default, it will create a View not a master page and then the system will try to route via + // MVC which means that the package will not work anymore. + // The code below that imports the templates should suffice because it's actually importing + // template data not just blank data. + //if (UmbracoSettings.UseAspNetMasterPages) + // t.SaveMasterPageFile(t.Design); + } + } + // Master templates can only be generated when their master is known + if (UmbracoSettings.UseAspNetMasterPages) + { + t.ImportDesign(xmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); + t.SaveMasterPageFile(t.Design); } } + + // Add documenttypes + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) + { + ImportDocumentType(n, u, false); + saveNeeded = true; + } + + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + + // Add documenttype structure + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("DocumentTypes/DocumentType")) + { + DocumentType dt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias"))); + if (dt != null) + { + ArrayList allowed = new ArrayList(); + foreach (XmlNode structure in n.SelectNodes("Structure/DocumentType")) + { + DocumentType dtt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(structure)); + if (dtt != null) + allowed.Add(dtt.Id); + } + + int[] adt = new int[allowed.Count]; + for (int i = 0; i < allowed.Count; i++) + adt[i] = (int)allowed[i]; + dt.AllowedChildContentTypeIDs = adt; + + //PPH we log the document type install here. + insPack.Data.Documenttypes.Add(dt.Id.ToString()); + saveNeeded = true; + } + } + + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + // Stylesheets + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Stylesheets/Stylesheet")) + { + StyleSheet s = StyleSheet.Import(n, u); + + insPack.Data.Stylesheets.Add(s.Id.ToString()); + saveNeeded = true; + } + + if (saveNeeded) { insPack.Save(); saveNeeded = false; } + + // Documents + foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) + { + insPack.Data.ContentNodeId = cms.businesslogic.web.Document.Import(-1, u, n).ToString(); + } + + //Package Actions + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action")) + { + + if (n.Attributes["undo"] == null || n.Attributes["undo"].Value == "true") + { + insPack.Data.Actions += n.OuterXml; + Log.Add(LogTypes.Debug, -1, HttpUtility.HtmlEncode(n.OuterXml)); + } + + if (n.Attributes["runat"] != null && n.Attributes["runat"].Value == "install") + { + try + { + packager.PackageAction.RunPackageAction(insPack.Data.Name, n.Attributes["alias"].Value, n); + } + catch + { + + } + } + } + + // Trigger update of Apps / Trees config. + // (These are ApplicationStartupHandlers so just instantiating them will trigger them) + new ApplicationRegistrar(); + new ApplicationTreeRegistrar(); + + insPack.Save(); } - - // Trigger update of Apps / Trees config. - // (These are ApplicationStartupHandlers so just instantiating them will trigger them) - new ApplicationRegistrar(); - new ApplicationTreeRegistrar(); - - insPack.Save(); } - public void InstallCleanUp(int packageId, string tempDir) { + public void InstallCleanUp(int packageId, string tempDir) + { //this will contain some logic to clean up all those old folders @@ -448,12 +498,12 @@ namespace umbraco.cms.businesslogic.packager { //PPH added logging of installs, this adds all install info in the installedPackages config file. string _packName = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/package/name")); - string _packAuthor= xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/name")); + string _packAuthor = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/name")); string _packAuthorUrl = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/website")); string _packVersion = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/package/version")); string _packReadme = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/readme")); string _packLicense = xmlHelper.GetNodeValue(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/package/license ")); - + //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 @@ -465,10 +515,11 @@ namespace umbraco.cms.businesslogic.packager insPack.Data.License = _packLicense; 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. - - + + //Install languages - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//Language")) { + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//Language")) + { language.Language newLang = language.Language.Import(n); if (newLang != null) @@ -476,11 +527,12 @@ namespace umbraco.cms.businesslogic.packager } //Install dictionary items - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) { - Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("./DictionaryItems/DictionaryItem")) + { + Dictionary.DictionaryItem newDi = Dictionary.DictionaryItem.Import(n); - if (newDi != null) - insPack.Data.DictionaryItems.Add(newDi.id.ToString()); + if (newDi != null) + insPack.Data.DictionaryItems.Add(newDi.id.ToString()); } // Install macros @@ -488,7 +540,7 @@ namespace umbraco.cms.businesslogic.packager { cms.businesslogic.macro.Macro m = cms.businesslogic.macro.Macro.Import(n); - if(m != null) + if (m != null) insPack.Data.Macros.Add(m.Id.ToString()); } @@ -523,8 +575,8 @@ namespace umbraco.cms.businesslogic.packager template.Template t = template.Template.MakeNew(xmlHelper.GetNodeValue(n.SelectSingleNode("Name")), u); t.Alias = xmlHelper.GetNodeValue(n.SelectSingleNode("Alias")); - t.ImportDesign( xmlHelper.GetNodeValue(n.SelectSingleNode("Design")) ); - + t.ImportDesign(xmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); + insPack.Data.Templates.Add(t.Id.ToString()); } @@ -536,14 +588,16 @@ namespace umbraco.cms.businesslogic.packager if (master.Trim() != "") { template.Template masterTemplate = template.Template.GetByAlias(master); - if (masterTemplate != null) { + if (masterTemplate != null) + { t.MasterTemplate = template.Template.GetByAlias(master).Id; if (UmbracoSettings.UseAspNetMasterPages) t.SaveMasterPageFile(t.Design); } } // Master templates can only be generated when their master is known - if (UmbracoSettings.UseAspNetMasterPages) { + if (UmbracoSettings.UseAspNetMasterPages) + { t.ImportDesign(xmlHelper.GetNodeValue(n.SelectSingleNode("Design"))); t.SaveMasterPageFile(t.Design); } @@ -602,27 +656,32 @@ namespace umbraco.cms.businesslogic.packager } // Documents - foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) { + foreach (XmlElement n in _packageConfig.DocumentElement.SelectNodes("Documents/DocumentSet [@importMode = 'root']/*")) + { cms.businesslogic.web.Document.Import(-1, u, n); //PPH todo log document install... } - - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action [@runat != 'uninstall']")) { - try { + + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action [@runat != 'uninstall']")) + { + try + { packager.PackageAction.RunPackageAction(_packName, n.Attributes["alias"].Value, n); - } catch { } + } + catch { } } //saving the uninstall actions untill the package is uninstalled. - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action [@undo != false()]")) { + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("Actions/Action [@undo != false()]")) + { insPack.Data.Actions += n.OuterXml; } - - + + insPack.Save(); - - + + } public static void ImportDocumentType(XmlNode n, BusinessLogic.User u, bool ImportStructure) @@ -633,7 +692,7 @@ namespace umbraco.cms.businesslogic.packager dt = DocumentType.MakeNew(u, xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Name"))); dt.Alias = xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Alias")); - + //Master content type DocumentType mdt = DocumentType.GetByAlias(xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Master"))); if (mdt != null) @@ -644,7 +703,7 @@ namespace umbraco.cms.businesslogic.packager dt.Text = xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Name")); } - + // Info dt.IconUrl = xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Icon")); dt.Thumbnail = xmlHelper.GetNodeValue(n.SelectSingleNode("Info/Thumbnail")); @@ -688,7 +747,7 @@ namespace umbraco.cms.businesslogic.packager for (int t = 0; t < tabs.Length; t++) tabNames += tabs[t].Caption + ";"; - + Hashtable ht = new Hashtable(); foreach (XmlNode t in n.SelectNodes("Tabs/Tab")) @@ -712,19 +771,24 @@ namespace umbraco.cms.businesslogic.packager // Generic Properties datatype.controls.Factory f = new datatype.controls.Factory(); foreach (XmlNode gp in n.SelectNodes("GenericProperties/GenericProperty")) - { + { int dfId = 0; Guid dtId = new Guid(xmlHelper.GetNodeValue(gp.SelectSingleNode("Type"))); - if (gp.SelectSingleNode("Definition") != null && !string.IsNullOrEmpty(xmlHelper.GetNodeValue(gp.SelectSingleNode("Definition")))) { + if (gp.SelectSingleNode("Definition") != null && !string.IsNullOrEmpty(xmlHelper.GetNodeValue(gp.SelectSingleNode("Definition")))) + { Guid dtdId = new Guid(xmlHelper.GetNodeValue(gp.SelectSingleNode("Definition"))); if (CMSNode.IsNode(dtdId)) dfId = new CMSNode(dtdId).Id; - } - if (dfId == 0) { - try { + } + if (dfId == 0) + { + try + { dfId = findDataTypeDefinitionFromType(ref dtId); - } catch { + } + catch + { throw new Exception(String.Format("Could not find datatype with id {0}.", dtId)); } } @@ -820,22 +884,22 @@ namespace umbraco.cms.businesslogic.packager //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('/','\\'); + 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); + bool pathEndsWithSlash = (path[path.Length - 1] == Path.DirectorySeparatorChar); // Path ends with a slash if (pathEndsWithSlash) { - if(!fileNameStartsWithSlash) + if (!fileNameStartsWithSlash) // No double slash, just concatenate return path + fileName; else @@ -884,27 +948,29 @@ namespace umbraco.cms.businesslogic.packager _reqPatch = int.Parse(_packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/package/requirements/patch").FirstChild.Value); _authorName = _packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/name").FirstChild.Value; _authorUrl = _packageConfig.DocumentElement.SelectSingleNode("/umbPackage/info/author/website").FirstChild.Value; - + string basePath = System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath; - - foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//file")) { + + foreach (XmlNode n in _packageConfig.DocumentElement.SelectNodes("//file")) + { bool badFile = false; string destPath = getFileName(basePath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgPath"))); string destFile = getFileName(destPath, xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - if (destPath.ToLower().Contains( IOHelper.DirSepChar + "app_code")) + if (destPath.ToLower().Contains(IOHelper.DirSepChar + "app_code")) badFile = true; - if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) + if (destPath.ToLower().Contains(IOHelper.DirSepChar + "bin")) badFile = true; - if (destFile.ToLower().EndsWith(".dll")) - badFile = true; + if (destFile.ToLower().EndsWith(".dll")) + badFile = true; - if (badFile) { - _containUnsecureFiles = true; - _unsecureFiles.Add(xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); - } + if (badFile) + { + _containUnsecureFiles = true; + _unsecureFiles.Add(xmlHelper.GetNodeValue(n.SelectSingleNode("orgName"))); + } } //this will check for existing macros with the same alias @@ -1028,7 +1094,8 @@ namespace umbraco.cms.businesslogic.packager } - public static void updatePackageInfo(Guid Package, int VersionMajor, int VersionMinor, int VersionPatch, User User) { + public static void updatePackageInfo(Guid Package, int VersionMajor, int VersionMinor, int VersionPatch, User User) + { } } @@ -1109,7 +1176,7 @@ namespace umbraco.cms.businesslogic.packager if (Id == 0) { // The method is synchronized - SqlHelper.ExecuteNonQuery("INSERT INTO umbracoInstalledPackages (uninstalled, upgradeId, installDate, userId, versionMajor, versionMinor, versionPatch) VALUES (@uninstalled, @upgradeId, @installDate, @userId, @versionMajor, @versionMinor, @versionPatch)",values); + SqlHelper.ExecuteNonQuery("INSERT INTO umbracoInstalledPackages (uninstalled, upgradeId, installDate, userId, versionMajor, versionMinor, versionPatch) VALUES (@uninstalled, @upgradeId, @installDate, @userId, @versionMajor, @versionMinor, @versionPatch)", values); Id = SqlHelper.ExecuteScalar("SELECT MAX(id) FROM umbracoInstalledPackages"); } @@ -1133,7 +1200,7 @@ namespace umbraco.cms.businesslogic.packager get { return _uninstalled; } set { _uninstalled = value; } } - + private User _user; @@ -1142,7 +1209,7 @@ namespace umbraco.cms.businesslogic.packager get { return _user; } set { _user = value; } } - + private DateTime _installDate; @@ -1151,7 +1218,7 @@ namespace umbraco.cms.businesslogic.packager get { return _installDate; } set { _installDate = value; } } - + private int _id; @@ -1160,7 +1227,7 @@ namespace umbraco.cms.businesslogic.packager get { return _id; } set { _id = value; } } - + private int _upgradeId; @@ -1169,7 +1236,7 @@ namespace umbraco.cms.businesslogic.packager get { return _upgradeId; } set { _upgradeId = value; } } - + private Guid _packageId; @@ -1178,7 +1245,7 @@ namespace umbraco.cms.businesslogic.packager get { return _packageId; } set { _packageId = value; } } - + private int _versionPatch; @@ -1187,7 +1254,7 @@ namespace umbraco.cms.businesslogic.packager get { return _versionPatch; } set { _versionPatch = value; } } - + private int _versionMinor; @@ -1196,7 +1263,7 @@ namespace umbraco.cms.businesslogic.packager get { return _versionMinor; } set { _versionMinor = value; } } - + private int _versionMajor; @@ -1207,6 +1274,6 @@ namespace umbraco.cms.businesslogic.packager } - + } }