From b384f1b0c3265329064be5aeac9e5627cdfa5118 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 11:39:43 +0200 Subject: [PATCH 1/7] adds a private identifier validation method Fixes issue with Umbraco Forms not being able to save --- .../services/contenteditinghelper.service.js | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js index 4576f3344b..ae4ca3cde8 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/contenteditinghelper.service.js @@ -2,13 +2,32 @@ /** * @ngdoc service * @name umbraco.services.contentEditingHelper -* @description A helper service for most editors, some methods are specific to content/media/member model types but most are used by +* @description A helper service for most editors, some methods are specific to content/media/member model types but most are used by * all editors to share logic and reduce the amount of replicated code among editors. **/ function contentEditingHelper(fileManager, $q, $location, $routeParams, notificationsService, serverValidationManager, dialogService, formHelper, appState, keyboardService) { + function isValidIdentifier(id){ + //empty id <= 0 + if(angular.isNumber(id) && id > 0){ + return true; + } + + //empty guid + if(id === "00000000-0000-0000-0000-000000000000"){ + return false; + } + + //empty string / alias + if(id === ""){ + return false; + } + + return true; + } + return { - + /** Used by the content editor and mini content editor to perform saving operations */ contentEditorPerformSave: function (args) { if (!angular.isObject(args)) { @@ -30,7 +49,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica var self = this; var deferred = $q.defer(); - + if (!args.scope.busy && formHelper.submitForm({ scope: args.scope, statusMessage: args.statusMessage })) { args.scope.busy = true; @@ -71,9 +90,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica return deferred.promise; }, - + /** Returns the action button definitions based on what permissions the user has. - The content.allowedActions parameter contains a list of chars, each represents a button by permission so + The content.allowedActions parameter contains a list of chars, each represents a button by permission so here we'll build the buttons according to the chars of the user. */ configureContentEditorButtons: function (args) { @@ -136,7 +155,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica handler: args.methods.unPublish }; default: - return null; + return null; } } @@ -160,8 +179,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica //Now we need to make the drop down button list, this is also slightly tricky because: //We cannot have any buttons if there's no default button above. - //We cannot have the unpublish button (Z) when there's no publish permission. - //We cannot have the unpublish button (Z) when the item is not published. + //We cannot have the unpublish button (Z) when there's no publish permission. + //We cannot have the unpublish button (Z) when the item is not published. if (buttons.defaultButton) { //get the last index of the button order @@ -174,7 +193,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } - //if we are not creating, then we should add unpublish too, + //if we are not creating, then we should add unpublish too, // so long as it's already published and if the user has access to publish if (!args.create) { if (args.content.publishDate && _.contains(args.content.allowedActions, "U")) { @@ -235,13 +254,13 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } } } - + actions.push(defaultAction); //Now we need to make the drop down button list, this is also slightly tricky because: //We cannot have any buttons if there's no default button above. - //We cannot have the unpublish button (Z) when there's no publish permission. - //We cannot have the unpublish button (Z) when the item is not published. + //We cannot have the unpublish button (Z) when there's no publish permission. + //We cannot have the unpublish button (Z) when the item is not published. if (defaultAction) { //get the last index of the button order var lastIndex = _.indexOf(actionOrder, defaultAction); @@ -253,7 +272,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } } - //if we are not creating, then we should add unpublish too, + //if we are not creating, then we should add unpublish too, // so long as it's already published and if the user has access to publish if (!creating) { if (content.publishDate && _.contains(content.allowedActions,"U")) { @@ -329,7 +348,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica var allOrigProps = this.getAllProps(origContent); var allNewProps = this.getAllProps(savedContent); - function getNewProp(alias) { + function getNewProp(alias) { return _.find(allNewProps, function (item) { return item.alias === alias; }); @@ -343,12 +362,12 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica }; //check for changed built-in properties of the content for (var o in origContent) { - + //ignore the ones listed in the array if (shouldIgnore(o)) { continue; } - + if (!_.isEqual(origContent[o], savedContent[o])) { origContent[o] = savedContent[o]; } @@ -362,8 +381,8 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica //they have changed so set the origContent prop to the new one var origVal = allOrigProps[p].value; allOrigProps[p].value = newProp.value; - - //instead of having a property editor $watch their expression to check if it has + + //instead of having a property editor $watch their expression to check if it has // been updated, instead we'll check for the existence of a special method on their model // and just call it. if (angular.isFunction(allOrigProps[p].onValueChanged)) { @@ -388,7 +407,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica * A function to handle what happens when we have validation issues from the server side */ handleSaveError: function (args) { - + if (!args.err) { throw "args.err cannot be null"; } @@ -402,7 +421,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica if (args.err.status === 400) { //now we need to look through all the validation errors if (args.err.data && (args.err.data.ModelState)) { - + //wire up the server validation errs formHelper.handleServerValidation(args.err.data.ModelState); @@ -414,7 +433,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica if (args.rebindCallback && angular.isFunction(args.rebindCallback)) { args.rebindCallback(); } - + serverValidationManager.executeAndClearAllSubscriptions(); } @@ -453,7 +472,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } if (!this.redirectToCreatedContent(args.redirectId ? args.redirectId : args.savedContent.id)) { - + //we are not redirecting because this is not new content, it is existing content. In this case // we need to detect what properties have changed and re-bind them with the server data. //call the callback @@ -471,14 +490,14 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica * * @description * Changes the location to be editing the newly created content after create was successful. - * We need to decide if we need to redirect to edito mode or if we will remain in create mode. + * We need to decide if we need to redirect to edito mode or if we will remain in create mode. * We will only need to maintain create mode if we have not fulfilled the basic requirements for creating an entity which is at least having a name and ID */ redirectToCreatedContent: function (id, modelState) { //only continue if we are currently in create mode and if there is no 'Name' modelstate errors // since we need at least a name to create content. - if ($routeParams.create && (id > 0 && (!modelState || !modelState["Name"]))) { + if ($routeParams.create && (isValidIdentifier(id) && (!modelState || !modelState["Name"]))) { //need to change the location to not be in 'create' mode. Currently the route will be something like: // /belle/#/content/edit/1234?doctype=newsArticle&create=true @@ -487,7 +506,7 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica //clear the query strings $location.search(""); - + //change to new path $location.path("/" + $routeParams.section + "/" + $routeParams.tree + "/" + $routeParams.method + "/" + id); //don't add a browser history for this @@ -498,4 +517,4 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica } }; } -angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper); \ No newline at end of file +angular.module('umbraco.services').factory('contentEditingHelper', contentEditingHelper); From 60041a2893361805c6b36b89c527fce34acec02a Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 13:12:44 +0200 Subject: [PATCH 2/7] U4-7058 New content type editor: saving new data types Removes toggle from repository --- .../Repositories/DataTypeDefinitionRepository.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs index e88152490b..d00d6dd2ee 100644 --- a/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/DataTypeDefinitionRepository.cs @@ -36,11 +36,9 @@ namespace Umbraco.Core.Persistence.Repositories _contentTypeRepository = contentTypeRepository; _preValRepository = new DataTypePreValueRepository(work, CacheHelper.CreateDisabledCacheHelper(), logger, sqlSyntax); - EnsureUniqueNaming = true; } - public bool EnsureUniqueNaming { get; set; } - + #region Overrides of RepositoryBase protected override IDataTypeDefinition PerformGet(int id) @@ -429,8 +427,7 @@ AND umbracoNode.id <> @id", private string EnsureUniqueNodeName(string nodeName, int id = 0) { - if (EnsureUniqueNaming == false) - return nodeName; + var sql = new Sql(); sql.Select("*") From 6dc4e2d2aee61963c8f993ca05048ea687a82599 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 15:30:08 +0200 Subject: [PATCH 3/7] U4-4761 Member translation Makes all trees translateable, does not break the current titles set on atttributes, but for trees that does not have a title set, the tree wil try to look up a treeheader. --- src/Umbraco.Web/Trees/MemberTreeController.cs | 2 +- src/Umbraco.Web/Trees/TreeAttribute.cs | 2 +- src/Umbraco.Web/Trees/TreeController.cs | 21 ++++++++++++++++++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Umbraco.Web/Trees/MemberTreeController.cs b/src/Umbraco.Web/Trees/MemberTreeController.cs index e80cabecfa..8c86793485 100644 --- a/src/Umbraco.Web/Trees/MemberTreeController.cs +++ b/src/Umbraco.Web/Trees/MemberTreeController.cs @@ -27,7 +27,7 @@ namespace Umbraco.Web.Trees Constants.Applications.Media, Constants.Applications.Members)] [LegacyBaseTree(typeof (loadMembers))] - [Tree(Constants.Applications.Members, Constants.Trees.Members, "Members")] + [Tree(Constants.Applications.Members, Constants.Trees.Members)] [PluginController("UmbracoTrees")] [CoreTree] public class MemberTreeController : TreeController diff --git a/src/Umbraco.Web/Trees/TreeAttribute.cs b/src/Umbraco.Web/Trees/TreeAttribute.cs index 72ae4044e9..67543085c1 100644 --- a/src/Umbraco.Web/Trees/TreeAttribute.cs +++ b/src/Umbraco.Web/Trees/TreeAttribute.cs @@ -20,7 +20,7 @@ namespace Umbraco.Web.Trees /// The sort order. public TreeAttribute(string appAlias, string alias, - string title, + string title = "", string iconClosed = "icon-folder", string iconOpen = "icon-folder-open", bool initialize = true, diff --git a/src/Umbraco.Web/Trees/TreeController.cs b/src/Umbraco.Web/Trees/TreeController.cs index 14a25b6f02..2f3bc8efb6 100644 --- a/src/Umbraco.Web/Trees/TreeController.cs +++ b/src/Umbraco.Web/Trees/TreeController.cs @@ -1,8 +1,12 @@ using System; using System.Collections.Concurrent; +using System.Globalization; using System.Linq; using System.Net.Http.Formatting; +using System.Threading; +using System.Web.Security; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Web.Models.Trees; using Umbraco.Web.Mvc; @@ -37,7 +41,22 @@ namespace Umbraco.Web.Trees /// public override string RootNodeDisplayName { - get { return _attribute.Title; } + get + { + //if title is defined, return that + if(string.IsNullOrEmpty(_attribute.Title) == false) + return _attribute.Title; + + + //try to look up a tree header matching the tree alias + var culture = CultureInfo.GetCultureInfo(Security.CurrentUser.Language.Replace("_", "-")); + var localizedLabel = Services.TextService.Localize("treeHeaders/" + _attribute.Alias, culture); + if (string.IsNullOrEmpty(localizedLabel) == false) + return localizedLabel; + + //is returned to signal that a label was not found + return "[" + _attribute.Alias + "]"; + } } /// From 8a3deb52708fb38773320ba65c58736a84b8414a Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 15:30:28 +0200 Subject: [PATCH 4/7] Minor css tweak to tabview control --- src/umbraco.controls/TabView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/umbraco.controls/TabView.cs b/src/umbraco.controls/TabView.cs index 8e796b0861..334a9f4212 100644 --- a/src/umbraco.controls/TabView.cs +++ b/src/umbraco.controls/TabView.cs @@ -33,7 +33,7 @@ namespace umbraco.uicontrols { base.CreateChildControls(); _tabList.TagName = "ul"; - _tabList.Attributes.Add("class", "nav nav-tabs umb-nav-tabs span12"); + _tabList.Attributes.Add("class", "nav nav-tabs umb-nav-tabs span12 -padding-left"); base.row.Controls.Add(_tabList); _body.TagName = "div"; @@ -41,7 +41,7 @@ namespace umbraco.uicontrols { base.Controls.Add(_body); _tabsHolder.TagName = "div"; - _tabsHolder.Attributes.Add("class", "tab-content form-horizontal umb-tab-content"); + _tabsHolder.Attributes.Add("class", "tab-content form-horizontal"); _tabsHolder.ID = this.ID + "_content"; _body.Controls.Add(_tabsHolder); From c13ae2088526d2fdbf7cd5984fab8759aab0ed09 Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 15:49:17 +0200 Subject: [PATCH 5/7] U4-4761: adds overload to treeattribute --- src/Umbraco.Web/Trees/TreeAttribute.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/TreeAttribute.cs b/src/Umbraco.Web/Trees/TreeAttribute.cs index 67543085c1..e3e302fb69 100644 --- a/src/Umbraco.Web/Trees/TreeAttribute.cs +++ b/src/Umbraco.Web/Trees/TreeAttribute.cs @@ -8,6 +8,16 @@ namespace Umbraco.Web.Trees [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class TreeAttribute : Attribute { + /// + /// Initializes a new instance of the class. + /// + /// The app alias. + /// The alias. + public TreeAttribute(string appAlias, + string alias) : this(appAlias, alias, null) + { + } + /// /// Initializes a new instance of the class. /// @@ -20,7 +30,7 @@ namespace Umbraco.Web.Trees /// The sort order. public TreeAttribute(string appAlias, string alias, - string title = "", + string title, string iconClosed = "icon-folder", string iconOpen = "icon-folder-open", bool initialize = true, @@ -35,6 +45,8 @@ namespace Umbraco.Web.Trees SortOrder = sortOrder; } + + public string ApplicationAlias { get; private set; } public string Alias { get; private set; } public string Title { get; private set; } From 8d57e0cd1dcdde5df0d2045cd24976a7426231ef Mon Sep 17 00:00:00 2001 From: Per Ploug Date: Thu, 24 Sep 2015 15:52:44 +0200 Subject: [PATCH 6/7] Less hacky way to lookup current user culture --- src/Umbraco.Web/Trees/TreeController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Umbraco.Web/Trees/TreeController.cs b/src/Umbraco.Web/Trees/TreeController.cs index 2f3bc8efb6..15dab9ba94 100644 --- a/src/Umbraco.Web/Trees/TreeController.cs +++ b/src/Umbraco.Web/Trees/TreeController.cs @@ -49,7 +49,7 @@ namespace Umbraco.Web.Trees //try to look up a tree header matching the tree alias - var culture = CultureInfo.GetCultureInfo(Security.CurrentUser.Language.Replace("_", "-")); + var culture = Security.CurrentUser.GetUserCulture(Services.TextService); var localizedLabel = Services.TextService.Localize("treeHeaders/" + _attribute.Alias, culture); if (string.IsNullOrEmpty(localizedLabel) == false) return localizedLabel; From 407cd0ca60617966393825cf367ce3d8943709b3 Mon Sep 17 00:00:00 2001 From: Shannon Date: Fri, 25 Sep 2015 11:29:09 +0200 Subject: [PATCH 7/7] Streamlines TypeFinder with better lock and an error check on app_code which could otherwise bring down the app --- src/Umbraco.Core/TypeFinder.cs | 251 ++++++++++++--------------------- 1 file changed, 93 insertions(+), 158 deletions(-) diff --git a/src/Umbraco.Core/TypeFinder.cs b/src/Umbraco.Core/TypeFinder.cs index 6323227893..0b4e85e237 100644 --- a/src/Umbraco.Core/TypeFinder.cs +++ b/src/Umbraco.Core/TypeFinder.cs @@ -26,11 +26,8 @@ namespace Umbraco.Core /// public static class TypeFinder { - private static readonly HashSet LocalFilteredAssemblyCache = new HashSet(); - private static readonly ReaderWriterLockSlim LocalFilteredAssemblyCacheLocker = new ReaderWriterLockSlim(); - private static HashSet _allAssemblies = null; - private static HashSet _binFolderAssemblies = null; - private static readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim(); + private static volatile HashSet _localFilteredAssemblyCache = null; + private static readonly object LocalFilteredAssemblyCacheLocker = new object(); /// /// lazily load a reference to all assemblies and only local assemblies. @@ -46,162 +43,97 @@ namespace Umbraco.Core /// internal static HashSet GetAllAssemblies() { - using (var lck = new UpgradeableReadLock(Locker)) + return AllAssemblies.Value; + } + + //Lazy access to the all assemblies list + private static readonly Lazy> AllAssemblies = new Lazy>(() => + { + HashSet assemblies = null; + try { - if (_allAssemblies == null) + var isHosted = HttpContext.Current != null; + + try { + if (isHosted) + { + assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); + } + } + catch (InvalidOperationException e) + { + if (!(e.InnerException is SecurityException)) + throw; + } - lck.UpgradeToWriteLock(); + if (assemblies == null) + { + //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have + // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. + var binFolder = IOHelper.GetRootDirectoryBinFolder(); + var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); + //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; + //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); + assemblies = new HashSet(); + foreach (var a in binAssemblyFiles) + { + try + { + var assName = AssemblyName.GetAssemblyName(a); + var ass = Assembly.Load(assName); + assemblies.Add(ass); + } + catch (Exception e) + { + if (e is SecurityException || e is BadImageFormatException) + { + //swallow these exceptions + } + else + { + throw; + } + } + } + } - HashSet assemblies = null; + //if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies. + if (!assemblies.Any()) + { + foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) + { + assemblies.Add(a); + } + } + + //here we are trying to get the App_Code assembly + var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported + var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code"))); + //check if the folder exists and if there are any files in it with the supported file extensions + if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))) + { try { - var isHosted = HttpContext.Current != null; - - try - { - if (isHosted) - { - assemblies = new HashSet(BuildManager.GetReferencedAssemblies().Cast()); - } - } - catch (InvalidOperationException e) - { - if (!(e.InnerException is SecurityException)) - throw; - } - - - if (assemblies == null) - { - //NOTE: we cannot use AppDomain.CurrentDomain.GetAssemblies() because this only returns assemblies that have - // already been loaded in to the app domain, instead we will look directly into the bin folder and load each one. - var binFolder = IOHelper.GetRootDirectoryBinFolder(); - var binAssemblyFiles = Directory.GetFiles(binFolder, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - //var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; - //var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - assemblies = new HashSet(); - foreach (var a in binAssemblyFiles) - { - try - { - var assName = AssemblyName.GetAssemblyName(a); - var ass = Assembly.Load(assName); - assemblies.Add(ass); - } - catch (Exception e) - { - if (e is SecurityException || e is BadImageFormatException) - { - //swallow these exceptions - } - else - { - throw; - } - } - } - } - - //if for some reason they are still no assemblies, then use the AppDomain to load in already loaded assemblies. - if (!assemblies.Any()) - { - foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) - { - assemblies.Add(a); - } - } - - //here we are trying to get the App_Code assembly - var fileExtensions = new[] { ".cs", ".vb" }; //only vb and cs files are supported - var appCodeFolder = new DirectoryInfo(IOHelper.MapPath(IOHelper.ResolveUrl("~/App_code"))); - //check if the folder exists and if there are any files in it with the supported file extensions - if (appCodeFolder.Exists && (fileExtensions.Any(x => appCodeFolder.GetFiles("*" + x).Any()))) - { - var appCodeAssembly = Assembly.Load("App_Code"); - if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already - assemblies.Add(appCodeAssembly); - } - - //now set the _allAssemblies - _allAssemblies = new HashSet(assemblies); - + var appCodeAssembly = Assembly.Load("App_Code"); + if (!assemblies.Contains(appCodeAssembly)) // BuildManager will find App_Code already + assemblies.Add(appCodeAssembly); } - catch (InvalidOperationException e) + catch (FileNotFoundException ex) { - if (!(e.InnerException is SecurityException)) - throw; - - _binFolderAssemblies = _allAssemblies; + //this will occur if it cannot load the assembly + LogHelper.Error(typeof(TypeFinder), "Could not load assembly App_Code", ex); } } - - return _allAssemblies; } - } - - /// - /// Returns only assemblies found in the bin folder that have been loaded into the app domain. - /// - /// - /// - /// This will be used if we implement App_Plugins from Umbraco v5 but currently it is not used. - /// - internal static HashSet GetBinAssemblies() - { - - if (_binFolderAssemblies == null) + catch (InvalidOperationException e) { - using (new WriteLock(Locker)) - { - var assemblies = GetAssembliesWithKnownExclusions().ToArray(); - var binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory; - var binAssemblyFiles = Directory.GetFiles(binFolder.FullName, "*.dll", SearchOption.TopDirectoryOnly).ToList(); - var domainAssemblyNames = binAssemblyFiles.Select(AssemblyName.GetAssemblyName); - var safeDomainAssemblies = new HashSet(); - var binFolderAssemblies = new HashSet(); - - foreach (var a in assemblies) - { - try - { - //do a test to see if its queryable in med trust - var assemblyFile = a.GetAssemblyFile(); - safeDomainAssemblies.Add(a); - } - catch (SecurityException) - { - //we will just ignore this because this will fail - //in medium trust for system assemblies, we get an exception but we just want to continue until we get to - //an assembly that is ok. - } - } - - foreach (var assemblyName in domainAssemblyNames) - { - try - { - var foundAssembly = - safeDomainAssemblies.FirstOrDefault(a => a.GetAssemblyFile() == assemblyName.GetAssemblyFile()); - if (foundAssembly != null) - { - binFolderAssemblies.Add(foundAssembly); - } - } - catch (SecurityException) - { - //we will just ignore this because if we are trying to do a call to: - // AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName))) - //in medium trust for system assemblies, we get an exception but we just want to continue until we get to - //an assembly that is ok. - } - } - - _binFolderAssemblies = new HashSet(binFolderAssemblies); - } + if (!(e.InnerException is SecurityException)) + throw; } - return _binFolderAssemblies; - } + + return assemblies; + }); /// /// Return a list of found local Assemblies excluding the known assemblies we don't want to scan @@ -213,20 +145,23 @@ namespace Umbraco.Core internal static HashSet GetAssembliesWithKnownExclusions( IEnumerable excludeFromResults = null) { - using (var lck = new UpgradeableReadLock(LocalFilteredAssemblyCacheLocker)) + if (_localFilteredAssemblyCache == null) { - if (LocalFilteredAssemblyCache.Any()) return LocalFilteredAssemblyCache; - - lck.UpgradeToWriteLock(); - - var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); - foreach (var a in assemblies) + lock (LocalFilteredAssemblyCacheLocker) { - LocalFilteredAssemblyCache.Add(a); + //double check + if (_localFilteredAssemblyCache == null) + { + _localFilteredAssemblyCache = new HashSet(); + var assemblies = GetFilteredAssemblies(excludeFromResults, KnownAssemblyExclusionFilter); + foreach (var a in assemblies) + { + _localFilteredAssemblyCache.Add(a); + } + } } - - return LocalFilteredAssemblyCache; } + return _localFilteredAssemblyCache; } ///