From eef13e74163cbd99e0104055bbf3bc5d69d417ea Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Mon, 1 May 2017 11:27:48 +0200 Subject: [PATCH 001/145] Fixing console search results not working when one of the data types from results does not have a [Title(...)] attribute --- Composite/Search/Crawling/SearchDocumentBuilder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composite/Search/Crawling/SearchDocumentBuilder.cs b/Composite/Search/Crawling/SearchDocumentBuilder.cs index a360413ac5..84239d4a0c 100644 --- a/Composite/Search/Crawling/SearchDocumentBuilder.cs +++ b/Composite/Search/Crawling/SearchDocumentBuilder.cs @@ -393,7 +393,7 @@ private static string GetDataTypeLabel(object datatype) var descriptor = DataMetaDataFacade.GetDataTypeDescriptor(dataTypeId); if (descriptor != null) { - return descriptor.Title; + return descriptor.Title ?? descriptor.Name; } } From 069c71d3e15292416857c08dd1c34f5715986067 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Mon, 1 May 2017 11:57:32 +0200 Subject: [PATCH 002/145] Fixing data type descriptors not being updated when some of their attributes changed. --- .../Data/DynamicTypes/DataTypeChangeDescriptor.cs | 14 ++++++++++++-- Composite/Data/DynamicTypes/DynamicTypeManager.cs | 11 ++++++++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/Composite/Data/DynamicTypes/DataTypeChangeDescriptor.cs b/Composite/Data/DynamicTypes/DataTypeChangeDescriptor.cs index 749e1d5cff..a43a9c4de1 100644 --- a/Composite/Data/DynamicTypes/DataTypeChangeDescriptor.cs +++ b/Composite/Data/DynamicTypes/DataTypeChangeDescriptor.cs @@ -9,7 +9,7 @@ namespace Composite.Data.DynamicTypes /// /// /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public sealed class DataTypeChangeDescriptor { private readonly DataTypeDescriptor _original; @@ -45,7 +45,6 @@ public bool AlteredTypeHasChanges bool alteredTypeHasChanges = false; alteredTypeHasChanges |= this.AlteredType.IsCodeGenerated != this.OriginalType.IsCodeGenerated; alteredTypeHasChanges |= this.AlteredType.Name != this.OriginalType.Name; - // alteredTypeHasChanges |= (this.AlteredType.Title != this.OriginalType.Title); alteredTypeHasChanges |= this.AlteredType.Namespace != this.OriginalType.Namespace; // Do we really need to regenerated the type if it has a new type manager type name? //alteredTypeHasChanges |= (this.AlteredType.TypeManagerTypeName != this.OriginalType.TypeManagerTypeName); @@ -64,6 +63,17 @@ public bool AlteredTypeHasChanges } + /// + /// Indicates if type's metadata should be updated. + /// + internal bool TypeHasMetaDataChanges => + !OriginalType.IsCodeGenerated + && (OriginalType.Title != AlteredType.Title + || OriginalType.LabelFieldName != AlteredType.LabelFieldName + || OriginalType.Cachable != AlteredType.Cachable + || OriginalType.Searchable != AlteredType.Searchable + || OriginalType.InternalUrlPrefix != AlteredType.InternalUrlPrefix); + /// public DataTypeDescriptor OriginalType => _original; diff --git a/Composite/Data/DynamicTypes/DynamicTypeManager.cs b/Composite/Data/DynamicTypes/DynamicTypeManager.cs index 16372aaf7c..9ecba39064 100644 --- a/Composite/Data/DynamicTypes/DynamicTypeManager.cs +++ b/Composite/Data/DynamicTypes/DynamicTypeManager.cs @@ -403,7 +403,16 @@ internal static bool EnsureUpdateStore(Type interfaceType, string providerName, var dataTypeChangeDescriptor = new DataTypeChangeDescriptor(oldDataTypeDescriptor, newDataTypeDescriptor); - if (!dataTypeChangeDescriptor.AlteredTypeHasChanges) return false; + if (!dataTypeChangeDescriptor.AlteredTypeHasChanges) + { + if (dataTypeChangeDescriptor.TypeHasMetaDataChanges) + { + Log.LogInformation(nameof(DynamicTypeManager), $"Updating data type descriptor for type '{newDataTypeDescriptor.GetFullInterfaceName()}'"); + DataMetaDataFacade.PersistMetaData(newDataTypeDescriptor); + } + + return false; + } Log.LogVerbose(nameof(DynamicTypeManager), "Updating the store for interface type '{0}' on the '{1}' data provider", interfaceType, From 99d27badf4fa7f992507dc1c578813d2fbd1d411 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Mon, 1 May 2017 15:25:52 +0200 Subject: [PATCH 003/145] Fix #438 QueryStrings stripped from RoutedData<> hyperlinks upon rendering --- .../DataInternalUrlConverter.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Composite/Plugins/Routing/InternalUrlConverters/DataInternalUrlConverter.cs b/Composite/Plugins/Routing/InternalUrlConverters/DataInternalUrlConverter.cs index bf363e0498..5d6d88f90b 100644 --- a/Composite/Plugins/Routing/InternalUrlConverters/DataInternalUrlConverter.cs +++ b/Composite/Plugins/Routing/InternalUrlConverters/DataInternalUrlConverter.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using Composite.Core; using Composite.Core.Routing; using Composite.Core.Types; using Composite.Data; @@ -36,6 +37,24 @@ public string ToPublicUrl(string internalDataUrl, UrlSpace urlSpace) if(data == null) return null; var pageUrlData = DataUrls.TryGetPageUrlData(data.ToDataReference()); + + if (internalDataUrl.IndexOf("?", StringComparison.Ordinal) > 0) + { + var parameters = new UrlBuilder(internalDataUrl).GetQueryParameters(); + + if (parameters.HasKeys()) + { + if (pageUrlData.QueryParameters == null) + { + pageUrlData.QueryParameters = parameters; + } + else + { + pageUrlData.QueryParameters.Add(parameters); + } + } + } + return pageUrlData != null ? PageUrls.BuildUrl(pageUrlData, UrlKind.Public, urlSpace) : null; } From 941d7478dadd2df82b9c44a73a28f6314b83afd3 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 3 May 2017 17:19:16 +0200 Subject: [PATCH 004/145] Previewing pdf/htm/txt files when browsing the media library --- Composite/Composite.csproj | 1 + Composite/Core/IO/MimeTypeInfo.cs | 20 ++++++++-- .../ApplicationLevelEventHandlers.cs | 1 + .../MediaUrlToEntityTokenMapper.cs | 39 +++++++++++++++++++ 4 files changed, 57 insertions(+), 4 deletions(-) create mode 100644 Composite/Plugins/Elements/UrlToEntityToken/MediaUrlToEntityTokenMapper.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 62a2d7d06f..1aefdc7d18 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -253,6 +253,7 @@ + diff --git a/Composite/Core/IO/MimeTypeInfo.cs b/Composite/Core/IO/MimeTypeInfo.cs index 3a31e36b22..8617f98a2c 100644 --- a/Composite/Core/IO/MimeTypeInfo.cs +++ b/Composite/Core/IO/MimeTypeInfo.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Concurrent; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Configuration; using System.Diagnostics.CodeAnalysis; @@ -8,7 +7,6 @@ using System.Web.Hosting; using System.Xml.Linq; using Composite.C1Console.Forms.CoreUiControls; -using Composite.Core.Localization; using Composite.Core.ResourceSystem; using Composite.Core.ResourceSystem.Icons; @@ -84,6 +82,9 @@ private static ResourceHandle GetIconHandle(string name) /// public static string QuickTime => "video/quicktime"; + /// + public static string Pdf = "application/pdf"; + /// public static string Wmv => "video/x-ms-wmv"; @@ -175,7 +176,7 @@ static MimeTypeInfo() // Applications RegisterMimeType("application/postscript", "eps", "mimetype-pps", true); RegisterMimeType("application/msaccess", "mdb", "mimetype-mdb", true); - RegisterMimeType("application/pdf", "pdf", "mimetype-pdf", true); + RegisterMimeType(Pdf, "pdf", "mimetype-pdf", true); RegisterMimeType("application/vnd.ms-powerpoint", "ppt", "mimetype-ppt", true); RegisterMimeType("application/vnd.openxmlformats-officedocument.presentationml.presentation", "pptx", "mimetype-ppt", true); RegisterMimeType("application/msword", "doc", "mimetype-doc", true); @@ -420,6 +421,17 @@ internal static bool IsTextFile(string mimeType) return canonicalMimeType.StartsWith("text") || _textMimeTypes.Contains(canonicalMimeType); } + /// + /// Indicates whether a file of a given mime type can be previewed by browser in an iframe. + /// + /// + /// + internal static bool IsBrowserPreviewableFile(string mimeType) + { + return mimeType == Pdf + || mimeType == Html + || mimeType == Text; + } internal static string TryGetLocalizedName(string mimeType) { diff --git a/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs index b2b19cc099..f3a926cdda 100644 --- a/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs +++ b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs @@ -94,6 +94,7 @@ public static void Application_Start(object sender, EventArgs e) private static void InitializeServices() { UrlToEntityTokenFacade.Register(new DataUrlToEntityTokenMapper()); + UrlToEntityTokenFacade.Register(new MediaUrlToEntityTokenMapper()); UrlToEntityTokenFacade.Register(new ServerLogUrlToEntityTokenMapper()); var services = ServiceLocator.ServiceCollection; diff --git a/Composite/Plugins/Elements/UrlToEntityToken/MediaUrlToEntityTokenMapper.cs b/Composite/Plugins/Elements/UrlToEntityToken/MediaUrlToEntityTokenMapper.cs new file mode 100644 index 0000000000..886471dedd --- /dev/null +++ b/Composite/Plugins/Elements/UrlToEntityToken/MediaUrlToEntityTokenMapper.cs @@ -0,0 +1,39 @@ +using Composite.C1Console.Elements; +using Composite.C1Console.Security; +using Composite.Core; +using Composite.Core.IO; +using Composite.Core.Routing; +using Composite.Data; +using Composite.Data.Types; + +namespace Composite.Plugins.Elements.UrlToEntityToken +{ + internal class MediaUrlToEntityTokenMapper : IUrlToEntityTokenMapper + { + public string TryGetUrl(EntityToken entityToken) => null; + + public BrowserViewSettings TryGetBrowserViewSettings(EntityToken entityToken, bool showPublishedView) + { + if (!(entityToken is DataEntityToken dataEntityToken) || dataEntityToken.InterfaceType != typeof(IMediaFile)) + { + return null; + } + + var file = (IMediaFile) dataEntityToken.Data; + + if(file == null || !MimeTypeInfo.IsBrowserPreviewableFile(file.MimeType)) { + return null; + } + + string url = MediaUrls.BuildUrl(file); + var urlBuilder = new UrlBuilder(url) + { + ["download"] = "false" + }; + + return new BrowserViewSettings { Url = urlBuilder.ToString(), ToolingOn = false }; + } + + public EntityToken TryGetEntityToken(string url) => null; + } +} From 67fb0990362b18d131039c3bc144b305b0c78921 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 4 May 2017 11:40:18 +0200 Subject: [PATCH 005/145] WebsiteFileElementProvider - refactoring --- .../WebsiteFileElementProvider.cs | 178 ++++++++---------- 1 file changed, 76 insertions(+), 102 deletions(-) diff --git a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs index b02babab34..a51f8087f6 100644 --- a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs @@ -27,27 +27,27 @@ namespace Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider internal sealed class WebsiteFileElementProvider : IHooklessElementProvider, IDataExchangingElementProvider { private ElementProviderContext _context; - private string _rootPath; - private string _rootLabel; - private string _folderWhiteListKeyName; - private List _manageableKeyNames; - private List _manageableKeyNameLabels; - - private static ResourceHandle FolderIcon { get { return CommonElementIcons.Folder; } } - private static ResourceHandle OpenFolderIcon { get { return CommonElementIcons.FolderOpen; } } - private static ResourceHandle EmptyFolderIcon { get { return CommonElementIcons.FolderOpen; } } - private static ResourceHandle ReadOnlyFolderOpen { get { return GetIconHandle("website-read-only-folder-open"); } } - private static ResourceHandle ReadOnlyFolderClosed { get { return GetIconHandle("website-read-only-folder-closed"); } } - private static ResourceHandle AddWebsiteFolder { get { return GetIconHandle("website-add-website-folder"); } } - private static ResourceHandle AddWebsiteFile { get { return GetIconHandle("website-create-website-file"); } } - private static ResourceHandle DeleteWebsiteFolder { get { return GetIconHandle("website-delete-website-folder"); } } - private static ResourceHandle DeleteWebsiteFile { get { return GetIconHandle("website-delete-website-file"); } } - private static ResourceHandle EditWebsiteFile { get { return GetIconHandle("website-edit-website-file"); } } - private static ResourceHandle UploadWebsiteFile { get { return GetIconHandle("website-upload-website-file"); } } - private static ResourceHandle UploadAndExtractZipFile { get { return GetIconHandle("website-upload-zip-file"); } } - private static ResourceHandle DownloadWebsiteFile { get { return GetIconHandle("media-download-file"); } } - private static ResourceHandle AddFolderToWhiteList { get { return GetIconHandle("website-add-folder-to-whitelist"); } } - private static ResourceHandle RemoveFolderFromWhiteList { get { return GetIconHandle("website-remove-folder-from-whitelist"); } } + private readonly string _rootPath; + private readonly string _rootLabel; + private readonly string _folderWhiteListKeyName; + private readonly List _manageableKeyNames; + private readonly List _manageableKeyNameLabels; + + private static ResourceHandle FolderIcon => CommonElementIcons.Folder; + private static ResourceHandle OpenFolderIcon => CommonElementIcons.FolderOpen; + private static ResourceHandle EmptyFolderIcon => CommonElementIcons.FolderOpen; + private static ResourceHandle ReadOnlyFolderOpen => GetIconHandle("website-read-only-folder-open"); + private static ResourceHandle ReadOnlyFolderClosed => GetIconHandle("website-read-only-folder-closed"); + private static ResourceHandle AddWebsiteFolder => GetIconHandle("website-add-website-folder"); + private static ResourceHandle AddWebsiteFile => GetIconHandle("website-create-website-file"); + private static ResourceHandle DeleteWebsiteFolder => GetIconHandle("website-delete-website-folder"); + private static ResourceHandle DeleteWebsiteFile => GetIconHandle("website-delete-website-file"); + private static ResourceHandle EditWebsiteFile => GetIconHandle("website-edit-website-file"); + private static ResourceHandle UploadWebsiteFile => GetIconHandle("website-upload-website-file"); + private static ResourceHandle UploadAndExtractZipFile => GetIconHandle("website-upload-zip-file"); + private static ResourceHandle DownloadWebsiteFile => GetIconHandle("media-download-file"); + private static ResourceHandle AddFolderToWhiteList => GetIconHandle("website-add-folder-to-whitelist"); + private static ResourceHandle RemoveFolderFromWhiteList => GetIconHandle("website-remove-folder-from-whitelist"); private static readonly ActionGroup PrimaryFolderActionGroup = new ActionGroup(ActionGroupPriority.PrimaryHigh); @@ -55,14 +55,14 @@ internal sealed class WebsiteFileElementProvider : IHooklessElementProvider, IDa // private static readonly ActionGroup PrimaryFileToolsActionGroup = new ActionGroup("FileTools", ActionGroupPriority.PrimaryMedium); private static readonly ActionGroup PrimaryFolderToolsActionGroup = new ActionGroup("FolderTools", ActionGroupPriority.PrimaryMedium); - private static readonly PermissionType[] _addNewWebsiteFolderPermissionTypes = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _addNewWebsiteFilePermissionTypes = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _uploadAndExtractZipFileWorkflow = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _deleteWebsiteFolderPermissionTypes = new PermissionType[] { PermissionType.Delete }; - private static readonly PermissionType[] _deleteWebsiteFilePermissionTypes = new PermissionType[] { PermissionType.Delete }; - private static readonly PermissionType[] _editWebsiteFilePermissionTypes = new PermissionType[] { PermissionType.Edit }; - private static readonly PermissionType[] _uploadWebsiteFilePermissionTypes = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _changeWhiteListPermissionTypes = new PermissionType[] { PermissionType.Administrate }; + private static readonly PermissionType[] _addNewWebsiteFolderPermissionTypes = { PermissionType.Add }; + private static readonly PermissionType[] _addNewWebsiteFilePermissionTypes = { PermissionType.Add }; + private static readonly PermissionType[] _uploadAndExtractZipFileWorkflow = { PermissionType.Add }; + private static readonly PermissionType[] _deleteWebsiteFolderPermissionTypes = { PermissionType.Delete }; + private static readonly PermissionType[] _deleteWebsiteFilePermissionTypes = { PermissionType.Delete }; + private static readonly PermissionType[] _editWebsiteFilePermissionTypes = { PermissionType.Edit }; + private static readonly PermissionType[] _uploadWebsiteFilePermissionTypes = { PermissionType.Add }; + private static readonly PermissionType[] _changeWhiteListPermissionTypes = { PermissionType.Administrate }; public WebsiteFileElementProvider(WebsiteFileElementProviderData objectConfiguration) @@ -70,7 +70,7 @@ public WebsiteFileElementProvider(WebsiteFileElementProviderData objectConfigura _rootLabel = objectConfiguration.RootLabel; _folderWhiteListKeyName = objectConfiguration.FolderWhiteListKeyName; - if (string.IsNullOrEmpty(objectConfiguration.ManageableKeyNames) == false) + if (!string.IsNullOrEmpty(objectConfiguration.ManageableKeyNames)) { _manageableKeyNames = objectConfiguration.ManageableKeyNames.Split(',').ToList(); _manageableKeyNameLabels = StringResourceSystemFacade.SplitParseableStrings(objectConfiguration.ManageableKeyNameLabels, ',').ToList(); @@ -87,7 +87,7 @@ public WebsiteFileElementProvider(WebsiteFileElementProviderData objectConfigura public ElementProviderContext Context { - set { _context = value; } + set => _context = value; } @@ -109,7 +109,7 @@ public IEnumerable GetRoots(SearchToken seachToken) //element.MovabilityInfo.AddDropType(typeof(WebsiteFolder)); //element.MovabilityInfo.AddDropType(typeof(WebsiteFile)); - List myWhiteLists = DataFacade.GetData(f => f.KeyName == _folderWhiteListKeyName).ToList(); + //List myWhiteLists = DataFacade.GetData(f => f.KeyName == _folderWhiteListKeyName).ToList(); if (string.IsNullOrEmpty(_folderWhiteListKeyName) || DataFacade.GetData(f => f.KeyName == _folderWhiteListKeyName && f.TildeBasedPath == "~\\").Any()) { @@ -195,7 +195,7 @@ public IEnumerable GetRoots(SearchToken seachToken) } }); - List actionsToAppend = new List(); + var actionsToAppend = new List(); IEnumerable manageableFolderWhiteLists = DataFacade.GetData().ToList(); AppendFolderManagementActions( PathUtil.BaseDirectory, manageableFolderWhiteLists, actionsToAppend); @@ -213,37 +213,27 @@ public IEnumerable GetChildren(EntityToken entityToken, SearchToken sea { return GetChildrenOnPath(_rootPath, searchToken); } - else if (entityToken is WebsiteFileElementProviderEntityToken) + + if (entityToken is WebsiteFileElementProviderEntityToken websiteFileEntityToken) { - string path = ((WebsiteFileElementProviderEntityToken)entityToken).Path; + string path = websiteFileEntityToken.Path; if (C1Directory.Exists(path)) { return GetChildrenOnPath(path, searchToken); } - else - { - return new Element[] { }; - } - } - else - { - throw new NotImplementedException(); + + return new Element[] { }; } + + throw new NotImplementedException(); } public object GetData(string name) { - if (name == "RootPath") - { - return _rootPath; - } - else - { - return null; - } + return name == "RootPath" ? _rootPath : null; } @@ -265,9 +255,9 @@ private IEnumerable CreateFileElements(IEnumerable website { foreach (WebsiteFile websiteFile in websiteFiles) { - Element element = new Element(_context.CreateElementHandle(new WebsiteFileElementProviderEntityToken(_context.ProviderName, websiteFile.FullPath, _rootPath))) + var element = new Element(_context.CreateElementHandle(new WebsiteFileElementProviderEntityToken(_context.ProviderName, websiteFile.FullPath, _rootPath))) { - VisualData = new ElementVisualizedData() + VisualData = new ElementVisualizedData { Label = websiteFile.FileName, ToolTip = websiteFile.FileName, @@ -298,16 +288,16 @@ private IEnumerable CreateFolderElements(IEnumerable web IEnumerable manageableFolderWhiteLists = DataFacade.GetData().ToList(); IEnumerable myWhiteLists = null; - if (string.IsNullOrEmpty(_folderWhiteListKeyName) == false) + if (!string.IsNullOrEmpty(_folderWhiteListKeyName)) { myWhiteLists = DataFacade.GetData(f=>f.KeyName==_folderWhiteListKeyName).ToList(); } foreach (WebsiteFolder websiteFolder in websiteFolders) { - Element element = new Element(_context.CreateElementHandle(new WebsiteFileElementProviderEntityToken(_context.ProviderName, websiteFolder.FullPath, _rootPath))) + var element = new Element(_context.CreateElementHandle(new WebsiteFileElementProviderEntityToken(_context.ProviderName, websiteFolder.FullPath, _rootPath))) { - VisualData = new ElementVisualizedData() + VisualData = new ElementVisualizedData { Label = websiteFolder.FolderName, ToolTip = websiteFolder.FolderName, @@ -346,7 +336,7 @@ where folder.FolderName.ToLowerInvariant().Contains(searchToken.Keyword.ToLowerI select folder; } - if (string.IsNullOrEmpty(_folderWhiteListKeyName) == false) + if (!string.IsNullOrEmpty(_folderWhiteListKeyName)) { List whiteList = DataFacade.GetData().Where(f => f.KeyName == _folderWhiteListKeyName).ToList(); @@ -363,7 +353,7 @@ where whiteList.Any(f => folder.FullPath.StartsWith(f.GetFullPath()) || f.GetFul private IEnumerable GetFilesOnPath(string parentPath, SearchToken searchToken) { - if (string.IsNullOrEmpty(_folderWhiteListKeyName) == false) + if (!string.IsNullOrEmpty(_folderWhiteListKeyName)) { string parentTildaPath = IFolderWhiteListExtensions.GetTildePath(parentPath); // NOTE: linq2sql conversion doesn't support xxx.StartsWith(someParameter) construction, that's why we're using two ling statements to get the data @@ -523,14 +513,14 @@ private void AppendFolderManagementActions(string websiteFolderPath, IEnumerable string keyName = _manageableKeyNames[i]; string itemLabel = StringResourceSystemFacade.ParseString(_manageableKeyNameLabels[i]); - ResourceHandle icon = null; - string label = null; - string tooltip = null; - WorkflowActionToken workflowActionToken = null; - - ActionCheckedStatus checkedStatus = ActionCheckedStatus.Uncheckable; + ResourceHandle icon; + string label; + string tooltip; + WorkflowActionToken workflowActionToken; - if (manageableFolderWhiteLists.Where(f => f.KeyName == keyName && f.TildeBasedPath == IFolderWhiteListExtensions.GetTildePath(websiteFolderPath)).Any()) + ActionCheckedStatus checkedStatus; + var tildeBasedPath = IFolderWhiteListExtensions.GetTildePath(websiteFolderPath); + if (manageableFolderWhiteLists.Any(f => f.KeyName == keyName && f.TildeBasedPath == tildeBasedPath)) { workflowActionToken = new WorkflowActionToken(WorkflowFacade.GetWorkflowType("Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider.RemoveWebsiteFolderFromWhiteListWorkflow"), _changeWhiteListPermissionTypes); label = StringResourceSystemFacade.GetString("Composite.Plugins.WebsiteFileElementProvider", "RemoveFolderFromWhiteListTitle"); @@ -653,7 +643,7 @@ private IEnumerable GetFileActions(WebsiteFile websiteFile) private static bool IsDeleteActionAllowed(WebsiteEntity websiteEntity) { - if ((websiteEntity is WebsiteFile)) + if (websiteEntity is WebsiteFile) { return true; //WebsiteFile websiteFile = websiteEntity as WebsiteFile; @@ -662,7 +652,7 @@ private static bool IsDeleteActionAllowed(WebsiteEntity websiteEntity) //return _editableMimeTypes.Contains(canonical); } - else if ((websiteEntity is WebsiteFolder)) + if (websiteEntity is WebsiteFolder) { //return false; // Deleting a folder causes the webserver to restart... @@ -672,19 +662,15 @@ private static bool IsDeleteActionAllowed(WebsiteEntity websiteEntity) return true; } - else - { - throw new NotImplementedException(); - } + + throw new NotImplementedException(); } private static bool IsEditActionAllowed(WebsiteEntity websiteEntity) { - if (websiteEntity is WebsiteFile) + if (websiteEntity is WebsiteFile websiteFile) { - WebsiteFile websiteFile = websiteEntity as WebsiteFile; - return MimeTypeInfo.IsTextFile(websiteFile.MimeType); } @@ -713,10 +699,8 @@ internal static ResourceHandle WebsiteFileIcon(string mimeType) internal sealed class DownloadFileActionExecutor : IActionExecutor { - public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowControllerServicesContainer flowControllerServicesContainer) + public static string GetDownloadLink(WebsiteFileElementProviderEntityToken fileToken) { - var fileToken = (WebsiteFileElementProviderEntityToken)entityToken; - var urlString = new UrlBuilder(UrlUtils.AdminRootPath + "/services/Admin/DownloadFile.ashx"); string relativeFilePath = fileToken.Path.Substring(fileToken.RootPath.Length); @@ -724,9 +708,18 @@ public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowC urlString["file"] = relativeFilePath; urlString["provider"] = fileToken.Source; + return urlString.ToString(); + } + + public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowControllerServicesContainer flowControllerServicesContainer) + { + var fileToken = (WebsiteFileElementProviderEntityToken)entityToken; + + string downloadLink = GetDownloadLink(fileToken); + string currentConsoleId = flowControllerServicesContainer.GetService().CurrentConsoleId; - ConsoleMessageQueueFacade.Enqueue(new DownloadFileMessageQueueItem(urlString.ToString()), currentConsoleId); + ConsoleMessageQueueFacade.Enqueue(new DownloadFileMessageQueueItem(downloadLink), currentConsoleId); return null; } @@ -735,32 +728,16 @@ public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowC [ActionExecutor(typeof(DownloadFileActionExecutor))] internal sealed class DownloadFileActionToken : ActionToken { - private static readonly IEnumerable _permissionTypes = new[] { PermissionType.Administrate, PermissionType.Edit }; + private static readonly PermissionType[] _permissionTypes = { PermissionType.Administrate, PermissionType.Edit }; - public override IEnumerable PermissionTypes - { - get { return _permissionTypes; } - } + public override IEnumerable PermissionTypes => _permissionTypes; - - public override string Serialize() - { - return "DownloadFile"; - } + public override string Serialize() => "DownloadFile"; - public static ActionToken Deserialize(string serializedData) - { - return new DownloadFileActionToken(); - } + public static ActionToken Deserialize(string serializedData) => new DownloadFileActionToken(); - public override bool IgnoreEntityTokenLocking - { - get - { - return true; - } - } + public override bool IgnoreEntityTokenLocking => true; } @@ -814,9 +791,6 @@ public string ManageableKeyNameLabels get { return (string)base[_manageableKeyNameLabels]; } set { base[_manageableKeyNameLabels] = value; } } - - - } From e12038d5193dbe46bac30e86d9a69c1e2a107e29 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 4 May 2017 13:19:51 +0200 Subject: [PATCH 006/145] Previewing text website files from within the "System" perspective --- Composite/Composite.csproj | 1 + Composite/Core/IO/MimeTypeInfo.cs | 4 +- .../ApplicationLevelEventHandlers.cs | 1 + .../WebsiteFileElementProvider.cs | 4 +- .../WebsiteFileUrlToEntityTokenMapper.cs | 64 +++++++++++++++ .../services/Admin/DownloadFile.ashx | 80 ++++++++++++------- 6 files changed, 121 insertions(+), 33 deletions(-) create mode 100644 Composite/Plugins/Elements/UrlToEntityToken/WebsiteFileUrlToEntityTokenMapper.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 1aefdc7d18..2192603ed2 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -253,6 +253,7 @@ + diff --git a/Composite/Core/IO/MimeTypeInfo.cs b/Composite/Core/IO/MimeTypeInfo.cs index 8617f98a2c..f6148db8c5 100644 --- a/Composite/Core/IO/MimeTypeInfo.cs +++ b/Composite/Core/IO/MimeTypeInfo.cs @@ -414,7 +414,7 @@ public static string GetMimeType(UploadedFile uploadedFile) /// /// /// - internal static bool IsTextFile(string mimeType) + public static bool IsTextFile(string mimeType) { string canonicalMimeType = GetCanonical(mimeType); @@ -426,7 +426,7 @@ internal static bool IsTextFile(string mimeType) /// /// /// - internal static bool IsBrowserPreviewableFile(string mimeType) + public static bool IsBrowserPreviewableFile(string mimeType) { return mimeType == Pdf || mimeType == Html diff --git a/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs index f3a926cdda..57abdaef53 100644 --- a/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs +++ b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs @@ -96,6 +96,7 @@ private static void InitializeServices() UrlToEntityTokenFacade.Register(new DataUrlToEntityTokenMapper()); UrlToEntityTokenFacade.Register(new MediaUrlToEntityTokenMapper()); UrlToEntityTokenFacade.Register(new ServerLogUrlToEntityTokenMapper()); + UrlToEntityTokenFacade.Register(new WebsiteFileUrlToEntityTokenMapper()); var services = ServiceLocator.ServiceCollection; services.AddLogging(); diff --git a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs index a51f8087f6..923b340934 100644 --- a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs @@ -728,9 +728,9 @@ public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowC [ActionExecutor(typeof(DownloadFileActionExecutor))] internal sealed class DownloadFileActionToken : ActionToken { - private static readonly PermissionType[] _permissionTypes = { PermissionType.Administrate, PermissionType.Edit }; + internal static readonly PermissionType[] RequiredPermissionTypes = { PermissionType.Administrate, PermissionType.Edit }; - public override IEnumerable PermissionTypes => _permissionTypes; + public override IEnumerable PermissionTypes => RequiredPermissionTypes; public override string Serialize() => "DownloadFile"; diff --git a/Composite/Plugins/Elements/UrlToEntityToken/WebsiteFileUrlToEntityTokenMapper.cs b/Composite/Plugins/Elements/UrlToEntityToken/WebsiteFileUrlToEntityTokenMapper.cs new file mode 100644 index 0000000000..f07be1eabe --- /dev/null +++ b/Composite/Plugins/Elements/UrlToEntityToken/WebsiteFileUrlToEntityTokenMapper.cs @@ -0,0 +1,64 @@ +using System.IO; +using Composite.C1Console.Elements; +using Composite.C1Console.Security; +using Composite.Core; +using Composite.Core.IO; +using Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider; + +namespace Composite.Plugins.Elements.UrlToEntityToken +{ + internal class WebsiteFileUrlToEntityTokenMapper : IUrlToEntityTokenMapper + { + public string TryGetUrl(EntityToken entityToken) => null; + + public BrowserViewSettings TryGetBrowserViewSettings(EntityToken entityToken, bool showPublishedView) + { + if (!(entityToken is WebsiteFileElementProviderEntityToken fileEntityToken)) + { + return null; + } + + var filePath = fileEntityToken.RootPath + fileEntityToken.Id; + if (!File.Exists(filePath) || !UserHasRightToDownload(entityToken)) return null; + + var fileInfo = new FileInfo(filePath); + + if(!FilePreviewable(fileInfo)) + { + return null; + } + + var downloadLink = DownloadFileActionExecutor.GetDownloadLink(fileEntityToken); + var urlBuilder = new UrlBuilder(downloadLink) + { + ["preview"] = "true" + }; + + return new BrowserViewSettings { Url = urlBuilder, ToolingOn = false }; + } + + private bool UserHasRightToDownload(EntityToken file) + { + var userToken = UserValidationFacade.GetUserToken(); + var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username); + var userGroupPermissionDefinitions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username); + + var requiredPermissions = DownloadFileActionToken.RequiredPermissionTypes; + return SecurityResolver.Resolve(userToken, requiredPermissions, file, userPermissionDefinitions, userGroupPermissionDefinitions) + == SecurityResult.Allowed; + } + + private bool FilePreviewable(FileInfo fileInfo) + { + if (string.IsNullOrEmpty(fileInfo.Extension)) return false; + + string mimeType = MimeTypeInfo.GetCanonicalFromExtension(fileInfo.Extension); + if (string.IsNullOrEmpty(mimeType)) return false; + + return MimeTypeInfo.IsBrowserPreviewableFile(mimeType) + || (MimeTypeInfo.IsTextFile(mimeType) && fileInfo.Length < 1 << 20 /* 1 MB */); + } + + public EntityToken TryGetEntityToken(string url) => null; + } +} diff --git a/Website/Composite/services/Admin/DownloadFile.ashx b/Website/Composite/services/Admin/DownloadFile.ashx index 3cc951a1b1..f4ad2f32e0 100644 --- a/Website/Composite/services/Admin/DownloadFile.ashx +++ b/Website/Composite/services/Admin/DownloadFile.ashx @@ -1,80 +1,102 @@ -<%@ WebHandler Language="C#" Class="ImageManipulator" %> +<%@ WebHandler Language="C#" Class="DownloadFile" %> using System; using System.IO; -using System.Collections.Generic; using System.Web; using Composite; using Composite.Core.IO; using Composite.C1Console.Security; -using Composite.Core.Logging; -using Composite.C1Console.Users; using Composite.Core.Extensions; using Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider; -public class ImageManipulator : IHttpHandler +public class DownloadFile : IHttpHandler { public void ProcessRequest(HttpContext context) { bool actionAllowed = false; - string fullFilePath = null ; - - if(UserValidationFacade.IsLoggedIn()) { + string fullFilePath = null; + bool isPreview = false; + + if(UserValidationFacade.IsLoggedIn()) + { string file = context.Request.QueryString["file"]; string providerName = context.Request.QueryString["provider"]; + isPreview = context.Request.QueryString["preview"] == "true"; + if (file.IsNullOrEmpty() || providerName.IsNullOrEmpty()) { - SetResponseCode(context, /* Bad request */ 400); + SetResponseCode(context, /* Bad request */ 400); return; } - + string basePath = Path.GetDirectoryName(PathUtil.BaseDirectory); - + fullFilePath = basePath + file; var fileEntityToken = new WebsiteFileElementProviderEntityToken(providerName, fullFilePath, basePath); - + actionAllowed = UserHasRightToDownload(fileEntityToken); } - + if(!actionAllowed) { - SetResponseCode(context, /* Forbidden */ 403); + SetResponseCode(context, /* Forbidden */ 403); return; } - + context.Response.Cache.SetExpires(DateTime.Now.AddYears(-1)); Verify.IsNotNull(fullFilePath, "Unexpected exception"); - - context.Response.AddHeader("Content-Disposition", "attachment;filename=" + HttpUtility.UrlEncode(Path.GetFileName(fullFilePath))); - context.Response.WriteFile(fullFilePath); + if (isPreview) + { + var extension = Path.GetExtension(fullFilePath); + string mimeType = MimeTypeInfo.GetCanonicalFromExtension(extension); + + if (!MimeTypeInfo.IsBrowserPreviewableFile(mimeType)) + { + if (!MimeTypeInfo.IsTextFile(mimeType)) + { + return; + } + + mimeType = "text/plain"; + } + + context.Response.ContentType = mimeType; + } + else + { + string fileName = HttpUtility.UrlEncode(Path.GetFileName(fullFilePath)); + context.Response.AddHeader("Content-Disposition", "attachment;filename=" + fileName); + } + + context.Response.TransmitFile(fullFilePath); context.ApplicationInstance.CompleteRequest(); } - private bool UserHasRightToDownload(EntityToken file) + private bool UserHasRightToDownload(EntityToken file) { - var userToken = UserValidationFacade.GetUserToken(); - var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username); - var userGroupPermissionDefinitions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username); - - var requiredPermissions = new [] { PermissionType.Administrate, PermissionType.Edit }; - return SecurityResolver.Resolve(userToken, requiredPermissions, file, userPermissionDefinitions, userGroupPermissionDefinitions) - == SecurityResult.Allowed; + var userToken = UserValidationFacade.GetUserToken(); + var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username); + var userGroupPermissionDefinitions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username); + + var requiredPermissions = new [] { PermissionType.Administrate, PermissionType.Edit }; + return SecurityResolver.Resolve(userToken, requiredPermissions, file, userPermissionDefinitions, userGroupPermissionDefinitions) + == SecurityResult.Allowed; } private void SetResponseCode(HttpContext context, int responseCode) { - context.Response.StatusCode = responseCode; + context.Response.StatusCode = responseCode; context.ApplicationInstance.CompleteRequest(); } - + public bool IsReusable { - get { return false; } + get { return true; } } } \ No newline at end of file From 2b3e625ee3ed06bc9cd7c61af42319230fe21e17 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 11 May 2017 12:58:07 +0200 Subject: [PATCH 007/145] Fixing #440 Inline C# code defined in doesn't work for XSLT functions --- Website/WebSite.csproj | 4 ++-- Website/packages.config | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Website/WebSite.csproj b/Website/WebSite.csproj index 5b212d79a4..a22fbdf178 100644 --- a/Website/WebSite.csproj +++ b/Website/WebSite.csproj @@ -83,8 +83,8 @@ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll True - - ..\Packages\Orckestra.AspNet.Roslyn.1.0.0\lib\net461\Orckestra.AspNet.Roslyn.dll + + ..\packages\Orckestra.AspNet.Roslyn.1.0.1\lib\net461\Orckestra.AspNet.Roslyn.dll diff --git a/Website/packages.config b/Website/packages.config index f907304b2e..4f7fc50222 100644 --- a/Website/packages.config +++ b/Website/packages.config @@ -11,14 +11,14 @@ - - + + - + \ No newline at end of file From 8932b20919409563459698ad9cc547bfdcbef791 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 11 May 2017 13:32:55 +0200 Subject: [PATCH 008/145] Refactoring --- Composite/C1Console/Events/ConsoleFacade.cs | 52 ++---- .../XsltBasedFunctionProvider.cs | 163 ++++++++---------- Package.nuspec | 2 +- 3 files changed, 84 insertions(+), 133 deletions(-) diff --git a/Composite/C1Console/Events/ConsoleFacade.cs b/Composite/C1Console/Events/ConsoleFacade.cs index 6708f94026..eee4e602ef 100644 --- a/Composite/C1Console/Events/ConsoleFacade.cs +++ b/Composite/C1Console/Events/ConsoleFacade.cs @@ -26,11 +26,7 @@ public ConsoleClosedEventArgs(string consoleId) /// - public string ConsoleId - { - get; - private set; - } + public string ConsoleId { get; } } @@ -41,13 +37,13 @@ public string ConsoleId [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class ConsoleFacade { - private static readonly string LogTitle = "ConsoleFacade"; + private static readonly string LogTitle = nameof(ConsoleFacade); /// public delegate void ConsoleClosedEventDelegate(ConsoleClosedEventArgs args); - private static TimeSpan? _timeout = null; - private static bool _initialized = false; + private static TimeSpan? _timeout; + private static bool _initialized; private static readonly object _lock = new object(); private static event ConsoleClosedEventDelegate _consoleClosedEvent; @@ -59,7 +55,7 @@ public static void Initialize() { lock (_lock) { - if (_initialized == false) + if (!_initialized) { WorkflowInstance workflowInstance = WorkflowFacade.CreateNewWorkflow(WorkflowFacade.GetWorkflowType("Composite.C1Console.Events.Workflows.UserConsoleInformationScavengerWorkflow")); workflowInstance.Start(); @@ -97,19 +93,16 @@ public static void UnsubscribeFromConsoleClosedEvent(ConsoleClosedEventDelegate /// public static void CloseConsole(string consoleId) { - UnregisterConsole(UserSettings.Username, consoleId); + UnregisterConsole(UserSettings.Username, consoleId); } /// public static IEnumerable GetConsoleIdsByUsername(string username) { - List consoleIds = - (from d in DataFacade.GetData() - where d.Username == username - select d.ConsoleId).ToList(); - - return consoleIds; + return (from d in DataFacade.GetData() + where d.Username == username + select d.ConsoleId).ToList(); } @@ -127,14 +120,14 @@ internal static void RegisterConsole(string username, string consoleId) if (userConsoleInformation == null) { - Log.LogVerbose(LogTitle, string.Format("New console registred by '{0}' id = '{1}'", username, consoleId)); + Log.LogVerbose(LogTitle, $"New console registred by '{username}' id = '{consoleId}'"); userConsoleInformation = DataFacade.BuildNew(); userConsoleInformation.Id = Guid.NewGuid(); userConsoleInformation.Username = username; userConsoleInformation.ConsoleId = consoleId; userConsoleInformation.TimeStamp = DateTime.Now; - userConsoleInformation = DataFacade.AddNew(userConsoleInformation); + DataFacade.AddNew(userConsoleInformation); } else { @@ -187,7 +180,7 @@ public static void Scavenge() { if (now - userConsoleInformation.TimeStamp > Timeout) { - Log.LogVerbose(LogTitle, "The console '{0}' owned by the user '{1}' timed out, closing it", userConsoleInformation.ConsoleId, userConsoleInformation.Username); + Log.LogWarning(LogTitle, "The console '{0}' owned by the user '{1}' timed out, closing it", userConsoleInformation.ConsoleId, userConsoleInformation.Username); DataFacade.Delete(userConsoleInformation); FireConsoleClosedEvent(userConsoleInformation.ConsoleId); } @@ -197,31 +190,14 @@ public static void Scavenge() - private static TimeSpan Timeout - { - get - { - if (_timeout.HasValue == false) - { - _timeout = GlobalSettingsFacade.ConsoleTimeout; - } - - return _timeout.Value; - } - } - + private static TimeSpan Timeout => _timeout ?? (_timeout = GlobalSettingsFacade.ConsoleTimeout).Value; private static void FireConsoleClosedEvent(string consoleId) { lock (_lock) { - ConsoleClosedEventDelegate consoleClosedEvent = _consoleClosedEvent; - - if (consoleClosedEvent != null) - { - consoleClosedEvent(new ConsoleClosedEventArgs(consoleId)); - } + _consoleClosedEvent?.Invoke(new ConsoleClosedEventArgs(consoleId)); } } } diff --git a/Composite/Plugins/Functions/FunctionProviders/XsltBasedFunctionProvider/XsltBasedFunctionProvider.cs b/Composite/Plugins/Functions/FunctionProviders/XsltBasedFunctionProvider/XsltBasedFunctionProvider.cs index 70abb579ec..696a55af79 100644 --- a/Composite/Plugins/Functions/FunctionProviders/XsltBasedFunctionProvider/XsltBasedFunctionProvider.cs +++ b/Composite/Plugins/Functions/FunctionProviders/XsltBasedFunctionProvider/XsltBasedFunctionProvider.cs @@ -83,13 +83,7 @@ public IEnumerable DynamicTypeDependentFunctions /// - public IEnumerable Functions - { - get - { - yield break; - } - } + public IEnumerable Functions => Enumerable.Empty(); @@ -106,7 +100,10 @@ private void OnDataChanged(object sender, StoreEventArgs storeEventArgs) /// public static void ResolveImportIncludePaths(XContainer doc) { - IEnumerable imports = doc.Descendants().Where(f => f.Name == Namespaces.Xsl + "import" || f.Name == Namespaces.Xsl + "include").ToList(); + IEnumerable imports = doc.Descendants().Where( + f => f.Name == Namespaces.Xsl + "import" + || f.Name == Namespaces.Xsl + "include"); + foreach (XElement import in imports) { XAttribute hrefAttribute = import.Attribute("href"); @@ -124,7 +121,7 @@ private sealed class XsltXmlFunction : IFunction { private readonly IXsltFunction _xsltFunction; // go through XsltFunction instead of this private IEnumerable _parameterProfiles; - private volatile IEnumerable _FunctionCalls; + private volatile IEnumerable _functionCalls; private readonly object _lock = new object(); private bool _subscribedToFileChanges; private readonly Hashtable _xslTransformations = new Hashtable(); @@ -137,31 +134,28 @@ public XsltXmlFunction(IXsltFunction xsltFunction) - public object Execute(ParameterList parameters, FunctionContextContainer context) { Guid xsltFunctionId = this._xsltFunction.Id; - if (_FunctionCalls == null) + if (_functionCalls == null) { lock (_lock) { - if (_FunctionCalls == null) + if (_functionCalls == null) { - _FunctionCalls = RenderHelper.GetValidatedFunctionCalls(xsltFunctionId); + _functionCalls = RenderHelper.GetValidatedFunctionCalls(xsltFunctionId); } } } - TransformationInputs transformationInput = RenderHelper.BuildInputDocument(_FunctionCalls, parameters, false); + TransformationInputs transformationInput = RenderHelper.BuildInputDocument(_functionCalls, parameters, false); XDocument newTree = new XDocument(); using (XmlWriter writer = new LimitedDepthXmlWriter(newTree.CreateWriter())) { - XslCompiledTransform xslTransformer = GetXslCompiledTransform(); - - XsltArgumentList transformArgs = new XsltArgumentList(); + var transformArgs = new XsltArgumentList(); XslExtensionsManager.Register(transformArgs); if (transformationInput.ExtensionDefinitions != null) @@ -172,14 +166,15 @@ public object Execute(ParameterList parameters, FunctionContextContainer context } } + var xslTransformer = GetXslCompiledTransform(); xslTransformer.Transform(transformationInput.InputDocument.CreateReader(), transformArgs, writer); } - if (this._xsltFunction.OutputXmlSubType == "XHTML") + if (this._xsltFunction.OutputXmlSubType == nameof(OutputXmlSubType.XHTML)) { - return new XhtmlDocument(newTree); } + return newTree.Root; } @@ -187,7 +182,7 @@ public object Execute(ParameterList parameters, FunctionContextContainer context private XslCompiledTransform GetXslCompiledTransform() { - CultureInfo currentCultureInfo = LocalizationScopeManager.CurrentLocalizationScope; + var currentCultureInfo = LocalizationScopeManager.CurrentLocalizationScope; XslCompiledTransform xslCompiledTransform; if (_xslTransformations.TryGetValue(currentCultureInfo, out xslCompiledTransform)) @@ -199,60 +194,67 @@ private XslCompiledTransform GetXslCompiledTransform() { if (!_xslTransformations.TryGetValue(currentCultureInfo, out xslCompiledTransform)) { - using ( - DebugLoggingScope.CompletionTime(this.GetType(), "Loading and compiling {0}".FormatWith(_xsltFunction.XslFilePath))) - { - string folderPath = Path.GetDirectoryName(_xsltFunction.XslFilePath); - string fileName = Path.GetFileName(_xsltFunction.XslFilePath); - - IXsltFile xsltFileHandle = null; - - try - { - var xsltFileHandles = - (from file in DataFacade.GetData() - where String.Equals(file.FolderPath, folderPath, StringComparison.OrdinalIgnoreCase) - && String.Equals(file.FileName, fileName, StringComparison.OrdinalIgnoreCase) - select file).ToList(); - - Verify.That(xsltFileHandles.Count == 1, "XSLT file path {0} found {1} times. Only one instance was expected.".FormatWith(_xsltFunction.XslFilePath, xsltFileHandles.Count)); - xsltFileHandle = xsltFileHandles[0]; - } - catch (Exception ex) - { - Log.LogError("XsltBasedFunctionProvider", ex); - throw; - } + xslCompiledTransform = BuildCompiledTransform(); - if(!_subscribedToFileChanges) - { - xsltFileHandle.SubscribeOnChanged(ClearCachedData); - _subscribedToFileChanges = true; - } + _xslTransformations.Add(currentCultureInfo, xslCompiledTransform); + } + } + return xslCompiledTransform; + } - xslCompiledTransform = new XslCompiledTransform(); + private XslCompiledTransform BuildCompiledTransform() + { + using (DebugLoggingScope.CompletionTime(this.GetType(), $"Loading and compiling {_xsltFunction.XslFilePath}")) + { + string folderPath = Path.GetDirectoryName(_xsltFunction.XslFilePath); + string fileName = Path.GetFileName(_xsltFunction.XslFilePath); + IXsltFile xsltFileHandle; - XDocument doc; - using (Stream xsltSourceStream = xsltFileHandle.GetReadStream()) - { - using (XmlReader xmlReader = XmlReader.Create(xsltSourceStream)) - { - doc = XDocument.Load(xmlReader); - } - } + try + { + var xsltFileHandles = + (from file in DataFacade.GetData() + where String.Equals(file.FolderPath, folderPath, StringComparison.OrdinalIgnoreCase) + && String.Equals(file.FileName, fileName, StringComparison.OrdinalIgnoreCase) + select file).ToList(); + + Verify.That(xsltFileHandles.Count == 1, "XSLT file path {0} found {1} times. Only one instance was expected.", _xsltFunction.XslFilePath, xsltFileHandles.Count); + xsltFileHandle = xsltFileHandles[0]; + } + catch (Exception ex) + { + Log.LogError("XsltBasedFunctionProvider", ex); + throw; + } - ResolveImportIncludePaths(doc); + if (!_subscribedToFileChanges) + { + xsltFileHandle.SubscribeOnChanged(ClearCachedData); + _subscribedToFileChanges = true; + } - LocalizationParser.Parse(doc); + var xslCompiledTransform = new XslCompiledTransform(); - xslCompiledTransform.Load(doc.CreateReader(), XsltSettings.TrustedXslt, new XmlUrlResolver()); - _xslTransformations.Add(currentCultureInfo, xslCompiledTransform); + XDocument doc; + using (Stream xsltSourceStream = xsltFileHandle.GetReadStream()) + { + using (XmlReader xmlReader = XmlReader.Create(xsltSourceStream)) + { + doc = XDocument.Load(xmlReader); } } + + ResolveImportIncludePaths(doc); + + LocalizationParser.Parse(doc); + + xslCompiledTransform.Load(doc.CreateReader(), XsltSettings.TrustedXslt, new XmlUrlResolver()); + + + return xslCompiledTransform; } - return xslCompiledTransform; } private void ClearCachedData(string filePath, FileChangeType changeType) @@ -264,33 +266,13 @@ private void ClearCachedData(string filePath, FileChangeType changeType) } - public string Name - { - get - { - return _xsltFunction.Name; - } - } - - + public string Name => _xsltFunction.Name; - public string Namespace - { - get - { - return _xsltFunction.Namespace; - } - } + public string Namespace => _xsltFunction.Namespace; - public string Description - { - get - { - return _xsltFunction.Description; - } - } + public string Description => _xsltFunction.Description; public Type ReturnType @@ -328,14 +310,7 @@ public IEnumerable ParameterProfiles } - - public EntityToken EntityToken - { - get - { - return _xsltFunction.GetDataEntityToken(); - } - } + public EntityToken EntityToken => _xsltFunction.GetDataEntityToken(); } } diff --git a/Package.nuspec b/Package.nuspec index ef7ef71109..9d74c8761c 100644 --- a/Package.nuspec +++ b/Package.nuspec @@ -7,7 +7,7 @@ Orckestra A/S Orckestra A/S https://c1.orckestra.com/MPL11 - https://github.com/Orckestra/CMS-Foundation + https://github.com/Orckestra/C1-CMS-Foundation true Contains dll-s distributed with C1 CMS. From 7d5520ba0eb4f02bc6de0ea76b12d8135aefacb7 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 16 May 2017 16:12:18 +0200 Subject: [PATCH 009/145] Refactoring --- .../Core/Instrumentation/ProfilerReport.cs | 19 ++++++++++++++++++- .../WebClient/Renderings/RenderingContext.cs | 18 +++++------------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/Composite/Core/Instrumentation/ProfilerReport.cs b/Composite/Core/Instrumentation/ProfilerReport.cs index f1ed68e641..4263823708 100644 --- a/Composite/Core/Instrumentation/ProfilerReport.cs +++ b/Composite/Core/Instrumentation/ProfilerReport.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Xml.Linq; using Composite.C1Console.Security; +using Composite.Core.WebClient; namespace Composite.Core.Instrumentation @@ -12,6 +13,22 @@ namespace Composite.Core.Instrumentation [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class ProfilerReport { + private static readonly string ProfilerXslPath = UrlUtils.AdminRootPath + "/Transformations/page_profiler.xslt"; + + /// + public static string BuildReport(Measurement measurement, string url) + { + string xmlHeader = $@" + "; + + XElement reportXml = ProfilerReport.BuildReportXml(measurement); + + reportXml.Add(new XAttribute("url", url), + new XAttribute("consoleUrl", UrlUtils.AdminRootPath)); + + return xmlHeader + reportXml; + } + /// public static XElement BuildReportXml(Measurement measurement) { @@ -40,7 +57,7 @@ private static XElement BuildReportXmlRec(Measurement measurement, /* int level, long ownTime = measurement.TotalTime - measurement.Nodes.Select(childNode => childNode.TotalTime).Sum(); - var entityToken = measurement.EntityTokenFactory != null ? measurement.EntityTokenFactory() : null; + var entityToken = measurement.EntityTokenFactory?.Invoke(); string serializedEntityToken = entityToken != null ? EntityTokenSerializer.Serialize(entityToken, true) : null; diff --git a/Composite/Core/WebClient/Renderings/RenderingContext.cs b/Composite/Core/WebClient/Renderings/RenderingContext.cs index f349517b85..09b3341623 100644 --- a/Composite/Core/WebClient/Renderings/RenderingContext.cs +++ b/Composite/Core/WebClient/Renderings/RenderingContext.cs @@ -49,8 +49,6 @@ public sealed class RenderingContext: IDisposable /// true if page caching is disabled; otherwise, false. public bool CachingDisabled { get; private set; } - private static readonly string ProfilerXslPath = UrlUtils.AdminRootPath + "/Transformations/page_profiler.xslt"; - private static readonly List _prettifyErrorUrls = new List(); private static int _prettifyErrorCount; @@ -176,18 +174,12 @@ public string BuildProfilerReport() Measurement measurement = Profiler.EndProfiling(); - string xmlHeader = @" - " - .FormatWith(ProfilerXslPath); - - XElement reportXml = ProfilerReport.BuildReportXml(measurement); - var url = new UrlBuilder(HttpContext.Current.Request.Url.ToString()); - url["c1mode"] = null; - - reportXml.Add(new XAttribute("url", url), - new XAttribute("consoleUrl", UrlUtils.AdminRootPath)); + var url = new UrlBuilder(HttpContext.Current.Request.Url.ToString()) + { + ["c1mode"] = null + }; - return xmlHeader + reportXml; + return ProfilerReport.BuildReport(measurement, url); } From 8c2a3b76902b5e0878c85388c7e4e85644f8af88 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 16 May 2017 16:15:11 +0200 Subject: [PATCH 010/145] XmlDataProvider - CPU and memory usage optimization --- .../DataWrapperClassGenerator.cs | 258 ++++++++++++------ 1 file changed, 171 insertions(+), 87 deletions(-) diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs index 01f522163f..f94f712b19 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; @@ -31,12 +31,15 @@ public DataWrapperClassGenerator(string wrapperClassName, DataTypeDescriptor dat public CodeTypeDeclaration CreateClass() { - CodeTypeDeclaration declaration = new CodeTypeDeclaration(_wrapperClassName); + var declaration = new CodeTypeDeclaration(_wrapperClassName) + { + IsClass = true, + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed + }; - declaration.IsClass = true; - declaration.TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed; declaration.BaseTypes.Add(InterfaceName); declaration.BaseTypes.Add(typeof(IXElementWrapper)); + declaration.CustomAttributes.Add( new CodeAttributeDeclaration( new CodeTypeReference(typeof(EditorBrowsableAttribute)), @@ -51,8 +54,10 @@ public CodeTypeDeclaration CreateClass() declaration.Members.Add(new CodeMemberField(new CodeTypeReference(typeof(XElement)), WrappedElementFieldName)); declaration.Members.Add(new CodeMemberField(typeof(DataSourceId), DataSourceIdFieldName)); - CodeMemberField elementMethodInfoField = new CodeMemberField(typeof(MethodInfo), ElementMethodInfoFieldName); - elementMethodInfoField.Attributes = MemberAttributes.Static | MemberAttributes.Private; + var elementMethodInfoField = new CodeMemberField(typeof(MethodInfo), ElementMethodInfoFieldName) + { + Attributes = MemberAttributes.Static | MemberAttributes.Private + }; declaration.Members.Add(elementMethodInfoField); elementMethodInfoField.InitExpression = new CodeMethodInvokeExpression( new CodeTypeOfExpression(typeof(XElement)), @@ -130,7 +135,7 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) var method = new CodeMemberMethod { - Name = "CommitData", + Name = nameof(IXElementWrapper.CommitData), Attributes = MemberAttributes.Public | MemberAttributes.Final, ReturnType = new CodeTypeReference(typeof (void)) }; @@ -179,21 +184,31 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor dataFieldDescriptor, List statements) { // CODEGEN: - // if(this._emailNullable != null) { + // if (this._isSet_email && this._emailNullable != null) { // [statements] // } string fieldName = CreateNullableFieldName(dataFieldDescriptor); + // this._isSet_email + var expression1 = + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + IsSetFieldName(dataFieldDescriptor) + ); + + // this._emailNullable != null + var expression2 = new CodeBinaryOperatorExpression( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + fieldName + ), + CodeBinaryOperatorType.IdentityInequality, + new CodePrimitiveExpression(null) + ); + return new CodeConditionStatement( - new CodeBinaryOperatorExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - fieldName - ), - CodeBinaryOperatorType.IdentityInequality, - new CodePrimitiveExpression(null) - ), + new CodeBinaryOperatorExpression(expression1, CodeBinaryOperatorType.BooleanAnd, expression2), statements.ToArray() ); } @@ -275,22 +290,20 @@ private static List AddCommitDataMethodHelper(string elementVaria }; - List statements = new List(); - - statements.Add(new CodeVariableDeclarationStatement( - typeof(XAttribute), - elementVariableName, - new CodeMethodInvokeExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - WrappedElementFieldName - ), - "Attribute", - new CodeExpression[] { - tagNameExpression - } - ) - )); + var statements = new List + { + new CodeVariableDeclarationStatement( + typeof(XAttribute), + elementVariableName, + new CodeMethodInvokeExpression( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + WrappedElementFieldName + ), + "Attribute", + tagNameExpression) + ) + }; statements.AddRange(innerStatements); @@ -305,12 +318,14 @@ private static void AddIDataSourceProperty(CodeTypeDeclaration declaration) { PropertyInfo info = typeof(IData).GetProperty("DataSourceId"); - CodeMemberProperty property = new CodeMemberProperty(); - property.Attributes = MemberAttributes.Public | MemberAttributes.Final; - property.Name = info.Name; - property.HasGet = true; - property.HasSet = false; - property.Type = new CodeTypeReference(info.PropertyType); + var property = new CodeMemberProperty + { + Attributes = MemberAttributes.Public | MemberAttributes.Final, + Name = info.Name, + HasGet = true, + HasSet = false, + Type = new CodeTypeReference(info.PropertyType) + }; property.GetStatements.Add( new CodeMethodReturnStatement( @@ -330,40 +345,54 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) { foreach (DataFieldDescriptor dataFieldDescriptor in _dataTypeDescriptor.Fields) { - string fieldName = CreateNullableFieldName(dataFieldDescriptor); + string nullableFieldName = CreateNullableFieldName(dataFieldDescriptor); // CODEGEN: // private ExtendedNullable _emailNullable; - - var field = new CodeMemberField(); - CodeTypeReference nullableType = new CodeTypeReference( - typeof(ExtendedNullable<>).FullName, - new [] { new CodeTypeReference(dataFieldDescriptor.InstanceType) } - ); - field.Name = fieldName; - field.Type = nullableType; - declaration.Members.Add(field); + var nullableType = new CodeTypeReference( + typeof(ExtendedNullable<>).FullName, + new[] { new CodeTypeReference(dataFieldDescriptor.InstanceType) } + ); + + declaration.Members.Add(new CodeMemberField + { + Name = nullableFieldName, + Type = nullableType + }); + + // CODEGEN: + // private bool _isSet_email; + + declaration.Members.Add(new CodeMemberField + { + Name = IsSetFieldName(dataFieldDescriptor), + Type = new CodeTypeReference(typeof(bool)) + }); // CODEGEN: // private static XName _pointsXName = "Points"; - - var xNameField = new CodeMemberField(); - xNameField.Name = CreateXNameFieldName(dataFieldDescriptor); - xNameField.Type = new CodeTypeReference(typeof(XName)); - xNameField.Attributes = MemberAttributes.Static | MemberAttributes.Private; - xNameField.InitExpression = new CodePrimitiveExpression(dataFieldDescriptor.Name); + + var xNameField = new CodeMemberField + { + Name = CreateXNameFieldName(dataFieldDescriptor), + Type = new CodeTypeReference(typeof(XName)), + Attributes = MemberAttributes.Static | MemberAttributes.Private, + InitExpression = new CodePrimitiveExpression(dataFieldDescriptor.Name) + }; declaration.Members.Add(xNameField); // CODEGEN: // public string Email { get {...} set { ...} } - var property = new CodeMemberProperty(); - property.Attributes = MemberAttributes.Public | MemberAttributes.Final; - property.Name = dataFieldDescriptor.Name; - property.HasGet = true; - property.HasSet = true; - property.Type = new CodeTypeReference(dataFieldDescriptor.InstanceType); + var property = new CodeMemberProperty + { + Attributes = MemberAttributes.Public | MemberAttributes.Final, + Name = dataFieldDescriptor.Name, + HasGet = true, + HasSet = true, + Type = new CodeTypeReference(dataFieldDescriptor.InstanceType) + }; // CODEGEN: // if(this._emailNullable != null) { @@ -374,7 +403,7 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName + nullableFieldName ), CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null) @@ -383,7 +412,7 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) new CodePropertyReferenceExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName + nullableFieldName ), "Value" ) @@ -424,16 +453,14 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) } else { - noAttributeReturnStatement = new CodeThrowExceptionStatement( - new CodeObjectCreateExpression( - typeof(InvalidOperationException), - new CodeExpression[] { - new CodePrimitiveExpression( - string.Format("The element attribute '{0}' is missing from the xml file", dataFieldDescriptor.Name) - ) - } - ) - ); + noAttributeReturnStatement = + new CodeThrowExceptionStatement( + new CodeObjectCreateExpression( + typeof(InvalidOperationException), + new CodePrimitiveExpression( + $"The element attribute '{dataFieldDescriptor.Name}' is missing from the xml file" + )) + ); } property.GetStatements.Add( @@ -448,28 +475,67 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) )); - CodeExpression resultExpression; + CodeExpression valueExpression; if (!dataFieldDescriptor.StoreType.IsDecimal) { // ({Type}) attribute; - resultExpression = new CodeCastExpression(dataFieldDescriptor.InstanceType, + valueExpression = new CodeCastExpression(dataFieldDescriptor.InstanceType, new CodeVariableReferenceExpression("attribute")); } else { // FixDecimal(attribute.Value, {decimal precision}); - resultExpression = new CodeMethodInvokeExpression( + valueExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(typeof(DataProviderHelperBase)), - "ParseDecimal"), + nameof(DataProviderHelperBase.ParseDecimal)), new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("attribute"), "Value"), new CodePrimitiveExpression(dataFieldDescriptor.StoreType.NumericScale) ); } - property.GetStatements.Add(new CodeMethodReturnStatement(resultExpression)); + // CODEGEN: + // Type value = ({Type}) attribute; + property.GetStatements.Add( + new CodeVariableDeclarationStatement( + dataFieldDescriptor.InstanceType, + "value", + valueExpression)); + + + // CODEGEN: + // this._emailNullable = new ExtendedNullable<{Type}>(); + + property.GetStatements.Add( + new CodeAssignStatement( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + nullableFieldName + ), + new CodeObjectCreateExpression(nullableType))); + + // CODEGEN: + // this._emailNullable.Value = value; + + property.GetStatements.Add( + new CodeAssignStatement( + new CodePropertyReferenceExpression( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + nullableFieldName + ), + nameof(ExtendedNullable.Value) + ), + new CodeVariableReferenceExpression("value") + )); + + // CODEGEN: + // return value; + property.GetStatements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("value"))); if (!dataFieldDescriptor.IsReadOnly) @@ -484,7 +550,7 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) new CodeBinaryOperatorExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName + nullableFieldName ), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null) @@ -492,9 +558,9 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) new CodeAssignStatement( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName + nullableFieldName ), - new CodeObjectCreateExpression(nullableType, new CodeExpression[] { })))); + new CodeObjectCreateExpression(nullableType)))); // CODEGEN: // this._emailNullable.Value = value; @@ -504,12 +570,24 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) new CodePropertyReferenceExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName + nullableFieldName ), - "Value" + nameof(ExtendedNullable.Value) ), new CodePropertySetValueReferenceExpression() )); + + // CODEGEN: + // this._isSet_email = true; + + property.SetStatements.Add( + new CodeAssignStatement( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + IsSetFieldName(dataFieldDescriptor) + ), + new CodePrimitiveExpression(true) + )); } declaration.Members.Add(property); @@ -520,16 +598,21 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) private static string CreateNullableFieldName(DataFieldDescriptor dataFieldDescriptor) { - return string.Format("_{0}Nullable", dataFieldDescriptor.Name.ToLowerInvariant()); + return $"_{dataFieldDescriptor.Name.ToLowerInvariant()}Nullable"; } private static string CreateXNameFieldName(DataFieldDescriptor dataFieldDescriptor) { - return string.Format("_{0}XName", dataFieldDescriptor.Name.ToLowerInvariant()); + return $"_{dataFieldDescriptor.Name.ToLowerInvariant()}XName"; + } + + private static string IsSetFieldName(DataFieldDescriptor dataFieldDescriptor) + { + return $"_isSet_{dataFieldDescriptor.Name.ToLowerInvariant()}"; } - private string InterfaceFullName { get { return TypeManager.GetRuntimeFullName(_dataTypeDescriptor.TypeManagerTypeName); } } + private string InterfaceFullName => TypeManager.GetRuntimeFullName(_dataTypeDescriptor.TypeManagerTypeName); private string InterfaceName @@ -539,9 +622,10 @@ private string InterfaceName if (_interfaceName == null) { _interfaceName = InterfaceFullName; - if (_interfaceName.IndexOf(',') >= 0) + int commaOffset = _interfaceName.IndexOf(','); + if (commaOffset >= 0) { - _interfaceName = _interfaceName.Remove(_interfaceName.IndexOf(',')); + _interfaceName = _interfaceName.Remove(commaOffset); } } From 3310945258944812a94f6da7dee184f0312b9639 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 16 May 2017 17:34:09 +0200 Subject: [PATCH 011/145] XmlDataProvider - fixing a potential race condition, refactoring --- .../DataWrapperClassGenerator.cs | 112 +++++++----------- 1 file changed, 40 insertions(+), 72 deletions(-) diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs index f94f712b19..53443679a0 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs @@ -167,7 +167,7 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) new CodeThisReferenceExpression(), fieldName ), - "Value" + nameof(ExtendedNullable.Value) )); statments.Add(AddCommitDataMethodFinalHelper(dataFieldDescriptor, newStatements)); @@ -248,22 +248,16 @@ private static List AddCommitDataMethodHelper(string elementVaria new CodeThisReferenceExpression(), WrappedElementFieldName ), - "Add", - new CodeExpression[] { - new CodeVariableReferenceExpression(elementVariableName) - } - ) + "Add", + new CodeVariableReferenceExpression(elementVariableName)) ) }, new CodeStatement[] { new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeVariableReferenceExpression(elementVariableName), - "SetValue", - new CodeExpression[] { - valueExpression - } - ) + "SetValue", + valueExpression) ), } ) @@ -279,8 +273,7 @@ private static List AddCommitDataMethodHelper(string elementVaria new CodeExpressionStatement( new CodeMethodInvokeExpression( new CodeVariableReferenceExpression(elementVariableName), - "Remove", - new CodeExpression[] {} + "Remove" ) ) } @@ -347,6 +340,16 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) { string nullableFieldName = CreateNullableFieldName(dataFieldDescriptor); + var nullableFieldReference = new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + nullableFieldName + ); + + var nullableFieldValueReference = new CodePropertyReferenceExpression( + nullableFieldReference, + nameof(ExtendedNullable.Value) + ); + // CODEGEN: // private ExtendedNullable _emailNullable; @@ -401,21 +404,12 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) property.GetStatements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), + nullableFieldReference, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null) ), new CodeMethodReturnStatement( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), - "Value" - ) + nullableFieldValueReference ) )); @@ -506,30 +500,12 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) // CODEGEN: - // this._emailNullable = new ExtendedNullable<{Type}>(); + // this._emailNullable = value; // Using the implicit cast to ExtendedEnumerable<> property.GetStatements.Add( new CodeAssignStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), - new CodeObjectCreateExpression(nullableType))); - - // CODEGEN: - // this._emailNullable.Value = value; - - property.GetStatements.Add( - new CodeAssignStatement( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), - nameof(ExtendedNullable.Value) - ), - new CodeVariableReferenceExpression("value") - )); + nullableFieldReference, + new CodeVariableReferenceExpression("value"))); // CODEGEN: // return value; @@ -542,40 +518,32 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) { // CODEGEN: // if(this.[fieldName] == null) { - // this.[fieldName] = new ExtendedNullable<...>(); + // this.[fieldName] = value; + // } + // else + // { + // // this._emailNullable.Value = value; // } - property.SetStatements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), + nullableFieldReference, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null) ), - new CodeAssignStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), - new CodeObjectCreateExpression(nullableType)))); - - // CODEGEN: - // this._emailNullable.Value = value; - - property.SetStatements.Add( - new CodeAssignStatement( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - nullableFieldName - ), - nameof(ExtendedNullable.Value) - ), - new CodePropertySetValueReferenceExpression() - )); + new CodeStatement[] + { + new CodeAssignStatement( + nullableFieldReference, + new CodePropertySetValueReferenceExpression()) + }, + new CodeStatement[] + { + new CodeAssignStatement( + nullableFieldValueReference, + new CodePropertySetValueReferenceExpression() + ) + })); // CODEGEN: // this._isSet_email = true; From 687a2e8aff19b2b45e46dfa39a6bed96a2d4aa26 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 17 May 2017 10:54:04 +0200 Subject: [PATCH 012/145] DynamicXmlDataProvider - optimizations in the generated data wrappers --- .../DataWrapperClassGenerator.cs | 160 +++++++++--------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs index 53443679a0..5b81a0abe6 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs @@ -2,6 +2,7 @@ using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reflection; using System.Xml.Linq; using Composite.Core.Types; @@ -159,9 +160,9 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) { string fieldName = CreateNullableFieldName(dataFieldDescriptor); - List newStatements = AddCommitDataMethodHelper( + var newStatements = AddCommitDataMethodHelper( "attribute", - new CodePrimitiveExpression(dataFieldDescriptor.Name), + new CodeFieldReferenceExpression(null, CreateXNameFieldName(dataFieldDescriptor)), new CodePropertyReferenceExpression( new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), @@ -181,17 +182,18 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) - private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor dataFieldDescriptor, List statements) + private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor dataFieldDescriptor, IEnumerable statements) { // CODEGEN: // if (this._isSet_email && this._emailNullable != null) { // [statements] + // _isSet_email = false; // } string fieldName = CreateNullableFieldName(dataFieldDescriptor); // this._isSet_email - var expression1 = + var fieldIsSetFieldReference = new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), IsSetFieldName(dataFieldDescriptor) @@ -208,100 +210,100 @@ private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor ); return new CodeConditionStatement( - new CodeBinaryOperatorExpression(expression1, CodeBinaryOperatorType.BooleanAnd, expression2), - statements.ToArray() + new CodeBinaryOperatorExpression( + fieldIsSetFieldReference, CodeBinaryOperatorType.BooleanAnd, expression2), + statements.Concat(new [] { + // CODEGEN: + // this._isSetEmail = false; + new CodeAssignStatement( + fieldIsSetFieldReference, + new CodePrimitiveExpression(false) + )}).ToArray() ); } - private static List AddCommitDataMethodHelper(string elementVariableName, CodeExpression tagNameExpression, CodeExpression valueExpression) + private static CodeStatement[] AddCommitDataMethodHelper(string elementVariableName, CodeExpression attributeNameExpression, CodeExpression valueExpression) { - CodeStatement[] innerStatements = new CodeStatement[] { + var elementVariable = new CodeVariableReferenceExpression(elementVariableName); + + return new CodeStatement[] { + new CodeVariableDeclarationStatement( + typeof(XAttribute), + elementVariableName, + new CodeMethodInvokeExpression( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + WrappedElementFieldName + ), + "Attribute", + attributeNameExpression) + ), + new CodeConditionStatement( + new CodeBinaryOperatorExpression( + valueExpression, + CodeBinaryOperatorType.IdentityInequality, + new CodePrimitiveExpression(null) + ), + new CodeStatement[] { new CodeConditionStatement( new CodeBinaryOperatorExpression( - valueExpression, - CodeBinaryOperatorType.IdentityInequality, + elementVariable, + CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null) ), new CodeStatement[] { - new CodeConditionStatement( - new CodeBinaryOperatorExpression( - new CodeVariableReferenceExpression(elementVariableName), - CodeBinaryOperatorType.IdentityEquality, - new CodePrimitiveExpression(null) - ), - new CodeStatement[] { - new CodeAssignStatement( - new CodeVariableReferenceExpression(elementVariableName), - new CodeObjectCreateExpression( - typeof(XAttribute), - new CodeExpression[] { - tagNameExpression, - valueExpression - } - ) - ), - new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - WrappedElementFieldName - ), - "Add", - new CodeVariableReferenceExpression(elementVariableName)) - ) - }, - new CodeStatement[] { - new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(elementVariableName), - "SetValue", - valueExpression) + new CodeAssignStatement( + elementVariable, + new CodeObjectCreateExpression( + typeof(XAttribute), + attributeNameExpression, + valueExpression + ) + ), + new CodeExpressionStatement( + new CodeMethodInvokeExpression( + new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + WrappedElementFieldName ), - } + "Add", + elementVariable) ) }, new CodeStatement[] { - new CodeConditionStatement( - new CodeBinaryOperatorExpression( - new CodeVariableReferenceExpression(elementVariableName), - CodeBinaryOperatorType.IdentityInequality, - new CodePrimitiveExpression(null) - ), - new CodeStatement[] { - new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(elementVariableName), - "Remove" - ) - ) - } - ) + new CodeExpressionStatement( + new CodeMethodInvokeExpression( + elementVariable, + "SetValue", + valueExpression) + ), } ) - }; - - - var statements = new List - { - new CodeVariableDeclarationStatement( - typeof(XAttribute), - elementVariableName, - new CodeMethodInvokeExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - WrappedElementFieldName - ), - "Attribute", - tagNameExpression) + }, + // CODEGEN: + // else if (attribute != null) + // { + // attribute.Remove(); + // } + new CodeStatement[] { + new CodeConditionStatement( + new CodeBinaryOperatorExpression( + elementVariable, + CodeBinaryOperatorType.IdentityInequality, + new CodePrimitiveExpression(null) + ), + new CodeExpressionStatement( + new CodeMethodInvokeExpression( + elementVariable, + "Remove" + ) + ) + ) + } ) }; - - statements.AddRange(innerStatements); - - - return statements; } From fe98309de876602d2b8e7cee55a4fd72e0c998f5 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 17 May 2017 10:55:21 +0200 Subject: [PATCH 013/145] XhtmlPrettifier - adding tag to the list of inline tags --- Composite/Core/Xml/XhtmlPrettifier.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Composite/Core/Xml/XhtmlPrettifier.cs b/Composite/Core/Xml/XhtmlPrettifier.cs index 4354d8778f..bd60ca5bb9 100644 --- a/Composite/Core/Xml/XhtmlPrettifier.cs +++ b/Composite/Core/Xml/XhtmlPrettifier.cs @@ -60,7 +60,8 @@ public static class XhtmlPrettifier new NamespaceName { Name = "img", Namespace = "" }, new NamespaceName { Name = "input", Namespace = "" }, new NamespaceName { Name = "kbd", Namespace = "" }, - new NamespaceName { Name = "label", Namespace = "" }, + new NamespaceName { Name = "label", Namespace = "" }, + new NamespaceName { Name = "mark", Namespace = "" }, new NamespaceName { Name = "page.description", Namespace = Namespaces.Rendering10.NamespaceName }, new NamespaceName { Name = "page.title", Namespace = Namespaces.Rendering10.NamespaceName }, new NamespaceName { Name = "q", Namespace = "" }, From 667ee9c93844bce32a4e9eedc2f5f492be876cc0 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 17 May 2017 16:46:40 +0200 Subject: [PATCH 014/145] Search - respecting inline xhtml elements when extracting text fragments for indexing --- Composite/Core/Xml/XhtmlPrettifier.cs | 4 +- .../Search/Crawling/XhtmlCrawlingHelper.cs | 49 ++++++++++++++++++- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/Composite/Core/Xml/XhtmlPrettifier.cs b/Composite/Core/Xml/XhtmlPrettifier.cs index bd60ca5bb9..fd13148db6 100644 --- a/Composite/Core/Xml/XhtmlPrettifier.cs +++ b/Composite/Core/Xml/XhtmlPrettifier.cs @@ -40,7 +40,7 @@ public static class XhtmlPrettifier new NamespaceName { Name = "body", Namespace = "" } }); - private static readonly HashSet InlineElements = new HashSet(new [] + internal static readonly HashSet InlineElements = new HashSet(new [] { new NamespaceName { Name = "a", Namespace = "" }, new NamespaceName { Name = "abbr", Namespace = "" }, @@ -695,7 +695,7 @@ private class XmlAttribute - private sealed class NamespaceName + internal sealed class NamespaceName { public string Name; public string Namespace; diff --git a/Composite/Search/Crawling/XhtmlCrawlingHelper.cs b/Composite/Search/Crawling/XhtmlCrawlingHelper.cs index c0094d25d4..ab5182be0c 100644 --- a/Composite/Search/Crawling/XhtmlCrawlingHelper.cs +++ b/Composite/Search/Crawling/XhtmlCrawlingHelper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Web; @@ -27,6 +28,8 @@ internal class XhtmlCrawlingHelper private IPage _page; + private readonly StringBuilder _currentFragment = new StringBuilder(); + public void SetPageContext(IPage page) { _page = page; @@ -41,24 +44,47 @@ public bool CrawlXhtml(string xhtml) try { var doc = XhtmlDocument.Parse(xhtml); + CrawlXhtml(doc); + CompleteTextFragment(); + return true; } catch (Exception ex) { Log.LogError(nameof(XhtmlCrawlingHelper), ex); + + _currentFragment.Clear(); return false; } } private void CrawlXhtml(XhtmlDocument document) => ProcessNode(document.Body); + private void AppendToCurrentTextFragment(string text) + { + _currentFragment.Append(text); + } + + private void CompleteTextFragment() + { + if (_currentFragment.Length == 0) return; + + var str = _currentFragment.ToString(); + if (!string.IsNullOrWhiteSpace(str)) + { + _textParts.Add(str); + } + + _currentFragment.Clear(); + } + private void ProcessNode(XNode node) { if (node is XText textNode) { - _textParts.Add(textNode.Value); + AppendToCurrentTextFragment(textNode.Value); return; } @@ -87,10 +113,29 @@ private void ProcessElement(XElement element) // TODO: process "href" attribute for page/data references } + bool isInlineElement = XhtmlPrettifier.InlineElements.Contains( + new XhtmlPrettifier.NamespaceName { + Name = element.Name.LocalName, + Namespace = "" + }); + + bool isFragmentContinuation = element.Name.LocalName != "br" + && (element.Name.LocalName == "body" || isInlineElement); + + if (!isFragmentContinuation) + { + CompleteTextFragment(); + } + foreach (var childNode in element.Nodes()) { ProcessNode(childNode); } + + if (!isFragmentContinuation) + { + CompleteTextFragment(); + } } private void ProcessFunctionCall(XElement functionNode) @@ -146,7 +191,7 @@ private void ProcessFunctionCall(XElement functionNode) } else { - _textParts.Add(str); + AppendToCurrentTextFragment(str); } } } From 9a3e43ff1e1a42f0cf2d6665e1572415a2c69abc Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 19 May 2017 12:05:48 +0200 Subject: [PATCH 015/145] Log msg "WAMP router initiated successfully" now a 'verbose' and not 'information' --- Composite/Core/WebClient/Services/WampRouter/WampRouter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composite/Core/WebClient/Services/WampRouter/WampRouter.cs b/Composite/Core/WebClient/Services/WampRouter/WampRouter.cs index 191c9838fb..e9b0b74f94 100644 --- a/Composite/Core/WebClient/Services/WampRouter/WampRouter.cs +++ b/Composite/Core/WebClient/Services/WampRouter/WampRouter.cs @@ -24,7 +24,7 @@ public WampRouter() try { StartWampRouter(); - Log.LogInformation(nameof(WampRouter),"WAMP router initiated successfully"); + Log.LogVerbose(nameof(WampRouter),"WAMP router initiated successfully"); } catch (Exception e) { From ad5e490d57decaf870aefb1f97f14dbc98c39d69 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 23 May 2017 11:15:35 +0200 Subject: [PATCH 016/145] Fix #441, fix #443 updating Roslyn compilation code to address website not being compilable and localization files not being picked up --- Composite/AspNet/TempAssembliesFix.cs | 40 --------------------------- Composite/Composite.csproj | 1 - Website/packages.config | 2 +- 3 files changed, 1 insertion(+), 42 deletions(-) delete mode 100644 Composite/AspNet/TempAssembliesFix.cs diff --git a/Composite/AspNet/TempAssembliesFix.cs b/Composite/AspNet/TempAssembliesFix.cs deleted file mode 100644 index 25480401a6..0000000000 --- a/Composite/AspNet/TempAssembliesFix.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Linq; -using System.Reflection; -using Composite.Core; -using Composite.Core.Application; - -namespace Composite.AspNet -{ - /// - /// Fixed the issue of temporary asp.net assemblies not being resolved correctly. - /// - [ApplicationStartup] - class TempAssembliesFix - { - public static void OnInitialized() - { - AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainOnAssemblyResolve; - } - - private static Assembly CurrentDomainOnAssemblyResolve(object sender, ResolveEventArgs args) - { - string asmName = args.Name; - - if (!asmName.StartsWith("App_") || !asmName.Contains(",")) - { - return null; - } - - var assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(asm => asm.FullName == asmName); - if (assembly != null) - { - return assembly; - } - - Log.LogVerbose("OnAssemblyResolve", $"Failed to resolve assembly: {args.Name}" - + (args.RequestingAssembly != null ? $", requesting assembly: '{args.RequestingAssembly.FullName}'" : "")); - return null; - } - } -} diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 2192603ed2..7454640a9e 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -228,7 +228,6 @@ - ASPXCodeBehind diff --git a/Website/packages.config b/Website/packages.config index 4f7fc50222..d0fea8cacb 100644 --- a/Website/packages.config +++ b/Website/packages.config @@ -18,7 +18,7 @@ - + \ No newline at end of file From 66720b0fc6d3cdad3806fdaff4f9b0f18fa4f2a8 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 23 May 2017 13:47:59 +0200 Subject: [PATCH 017/145] Removing the finalizer method from FileSystemFileBase for better GC performance --- .../DataProvider/Streams/FileSystemFileBase.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Composite/Data/Plugins/DataProvider/Streams/FileSystemFileBase.cs b/Composite/Data/Plugins/DataProvider/Streams/FileSystemFileBase.cs index ecad511938..c71efdedd1 100644 --- a/Composite/Data/Plugins/DataProvider/Streams/FileSystemFileBase.cs +++ b/Composite/Data/Plugins/DataProvider/Streams/FileSystemFileBase.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.IO; using Composite.Core.Configuration; @@ -84,21 +84,5 @@ public void CommitChanges() this.TemporaryFileStream = null; this.TemporaryFilePath = null; } - - /// - [SuppressMessage("Composite.IO", "Composite.DoNotUseFileClass:DoNotUseFileClass")] - ~FileSystemFileBase() - { - if (TemporaryFilePath != null) - { - try - { - File.Delete(TemporaryFilePath); - } - catch - { - } - } - } } } From 6d432395ef3853529cf0ccc2271563f6e09c47df Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 23 May 2017 15:20:24 +0200 Subject: [PATCH 018/145] Fixing the build --- Website/WebSite.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Website/WebSite.csproj b/Website/WebSite.csproj index a22fbdf178..4417785d55 100644 --- a/Website/WebSite.csproj +++ b/Website/WebSite.csproj @@ -83,8 +83,8 @@ ..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll True - - ..\packages\Orckestra.AspNet.Roslyn.1.0.1\lib\net461\Orckestra.AspNet.Roslyn.dll + + ..\packages\Orckestra.AspNet.Roslyn.1.0.2\lib\net461\Orckestra.AspNet.Roslyn.dll From 3251178922ee48352dba99024a3e05bcb1db6af2 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 23 May 2017 15:30:05 +0200 Subject: [PATCH 019/145] Refactoring, fixing finalizable C1StreamReader-s not being disposed immediately after usage --- .../Core/WebClient/Captcha/Encryption.cs | 87 ++++-------- .../OutputTransformationManager.cs | 48 +++---- .../MarkupTransformationServices.cs | 126 ++++++++---------- 3 files changed, 106 insertions(+), 155 deletions(-) diff --git a/Composite/Core/WebClient/Captcha/Encryption.cs b/Composite/Core/WebClient/Captcha/Encryption.cs index 8d1cf89e74..dc0eded538 100644 --- a/Composite/Core/WebClient/Captcha/Encryption.cs +++ b/Composite/Core/WebClient/Captcha/Encryption.cs @@ -27,24 +27,12 @@ static Encryption() public static string Encrypt(string value) { - Verify.ArgumentNotNullOrEmpty(value, "value"); + Verify.ArgumentNotNullOrEmpty(value, nameof(value)); - // Declare the streams used - // to encrypt to an in memory - // array of bytes. - MemoryStream msEncrypt = null; - CryptoStream csEncrypt = null; - C1StreamWriter swEncrypt = null; - - // Declare the RijndaelManaged object - // used to encrypt the data. - RijndaelManaged rima = null; - - try + // Create a RijndaelManaged object + // with the specified key and IV. + using (var rima = new RijndaelManaged()) { - // Create a RijndaelManaged object - // with the specified key and IV. - rima = new RijndaelManaged(); rima.Key = _encryptionKey; rima.IV = RijndaelIV; @@ -52,46 +40,27 @@ public static string Encrypt(string value) ICryptoTransform encryptor = rima.CreateEncryptor(); // Create the streams used for encryption. - msEncrypt = new MemoryStream(); - csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write); - swEncrypt = new C1StreamWriter(csEncrypt); - - //Write all data to the stream. - swEncrypt.Write(value); + using (var msEncrypt = new MemoryStream()) + { + using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) + using (var swEncrypt = new C1StreamWriter(csEncrypt)) + { + //Write all data to the stream. + swEncrypt.Write(value); + } + // Return the encrypted bytes from the memory stream. + return ByteToHexString(msEncrypt.ToArray()); + } } - finally - { - if (swEncrypt != null) swEncrypt.Close(); - if (csEncrypt != null) csEncrypt.Close(); - if (msEncrypt != null) msEncrypt.Close(); - if (rima != null) rima.Clear(); - } - - // Return the encrypted bytes from the memory stream. - return ByteToHexString(msEncrypt.ToArray()); } public static string Decrypt(string encryptedValue) { - Verify.ArgumentNotNullOrEmpty(encryptedValue, "encryptedValue"); + Verify.ArgumentNotNullOrEmpty(encryptedValue, nameof(encryptedValue)); byte[] encodedSequence = HexStringToByteArray(encryptedValue); - // TDeclare the streams used - // to decrypt to an in memory - // array of bytes. - MemoryStream msDecrypt = null; - CryptoStream csDecrypt = null; - C1StreamReader srDecrypt = null; - - // Declare the RijndaelManaged object - // used to decrypt the data. - RijndaelManaged rima = null; - - try + using (var rima = new RijndaelManaged()) { - // Create a RijndaelManaged object - // with the specified key and IV. - rima = new RijndaelManaged(); rima.Key = _encryptionKey; rima.IV = RijndaelIV; @@ -99,20 +68,14 @@ public static string Decrypt(string encryptedValue) ICryptoTransform decryptor = rima.CreateDecryptor(); // Create the streams used for decryption. - msDecrypt = new MemoryStream(encodedSequence); - csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read); - srDecrypt = new C1StreamReader(csDecrypt); - - // Read the decrypted bytes from the decrypting stream - // and place them in a string. - return srDecrypt.ReadToEnd(); - } - finally - { - if (srDecrypt != null) srDecrypt.Close(); - if (csDecrypt != null) csDecrypt.Close(); - if (msDecrypt != null) msDecrypt.Close(); - if (rima != null) rima.Clear(); + using (var msDecrypt = new MemoryStream(encodedSequence)) + using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + using (var srDecrypt = new C1StreamReader(csDecrypt)) + { + // Read the decrypted bytes from the decrypting stream + // and place them in a string. + return srDecrypt.ReadToEnd(); + } } } diff --git a/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs b/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs index 87ac48d995..f435cc482d 100644 --- a/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs +++ b/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Web; using System.Web.Caching; using System.Web.Hosting; @@ -94,11 +95,6 @@ public XslTransformationStream(Stream outputStream) public static MemoryStream Transform(MemoryStream buffer, String mode, String browser, String platform) { - var readerSettings = new XmlReaderSettings(); - readerSettings.XmlResolver = null; - readerSettings.DtdProcessing = DtdProcessing.Parse; - readerSettings.CheckCharacters = false; - List xsltFilePaths = GetTransformationsInPriority().ToList(); if (xsltFilePaths.Count == 0) @@ -108,11 +104,23 @@ public static MemoryStream Transform(MemoryStream buffer, String mode, String br // Detection doctype buffer.Seek(0, SeekOrigin.Begin); - var line = new C1StreamReader(buffer).ReadLine(); + string line; + + using (var reader = new StreamReader(buffer, Encoding.UTF8, true, 1024, true)) + { + line = reader.ReadLine(); + } var doctype = line.Contains(" _responseOutputStream.CanRead; - public override bool CanSeek - { - get { return _responseOutputStream.CanSeek; } - } + public override bool CanSeek => _responseOutputStream.CanSeek; - public override bool CanWrite - { - get { return _responseOutputStream.CanWrite; } - } + public override bool CanWrite => _responseOutputStream.CanWrite; - public override long Length - { - get { return _responseOutputStream.Length; } - } + public override long Length => _responseOutputStream.Length; public override long Position { diff --git a/Composite/Core/WebClient/Services/WysiwygEditor/MarkupTransformationServices.cs b/Composite/Core/WebClient/Services/WysiwygEditor/MarkupTransformationServices.cs index 1e8ea1e0e3..759bb474e4 100644 --- a/Composite/Core/WebClient/Services/WysiwygEditor/MarkupTransformationServices.cs +++ b/Composite/Core/WebClient/Services/WysiwygEditor/MarkupTransformationServices.cs @@ -118,40 +118,16 @@ public static XDocument RepairXmlAndTransform(string xml, string xsltPath) /// A fully structured XHTML document, incl. html, head and body elements. public static TidyHtmlResult TidyHtml(string htmlMarkup) { - byte[] htmlByteArray = Encoding.UTF8.GetBytes(htmlMarkup); - Tidy tidy = GetXhtmlConfiguredTidy(); List namespacePrefixedElementNames = LocateNamespacePrefixedElementNames(htmlMarkup); Dictionary namespacePrefixToUri = LocateNamespacePrefixToUriDeclarations(htmlMarkup); - List badNamespacePrefixedElementNames = namespacePrefixedElementNames.Where(s => namespacePrefixToUri.Where(d => s.StartsWith(d.Key)).Any() == false).ToList(); + List badNamespacePrefixedElementNames = namespacePrefixedElementNames + .Where(s => !namespacePrefixToUri.Any(d => s.StartsWith(d.Key))).ToList(); AllowNamespacePrefixedElementNames(tidy, namespacePrefixedElementNames); AllowHtml5ElementNames(tidy); - TidyMessageCollection tidyMessages = new TidyMessageCollection(); - string xhtml = ""; - - using (MemoryStream inputStream = new MemoryStream(htmlByteArray)) - { - using (MemoryStream outputStream = new MemoryStream()) - { - tidy.Parse(inputStream, outputStream, tidyMessages); - outputStream.Position = 0; - C1StreamReader sr = new C1StreamReader(outputStream); - xhtml = sr.ReadToEnd(); - } - } - - if (tidyMessages.Errors > 0) - { - StringBuilder errorMessageBuilder = new StringBuilder(); - foreach (TidyMessage message in tidyMessages) - { - if (message.Level == MessageLevel.Error) - errorMessageBuilder.AppendLine(message.ToString()); - } - throw new InvalidOperationException(string.Format("Failed to parse html:\n\n{0}", errorMessageBuilder.ToString())); - } + string xhtml = ParseMarkup(htmlMarkup, tidy, out TidyMessageCollection tidyMessages); if (xhtml.IndexOf("")>-1) { @@ -179,8 +155,8 @@ public static TidyHtmlResult TidyHtml(string htmlMarkup) XDocument outputResult; if (badNamespacePrefixedElementNames.Any()) { - string badDeclared = string.Join(" ", badNamespacePrefixes.Select(p => string.Format("xmlns:{0}='#bad'", p)).ToArray()); - XDocument badDoc = XDocument.Parse(string.Format("{1}", badDeclared, xhtml)); + string badDeclared = string.Join(" ", badNamespacePrefixes.Select(p => $"xmlns:{p}='#bad'")); + XDocument badDoc = XDocument.Parse($"{xhtml}"); badDoc.Descendants().Attributes().Where(e => e.Name.Namespace == "#bad").Remove(); badDoc.Descendants().Where(e => e.Name.Namespace == "#bad").Remove(); outputResult = new XDocument(badDoc.Root.Descendants().First()); @@ -193,6 +169,40 @@ public static TidyHtmlResult TidyHtml(string htmlMarkup) return new TidyHtmlResult { Output = outputResult, ErrorSummary = messageBuilder.ToString() }; } + private static string ParseMarkup(string markup, Tidy tidy, out TidyMessageCollection tidyMessages) + { + string result; + + tidyMessages = new TidyMessageCollection(); + byte[] htmlByteArray = Encoding.UTF8.GetBytes(markup); + + using (var inputStream = new MemoryStream(htmlByteArray)) + { + using (var outputStream = new MemoryStream()) + { + tidy.Parse(inputStream, outputStream, tidyMessages); + outputStream.Position = 0; + using (var sr = new C1StreamReader(outputStream)) + { + result = sr.ReadToEnd(); + } + } + } + + if (tidyMessages.Errors > 0) + { + var errorMessageBuilder = new StringBuilder(); + foreach (TidyMessage message in tidyMessages) + { + if (message.Level == MessageLevel.Error) + errorMessageBuilder.AppendLine(message.ToString()); + } + throw new InvalidOperationException($"Failed to parse html:\n\n{errorMessageBuilder}"); + } + + return result; + } + /// /// Cleans HTML documents or fragments into XHTML conformant markup @@ -210,38 +220,13 @@ public static XDocument TidyXml(string xmlMarkup) // take the slow road below... } - byte[] xmlByteArray = Encoding.UTF8.GetBytes(xmlMarkup); - Tidy tidy = GetXmlConfiguredTidy(); List namespacePrefixedElementNames = LocateNamespacePrefixedElementNames(xmlMarkup); AllowNamespacePrefixedElementNames(tidy, namespacePrefixedElementNames); AllowHtml5ElementNames(tidy); - TidyMessageCollection tidyMessages = new TidyMessageCollection(); - string xml = ""; - - using (MemoryStream inputStream = new MemoryStream(xmlByteArray)) - { - using (MemoryStream outputStream = new MemoryStream()) - { - tidy.Parse(inputStream, outputStream, tidyMessages); - outputStream.Position = 0; - C1StreamReader sr = new C1StreamReader(outputStream); - xml = sr.ReadToEnd(); - } - } - - if (tidyMessages.Errors > 0) - { - StringBuilder errorMessageBuilder = new StringBuilder(); - foreach (TidyMessage message in tidyMessages) - { - if (message.Level == MessageLevel.Error) - errorMessageBuilder.AppendLine(message.ToString()); - } - throw new InvalidOperationException(string.Format("Failed to parse html:\n\n{0}", errorMessageBuilder.ToString())); - } + string xml = ParseMarkup(xmlMarkup, tidy, out TidyMessageCollection _); xml = RemoveDuplicateAttributes(xml); @@ -257,7 +242,7 @@ public static string OutputBodyDescendants(XDocument source) XmlWriterSettings settings = CustomizedWriterSettings(); using (var memoryStream = new MemoryStream()) { - using (XmlWriter writer = XmlWriter.Create(memoryStream, settings)) + using (var writer = XmlWriter.Create(memoryStream, settings)) { XNamespace xhtml = "http://www.w3.org/1999/xhtml"; XElement bodyElement = source.Descendants(xhtml + "body").First(); @@ -267,9 +252,12 @@ public static string OutputBodyDescendants(XDocument source) element.WriteTo(writer); } - writer.Flush(); - memoryStream.Position = 0; - var sr = new C1StreamReader(memoryStream); + writer.Close(); + } + + memoryStream.Position = 0; + using (var sr = new C1StreamReader(memoryStream)) + { bodyInnerXhtml = sr.ReadToEnd(); } } @@ -311,14 +299,14 @@ public static string OutputBodyDescendants(XDocument source) private static XmlWriterSettings CustomizedWriterSettings() { - var settings = new XmlWriterSettings(); - settings.OmitXmlDeclaration = true; - settings.ConformanceLevel = ConformanceLevel.Fragment; - settings.CloseOutput = false; - settings.Indent = true; - settings.IndentChars = "\t"; - - return settings; + return new XmlWriterSettings + { + OmitXmlDeclaration = true, + ConformanceLevel = ConformanceLevel.Fragment, + CloseOutput = false, + Indent = true, + IndentChars = "\t" + }; } @@ -452,7 +440,7 @@ private static List LocateNamespacePrefixedElementNames(string htmlMarku foreach (Match match in matches) { - string prefixedElementName = string.Format("{0}:{1}", match.Groups[1].Value, match.Groups[2].Value); + string prefixedElementName = $"{match.Groups[1].Value}:{match.Groups[2].Value}"; if (!prefixedElementNames.Contains(prefixedElementName)) { prefixedElementNames.Add(prefixedElementName); @@ -500,7 +488,7 @@ private static Dictionary LocateNamespacePrefixToUriDeclarations } else { - if (prefixToUri[prefix] != uri) throw new NotImplementedException(string.Format("The namespace prefix {0} is used to identify multiple namespaces. This may be legal XML but is not supported here", prefix)); + if (prefixToUri[prefix] != uri) throw new NotImplementedException($"The namespace prefix {prefix} is used to identify multiple namespaces. This may be legal XML but is not supported here"); } } return prefixToUri; From 3d8c3684267597a2759334c8466b0877c4896397 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 23 May 2017 15:49:29 +0200 Subject: [PATCH 020/145] Refactoring, fixing GC.SuppressFinalize() not being called in some classes --- Composite/C1Console/Tasks/TaskContainer.cs | 17 +++-- Composite/Core/Threading/ThreadDataManager.cs | 67 +++++++++++-------- .../Core/Threading/ThreadDataManagerData.cs | 21 ++---- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/Composite/C1Console/Tasks/TaskContainer.cs b/Composite/C1Console/Tasks/TaskContainer.cs index 20c84098f8..7d22cc5800 100644 --- a/Composite/C1Console/Tasks/TaskContainer.cs +++ b/Composite/C1Console/Tasks/TaskContainer.cs @@ -17,8 +17,8 @@ public sealed class TaskContainer : IDisposable { private static readonly string LogTitle = typeof (TaskContainer).Name; - private List _tasks; - private bool _disposed = false; + private readonly List _tasks; + private bool _disposed; @@ -96,15 +96,22 @@ public void SaveTasks() /// ~TaskContainer() { - Dispose(); + Dispose(false); } - /// public void Dispose() { - if (_disposed == false) + Dispose(true); + GC.SuppressFinalize(this); + } + + + /// + public void Dispose(bool disposing) + { + if (disposing && !_disposed) { _disposed = true; diff --git a/Composite/Core/Threading/ThreadDataManager.cs b/Composite/Core/Threading/ThreadDataManager.cs index 47d132e9d1..3715d0491e 100644 --- a/Composite/Core/Threading/ThreadDataManager.cs +++ b/Composite/Core/Threading/ThreadDataManager.cs @@ -218,7 +218,7 @@ public void Dispose() private sealed class ThreadDataManagerScope : IDisposable { - private bool _disposed = false; + private bool _disposed; private readonly bool _disposeData; private readonly ThreadDataManagerData _threadData; private readonly ThreadDataManagerData _threadDataValueToBeRestored; @@ -239,52 +239,63 @@ public ThreadDataManagerScope(ThreadDataManagerData newCurrentData, bool dispose } - public void Dispose() { - try + Dispose(true); + GC.SuppressFinalize(this); + } + + + public void Dispose(bool disposing) + { + if (disposing) { - if (_disposed) throw new ObjectDisposedException(typeof(ThreadDataManagerData).FullName); + try + { + if (_disposed) throw new ObjectDisposedException(typeof(ThreadDataManagerData).FullName); - _disposed = true; + _disposed = true; - if (_disposeData) + if (_disposeData) + { + try + { + _threadData.Dispose(); + } + catch (Exception e) + { + Log.LogError(LogTitle, e); + } + + Verify.IsTrue(Current == _threadData, + "ThreadDataManager.Current points to a different thread data object!!!"); + } + } + finally + { + _threadDataManagerData = _threadDataValueToBeRestored; + } + } + else + { + if (!_disposed && _disposeData) { try { _threadData.Dispose(); } - catch(Exception e) + catch (Exception) { - Log.LogError(LogTitle, e); + // silent... } - - Verify.IsTrue(Current == _threadData, "ThreadDataManager.Current points to a different thread data object!!!"); } } - finally - { - _threadDataManagerData = _threadDataValueToBeRestored; - } } ~ThreadDataManagerScope() { - if (_disposed || !_disposeData) - { - return; - } - - - try - { - _threadData.Dispose(); - } - catch (Exception) - { - // silent... - } + Dispose(false); } } } diff --git a/Composite/Core/Threading/ThreadDataManagerData.cs b/Composite/Core/Threading/ThreadDataManagerData.cs index ac2f992e41..18f13fdc1e 100644 --- a/Composite/Core/Threading/ThreadDataManagerData.cs +++ b/Composite/Core/Threading/ThreadDataManagerData.cs @@ -48,7 +48,7 @@ public bool TryGetParentValue(object key, out object value) CheckNotDisposed(); ThreadDataManagerData current = this; - while ((current != null) && (current.Data.ContainsKey(key) == false)) + while (current != null && !current.Data.ContainsKey(key)) { current = current.Parent; } @@ -69,14 +69,7 @@ public void SetValue(object key, object value) { CheckNotDisposed(); - if (this.Data.ContainsKey(key) == false) - { - this.Data.Add(key, value); - } - else - { - this.Data[key] = value; - } + this.Data[key] = value; } /// @@ -96,10 +89,7 @@ public bool HasValue(object key) } /// - public object this[object key] - { - get { return GetValue(key); } - } + public object this[object key] => GetValue(key); /// public void CheckNotDisposed() @@ -113,10 +103,7 @@ public void CheckNotDisposed() /// public void Dispose() { - if (OnDispose != null) - { - OnDispose(); - } + OnDispose?.Invoke(); _disposed = true; } From d0dcab779dbf003f8b60329cb8cc5f4f54d0d145 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 23 May 2017 17:06:18 +0200 Subject: [PATCH 021/145] Reset installation leave UserControls web.config intact --- Website/App_Data/Composite/reset-installation.bat | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Website/App_Data/Composite/reset-installation.bat b/Website/App_Data/Composite/reset-installation.bat index 4c58a9f662..855974e8d0 100644 --- a/Website/App_Data/Composite/reset-installation.bat +++ b/Website/App_Data/Composite/reset-installation.bat @@ -1,4 +1,4 @@ -del DataMetaData\*.xml +del DataMetaData\*.xml del DataStores\*.xml del Configuration\DynamicSqlDataProvider.config del Configuration\DynamicXmlDataProvider.config @@ -46,7 +46,6 @@ del TreeDefinitions\PageType.xml.backup :: Basic cleanup rd ..\..\Frontend\Composite /S /Q -rd ..\..\App_Data\UserControls /S /Q rd ..\..\App_Data\PageTemplateFeatures /S /Q :: Razor cleanup @@ -56,6 +55,13 @@ md ..\..\App_Data\Razor copy ..\..\App_Data\Razor.web.config ..\..\App_Data\Razor\web.config del ..\..\App_Data\Razor.web.config /f +:: UserControls cleanup +copy ..\..\App_Data\UserControls\web.config ..\..\App_Data\UserControls.web.config +rd ..\..\App_Data\UserControls /S /Q +md ..\..\App_Data\UserControls +copy ..\..\App_Data\UserControls.web.config ..\..\App_Data\UserControls\web.config +del ..\..\App_Data\UserControls.web.config /f + :: PageTemplates cleanup copy ..\..\App_Data\PageTemplates\web.config ..\..\App_Data\PageTemplates.web.config From d0112ef43aa7959865cc94b482f969d63c45b4bb Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 24 May 2017 10:36:40 +0200 Subject: [PATCH 022/145] Refactoring --- .../Workflow/FilePersistenceService.cs | 1 - Composite/Core/IO/C1FileStream.cs | 124 ++---------------- .../Implementation/ImplementationContainer.cs | 13 +- .../LocalIOProvider/LocalC1FileStream.cs | 62 ++------- .../FileLogTraceListener/FileLogger.cs | 1 + .../FileLogTraceListener/LogFileInfo.cs | 7 +- 6 files changed, 24 insertions(+), 184 deletions(-) diff --git a/Composite/C1Console/Workflow/FilePersistenceService.cs b/Composite/C1Console/Workflow/FilePersistenceService.cs index 6d204fe391..3f22a2f45d 100644 --- a/Composite/C1Console/Workflow/FilePersistenceService.cs +++ b/Composite/C1Console/Workflow/FilePersistenceService.cs @@ -257,7 +257,6 @@ private object DeserializeActivity(Activity rootActivity, Guid id) using (var stream = new C1FileStream(filename, FileMode.Open)) { result = Activity.Load(stream, rootActivity, formatter); - stream.Close(); } // Log.LogVerbose(LogTitle, $"Workflow loaded. Id = {id}, Type = {result.GetType()}"); diff --git a/Composite/Core/IO/C1FileStream.cs b/Composite/Core/IO/C1FileStream.cs index a0c6f2a6ec..9d053d36cc 100644 --- a/Composite/Core/IO/C1FileStream.cs +++ b/Composite/Core/IO/C1FileStream.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using Composite.Core.Implementation; @@ -13,10 +12,10 @@ namespace Composite.Core.IO /// See System.IO.FileStream for more documentation on the methods of this class. /// See . /// - public class C1FileStream : Stream, IDisposable + public class C1FileStream : Stream { - private bool _disposed = false; - private ImplementationContainer _implementation; + private bool _disposed; + private readonly ImplementationContainer _implementation; @@ -94,27 +93,13 @@ public C1FileStream(string path, FileMode mode, FileAccess access, FileShare sha /// /// Name of the file. /// - public string Name - { - get - { - return _implementation.Implementation.Name; - } - } - + public string Name => _implementation.Implementation.Name; /// /// Size of the file in bytes. /// - public override long Length - { - get - { - return _implementation.Implementation.Length; - } - } - + public override long Length => _implementation.Implementation.Length; /// @@ -210,40 +195,19 @@ public override long Seek(long offset, SeekOrigin origin) /// /// Returns true if its possible to read from the stream. /// - public override bool CanRead - { - get - { - return _implementation.Implementation.CanRead; - } - } - + public override bool CanRead => _implementation.Implementation.CanRead; /// /// Returns true if its possible to seek in the stream. /// - public override bool CanSeek - { - get - { - return _implementation.Implementation.CanSeek; - } - } - + public override bool CanSeek => _implementation.Implementation.CanSeek; /// /// Returns true if its possible to write to the stream. /// - public override bool CanWrite - { - get - { - return _implementation.Implementation.CanWrite; - } - } - + public override bool CanWrite => _implementation.Implementation.CanWrite; /// @@ -267,27 +231,6 @@ public virtual void Flush(bool flushToDisk) - /// - /// Closes the file stream. - /// - public override void Close() - { - base.Close(); - Dispose(true); - } - - - - /// - /// Destructor. - /// - ~C1FileStream() - { - Dispose(false); - } - - - /// /// Disposes the file stream. /// @@ -297,57 +240,8 @@ protected override void Dispose(bool disposing) if (disposing && !_disposed) { _disposed = true; - _implementation.DisposeImplementation(); + _implementation.DisposeImplementation(); } } - - - - //public virtual bool IsAsync - //{ - // get - // { - // throw new NotImplementedException(); - // } - //} - - - - ////[Obsolete("This property has been deprecated. Please use FileStream's SafeFileHandle property instead. http://go.microsoft.com/fwlink/?linkid=14202")] - //public virtual IntPtr Handle - //{ - // get - // { - // throw new NotImplementedException(); - // } - //} - - - - //public FileSecurity GetAccessControl() - //{ - // throw new NotImplementedException(); - //} - - - - //public void SetAccessControl(FileSecurity fileSecurity) - //{ - // throw new NotImplementedException(); - //} - - - - //public virtual void Lock(long position, long length) - //{ - // throw new NotImplementedException(); - //} - - - - //public virtual void Unlock(long position, long length) - //{ - // throw new NotImplementedException(); - //} } } diff --git a/Composite/Core/Implementation/ImplementationContainer.cs b/Composite/Core/Implementation/ImplementationContainer.cs index 016bf67467..05f9664686 100644 --- a/Composite/Core/Implementation/ImplementationContainer.cs +++ b/Composite/Core/Implementation/ImplementationContainer.cs @@ -1,6 +1,4 @@ using System; -using Composite.Core.Collections.Generic; - namespace Composite.Core.Implementation { @@ -12,8 +10,8 @@ namespace Composite.Core.Implementation public class ImplementationContainer where T : class { - private T _implementation = null; - private bool _disposed = false; + private T _implementation; + private bool _disposed; private readonly Func _create; @@ -54,12 +52,7 @@ internal void DisposeImplementation() { _disposed = true; - IDisposable disposable = _implementation as IDisposable; - - if (disposable != null) - { - disposable.Dispose(); - } + (_implementation as IDisposable)?.Dispose(); _implementation = null; } diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs index 9833afdbcd..098a51296b 100644 --- a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs +++ b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; using Composite.Core.IO.Plugins.IOProvider; @@ -19,25 +20,11 @@ public LocalC1FileStream(string path, FileMode mode, FileAccess access, FileShar [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] - public string Name - { - get - { - return _fileStream.Name; - } - } - + public string Name => _fileStream.Name; [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] - public long Length - { - get - { - return _fileStream.Length; - } - } - + public long Length => _fileStream.Length; [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] @@ -51,14 +38,8 @@ public void SetLength(long value) [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] public long Position { - get - { - return _fileStream.Position; - } - set - { - _fileStream.Position = value; - } + get => _fileStream.Position; + set => _fileStream.Position = value; } @@ -104,36 +85,15 @@ public long Seek(long offset, SeekOrigin origin) [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] - public bool CanRead - { - get - { - return _fileStream.CanRead; - } - } - + public bool CanRead => _fileStream.CanRead; [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] - public bool CanSeek - { - get - { - return _fileStream.CanSeek; - } - } - + public bool CanSeek => _fileStream.CanSeek; [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] - public bool CanWrite - { - get - { - return _fileStream.CanWrite; - } - } - + public bool CanWrite => _fileStream.CanWrite; [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] @@ -163,9 +123,7 @@ public void Close() [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] public void Dispose() { - this.Close(); - - _fileStream.Dispose(); + _fileStream?.Dispose(); _fileStream = null; } } diff --git a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs index ad32e71d75..ef05424bd1 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs @@ -465,6 +465,7 @@ protected virtual void Dispose(bool disposing) public void Dispose() { Dispose(true); + GC.SuppressFinalize(this); } diff --git a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs index 36ee7aa274..a07ec997c3 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs @@ -20,14 +20,9 @@ public void Dispose() { if (!_disposed) { - FileStream.Close(); + FileStream.Dispose(); _disposed = true; } } - - ~LogFileInfo() - { - Dispose(); - } } } From d737b592b0cd13f0b1921437a282e642e26e5291 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 24 May 2017 10:53:35 +0200 Subject: [PATCH 023/145] Adding SatelessDataConnectionImplementation to avoid leaking DataConnectionImplementation objects --- Composite/Composite.csproj | 1 + .../DataConnectionImplementation.cs | 28 ----------------- .../Implementation/ImplementationFactory.cs | 9 ++---- .../StatelessDataConnectionImplementation.cs | 31 +++++++++++++++++++ 4 files changed, 34 insertions(+), 35 deletions(-) create mode 100644 Composite/Core/Implementation/StatelessDataConnectionImplementation.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 7454640a9e..92cc226030 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -248,6 +248,7 @@ + diff --git a/Composite/Core/Implementation/DataConnectionImplementation.cs b/Composite/Core/Implementation/DataConnectionImplementation.cs index 3a98273486..7fbaf95a27 100644 --- a/Composite/Core/Implementation/DataConnectionImplementation.cs +++ b/Composite/Core/Implementation/DataConnectionImplementation.cs @@ -24,15 +24,6 @@ public class DataConnectionImplementation : DataConnectionBase, IDisposable private readonly DataScope _dataScope; internal DataScope DataScope => _dataScope; - /// - /// Stateless constructor. This is used when implementations of static methods needs to be called - /// Used when New and AllLocales are called - /// - public DataConnectionImplementation() - { - InitializeThreadData(); - InitializeScope(); - } /// @@ -171,21 +162,6 @@ public virtual void Delete(IEnumerable items) } - - /// - /// Documentation pending - /// - /// - /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "New", Justification = "This is what we want")] - public virtual TData New() - where TData : class, IData - { - return DataFacade.BuildNew(); - } - - - /// /// Documentation pending /// @@ -198,10 +174,6 @@ public virtual TData New() public virtual CultureInfo CurrentLocale => this.Locale; - /// - /// Documentation pending - /// - public virtual IEnumerable AllLocales => DataLocalizationFacade.ActiveLocalizationCultures; /// diff --git a/Composite/Core/Implementation/ImplementationFactory.cs b/Composite/Core/Implementation/ImplementationFactory.cs index 8b6758de5e..4132930483 100644 --- a/Composite/Core/Implementation/ImplementationFactory.cs +++ b/Composite/Core/Implementation/ImplementationFactory.cs @@ -39,13 +39,8 @@ public virtual LogImplementation StatelessLog /// /// Documentation pending /// - public virtual DataConnectionImplementation StatelessDataConnection - { - get - { - return new DataConnectionImplementation(); - } - } + public virtual StatelessDataConnectionImplementation StatelessDataConnection + => new StatelessDataConnectionImplementation(); internal object ResolveService(Type t) { diff --git a/Composite/Core/Implementation/StatelessDataConnectionImplementation.cs b/Composite/Core/Implementation/StatelessDataConnectionImplementation.cs new file mode 100644 index 0000000000..7d39f4a52d --- /dev/null +++ b/Composite/Core/Implementation/StatelessDataConnectionImplementation.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Globalization; +using Composite.Data; + +namespace Composite.Core.Implementation +{ + /// + /// Base class for the implementation of the static methods on the class. + /// + public class StatelessDataConnectionImplementation + { + /// + /// Creates a new data object of the given type. + /// + /// The data type. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1716:IdentifiersShouldNotMatchKeywords", MessageId = "New", Justification = "This is what we want")] + public virtual TData New() + where TData : class, IData + { + return DataFacade.BuildNew(); + } + + + /// + /// All locales added to C1. + /// + public virtual IEnumerable AllLocales => + DataLocalizationFacade.ActiveLocalizationCultures; + } +} From d0bb15eaaab39349bdeffa3c1b51d5406cd166ab Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Wed, 24 May 2017 13:59:08 +0200 Subject: [PATCH 024/145] Handling missing filePath for error message formatter --- Composite/Core/Xml/XhtmlErrorFormatter.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Composite/Core/Xml/XhtmlErrorFormatter.cs b/Composite/Core/Xml/XhtmlErrorFormatter.cs index d19bfceeb1..1f20cef44f 100644 --- a/Composite/Core/Xml/XhtmlErrorFormatter.cs +++ b/Composite/Core/Xml/XhtmlErrorFormatter.cs @@ -148,10 +148,13 @@ private static XElement GetSourceCodeInfo(Exception ex) if (firstCompileError != null) { - string filePath = (string) GetPropertyValue(firstCompileError, "FileName"); - int line = (int) GetPropertyValue(firstCompileError, "Line"); + string filePath = (string)GetPropertyValue(firstCompileError, "FileName"); + int line = (int)GetPropertyValue(firstCompileError, "Line"); - return SourceCodePreview(filePath, line); + if (!string.IsNullOrEmpty(filePath)) + { + return SourceCodePreview(filePath, line); + } } } From 8118db80b5c3cc243a252cc4572f1fc8b0a3be23 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Wed, 24 May 2017 14:34:58 +0200 Subject: [PATCH 025/145] Removing finalizers for classes that only track managed resources. Can be re-introduced with Conditional Compolation symbol "LeakCheck" - adding this will register call stacks for construction of IDisposable objects (that have been tooled with code) and a dump will be written to the website root with details. --- Composite/C1Console/Tasks/TaskContainer.cs | 6 +- Composite/Composite.csproj | 1 + Composite/Core/Application/AppDomainLocker.cs | 5 +- .../Core/Application/GlobalFileLocker.cs | 5 +- Composite/Core/IO/C1FileSystemWatcher.cs | 6 +- Composite/Core/IO/C1StreamReader.cs | 6 +- Composite/Core/IO/C1StreamWriter.cs | 6 +- .../C1FileStreamImplementation.cs | 4 + .../C1StreamReaderImplementation.cs | 5 +- .../C1StreamWriterImplementation.cs | 5 +- .../DataConnectionImplementation.cs | 23 ++--- .../PageDataConnectionImplementation.cs | 7 +- .../DisposableResourceTracer.cs | 94 +++++++++++++++++++ Composite/Core/Threading/ThreadDataManager.cs | 6 ++ .../Core/WebClient/PhantomJs/PhantomServer.cs | 7 ++ Composite/Data/DataConnection.cs | 5 +- Composite/Data/DataScope.cs | 6 +- 17 files changed, 165 insertions(+), 32 deletions(-) create mode 100644 Composite/Core/Instrumentation/DisposableResourceTracer.cs diff --git a/Composite/C1Console/Tasks/TaskContainer.cs b/Composite/C1Console/Tasks/TaskContainer.cs index 7d22cc5800..dfe0e47d3c 100644 --- a/Composite/C1Console/Tasks/TaskContainer.cs +++ b/Composite/C1Console/Tasks/TaskContainer.cs @@ -93,12 +93,16 @@ public void SaveTasks() } +#if LeakCheck + private string stack = Environment.StackTrace; + /// ~TaskContainer() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// public void Dispose() diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 92cc226030..23d7e9350f 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -249,6 +249,7 @@ + diff --git a/Composite/Core/Application/AppDomainLocker.cs b/Composite/Core/Application/AppDomainLocker.cs index 2f9a2f2d41..b3d78c230b 100644 --- a/Composite/Core/Application/AppDomainLocker.cs +++ b/Composite/Core/Application/AppDomainLocker.cs @@ -215,11 +215,14 @@ void Dispose(bool disposing) } - +#if LeakCheck + private string stack = Environment.StackTrace; ~DisposableLock() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } +#endif } } } diff --git a/Composite/Core/Application/GlobalFileLocker.cs b/Composite/Core/Application/GlobalFileLocker.cs index 8767185ac9..edb73ad236 100644 --- a/Composite/Core/Application/GlobalFileLocker.cs +++ b/Composite/Core/Application/GlobalFileLocker.cs @@ -170,11 +170,14 @@ protected virtual void Dispose(bool disposing) } - +#if LeakCheck + private string stack = Environment.StackTrace; ~DisposableLock() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } +#endif } } } diff --git a/Composite/Core/IO/C1FileSystemWatcher.cs b/Composite/Core/IO/C1FileSystemWatcher.cs index 9bdf3f9904..47c20618bf 100644 --- a/Composite/Core/IO/C1FileSystemWatcher.cs +++ b/Composite/Core/IO/C1FileSystemWatcher.cs @@ -268,16 +268,20 @@ public C1WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int void IDisposable.Dispose() { Dispose(true); + GC.SuppressFinalize(this); } +#if LeakCheck + private string stack = Environment.StackTrace; /// /// Destructor. /// ~C1FileSystemWatcher() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// /// Disposes the stream. diff --git a/Composite/Core/IO/C1StreamReader.cs b/Composite/Core/IO/C1StreamReader.cs index 237cb6d711..e598e3057c 100644 --- a/Composite/Core/IO/C1StreamReader.cs +++ b/Composite/Core/IO/C1StreamReader.cs @@ -263,15 +263,17 @@ public virtual Encoding CurrentEncoding } - +#if LeakCheck + private string stack = Environment.StackTrace; /// /// Destructor. /// ~C1StreamReader() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/IO/C1StreamWriter.cs b/Composite/Core/IO/C1StreamWriter.cs index 3eaf09907a..204eb9c10e 100644 --- a/Composite/Core/IO/C1StreamWriter.cs +++ b/Composite/Core/IO/C1StreamWriter.cs @@ -599,15 +599,17 @@ public override Encoding Encoding +#if LeakCheck + private string stack = Environment.StackTrace; /// /// Desctructor. /// ~C1StreamWriter() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - - +#endif /// /// Disposes the stream. diff --git a/Composite/Core/Implementation/C1FileStreamImplementation.cs b/Composite/Core/Implementation/C1FileStreamImplementation.cs index 5e383f0b65..a3327aeb67 100644 --- a/Composite/Core/Implementation/C1FileStreamImplementation.cs +++ b/Composite/Core/Implementation/C1FileStreamImplementation.cs @@ -226,11 +226,15 @@ public void Dispose() +#if LeakCheck + private string stack = Environment.StackTrace; /// ~C1FileStreamImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } +#endif diff --git a/Composite/Core/Implementation/C1StreamReaderImplementation.cs b/Composite/Core/Implementation/C1StreamReaderImplementation.cs index be51bd7f84..16026c83b5 100644 --- a/Composite/Core/Implementation/C1StreamReaderImplementation.cs +++ b/Composite/Core/Implementation/C1StreamReaderImplementation.cs @@ -176,14 +176,17 @@ public void Dispose() +#if LeakCheck + private string stack = Environment.StackTrace; /// /// See . /// ~C1StreamReaderImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Implementation/C1StreamWriterImplementation.cs b/Composite/Core/Implementation/C1StreamWriterImplementation.cs index 0b94d93995..9ebb7c356e 100644 --- a/Composite/Core/Implementation/C1StreamWriterImplementation.cs +++ b/Composite/Core/Implementation/C1StreamWriterImplementation.cs @@ -549,12 +549,15 @@ public void Dispose() +#if LeakCheck + private string stack = Environment.StackTrace; /// ~C1StreamWriterImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Implementation/DataConnectionImplementation.cs b/Composite/Core/Implementation/DataConnectionImplementation.cs index 7fbaf95a27..fd66db0086 100644 --- a/Composite/Core/Implementation/DataConnectionImplementation.cs +++ b/Composite/Core/Implementation/DataConnectionImplementation.cs @@ -1,6 +1,4 @@ -//#define ConnectionLeakCheck - -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -16,10 +14,6 @@ namespace Composite.Core.Implementation /// public class DataConnectionImplementation : DataConnectionBase, IDisposable { -#if ConnectionLeakCheck - private string _allocationCallStack; -#endif - private IDisposable _threadDataManager; private readonly DataScope _dataScope; @@ -42,10 +36,6 @@ public DataConnectionImplementation(PublicationScope scope, CultureInfo locale) private void InitializeThreadData() { _threadDataManager = ThreadDataManager.EnsureInitialize(); - -#if ConnectionLeakCheck - _allocationCallStack = new StackTrace().ToString(); -#endif } /// @@ -187,16 +177,15 @@ public void Dispose() - /// +#if LeakCheck + private string stack = Environment.StackTrace; +/// ~DataConnectionImplementation() { -#if ConnectionLeakCheck - Log.LogError(nameof(DataConnection), "Not disposed data connection allocated at: " + _allocationCallStack); -#endif - + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Implementation/PageDataConnectionImplementation.cs b/Composite/Core/Implementation/PageDataConnectionImplementation.cs index fb762721a1..14c1080642 100644 --- a/Composite/Core/Implementation/PageDataConnectionImplementation.cs +++ b/Composite/Core/Implementation/PageDataConnectionImplementation.cs @@ -91,14 +91,15 @@ public void Dispose() GC.SuppressFinalize(this); } - - +#if LeakCheck + private string stack = Environment.StackTrace; /// ~PageDataConnectionImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs new file mode 100644 index 0000000000..30caa09fe9 --- /dev/null +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -0,0 +1,94 @@ +using Composite.C1Console.Events; +using Composite.Core.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Composite.Core.Instrumentation +{ +#if LeakCheck + /// + /// This class only provide functionality if the conditional compilation symbol #LeakCheck is defined. + /// It is defined for this build... + /// It is NOT defined for this build... + /// + public static class DisposableResourceTracer + { + private static Dictionary stacks = new Dictionary(); + private static object _lock = new object(); + private static object _dumpLock = new object(); + private static bool dumpAlways = false; + + public static readonly DateTime TraceStart = DateTime.Now; + + static DisposableResourceTracer() + { + GlobalEventSystemFacade.SubscribeToShutDownEvent(OnShutDownEvent); + } + + private static void OnShutDownEvent(ShutDownEventArgs args) + { + dumpAlways = true; + DumpStacks(); + } + + public static void Register(string stack) + { + lock (_lock) + { + int count = 0; + if (stacks.TryGetValue(stack, out count)) + { + stacks[stack] = count + 1; + } + else + { + stacks.Add(stack, 1); + } + } + + if (dumpAlways) DumpStacks(); + } + + private static void DumpStacks() + { + string rootDir = PathUtil.Resolve("~/"); + string fileName = String.Format("DisposableResourceTrace_{0}.xml", TraceStart.ToString("yyyy_MM_dd_HH_mm_ss")); + string fullPath = Path.Combine(rootDir, fileName); + + var redundant = @" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) + at System.Environment.get_StackTrace()"; + var stacks = GetStacks(); + + var ordered = stacks.OrderByDescending(f => f.Value); + + XElement dumpDoc = new XElement("DisposableResourceTrace" + , new XAttribute("start", TraceStart) + , new XAttribute("end", DateTime.Now) + , new XAttribute("seconds", (DateTime.Now - TraceStart).TotalSeconds) + ); + dumpDoc.Add( + ordered.Select(f => new XElement("Trace", new XAttribute("count", f.Value), f.Key.Replace(redundant,""))) + ); + + lock (_dumpLock) + { + dumpDoc.Save(fullPath); + } + } + + public static Dictionary GetStacks() + { + lock (_lock) + { + return new Dictionary(stacks); + } + } + } +#endif +} + diff --git a/Composite/Core/Threading/ThreadDataManager.cs b/Composite/Core/Threading/ThreadDataManager.cs index 3715d0491e..2db1877b9c 100644 --- a/Composite/Core/Threading/ThreadDataManager.cs +++ b/Composite/Core/Threading/ThreadDataManager.cs @@ -293,8 +293,14 @@ public void Dispose(bool disposing) } +#if LeakCheck + private string stack = Environment.StackTrace; +#endif ~ThreadDataManagerScope() { +#if LeakCheck + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); +#endif Dispose(false); } } diff --git a/Composite/Core/WebClient/PhantomJs/PhantomServer.cs b/Composite/Core/WebClient/PhantomJs/PhantomServer.cs index 811753ef56..6a60f4ddc2 100644 --- a/Composite/Core/WebClient/PhantomJs/PhantomServer.cs +++ b/Composite/Core/WebClient/PhantomJs/PhantomServer.cs @@ -302,8 +302,15 @@ public void Dispose() GC.SuppressFinalize(this); } + +#if LeakCheck + private string stack = Environment.StackTrace; +#endif ~PhantomServer() { +#if LeakCheck + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); +#endif // Finalizer calls Dispose(false) Dispose(false); } diff --git a/Composite/Data/DataConnection.cs b/Composite/Data/DataConnection.cs index c3ea89ba42..2176d8d51e 100644 --- a/Composite/Data/DataConnection.cs +++ b/Composite/Data/DataConnection.cs @@ -520,12 +520,15 @@ public void Dispose() +#if LeakCheck + private string stack = Environment.StackTrace; /// ~DataConnection() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Data/DataScope.cs b/Composite/Data/DataScope.cs index 681d915e8e..c9e8478cca 100644 --- a/Composite/Data/DataScope.cs +++ b/Composite/Data/DataScope.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; @@ -92,11 +92,15 @@ public DataScope(PublicationScope publicationScope, CultureInfo cultureInfo) { } +#if LeakCheck + private string stack = Environment.StackTrace; /// ~DataScope() { + Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); Dispose(false); } +#endif /// public void Dispose() From c293fafb2805441534ed4ab92c7b256df80c789c Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Wed, 24 May 2017 15:00:55 +0200 Subject: [PATCH 026/145] Project file update (removing unused conditional compilation symbols) --- Composite/Composite.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 23d7e9350f..927a264784 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -42,7 +42,7 @@ full false bin\Debug\ - TRACE;DEBUG;CODE_ANALYSIS + TRACE;DEBUG prompt @@ -57,7 +57,8 @@ pdbonly true bin\Release\ - TRACE;CODE_ANALYSIS + + prompt 4 From 21482b175d2de162ec60d8657406ebcc2e0f1dad Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Wed, 24 May 2017 17:56:12 +0200 Subject: [PATCH 027/145] Conditional compilation "LeakCheck" now instrument most IDisposable implementations with a finalizer that will trace construction and ultimately document on disk. --- Composite/AspNet/Razor/CompositeC1WebPage.cs | 18 ++++++++---- Composite/AspNet/SiteMapContext.cs | 11 ++++++++ .../C1Console/Actions/ActionLockingFacade.cs | 12 ++++++++ .../C1Console/Drawing/FunctionPresentation.cs | 12 ++++++++ .../Components/ComponentChangeNotifier.cs | 22 +++++++++++---- .../RelationshipGraphLevelEnumerator.cs | 14 +++++++++- Composite/C1Console/Tasks/TaskContainer.cs | 4 ++- Composite/Core/Application/AppDomainLocker.cs | 4 ++- .../ApplicationOnlineHandlerFacade.cs | 12 ++++++++ .../Core/Application/GlobalFileLocker.cs | 4 ++- Composite/Core/Application/Job.cs | 10 +++++++ Composite/Core/Application/ShutdownGuard.cs | 13 +++++++++ .../Collections/Generic/CastEnumerator.cs | 13 ++++++++- .../Collections/Generic/ResourceLocker.cs | 12 ++++++++ Composite/Core/IO/C1FileSystemWatcher.cs | 4 ++- Composite/Core/IO/C1StreamReader.cs | 2 +- Composite/Core/IO/C1StreamWriter.cs | 2 +- Composite/Core/IO/Zip/ZipFileSystem.cs | 12 ++++++++ .../C1FileStreamImplementation.cs | 4 ++- .../C1StreamReaderImplementation.cs | 8 +++--- .../C1StreamWriterImplementation.cs | 4 ++- .../DataConnectionImplementation.cs | 4 ++- .../PageDataConnectionImplementation.cs | 4 ++- .../DisposableResourceTracer.cs | 28 +++++++++++++++++-- .../Core/Instrumentation/LogExecutionTime.cs | 12 ++++++++ Composite/Core/Instrumentation/Profiler.cs | 12 ++++++++ Composite/Core/Logging/DebugLoggingScope.cs | 13 +++++++++ Composite/Core/Parallelization/AsyncLock.cs | 12 ++++++++ .../Core/Threading/ThreadCultureScope.cs | 13 +++++++++ Composite/Core/Threading/ThreadDataManager.cs | 2 +- .../Core/Threading/ThreadDataManagerData.cs | 12 ++++++++ Composite/Core/UrlBuilder.cs | 12 ++++++++ .../Core/WebClient/BuildManagerHelper.cs | 12 ++++++++ .../Core/WebClient/PhantomJs/PhantomServer.cs | 2 +- Composite/Data/Caching/CachingEnumerator.cs | 16 +++++++++-- Composite/Data/DataConnection.cs | 4 ++- Composite/Data/DataEventSystemFacade.cs | 14 ++++++++-- Composite/Data/DataScope.cs | 6 ++-- .../ProcessControllerFacade.cs | 12 ++++++++ Composite/GlobalInitializerFacade.cs | 24 ++++++++++++++++ .../Foundation/RequireTransactionScope.cs | 13 +++++++++ .../XmlDataProviderDocumentCache.cs | 14 +++++++++- .../LocalIOProvider/LocalC1FileStream.cs | 12 ++++++++ .../LocalIOProvider/LocalC1StreamReader.cs | 12 ++++++++ .../LocalIOProvider/LocalC1StreamWriter.cs | 12 ++++++++ .../FileLogTraceListener/FileLogger.cs | 7 +++++ .../FileLogTraceListener/LogFileInfo.cs | 13 +++++++++ .../MasterPages/MasterPageBase.cs | 9 ++++++ .../Search/Crawling/DocumentFieldNames.cs | 2 +- .../Search/Crawling/XhtmlCrawlingHelper.cs | 12 ++++++++ .../IndexUpdateActionContainer.cs | 12 ++++++++ 51 files changed, 487 insertions(+), 42 deletions(-) diff --git a/Composite/AspNet/Razor/CompositeC1WebPage.cs b/Composite/AspNet/Razor/CompositeC1WebPage.cs index de375c995f..7dc2b3036d 100644 --- a/Composite/AspNet/Razor/CompositeC1WebPage.cs +++ b/Composite/AspNet/Razor/CompositeC1WebPage.cs @@ -170,8 +170,10 @@ public void Dispose() { Dispose(true); - GC.SuppressFinalize(this); - } +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } /// protected virtual void Dispose(bool disposing) @@ -186,10 +188,14 @@ protected virtual void Dispose(bool disposing) _disposed = true; } +#if LeakCheck + private string stack = Environment.StackTrace; /// - ~CompositeC1WebPage() - { - Dispose(false); - } + ~CompositeC1WebPage() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + Dispose(false); + } +#endif } } diff --git a/Composite/AspNet/SiteMapContext.cs b/Composite/AspNet/SiteMapContext.cs index 2fa0787a93..1964ca7639 100644 --- a/Composite/AspNet/SiteMapContext.cs +++ b/Composite/AspNet/SiteMapContext.cs @@ -48,8 +48,19 @@ public void Dispose() { var top = SiteMapStack.Pop(); Verify.That(object.ReferenceEquals(top, this), "SiteMapContext weren't disposed properly"); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~SiteMapContext() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif private static Stack SiteMapStack diff --git a/Composite/C1Console/Actions/ActionLockingFacade.cs b/Composite/C1Console/Actions/ActionLockingFacade.cs index 52f660976b..40beeda0a2 100644 --- a/Composite/C1Console/Actions/ActionLockingFacade.cs +++ b/Composite/C1Console/Actions/ActionLockingFacade.cs @@ -422,7 +422,19 @@ internal LockerToken() public void Dispose() { ActionLockingFacade.Exit(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LockerToken() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/C1Console/Drawing/FunctionPresentation.cs b/Composite/C1Console/Drawing/FunctionPresentation.cs index ce4ffc8a93..d5d68ced18 100644 --- a/Composite/C1Console/Drawing/FunctionPresentation.cs +++ b/Composite/C1Console/Drawing/FunctionPresentation.cs @@ -355,7 +355,19 @@ public void Dispose() _titleFont.Dispose(); _buttonFont.Dispose(); _functionIcon.Dispose(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~FunctionHeader() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/C1Console/RichContent/Components/ComponentChangeNotifier.cs b/Composite/C1Console/RichContent/Components/ComponentChangeNotifier.cs index c5b6698f5e..c87f2bbdff 100644 --- a/Composite/C1Console/RichContent/Components/ComponentChangeNotifier.cs +++ b/Composite/C1Console/RichContent/Components/ComponentChangeNotifier.cs @@ -67,14 +67,26 @@ public void Dispose() { if (_observer != null && _observers.Contains(_observer)) _observers.Remove(_observer); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~Unsubscriber() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } - /// - /// Notify a change in the provider - /// - /// - public void ProviderChange(string providerId) + /// + /// Notify a change in the provider + /// + /// + public void ProviderChange(string providerId) { var change = new ComponentChange { ProviderId = providerId }; foreach (var observer in _observers) diff --git a/Composite/C1Console/Security/Foundation/RelationshipGraphLevelEnumerator.cs b/Composite/C1Console/Security/Foundation/RelationshipGraphLevelEnumerator.cs index b2a6c52378..de293f4a12 100644 --- a/Composite/C1Console/Security/Foundation/RelationshipGraphLevelEnumerator.cs +++ b/Composite/C1Console/Security/Foundation/RelationshipGraphLevelEnumerator.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; @@ -21,8 +21,20 @@ public RelationshipGraphLevel Current public void Dispose() { +#if LeakCheck + System.GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = System.Environment.StackTrace; + /// + ~RelationshipGraphLevelEnumerator() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif + object IEnumerator.Current { get { return _currentLevel; } diff --git a/Composite/C1Console/Tasks/TaskContainer.cs b/Composite/C1Console/Tasks/TaskContainer.cs index dfe0e47d3c..48db38a686 100644 --- a/Composite/C1Console/Tasks/TaskContainer.cs +++ b/Composite/C1Console/Tasks/TaskContainer.cs @@ -99,7 +99,7 @@ public void SaveTasks() /// ~TaskContainer() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif @@ -108,7 +108,9 @@ public void SaveTasks() public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } diff --git a/Composite/Core/Application/AppDomainLocker.cs b/Composite/Core/Application/AppDomainLocker.cs index b3d78c230b..aa137dbdbf 100644 --- a/Composite/Core/Application/AppDomainLocker.cs +++ b/Composite/Core/Application/AppDomainLocker.cs @@ -200,7 +200,9 @@ public DisposableLock(bool verbose = true) public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -219,7 +221,7 @@ void Dispose(bool disposing) private string stack = Environment.StackTrace; ~DisposableLock() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Application/ApplicationOnlineHandlerFacade.cs b/Composite/Core/Application/ApplicationOnlineHandlerFacade.cs index 0b368c8d42..6c594c09d0 100644 --- a/Composite/Core/Application/ApplicationOnlineHandlerFacade.cs +++ b/Composite/Core/Application/ApplicationOnlineHandlerFacade.cs @@ -76,7 +76,19 @@ private sealed class TurnOffToken : IDisposable public void Dispose() { ApplicationOnlineHandlerFacade.TurnApplicationOnline(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~TurnOffToken() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Core/Application/GlobalFileLocker.cs b/Composite/Core/Application/GlobalFileLocker.cs index edb73ad236..664b074650 100644 --- a/Composite/Core/Application/GlobalFileLocker.cs +++ b/Composite/Core/Application/GlobalFileLocker.cs @@ -155,7 +155,9 @@ public DisposableLock(GlobalFileLocker globalFileLocker) public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -174,7 +176,7 @@ protected virtual void Dispose(bool disposing) private string stack = Environment.StackTrace; ~DisposableLock() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Application/Job.cs b/Composite/Core/Application/Job.cs index 7373799003..6c39757624 100644 --- a/Composite/Core/Application/Job.cs +++ b/Composite/Core/Application/Job.cs @@ -102,9 +102,19 @@ public Job() public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~Job() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif #endregion private void Dispose(bool disposing) diff --git a/Composite/Core/Application/ShutdownGuard.cs b/Composite/Core/Application/ShutdownGuard.cs index 5d6bb51bd8..95cf9504b5 100644 --- a/Composite/Core/Application/ShutdownGuard.cs +++ b/Composite/Core/Application/ShutdownGuard.cs @@ -77,6 +77,9 @@ public ShutdownGuard() /// public void Dispose() { +#if LeakCheck + GC.SuppressFinalize(this); +#endif if (!HostingEnvironment.IsHosted) { return; @@ -88,5 +91,15 @@ public void Dispose() //_callbackFieldInfo.SetValue(_fileChangesManager, _savedCallbackValue); //_isFCNDisabledFieldInfo.SetValue(_fileChangesManager, (Int32)0); } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~ShutdownGuard() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif + } } diff --git a/Composite/Core/Collections/Generic/CastEnumerator.cs b/Composite/Core/Collections/Generic/CastEnumerator.cs index 8dd17ec075..3e37e09860 100644 --- a/Composite/Core/Collections/Generic/CastEnumerator.cs +++ b/Composite/Core/Collections/Generic/CastEnumerator.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; namespace Composite.Core.Collections.Generic @@ -23,8 +23,19 @@ public T Current public void Dispose() { _enumerator = null; +#if LeakCheck + System.GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = System.Environment.StackTrace; + /// + ~CastEnumerator() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif object IEnumerator.Current { get diff --git a/Composite/Core/Collections/Generic/ResourceLocker.cs b/Composite/Core/Collections/Generic/ResourceLocker.cs index f32acf3eb3..0264dcbd87 100644 --- a/Composite/Core/Collections/Generic/ResourceLocker.cs +++ b/Composite/Core/Collections/Generic/ResourceLocker.cs @@ -213,7 +213,19 @@ internal ResourceLockerToken(ResourceLocker resourceLocker) public void Dispose() { _resourceLocker.Exit(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~ResourceLockerToken() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Core/IO/C1FileSystemWatcher.cs b/Composite/Core/IO/C1FileSystemWatcher.cs index 47c20618bf..722ae06270 100644 --- a/Composite/Core/IO/C1FileSystemWatcher.cs +++ b/Composite/Core/IO/C1FileSystemWatcher.cs @@ -268,7 +268,9 @@ public C1WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int void IDisposable.Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } #if LeakCheck @@ -278,7 +280,7 @@ void IDisposable.Dispose() /// ~C1FileSystemWatcher() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/IO/C1StreamReader.cs b/Composite/Core/IO/C1StreamReader.cs index e598e3057c..214d966735 100644 --- a/Composite/Core/IO/C1StreamReader.cs +++ b/Composite/Core/IO/C1StreamReader.cs @@ -270,7 +270,7 @@ public virtual Encoding CurrentEncoding /// ~C1StreamReader() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/IO/C1StreamWriter.cs b/Composite/Core/IO/C1StreamWriter.cs index 204eb9c10e..0eb6a2e85a 100644 --- a/Composite/Core/IO/C1StreamWriter.cs +++ b/Composite/Core/IO/C1StreamWriter.cs @@ -606,7 +606,7 @@ public override Encoding Encoding /// ~C1StreamWriter() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/IO/Zip/ZipFileSystem.cs b/Composite/Core/IO/Zip/ZipFileSystem.cs index 82140e33a0..b3086abfb5 100644 --- a/Composite/Core/IO/Zip/ZipFileSystem.cs +++ b/Composite/Core/IO/Zip/ZipFileSystem.cs @@ -244,7 +244,19 @@ void IDisposable.Dispose() _innerStream.Dispose(); _disposeAction(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~StreamWrapper() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Core/Implementation/C1FileStreamImplementation.cs b/Composite/Core/Implementation/C1FileStreamImplementation.cs index a3327aeb67..e3da3dbd9d 100644 --- a/Composite/Core/Implementation/C1FileStreamImplementation.cs +++ b/Composite/Core/Implementation/C1FileStreamImplementation.cs @@ -221,7 +221,9 @@ public virtual void Close() public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -231,7 +233,7 @@ public void Dispose() /// ~C1FileStreamImplementation() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Implementation/C1StreamReaderImplementation.cs b/Composite/Core/Implementation/C1StreamReaderImplementation.cs index 16026c83b5..257bc16ed1 100644 --- a/Composite/Core/Implementation/C1StreamReaderImplementation.cs +++ b/Composite/Core/Implementation/C1StreamReaderImplementation.cs @@ -171,19 +171,19 @@ public virtual Encoding CurrentEncoding public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } #if LeakCheck private string stack = Environment.StackTrace; - /// - /// See . - /// + /// ~C1StreamReaderImplementation() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Implementation/C1StreamWriterImplementation.cs b/Composite/Core/Implementation/C1StreamWriterImplementation.cs index 9ebb7c356e..2446869aff 100644 --- a/Composite/Core/Implementation/C1StreamWriterImplementation.cs +++ b/Composite/Core/Implementation/C1StreamWriterImplementation.cs @@ -544,7 +544,9 @@ public virtual Encoding Encoding public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -554,7 +556,7 @@ public void Dispose() /// ~C1StreamWriterImplementation() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Implementation/DataConnectionImplementation.cs b/Composite/Core/Implementation/DataConnectionImplementation.cs index fd66db0086..4c0ac58939 100644 --- a/Composite/Core/Implementation/DataConnectionImplementation.cs +++ b/Composite/Core/Implementation/DataConnectionImplementation.cs @@ -172,7 +172,9 @@ public virtual void Delete(IEnumerable items) public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -182,7 +184,7 @@ public void Dispose() /// ~DataConnectionImplementation() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Implementation/PageDataConnectionImplementation.cs b/Composite/Core/Implementation/PageDataConnectionImplementation.cs index 14c1080642..fbe473834f 100644 --- a/Composite/Core/Implementation/PageDataConnectionImplementation.cs +++ b/Composite/Core/Implementation/PageDataConnectionImplementation.cs @@ -88,7 +88,9 @@ public IQueryable GetPageData(SitemapScope scope, Guid sourcePageI public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } #if LeakCheck @@ -96,7 +98,7 @@ public void Dispose() /// ~PageDataConnectionImplementation() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs index 30caa09fe9..9a52a79918 100644 --- a/Composite/Core/Instrumentation/DisposableResourceTracer.cs +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -13,8 +13,9 @@ namespace Composite.Core.Instrumentation #if LeakCheck /// /// This class only provide functionality if the conditional compilation symbol #LeakCheck is defined. - /// It is defined for this build... - /// It is NOT defined for this build... + /// See the method for how to feed this class data. + /// When appPool shuts down a xml file with stack traces and count will be written to the root. This file indicate areas where (tooled) IDisposable classes were not + /// disposed as expected. /// public static class DisposableResourceTracer { @@ -23,6 +24,7 @@ public static class DisposableResourceTracer private static object _dumpLock = new object(); private static bool dumpAlways = false; + /// public static readonly DateTime TraceStart = DateTime.Now; static DisposableResourceTracer() @@ -36,7 +38,23 @@ private static void OnShutDownEvent(ShutDownEventArgs args) DumpStacks(); } - public static void Register(string stack) + /// Register a stack trace - expected usage if for this to be called from IDisposable finalizers, indicating they did not dispose as expected from user code. + /// + /// #if LeakCheck + /// private string stack = Environment.StackTrace; + /// ~MyDisposableClass() + /// { + /// Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + /// } + /// #endif + /// + /// You should ensure finalizer is not called, when Dispose() execute as expected. Put the following inside your Dispose() method: + /// + /// #if LeakCheck + /// GC.SuppressFinalize(this); + /// #endif + /// + public static void RegisterFinalizerExecution(string stack) { lock (_lock) { @@ -81,6 +99,10 @@ private static void DumpStacks() } } + /// + /// Returns the currently registered stacks - since stacks are typically only registered when GC Generation 2 heap is cleaned - and this happens rarely - the result here is not guaranteed to be complete... + /// + /// public static Dictionary GetStacks() { lock (_lock) diff --git a/Composite/Core/Instrumentation/LogExecutionTime.cs b/Composite/Core/Instrumentation/LogExecutionTime.cs index 959b8c477b..43fa749dce 100644 --- a/Composite/Core/Instrumentation/LogExecutionTime.cs +++ b/Composite/Core/Instrumentation/LogExecutionTime.cs @@ -24,6 +24,18 @@ public void Dispose() { int executionTime = Environment.TickCount - _startTime; Log.LogVerbose(_logTitle, "Finished: " + _message + " ({0} ms)".FormatWith(executionTime)); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LogExecutionTime() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Core/Instrumentation/Profiler.cs b/Composite/Core/Instrumentation/Profiler.cs index 24ae6f5cd1..6883483895 100644 --- a/Composite/Core/Instrumentation/Profiler.cs +++ b/Composite/Core/Instrumentation/Profiler.cs @@ -186,7 +186,19 @@ public void Dispose() #endif _stack.Pop(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~InfoCollector() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } internal static void AddSubMeasurement(Measurement measurement) diff --git a/Composite/Core/Logging/DebugLoggingScope.cs b/Composite/Core/Logging/DebugLoggingScope.cs index 7244d16b9c..bff952a137 100644 --- a/Composite/Core/Logging/DebugLoggingScope.cs +++ b/Composite/Core/Logging/DebugLoggingScope.cs @@ -91,9 +91,22 @@ public void Dispose() { LoggingService.LogVerbose(_scopeName, string.Format("Finished {0} ({1} ms)", _actionInfo, endTickCount - _startTickCount)); } +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~DebugLoggingScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif + + private class NoActionDisposable : IDisposable { public void Dispose() diff --git a/Composite/Core/Parallelization/AsyncLock.cs b/Composite/Core/Parallelization/AsyncLock.cs index 2406e0fcef..75e4cf7f75 100644 --- a/Composite/Core/Parallelization/AsyncLock.cs +++ b/Composite/Core/Parallelization/AsyncLock.cs @@ -58,7 +58,19 @@ internal Releaser(AsyncLock toRelease) public void Dispose() { _toRelease.Release(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~Releaser() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Core/Threading/ThreadCultureScope.cs b/Composite/Core/Threading/ThreadCultureScope.cs index 61c4c14202..4b4d40c97b 100644 --- a/Composite/Core/Threading/ThreadCultureScope.cs +++ b/Composite/Core/Threading/ThreadCultureScope.cs @@ -86,6 +86,19 @@ public void Dispose() _disposed = true; } + +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~ThreadCultureScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); } +#endif } } diff --git a/Composite/Core/Threading/ThreadDataManager.cs b/Composite/Core/Threading/ThreadDataManager.cs index 2db1877b9c..ae2c530a4c 100644 --- a/Composite/Core/Threading/ThreadDataManager.cs +++ b/Composite/Core/Threading/ThreadDataManager.cs @@ -299,7 +299,7 @@ public void Dispose(bool disposing) ~ThreadDataManagerScope() { #if LeakCheck - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); #endif Dispose(false); } diff --git a/Composite/Core/Threading/ThreadDataManagerData.cs b/Composite/Core/Threading/ThreadDataManagerData.cs index 18f13fdc1e..cf3c0d9d37 100644 --- a/Composite/Core/Threading/ThreadDataManagerData.cs +++ b/Composite/Core/Threading/ThreadDataManagerData.cs @@ -105,8 +105,20 @@ public void Dispose() { OnDispose?.Invoke(); _disposed = true; + +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~ThreadDataManagerData() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif #endregion } } diff --git a/Composite/Core/UrlBuilder.cs b/Composite/Core/UrlBuilder.cs index 73f030331a..3ffda34a3f 100644 --- a/Composite/Core/UrlBuilder.cs +++ b/Composite/Core/UrlBuilder.cs @@ -113,7 +113,19 @@ public NoHttpContext() public void Dispose() { HttpContext.Current = _context; +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~NoHttpContext() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Core/WebClient/BuildManagerHelper.cs b/Composite/Core/WebClient/BuildManagerHelper.cs index 80870dbe58..f3fbde6023 100644 --- a/Composite/Core/WebClient/BuildManagerHelper.cs +++ b/Composite/Core/WebClient/BuildManagerHelper.cs @@ -193,7 +193,19 @@ public DisableUrlMedataScope() public void Dispose() { DisableUrlMetadataCaching(false); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~DisableUrlMedataScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Core/WebClient/PhantomJs/PhantomServer.cs b/Composite/Core/WebClient/PhantomJs/PhantomServer.cs index 6a60f4ddc2..6aa995b81e 100644 --- a/Composite/Core/WebClient/PhantomJs/PhantomServer.cs +++ b/Composite/Core/WebClient/PhantomJs/PhantomServer.cs @@ -309,7 +309,7 @@ public void Dispose() ~PhantomServer() { #if LeakCheck - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); #endif // Finalizer calls Dispose(false) Dispose(false); diff --git a/Composite/Data/Caching/CachingEnumerator.cs b/Composite/Data/Caching/CachingEnumerator.cs index eb7ae76991..2f71adacfc 100644 --- a/Composite/Data/Caching/CachingEnumerator.cs +++ b/Composite/Data/Caching/CachingEnumerator.cs @@ -1,4 +1,4 @@ -using System.Collections; +using System.Collections; using System.Collections.Generic; using System; using Composite.Data.Foundation.CodeGeneration; @@ -24,10 +24,20 @@ public CachingEnumerator(IEnumerator enumerator) public void Dispose() { _enumerator.Dispose(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } - - object IEnumerator.Current => WrapperConstructor(_enumerator.Current); +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~CachingEnumerator() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif + object IEnumerator.Current => WrapperConstructor(_enumerator.Current); public bool MoveNext() diff --git a/Composite/Data/DataConnection.cs b/Composite/Data/DataConnection.cs index 2176d8d51e..fe77b05724 100644 --- a/Composite/Data/DataConnection.cs +++ b/Composite/Data/DataConnection.cs @@ -515,7 +515,9 @@ public SitemapNavigator SitemapNavigator public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } @@ -525,7 +527,7 @@ public void Dispose() /// ~DataConnection() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif diff --git a/Composite/Data/DataEventSystemFacade.cs b/Composite/Data/DataEventSystemFacade.cs index 069da35f75..30a428263c 100644 --- a/Composite/Data/DataEventSystemFacade.cs +++ b/Composite/Data/DataEventSystemFacade.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using Composite.C1Console.Events; @@ -670,9 +670,19 @@ public void Dispose() if(!_active) return; Counter(-1); - +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~SuppressEventScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif #endregion } diff --git a/Composite/Data/DataScope.cs b/Composite/Data/DataScope.cs index c9e8478cca..bc79a1bd9c 100644 --- a/Composite/Data/DataScope.cs +++ b/Composite/Data/DataScope.cs @@ -93,11 +93,11 @@ public DataScope(PublicationScope publicationScope, CultureInfo cultureInfo) } #if LeakCheck - private string stack = Environment.StackTrace; + private string stack = Environment.StackTrace; /// ~DataScope() { - Composite.Core.Instrumentation.DisposableResourceTracer.Register(stack); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } #endif @@ -106,7 +106,9 @@ public DataScope(PublicationScope publicationScope, CultureInfo cultureInfo) public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } void Dispose(bool disposing) diff --git a/Composite/Data/ProcessControlled/ProcessControllerFacade.cs b/Composite/Data/ProcessControlled/ProcessControllerFacade.cs index 92a831a4c1..bd5849e97e 100644 --- a/Composite/Data/ProcessControlled/ProcessControllerFacade.cs +++ b/Composite/Data/ProcessControlled/ProcessControllerFacade.cs @@ -397,7 +397,19 @@ public NoProcessControllersDisposable() public void Dispose() { ProcessControllersCounter.Counter--; +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~NoProcessControllersDisposable() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } diff --git a/Composite/GlobalInitializerFacade.cs b/Composite/GlobalInitializerFacade.cs index d962b088e8..e6ab51fc03 100644 --- a/Composite/GlobalInitializerFacade.cs +++ b/Composite/GlobalInitializerFacade.cs @@ -470,7 +470,19 @@ public PreInitHandlersScope() public void Dispose() { _preInitHandlersRunning = false; +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~PreInitHandlersScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } @@ -849,7 +861,19 @@ where sf.GetMethod().DeclaringType.Assembly.FullName.Contains("Composite.Test") #endregion ReleaseWriterLock(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LockerToken() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); } +#endif } #endregion diff --git a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/RequireTransactionScope.cs b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/RequireTransactionScope.cs index ccd1893dfc..82756e72bf 100644 --- a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/RequireTransactionScope.cs +++ b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/RequireTransactionScope.cs @@ -35,6 +35,19 @@ public void Dispose() { _scope.Dispose(); } + +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~RequireTransactionScope() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); } +#endif } } diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs index 7fe83740c4..8fd6e53d86 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -389,7 +389,19 @@ public void Dispose() { Monitor.Exit(SyncRoot); } +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~EditingContext() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs index 098a51296b..97e46830ed 100644 --- a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs +++ b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs @@ -125,6 +125,18 @@ public void Dispose() { _fileStream?.Dispose(); _fileStream = null; +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LocalC1FileStream() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamReader.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamReader.cs index c57411e208..e274f4f859 100644 --- a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamReader.cs +++ b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamReader.cs @@ -119,6 +119,18 @@ public Encoding CurrentEncoding public void Dispose() { _streamReader.Dispose(); +#if LeakCheck + System.GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = System.Environment.StackTrace; + /// + ~LocalC1StreamReader() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamWriter.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamWriter.cs index f5455eab32..73365572e4 100644 --- a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamWriter.cs +++ b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1StreamWriter.cs @@ -390,6 +390,18 @@ public Encoding Encoding public void Dispose() { _streamWriter.Dispose(); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LocalC1StreamWriter() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs index ef05424bd1..c5080909c0 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs @@ -465,13 +465,20 @@ protected virtual void Dispose(bool disposing) public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// ~FileLogger() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } +#endif } } diff --git a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs index a07ec997c3..ffec4cce92 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs @@ -23,6 +23,19 @@ public void Dispose() FileStream.Dispose(); _disposed = true; } + +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LogFileInfo() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); } +#endif } } diff --git a/Composite/Plugins/PageTemplates/MasterPages/MasterPageBase.cs b/Composite/Plugins/PageTemplates/MasterPages/MasterPageBase.cs index a2a3f4acdc..1ead3bcb74 100644 --- a/Composite/Plugins/PageTemplates/MasterPages/MasterPageBase.cs +++ b/Composite/Plugins/PageTemplates/MasterPages/MasterPageBase.cs @@ -64,5 +64,14 @@ public override void Dispose() base.Dispose(); } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~MasterPageBase() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } diff --git a/Composite/Search/Crawling/DocumentFieldNames.cs b/Composite/Search/Crawling/DocumentFieldNames.cs index 22869d0f7c..7846b6c191 100644 --- a/Composite/Search/Crawling/DocumentFieldNames.cs +++ b/Composite/Search/Crawling/DocumentFieldNames.cs @@ -2,8 +2,8 @@ namespace Composite.Search.Crawling { - [Obsolete("Use DocumentFieldNames instead", true)] /// + [Obsolete("Use DocumentFieldNames instead", true)] public static class DefaultDocumentFieldNames { /// diff --git a/Composite/Search/Crawling/XhtmlCrawlingHelper.cs b/Composite/Search/Crawling/XhtmlCrawlingHelper.cs index ab5182be0c..ff05b2b9fd 100644 --- a/Composite/Search/Crawling/XhtmlCrawlingHelper.cs +++ b/Composite/Search/Crawling/XhtmlCrawlingHelper.cs @@ -259,7 +259,19 @@ public FakeHttpContext() public void Dispose() { HttpContext.Current = _originalContext; +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~FakeHttpContext() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } } diff --git a/Composite/Search/DocumentSources/IndexUpdateActionContainer.cs b/Composite/Search/DocumentSources/IndexUpdateActionContainer.cs index 468be20ff8..d4cc4c93e7 100644 --- a/Composite/Search/DocumentSources/IndexUpdateActionContainer.cs +++ b/Composite/Search/DocumentSources/IndexUpdateActionContainer.cs @@ -22,6 +22,18 @@ public void Dispose() { action(); } +#if LeakCheck + GC.SuppressFinalize(this); +#endif } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~IndexUpdateActionContainer() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } } From b40d7643ce2166963db552377e8813773ee08ebb Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 11:10:43 +0200 Subject: [PATCH 028/145] Fixing issue in "if #LeakCheck" debug code - was giving false positives. Also file dump will write / count for minimal top stack. --- .../DisposableResourceTracer.cs | 35 +++++++++++++++---- Composite/GlobalInitializerFacade.cs | 6 ++-- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs index 9a52a79918..92abf751c6 100644 --- a/Composite/Core/Instrumentation/DisposableResourceTracer.cs +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -23,6 +23,10 @@ public static class DisposableResourceTracer private static object _lock = new object(); private static object _dumpLock = new object(); private static bool dumpAlways = false; + private const string _redundant = @" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) + at System.Environment.get_StackTrace() +"; + /// public static readonly DateTime TraceStart = DateTime.Now; @@ -58,14 +62,15 @@ public static void RegisterFinalizerExecution(string stack) { lock (_lock) { + string topStack = GetMinimalStackTop(stack); int count = 0; - if (stacks.TryGetValue(stack, out count)) + if (stacks.TryGetValue(topStack, out count)) { - stacks[stack] = count + 1; + stacks[topStack] = count + 1; } else { - stacks.Add(stack, 1); + stacks.Add(topStack, 1); } } @@ -78,8 +83,6 @@ private static void DumpStacks() string fileName = String.Format("DisposableResourceTrace_{0}.xml", TraceStart.ToString("yyyy_MM_dd_HH_mm_ss")); string fullPath = Path.Combine(rootDir, fileName); - var redundant = @" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo) - at System.Environment.get_StackTrace()"; var stacks = GetStacks(); var ordered = stacks.OrderByDescending(f => f.Value); @@ -90,7 +93,7 @@ private static void DumpStacks() , new XAttribute("seconds", (DateTime.Now - TraceStart).TotalSeconds) ); dumpDoc.Add( - ordered.Select(f => new XElement("Trace", new XAttribute("count", f.Value), f.Key.Replace(redundant,""))) + ordered.Select(f => new XElement("Trace", new XAttribute("count", f.Value), f.Key)) ); lock (_dumpLock) @@ -99,6 +102,26 @@ private static void DumpStacks() } } + private static string GetMinimalStackTop(string stack) + { + string[] stackLines = stack.Replace(_redundant,"").Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + + bool foundCaller = false; + int lineNum = 1; + + StringBuilder sb = new StringBuilder(stackLines[0]+"\n"); + + while(!foundCaller && lineNum < stackLines.Length) + { + string line = stackLines[lineNum]; + sb.AppendLine(line); + foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_"); + lineNum++; + } + + return sb.ToString(); + } + /// /// Returns the currently registered stacks - since stacks are typically only registered when GC Generation 2 heap is cleaned - and this happens rarely - the result here is not guaranteed to be complete... /// diff --git a/Composite/GlobalInitializerFacade.cs b/Composite/GlobalInitializerFacade.cs index e6ab51fc03..5965aa4030 100644 --- a/Composite/GlobalInitializerFacade.cs +++ b/Composite/GlobalInitializerFacade.cs @@ -832,6 +832,9 @@ where sf.GetMethod().DeclaringType.Assembly.FullName.Contains("Composite.Test") public void Dispose() { +#if LeakCheck + GC.SuppressFinalize(this); +#endif if (!_isWriterLock) { ReleaseReaderLock(); @@ -861,9 +864,6 @@ where sf.GetMethod().DeclaringType.Assembly.FullName.Contains("Composite.Test") #endregion ReleaseWriterLock(); -#if LeakCheck - GC.SuppressFinalize(this); -#endif } #if LeakCheck From 69234b38ea43547b02af58a39ecb70ff0c12e55e Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 26 May 2017 13:27:34 +0200 Subject: [PATCH 029/145] Fixing razor pages not being disposed during system initialization --- .../RazorFunctionProvider.cs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/Composite/Plugins/Functions/FunctionProviders/RazorFunctionProvider/RazorFunctionProvider.cs b/Composite/Plugins/Functions/FunctionProviders/RazorFunctionProvider/RazorFunctionProvider.cs index ebec86264d..81e6e9fcda 100644 --- a/Composite/Plugins/Functions/FunctionProviders/RazorFunctionProvider/RazorFunctionProvider.cs +++ b/Composite/Plugins/Functions/FunctionProviders/RazorFunctionProvider/RazorFunctionProvider.cs @@ -9,44 +9,45 @@ namespace Composite.Plugins.Functions.FunctionProviders.RazorFunctionProvider { - [ConfigurationElementType(typeof(RazorFunctionProviderData))] + [ConfigurationElementType(typeof(RazorFunctionProviderData))] internal class RazorFunctionProvider : FileBasedFunctionProvider - { - protected override string FileExtension - { - get { return "cshtml"; } - } + { + protected override string FileExtension => "cshtml"; - protected override string DefaultFunctionNamespace - { - get { return "Razor"; } - } + protected override string DefaultFunctionNamespace => "Razor"; - public RazorFunctionProvider(string name, string folder) : base(name, folder) { } + public RazorFunctionProvider(string name, string folder) : base(name, folder) { } - override protected IFunction InstantiateFunction(string virtualPath, string @namespace, string name) - { - WebPageBase razorPage; - using(BuildManagerHelper.DisableUrlMetadataCachingScope()) + protected override IFunction InstantiateFunction(string virtualPath, string @namespace, string name) + { + WebPageBase razorPage; + using (BuildManagerHelper.DisableUrlMetadataCachingScope()) { razorPage = WebPage.CreateInstanceFromVirtualPath(virtualPath); } - if(!(razorPage is RazorFunction)) + if (!(razorPage is RazorFunction razorFunction)) { - Log.LogWarning(typeof(RazorFunctionProvider).Name, "Razor page '{0}' does not inherit from the base class for razor functions '{1}' and will be ignored", - virtualPath, typeof(RazorFunction).FullName); + Log.LogWarning(nameof(RazorFunctionProvider), + $"Razor page '{virtualPath}' does not inherit from the base class for razor functions '{typeof(RazorFunction).FullName}' and will be ignored"); return null; } - var razorFunction = razorPage as RazorFunction; - - var functionParameters = FunctionBasedFunctionProviderHelper.GetParameters(razorFunction, typeof(RazorFunction), virtualPath); + try + { + var functionParameters = FunctionBasedFunctionProviderHelper.GetParameters( + razorFunction, typeof(RazorFunction), virtualPath); - return new RazorBasedFunction(@namespace, name, razorFunction.FunctionDescription, functionParameters, razorFunction.FunctionReturnType, virtualPath, this); - } + return new RazorBasedFunction(@namespace, name, razorFunction.FunctionDescription, functionParameters, + razorFunction.FunctionReturnType, virtualPath, this); + } + finally + { + razorFunction.Dispose(); + } + } protected override IFunction InstantiateFunctionFromCache(string virtualPath, string @namespace, string name, Type returnType, string cachedDescription) { @@ -58,9 +59,9 @@ protected override IFunction InstantiateFunctionFromCache(string virtualPath, st return InstantiateFunction(virtualPath, @namespace, name); } - protected override bool HandleChange(string path) - { + protected override bool HandleChange(string path) + { return path.EndsWith(FileExtension); - } + } } } From a1a1ff8a2ec514172fbf2a8dbb845d10bcbd5747 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 15:13:30 +0200 Subject: [PATCH 030/145] Surfacing setting "auto select parents" on Hierarchical Selecter ui control --- .../Forms/CoreUiControls/HierarchicalSelectorUiControl.cs | 8 ++++++++ .../TemplatedHierarchicalSelectorUiControlFactory.cs | 4 ++++ .../Selectors/HierarchicalSelector.ascx | 3 ++- .../data/selectors/HierarchicalSelectorBinding.js | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/Composite/C1Console/Forms/CoreUiControls/HierarchicalSelectorUiControl.cs b/Composite/C1Console/Forms/CoreUiControls/HierarchicalSelectorUiControl.cs index 20f34b9ade..a737a37b4f 100644 --- a/Composite/C1Console/Forms/CoreUiControls/HierarchicalSelectorUiControl.cs +++ b/Composite/C1Console/Forms/CoreUiControls/HierarchicalSelectorUiControl.cs @@ -7,6 +7,11 @@ namespace Composite.C1Console.Forms.CoreUiControls [ControlValueProperty("SelectedKeys")] internal abstract class HierarchicalSelectorUiControl : UiControl { + internal HierarchicalSelectorUiControl() + { + this.AutoSelectParents = true; + } + [BindableProperty] [FormsProperty] public IEnumerable SelectedKeys { get; set; } @@ -17,6 +22,9 @@ internal abstract class HierarchicalSelectorUiControl : UiControl [FormsProperty] public bool AutoSelectChildren { get; set; } + [FormsProperty] + public bool AutoSelectParents { get; set; } + [FormsProperty] public bool Required { get; set; } } diff --git a/Composite/Plugins/Forms/WebChannel/UiControlFactories/TemplatedHierarchicalSelectorUiControlFactory.cs b/Composite/Plugins/Forms/WebChannel/UiControlFactories/TemplatedHierarchicalSelectorUiControlFactory.cs index e1ec466fb2..8e94310aa8 100644 --- a/Composite/Plugins/Forms/WebChannel/UiControlFactories/TemplatedHierarchicalSelectorUiControlFactory.cs +++ b/Composite/Plugins/Forms/WebChannel/UiControlFactories/TemplatedHierarchicalSelectorUiControlFactory.cs @@ -48,6 +48,9 @@ internal void InitializeWebViewState() /// public bool AutoSelectChildren { get; set; } + /// + public bool AutoSelectParents { get; set; } + /// public bool Required { get; set; } } @@ -84,6 +87,7 @@ public Control BuildWebControl() _userControl.SelectedKeys = this.SelectedKeys; _userControl.TreeNodes = this.TreeNodes; _userControl.AutoSelectChildren = this.AutoSelectChildren; + _userControl.AutoSelectParents = this.AutoSelectParents; _userControl.Required = this.Required; return _userControl; diff --git a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/HierarchicalSelector.ascx b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/HierarchicalSelector.ascx index 6a238b9df8..a4e44ca6ca 100644 --- a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/HierarchicalSelector.ascx +++ b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Selectors/HierarchicalSelector.ascx @@ -1,4 +1,4 @@ -<%@ Control Language="C#" Inherits="Composite.Plugins.Forms.WebChannel.UiControlFactories.HierarchicalSelectorTemplateUserControlBase" %> +<%@ Control Language="C#" Inherits="Composite.Plugins.Forms.WebChannel.UiControlFactories.HierarchicalSelectorTemplateUserControlBase" %> <%@ Import Namespace="Composite.C1Console.Forms.CoreUiControls" %> <%@ Import Namespace="Composite.Core.Extensions" %> <%@ Import Namespace="Composite.Core.Types" %> @@ -95,6 +95,7 @@ " autoselectchildren="<%= AutoSelectChildren ? "true" : "false" %>" + autoselectparents="<%= AutoSelectParents ? "true" : "false" %>" hascounter="true" > <%= _treeXhml %> diff --git a/Website/Composite/scripts/source/top/ui/bindings/data/selectors/HierarchicalSelectorBinding.js b/Website/Composite/scripts/source/top/ui/bindings/data/selectors/HierarchicalSelectorBinding.js index 2f3fc28708..d88754ed08 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/data/selectors/HierarchicalSelectorBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/data/selectors/HierarchicalSelectorBinding.js @@ -1,4 +1,4 @@ -HierarchicalSelectorBinding.prototype = new DataBinding; +HierarchicalSelectorBinding.prototype = new DataBinding; HierarchicalSelectorBinding.prototype.constructor = HierarchicalSelectorBinding; HierarchicalSelectorBinding.superclass = DataBinding.prototype; @@ -101,6 +101,7 @@ HierarchicalSelectorBinding.prototype._buildDOMContent = function () { HierarchicalSelectorBinding.prototype._parseDOMProperties = function () { this.autoSelectChildren = this.getProperty("autoselectchildren") === true; + this.autoSelectParents = this.getProperty("autoselectparents") === true; this.isRequired = this.getProperty("required") === true; this.hasCounter = this.getProperty("hascounter") === true; } From 6bb8a21d5bb5ebd18d83597302b1cb504329e977 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 16:09:04 +0200 Subject: [PATCH 031/145] Nightwatch clean file list alphabetical --- Website/test/e2e/reset.js | 281 ++++++++++++++++++-------------------- 1 file changed, 135 insertions(+), 146 deletions(-) diff --git a/Website/test/e2e/reset.js b/Website/test/e2e/reset.js index 2293924edc..98b2a209e7 100644 --- a/Website/test/e2e/reset.js +++ b/Website/test/e2e/reset.js @@ -1,149 +1,138 @@ -const removePaths = [ - 'App_Data/Composite/Configuration/Composite.Forms.FormBuilder.xml', - 'App_Data/Composite/Configuration/Composite.Media.ImageCrop.xml', - 'App_Data/Composite/Configuration/DynamicXmlDataProvider.config', - 'App_Data/Composite/Configuration/DynamicSqlDataProvider.config', - 'App_Data/Composite/Configuration/FirstTimeStart.xml', - 'App_Data/Composite/Configuration/InstallationInformation.xml', - 'App_Data/Composite/Configuration/SystemInitialized.xml', - 'App_Data/Composite/DataMetaData/', - 'App_Data/Composite/DataStores/', - 'App_Data/Composite/DynamicTypeForms/*', - 'App_Data/Composite/InlineCSharpFunctions/', - 'App_Data/Composite/LanguagePacks/', - 'App_Data/Composite/PackageLicenses/', - 'App_Data/Composite/Azure/', - 'App_Data/Composite/Packages/*', - 'App_Data/Media/*', - 'App_Data/PageTemplateFeatures/', - 'App_Data/PageTemplates/*.cshtml', - 'App_Data/PageTemplates/*.master*', - 'App_Data/Razor/Composite/', - 'App_Data/Razor/Orckestra/', - 'App_Data/Razor/Layout/', - 'App_Data/Razor/PageBlocks/', - 'App_Data/Razor/Widgets/', - 'App_Data/Razor/Content/', - 'App_Data/Razor/ -- !(web.config) ', - 'App_Data/Components/', - 'App_GlobalResources/', - 'BlogMetaWeblog.ashx', - 'BlogRssFeed.ashx', - 'Composite/InstalledPackages/', - 'Frontend/Composite/', - 'Frontend/Orckestra/', - 'Frontend/Images/*', - 'Frontend/Scripts/*', - 'Frontend/Styles/VisualEditor.less', - 'Frontend/Styles/style.less', - 'Frontend/Styles/style.min.css', - 'Frontend/Styles/bootstrap', - 'Frontend/Styles/font-awesome', - 'Frontend/Styles/includes', - 'Frontend/Styles/PageBlocks', - 'Frontend/Styles/style.less', - 'Frontend/Styles/style.min.css', - 'Frontend/Styles/VisualEditor.common.less', - 'Frontend/Styles/VisualEditor.less', - 'Frontend/fonts/', - 'bin/Antlr3.Runtime.dll', - 'bin/Composite.Community.*.*', - 'bin/Composite.Forms.FormBuilder.dll', - 'bin/Composite.Forms.Renderer.dll', - 'bin/Composite.Media.*.*', - 'bin/Composite.Search.SimplePageSearch.dll', - 'bin/Composite.XmlSerializers.dll', - 'bin/Composite.Tools.*.*', - 'bin/Composite.Web.*.*', - 'bin/Orckestra.Web.Css.Less.dll', - 'bin/CookComputing.XmlRpcV2.dll', - 'bin/ICSharpCode.SharpZipLib.dll', - 'bin/System.Web.Optimization.dll', - 'bin/WebGrease.dll', - 'Frontend/Composite/', - 'App_Data/UserControls/ -- !(web.config)', - 'App_Data/PageTemplateFeatures/', - 'Web.config', - 'App_Data/Composite/Composite.config', - 'App_Data/Composite/Cache/Assemblies/*', - 'App_Data/Composite/Cache/', - 'App_Data/Xslt/*', - 'App_Data/Composite/LogFiles/*', - 'App_Code/*', - 'App_Data/Composite/ApplicationState/', - 'Composite/InstalledPackages/', - 'BlogRssFeed.ashx', - 'BlogCommentsRssFeed.ashx', - 'BlogRssFeed.ashx', - 'Bin/Composite.Community.Blog.dll', - 'Bin/CookComputing.XmlRpcV2.dll', - 'BlogMetaWeblog.ashx', - 'App_Data/Razor/Composite/Community/Blog/', - 'App_GlobalResources/Composite.Community.Blog/', - 'Composite/InstalledPackages/content/forms/Composite.Community.Blog/', - 'Composite/InstalledPackages/controls/FormsControls/Composite.Community.Blog/', - 'Frontend/Composite/Community/Blog/', - 'App_GlobalResources/Composite/Community/Blog.resx', - 'App_GlobalResources/Composite/Community/Blog.ru-ru.resx', - 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Entries.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Settings.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Settings.xmll', - 'App_Data/Composite/DynamicTypeForms/Composite/Community/Blog/Authors.xml', - 'App_Data/Composite/DynamicTypeForms/Composite/Community/Blog/Entries.xml', - 'App_Data/Search/', - 'Bin/CompositeC1Contrib.RazorFunctions.dll', - 'Bin/Microsoft.Web.*.dll', - 'Bin/Composite.Community.Extranet.dll', - 'Bin/Composite.Community.Newsletter.dll', - 'Bin/Composite.Community.Newsletter.SubjectBased.dll', - 'Bin/Composite.Community.Newsletter.DataTypeBased.dll', - 'Bin/Composite.Community.Newsletter.FunctionBased.dll', - 'Bin/Common.Logging*.dll', - 'Bin/BoboBrowse.Net.dll', - 'Bin/C5.dll', - 'Bin/Orckestra.Search*.dll', - 'Newsletter.ashx', - 'App_Data/Composite/TreeDefinitions/Composite.Community.Newsletter.SubjectBased.xml', - 'App_Data/NewsletterType/', - 'App_GlobalResources/Composite/Community/Newsletter.resx', - 'Bin/Composite.Community.EventCalendar.dll', - 'App_Data/Composite/TreeDefinitions/Composite.Community.EventCalendar.EventsApp.xml', - 'App_GlobalResources/Composite/Community/ContactForm.resx', - 'App_GlobalResources/Composite/Community/ContactForm.ru-RU.resx', - 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactForm.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactFrom.EmailTemplate.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactForm.xml', - 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactFrom.EmailTemplate.xml', - 'Bin/Composite.Versioning.ContentVersioning.dll', - 'App_Data/Composite/TreeDefinitions/Composite.Versioning.ContentVersioning.xml', - 'Frontend/Composite/C1BaseSite/', - 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.News.NewsItem.da-dk.xml', - 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.News.NewsItem.en-us.xml', - 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.TeaserSpot.da-dk.xml', - 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.TeaserSpot.en-us.xml', - 'App_Data/Composite/TreeDefinitions/Composite.TemplateSites.Base01.News.NewsItem.xml', - 'App_Data/Composite/TreeDefinitions/Composite.TemplateSites.Base01.TeaserSpot.xml', - 'App_Data/Composite/TreeDefinitions/Orckestra.Lists.Tabs.xml', - 'App_Data/Composite/TreeDefinitions/Orckestra.Lists.Portfolio.Application.xml', - 'Frontend/Styles/Print.css', - 'Frontend/Styles/Screen.css', - 'Frontend/Styles/VisualEditor/VisualEditor.Config.css', - 'Frontend/Styles/VisualEditor/VisualEditor.Config.xml', - 'Frontend/Styles/VisualEditor/VisualEditor.Default.css', - 'Frontend/Composite/Search/SimplePageSearch/Styles.css', - 'Composite/content/forms/InstalledPackages/Composite.Tools.PackageCreator/', - 'Bin/Composite.Tools.PackageCreator.dll', - 'Composite/InstalledPackages/localization/Composite.Tools.PackageCreator.en-us.xml', - 'App_Data/PackageCreator', - 'App_Data/Composite/Configuration\Composite.Forms.FormBuilder.xml', - 'App_Data/Composite/Configuration\Composite.Media.ImageCrop.xml', - 'Frontend/Scripts/', - 'Frontend/Styles/ -- !(VisualEditor.common.css)', - 'Frontend/Images/', - 'Frontend/Composite/', - 'bin/Composite.Generated.dll', - 'ZipTest/' +const removePaths = [ + 'App_Code/*', + 'App_Data/Components/', + 'App_Data/Composite/ApplicationState/', + 'App_Data/Composite/Azure/', + 'App_Data/Composite/Cache/', + 'App_Data/Composite/Cache/Assemblies/*', + 'App_Data/Composite/Composite.config', + 'App_Data/Composite/Configuration/Composite.Forms.FormBuilder.xml', + 'App_Data/Composite/Configuration/Composite.Media.ImageCrop.xml', + 'App_Data/Composite/Configuration/DynamicSqlDataProvider.config', + 'App_Data/Composite/Configuration/DynamicXmlDataProvider.config', + 'App_Data/Composite/Configuration/FirstTimeStart.xml', + 'App_Data/Composite/Configuration/InstallationInformation.xml', + 'App_Data/Composite/Configuration/SystemInitialized.xml', + 'App_Data/Composite/Configuration\Composite.Forms.FormBuilder.xml', + 'App_Data/Composite/Configuration\Composite.Media.ImageCrop.xml', + 'App_Data/Composite/DataMetaData/', + 'App_Data/Composite/DataStores/', + 'App_Data/Composite/DynamicTypeForms/*', + 'App_Data/Composite/DynamicTypeForms/Composite/Community/Blog/Authors.xml', + 'App_Data/Composite/DynamicTypeForms/Composite/Community/Blog/Entries.xml', + 'App_Data/Composite/InlineCSharpFunctions/', + 'App_Data/Composite/LanguagePacks/', + 'App_Data/Composite/LogFiles/*', + 'App_Data/Composite/PackageLicenses/', + 'App_Data/Composite/Packages/*', + 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Entries.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Settings.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.Settings.xmll', + 'App_Data/Composite/TreeDefinitions/Composite.Community.Blog.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactForm.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactFrom.EmailTemplate.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.ContactFrom.EmailTemplate.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.EventCalendar.EventsApp.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Community.Newsletter.SubjectBased.xml', + 'App_Data/Composite/TreeDefinitions/Composite.TemplateSites.Base01.News.NewsItem.xml', + 'App_Data/Composite/TreeDefinitions/Composite.TemplateSites.Base01.TeaserSpot.xml', + 'App_Data/Composite/TreeDefinitions/Composite.Versioning.ContentVersioning.xml', + 'App_Data/Composite/TreeDefinitions/Orckestra.Lists.Portfolio.Application.xml', + 'App_Data/Composite/TreeDefinitions/Orckestra.Lists.Tabs.xml', + 'App_Data/Media/*', + 'App_Data/NewsletterType/', + 'App_Data/PackageCreator', + 'App_Data/PageTemplateFeatures/', + 'App_Data/PageTemplates/*.cshtml', + 'App_Data/PageTemplates/*.master*', + 'App_Data/Razor/ -- !(web.config) ', + 'App_Data/Razor/Composite/', + 'App_Data/Razor/Composite/Community/Blog/', + 'App_Data/Razor/Content/', + 'App_Data/Razor/Layout/', + 'App_Data/Razor/Orckestra/', + 'App_Data/Razor/PageBlocks/', + 'App_Data/Razor/Widgets/', + 'App_Data/Search/', + 'App_Data/UserControls/ -- !(web.config)', + 'App_Data/Xslt/*', + 'App_GlobalResources/', + 'App_GlobalResources/Composite.Community.Blog/', + 'App_GlobalResources/Composite/Community/Blog.resx', + 'App_GlobalResources/Composite/Community/Blog.ru-ru.resx', + 'App_GlobalResources/Composite/Community/ContactForm.resx', + 'App_GlobalResources/Composite/Community/ContactForm.ru-RU.resx', + 'App_GlobalResources/Composite/Community/Newsletter.resx', + 'bin/Antlr3.Runtime.dll', + 'Bin/BoboBrowse.Net.dll', + 'Bin/C5.dll', + 'Bin/Common.Logging*.dll', + 'bin/Composite.Community.*.*', + 'Bin/Composite.Community.Blog.dll', + 'Bin/Composite.Community.EventCalendar.dll', + 'Bin/Composite.Community.Extranet.dll', + 'Bin/Composite.Community.Newsletter.DataTypeBased.dll', + 'Bin/Composite.Community.Newsletter.dll', + 'Bin/Composite.Community.Newsletter.FunctionBased.dll', + 'Bin/Composite.Community.Newsletter.SubjectBased.dll', + 'bin/Composite.Forms.FormBuilder.dll', + 'bin/Composite.Forms.Renderer.dll', + 'bin/Composite.Generated.dll', + 'bin/Composite.Media.*.*', + 'bin/Composite.Search.SimplePageSearch.dll', + 'bin/Composite.Tools.*.*', + 'Bin/Composite.Tools.PackageCreator.dll', + 'Bin/Composite.Versioning.ContentVersioning.dll', + 'bin/Composite.Web.*.*', + 'bin/Composite.XmlSerializers.dll', + 'Bin/CompositeC1Contrib.RazorFunctions.dll', + 'bin/CookComputing.XmlRpcV2.dll', + 'Bin/CookComputing.XmlRpcV2.dll', + 'bin/ICSharpCode.SharpZipLib.dll', + 'Bin/Microsoft.Web.*.dll', + 'Bin/Orckestra.Search*.dll', + 'bin/Orckestra.Web.Css.Less.dll', + 'bin/System.Web.Optimization.dll', + 'bin/WebGrease.dll', + 'BlogCommentsRssFeed.ashx', + 'BlogMetaWeblog.ashx', + 'BlogRssFeed.ashx', + 'Composite/content/forms/InstalledPackages/Composite.Tools.PackageCreator/', + 'Composite/InstalledPackages/', + 'Composite/InstalledPackages/content/forms/Composite.Community.Blog/', + 'Composite/InstalledPackages/controls/FormsControls/Composite.Community.Blog/', + 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.News.NewsItem.da-dk.xml', + 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.News.NewsItem.en-us.xml', + 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.TeaserSpot.da-dk.xml', + 'Composite/InstalledPackages/localization/Composite.TemplateSites.Base01.TeaserSpot.en-us.xml', + 'Composite/InstalledPackages/localization/Composite.Tools.PackageCreator.en-us.xml', + 'Frontend/Composite/', + 'Frontend/Composite/C1BaseSite/', + 'Frontend/Composite/Community/Blog/', + 'Frontend/Composite/Search/SimplePageSearch/Styles.css', + 'Frontend/fonts/', + 'Frontend/Images/', + 'Frontend/Images/*', + 'Frontend/Orckestra/', + 'Frontend/Scripts/', + 'Frontend/Scripts/*', + 'Frontend/Styles/ -- !(VisualEditor.common.css)', + 'Frontend/Styles/bootstrap', + 'Frontend/Styles/font-awesome', + 'Frontend/Styles/includes', + 'Frontend/Styles/PageBlocks', + 'Frontend/Styles/Print.css', + 'Frontend/Styles/Screen.css', + 'Frontend/Styles/style.less', + 'Frontend/Styles/style.min.css', + 'Frontend/Styles/VisualEditor.common.less', + 'Frontend/Styles/VisualEditor.less', + 'Frontend/Styles/VisualEditor/VisualEditor.Config.css', + 'Frontend/Styles/VisualEditor/VisualEditor.Config.xml', + 'Frontend/Styles/VisualEditor/VisualEditor.Default.css', + 'Newsletter.ashx', + 'Web.config', + 'ZipTest/' ] const rimraf = require('rimraf'); const fs = require('fs'); From 3c2c3dbf89fe929a32b3b8edd8ce6988fa7f095c Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 16:44:11 +0200 Subject: [PATCH 032/145] Nightwatch clean list allow for Venus site reset --- Website/test/e2e/reset.js | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/Website/test/e2e/reset.js b/Website/test/e2e/reset.js index 98b2a209e7..81118073a9 100644 --- a/Website/test/e2e/reset.js +++ b/Website/test/e2e/reset.js @@ -64,33 +64,36 @@ 'App_GlobalResources/Composite/Community/ContactForm.ru-RU.resx', 'App_GlobalResources/Composite/Community/Newsletter.resx', 'bin/Antlr3.Runtime.dll', - 'Bin/BoboBrowse.Net.dll', - 'Bin/C5.dll', - 'Bin/Common.Logging*.dll', + 'bin/BoboBrowse.Net.dll', + 'bin/C5.dll', + 'bin/Common.Logging*.dll', 'bin/Composite.Community.*.*', - 'Bin/Composite.Community.Blog.dll', - 'Bin/Composite.Community.EventCalendar.dll', - 'Bin/Composite.Community.Extranet.dll', - 'Bin/Composite.Community.Newsletter.DataTypeBased.dll', - 'Bin/Composite.Community.Newsletter.dll', - 'Bin/Composite.Community.Newsletter.FunctionBased.dll', - 'Bin/Composite.Community.Newsletter.SubjectBased.dll', + 'bin/Composite.Community.Blog.dll', + 'bin/Composite.Community.EventCalendar.dll', + 'bin/Composite.Community.Extranet.dll', + 'bin/Composite.Community.Newsletter.DataTypeBased.dll', + 'bin/Composite.Community.Newsletter.dll', + 'bin/Composite.Community.Newsletter.FunctionBased.dll', + 'bin/Composite.Community.Newsletter.SubjectBased.dll', 'bin/Composite.Forms.FormBuilder.dll', 'bin/Composite.Forms.Renderer.dll', 'bin/Composite.Generated.dll', 'bin/Composite.Media.*.*', 'bin/Composite.Search.SimplePageSearch.dll', 'bin/Composite.Tools.*.*', - 'Bin/Composite.Tools.PackageCreator.dll', - 'Bin/Composite.Versioning.ContentVersioning.dll', + 'bin/Composite.Tools.PackageCreator.dll', + 'bin/Composite.Versioning.ContentVersioning.dll', 'bin/Composite.Web.*.*', 'bin/Composite.XmlSerializers.dll', - 'Bin/CompositeC1Contrib.RazorFunctions.dll', + 'bin/CompositeC1Contrib.RazorFunctions.dll', + 'bin/CookComputing.XmlRpcV2.dll', 'bin/CookComputing.XmlRpcV2.dll', - 'Bin/CookComputing.XmlRpcV2.dll', 'bin/ICSharpCode.SharpZipLib.dll', - 'Bin/Microsoft.Web.*.dll', - 'Bin/Orckestra.Search*.dll', + 'bin/Lucene.Net.dll', + 'bin/Lucene.Net.*.dll', +// 'bin/Microsoft.Web.*.dll', + 'bin/Microsoft.WindowsAzure.*.dll', + 'bin/Orckestra.Search*.dll', 'bin/Orckestra.Web.Css.Less.dll', 'bin/System.Web.Optimization.dll', 'bin/WebGrease.dll', From c10e6c44fe3b91cae8b983fa0e21b22bdaf8a5e4 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 16:58:02 +0200 Subject: [PATCH 033/145] Cleaning using statements --- .../String/SelectorWidgetFunction.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/SelectorWidgetFunction.cs b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/SelectorWidgetFunction.cs index 55eb2c00c7..bd4b0b74c1 100644 --- a/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/SelectorWidgetFunction.cs +++ b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/SelectorWidgetFunction.cs @@ -1,14 +1,11 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; + using Composite.Functions; -using Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.DataReference; using Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.Foundation; -using Composite.Core.Types; -using Composite.Core.Xml; -using Composite.Core.Logging; -using System.Collections.Generic; namespace Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.String From ee8721e5bc630f3a5501a41d960a02cd34bc3986 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 26 May 2017 17:36:20 +0200 Subject: [PATCH 034/145] Hierarchical Selector (introduced as a form element in v5.1) surfaced as a string widget - let you write a function that return IEnumerable and pass this as options to the widget. The user can select one or more elements from the hierarchical structure you defined. You can control is ancestors / decendants should be auto selected, --- Composite/Composite.csproj | 1 + Composite/Core/Types/ValueTypeConverter.cs | 12 ++- .../StandardWidgetFunctionProvider.cs | 3 +- .../HierarchicalSelectorWidgetFunction.cs | 92 +++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/HierarchicalSelectorWidgetFunction.cs diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 927a264784..7a111b4d12 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -257,6 +257,7 @@ + diff --git a/Composite/Core/Types/ValueTypeConverter.cs b/Composite/Core/Types/ValueTypeConverter.cs index 29701c0b91..a1e3561eef 100644 --- a/Composite/Core/Types/ValueTypeConverter.cs +++ b/Composite/Core/Types/ValueTypeConverter.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; @@ -113,7 +113,14 @@ internal static object TryConvert(object value, Type targetType, out Exception c if (targetItemType.IsInstanceOfType(value)) { IList targetValue = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(new [] { targetItemType })); - targetValue.Add(value); + if (value is string) + { + ((string)value).Split(',').ForEach(f => targetValue.Add(f)); + } + else + { + targetValue.Add(value); + } return targetValue; } } @@ -128,6 +135,7 @@ internal static object TryConvert(object value, Type targetType, out Exception c { if (value is Type) return TypeManager.SerializeType((Type)value); if (value is DateTime) return XmlConvert.ToString((DateTime)value, XmlDateTimeSerializationMode.Local); + if (value is IEnumerable) return string.Join(",", ((IEnumerable)value).Cast().ToArray()); } TypeConverter targetConverter = TypeDescriptor.GetConverter(targetType); diff --git a/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/StandardWidgetFunctionProvider.cs b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/StandardWidgetFunctionProvider.cs index 60339043d5..dbb52c6ffc 100644 --- a/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/StandardWidgetFunctionProvider.cs +++ b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/StandardWidgetFunctionProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Composite.Data; @@ -89,6 +89,7 @@ private void InitializeStaticTypeFunctions() _widgetStaticTypeFunctions.Add(new TextBoxWidgetFuntion(_entityTokenFactory)); _widgetStaticTypeFunctions.Add(new TextAreaWidgetFuntion(_entityTokenFactory)); _widgetStaticTypeFunctions.Add(new String.SelectorWidgetFunction(_entityTokenFactory)); + _widgetStaticTypeFunctions.Add(new String.HierarchicalSelectorWidgetFunction(_entityTokenFactory)); _widgetStaticTypeFunctions.Add(new DataIdMultiSelectorWidgetFunction(_entityTokenFactory)); _widgetStaticTypeFunctions.Add(new String.VisualXhtmlEditorFuntion(_entityTokenFactory)); _widgetStaticTypeFunctions.Add(new String.UrlComboBoxWidgetFunction(_entityTokenFactory)); diff --git a/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/HierarchicalSelectorWidgetFunction.cs b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/HierarchicalSelectorWidgetFunction.cs new file mode 100644 index 0000000000..2a59f42d50 --- /dev/null +++ b/Composite/Plugins/Functions/WidgetFunctionProviders/StandardWidgetFunctionProvider/String/HierarchicalSelectorWidgetFunction.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Xml.Linq; + +using Composite.Functions; +using Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.Foundation; +using Composite.C1Console.Forms.CoreUiControls; +using Composite.Core.Xml; + +namespace Composite.Plugins.Functions.WidgetFunctionProviders.StandardWidgetFunctionProvider.String +{ + internal sealed class HierarchicalSelectorWidgetFunction : CompositeWidgetFunctionBase + { + private const string _functionName = "HierarchicalSelector"; + public const string CompositeName = CompositeWidgetFunctionBase.CommonNamespace + ".String." + _functionName; + + + public HierarchicalSelectorWidgetFunction(EntityTokenFactory entityTokenFactory) + : base(CompositeName, typeof(string), entityTokenFactory) + { + SetParameterProfiles(); + } + + + private void SetParameterProfiles() + { + base.AddParameterProfile( + new ParameterProfile("TreeNodes", + typeof(IEnumerable), + true, + new NoValueValueProvider(), + null, + null, + "Tree Nodes", new HelpDefinition("The structure to use for building hierarchy for selection. Call a function that return IEnumerable."))); + + base.AddParameterProfile( + new ParameterProfile("Required", + typeof(bool), + false, + new ConstantValueProvider(true), + StandardWidgetFunctions.GetBoolSelectorWidget("Yes, selection is required", "No, a 'none' selection is allowed.") , + null, + "Selection required", new HelpDefinition("When true the user is forced to make a selection. This feature is not available when 'multiple selection' is enabled."))); + + base.AddParameterProfile( + new ParameterProfile("AutoSelectChildren", + typeof(bool), + false, + new ConstantValueProvider(false), + StandardWidgetFunctions.GetBoolSelectorWidget("Yes, auto select child elements.", "No, only one selection on click."), + null, + "Auto select children", new HelpDefinition("When true a selection will automatically select all descendant elements in the hierarchy."))); + + base.AddParameterProfile( + new ParameterProfile("AutoSelectParents", + typeof(bool), + false, + new ConstantValueProvider(false), + StandardWidgetFunctions.GetBoolSelectorWidget("Yes, auto select parents.", "No, only one selection on click."), + null, + "Auto select children", new HelpDefinition("When true a selection will automatically select all ancestor elements in the hierarchy."))); + + } + + + public override XElement GetWidgetMarkup(ParameterList parameters, string label, HelpDefinition helpDefinition, string bindingSourceName) + { + BaseRuntimeTreeNode optionsRuntimeTreeNode = null; + + if (parameters.TryGetParameterRuntimeTreeNode("TreeNodes", out optionsRuntimeTreeNode)) + { + bool required = parameters.GetParameter("Required"); + bool autoSelectChildren = parameters.GetParameter("AutoSelectChildren"); + bool autoSelectParents = parameters.GetParameter("AutoSelectParents"); + + XElement formElement = base.BuildBasicWidgetMarkup("HierarchicalSelector", "SelectedKeys", label, helpDefinition, bindingSourceName); + formElement.Add(new XElement(Namespaces.BindingFormsStdUiControls10 + "HierarchicalSelector.TreeNodes", + optionsRuntimeTreeNode.Serialize())); + formElement.Add(new XAttribute("AutoSelectChildren", autoSelectChildren)); + formElement.Add(new XAttribute("AutoSelectParents", autoSelectParents)); + + return formElement; + } + else + { + throw new InvalidOperationException("Could not get BaseRuntimeTreeNode for parameter 'TreeNodes'."); + } + } + } +} From 89e2ff407dac61bf97abe8b5e5a0ac5522de445c Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 30 May 2017 11:59:55 +0200 Subject: [PATCH 035/145] Default support for faceted search in reference fields. --- .../Endpoint/ConsoleSearchRpcService.cs | 36 ++++++++++--------- .../Crawling/DefaultDataFieldProcessor.cs | 18 ++++++++++ .../DeveloperTools/TypeFieldDesigner.ascx.cs | 10 +++--- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/Composite/Plugins/Search/Endpoint/ConsoleSearchRpcService.cs b/Composite/Plugins/Search/Endpoint/ConsoleSearchRpcService.cs index 23a501fb9c..54f96a4c2e 100644 --- a/Composite/Plugins/Search/Endpoint/ConsoleSearchRpcService.cs +++ b/Composite/Plugins/Search/Endpoint/ConsoleSearchRpcService.cs @@ -13,6 +13,7 @@ using Composite.Core.ResourceSystem; using Composite.Core.WebClient; using Composite.Core.WebClient.Services.WampRouter; +using Composite.Data; using Microsoft.Extensions.DependencyInjection; using WampSharp.V2.Rpc; @@ -159,24 +160,27 @@ public async Task QueryAsync(ConsoleSearchQuery query) .Where(f => f.FieldValuePreserved), f => f.Name).ToList(); - return new ConsoleSearchResult + using (new DataConnection(culture)) { - QueryText = query.Text, - Columns = previewFields.Select(pf => new ConsoleSearchResultColumn - { - FieldName = MakeFieldNameJsFriendly(pf.Name), - Label = StringResourceSystemFacade.ParseString(pf.Label), - Sortable = pf.Preview.Sortable - }).ToArray(), - Rows = documents.Select(doc => new ConsoleSearchResultRow + return new ConsoleSearchResult { - Label = doc.Label, - Url = GetFocusUrl(doc.SerializedEntityToken), - Values = GetPreviewValues(doc, previewFields) - }).ToArray(), - FacetFields = GetFacets(result, facetFields), - TotalHits = result.TotalHits - }; + QueryText = query.Text, + Columns = previewFields.Select(pf => new ConsoleSearchResultColumn + { + FieldName = MakeFieldNameJsFriendly(pf.Name), + Label = StringResourceSystemFacade.ParseString(pf.Label), + Sortable = pf.Preview.Sortable + }).ToArray(), + Rows = documents.Select(doc => new ConsoleSearchResultRow + { + Label = doc.Label, + Url = GetFocusUrl(doc.SerializedEntityToken), + Values = GetPreviewValues(doc, previewFields) + }).ToArray(), + FacetFields = GetFacets(result, facetFields), + TotalHits = result.TotalHits + }; + } } private ConsoleSearchResultFacetField[] EmptyFacetsFromSelections( diff --git a/Composite/Search/Crawling/DefaultDataFieldProcessor.cs b/Composite/Search/Crawling/DefaultDataFieldProcessor.cs index 4bf2050449..df96d08cea 100644 --- a/Composite/Search/Crawling/DefaultDataFieldProcessor.cs +++ b/Composite/Search/Crawling/DefaultDataFieldProcessor.cs @@ -167,6 +167,24 @@ protected virtual DocumentFieldPreview.ValuePreviewDelegate GetPreviewFunction(P /// protected virtual DocumentFieldFacet.FacetValuePreviewDelegate GetFacetValuePreviewFunction(PropertyInfo propertyInfo) { + var foreignKey = propertyInfo.GetCustomAttributes().FirstOrDefault(); + if (foreignKey != null) + { + var targetType = foreignKey.InterfaceType; + if (targetType != null + && DynamicTypeManager.TryGetDataTypeDescriptor(targetType, out DataTypeDescriptor typeDescriptor) + && !string.IsNullOrEmpty(typeDescriptor.LabelFieldName)) + { + return key => + { + if (key == null) return string.Empty; + + var data = DataFacade.TryGetDataByUniqueKey(targetType, key); + return data?.GetLabel() ?? key.ToString(); + }; + } + } + return obj => obj; } diff --git a/Website/Composite/controls/FormsControls/FormUiControlTemplates/DeveloperTools/TypeFieldDesigner.ascx.cs b/Website/Composite/controls/FormsControls/FormUiControlTemplates/DeveloperTools/TypeFieldDesigner.ascx.cs index a670103db9..d40796db8e 100644 --- a/Website/Composite/controls/FormsControls/FormUiControlTemplates/DeveloperTools/TypeFieldDesigner.ascx.cs +++ b/Website/Composite/controls/FormsControls/FormUiControlTemplates/DeveloperTools/TypeFieldDesigner.ascx.cs @@ -463,11 +463,11 @@ private void InitSearchProperties() || fieldType == typeof(bool)); bool facetedSearchEnabled = fieldIsSearchable - && !isDataReference - && (fieldType == typeof(string) - || fieldType == typeof(DateTime) - || fieldType == typeof(DateTime?) - || fieldType == typeof(bool)); + && (isDataReference + || fieldType == typeof(string) + || fieldType == typeof(DateTime) + || fieldType == typeof(DateTime?) + || fieldType == typeof(bool)); plhSearch.Visible = indexTextEnabled || searchPreviewEnabled || facetedSearchEnabled; From 92f3b20b2e3acb565b0b8391c6c9b51e65cf6a7d Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Thu, 1 Jun 2017 10:41:57 +0200 Subject: [PATCH 036/145] Adding an overload to SeachQuery.AddFieldFacet(...) that accepts filtering options --- Composite/Search/SearchQuery.cs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Composite/Search/SearchQuery.cs b/Composite/Search/SearchQuery.cs index 8a104ada45..2201dc209e 100644 --- a/Composite/Search/SearchQuery.cs +++ b/Composite/Search/SearchQuery.cs @@ -246,6 +246,18 @@ public void FilterByUser(string userName) /// /// public void AddFieldFacet(string fieldName) + { + AddFieldFacet(fieldName, null, null); + } + + + /// + /// Will indicate that the search results should return facet information for the given field. + /// + /// + /// The array of values that are required to appear in the search documents. + /// The array of values that are required not to appear in the search documents. + public void AddFieldFacet(string fieldName, string[] values, string[] notValues) { Verify.ArgumentNotNullOrEmpty(fieldName, nameof(fieldName)); @@ -270,6 +282,20 @@ public void AddFieldFacet(string fieldName) Facets.Add(new KeyValuePair( fieldName, field.Facet)); + + if ((values != null && values.Length > 0) + || (notValues != null && notValues.Length > 0)) + { + Selection.Add(new SearchQuerySelection + { + FieldName = fieldName, + Values = values, + NotValues = notValues, + Operation = field.Facet.FacetType == FacetType.SingleValue + ? SearchQuerySelectionOperation.Or + : SearchQuerySelectionOperation.And + }); + } } } } From 993932d1f3135c4d8927ba670b0902e892331b36 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Thu, 1 Jun 2017 13:59:17 +0200 Subject: [PATCH 037/145] Minor error logging chance in Tree Definitions - omitting xpath from msg when no xpath exists. --- Composite/C1Console/Trees/TreeFacadeImpl.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Composite/C1Console/Trees/TreeFacadeImpl.cs b/Composite/C1Console/Trees/TreeFacadeImpl.cs index b18ab0334e..01079d1bb9 100644 --- a/Composite/C1Console/Trees/TreeFacadeImpl.cs +++ b/Composite/C1Console/Trees/TreeFacadeImpl.cs @@ -271,18 +271,17 @@ private void LoadAllTrees() { Log.LogError(LogTitle, "Tree {0} was not loaded due to the following validation errors", treeId); - var sb = new StringBuilder(); foreach (ValidationError validationError in tree.BuildResult.ValidationErrors) { - sb.AppendLine($"{validationError.Message} at {validationError.XPath}"); - Log.LogError("TreeFacade", $"{validationError.Message} at {validationError.XPath} in {filename}"); + if (string.IsNullOrEmpty(validationError.XPath)) + { + Log.LogError("TreeFacade", $"{validationError.Message} in {filename}"); + } + else + { + Log.LogError("TreeFacade", $"{validationError.Message} at {validationError.XPath} in {filename}"); + } } - - //Tree errorTree = CreateErrorTree(treeId, sb.ToString()); - //if (_resourceLocker.Resources.Trees.ContainsKey(errorTree.TreeId) == false) - //{ - // _resourceLocker.Resources.Trees.Add(errorTree.TreeId, errorTree); - //} } else { From 9a441e402cb833e40bdceed082aa0ff537ea734f Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Thu, 1 Jun 2017 14:04:18 +0200 Subject: [PATCH 038/145] Expanding on Tree Definition label options - now you can also put ${C1:Data:[TypeName]:[FieldName]:[Format]} for DateTime, int and decimal fields - giving you control over how these values are formatted on labels. Example: Fix #442. --- .../C1Console/Trees/DataFieldValueHelper.cs | 24 +++++++++++++++---- .../Composite.C1Console.Trees.en-us.xml | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/Composite/C1Console/Trees/DataFieldValueHelper.cs b/Composite/C1Console/Trees/DataFieldValueHelper.cs index 6d3f0a58d8..6f4da28e99 100644 --- a/Composite/C1Console/Trees/DataFieldValueHelper.cs +++ b/Composite/C1Console/Trees/DataFieldValueHelper.cs @@ -62,7 +62,15 @@ public string ReplaceValues(string currentValue, PiggybagDataFinder piggybagData } else if (entry.PropertyInfo.PropertyType == typeof(DateTime)) { - value = ((DateTime)value).ToString("yyyy MM dd"); + value = ((DateTime)value).ToString(entry.Format ?? "yyyy MM dd"); + } + else if (entry.PropertyInfo.PropertyType == typeof(Decimal)) + { + value = ((Decimal)value).ToString(entry.Format ?? "G"); + } + else if (entry.PropertyInfo.PropertyType == typeof(int)) + { + value = ((int)value).ToString(entry.Format ?? "G"); } } else @@ -99,9 +107,12 @@ public void Initialize(TreeNode ownerTreeNode) string value = match.Groups["string"].Value; string[] values = value.Split(':'); - if (values.Length != 4) + if (values.Length != 4 && values.Length != 5) { - ownerTreeNode.AddValidationError("TreeValidationError.DataFieldValueHelper.WrongFormat", match.Value, string.Format(@"{0}[InterfaceType]:[FieldName]}}", PreFix)); + ownerTreeNode.AddValidationError("TreeValidationError.DataFieldValueHelper.WrongFormat", + match.Value, + string.Format(@"{0}[InterfaceType]:[FieldName]}}", PreFix), + string.Format(@"{0}[InterfaceType]:[FieldName]:[Format]}}", PreFix)); return; } @@ -139,12 +150,15 @@ public void Initialize(TreeNode ownerTreeNode) return; } + string format = (values.Length == 5 ? values[4] : null); + bool isReferencingProperty = DataReferenceFacade.GetForeignKeyProperties(interfaceType).Any(f => f.SourcePropertyInfo.Equals(propertyInfo)); DataFieldValueHelperEntry entry = new DataFieldValueHelperEntry( match.Value, interfaceType, propertyInfo, + format, isReferencingProperty ); @@ -160,11 +174,12 @@ public void Initialize(TreeNode ownerTreeNode) private sealed class DataFieldValueHelperEntry { - public DataFieldValueHelperEntry(string match, Type interfaceType, PropertyInfo propertyInfo, bool isReference) + public DataFieldValueHelperEntry(string match, Type interfaceType, PropertyInfo propertyInfo, string format, bool isReference) { this.Match = match; this.InterfaceType = interfaceType; this.PropertyInfo = propertyInfo; + this.Format = format; this.IsReference = isReference; } @@ -172,6 +187,7 @@ public DataFieldValueHelperEntry(string match, Type interfaceType, PropertyInfo public string Match { get; private set; } public Type InterfaceType { get; private set; } public PropertyInfo PropertyInfo { get; private set; } + public string Format { get; private set; } public bool IsReference { get; private set; } diff --git a/Website/Composite/localization/Composite.C1Console.Trees.en-us.xml b/Website/Composite/localization/Composite.C1Console.Trees.en-us.xml index 395323cfbb..dbb9d239a7 100644 --- a/Website/Composite/localization/Composite.C1Console.Trees.en-us.xml +++ b/Website/Composite/localization/Composite.C1Console.Trees.en-us.xml @@ -70,7 +70,7 @@ - + From c59f78724d2b0c1d2eead1aa464268e83f2570f9 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Thu, 1 Jun 2017 14:45:10 +0200 Subject: [PATCH 039/145] Updating Tree.xsd to include help on new format option for labels (and tooltips) introduced in 9a441e4 (for new feature #442) --- Website/Composite/schemas/Trees/Tree.xsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Website/Composite/schemas/Trees/Tree.xsd b/Website/Composite/schemas/Trees/Tree.xsd index e05a785d36..8cb5bb4bc8 100644 --- a/Website/Composite/schemas/Trees/Tree.xsd +++ b/Website/Composite/schemas/Trees/Tree.xsd @@ -807,13 +807,13 @@ - The label of the element. Use ${C1:Data:[TypeName]:[FieldName]} to get a field value of a parent (or self) data element + The label of the element. Use ${C1:Data:[TypeName]:[FieldName]} to get a field value of a parent (or self) data element. Use ${C1:Data:[TypeName]:[FieldName]:[Format]} to format dates, decimals and ints using a format supported by .NET ToString() for the type. - The tool tip of the element. Use ${C1:Data:[TypeName]:[FieldName]} to get a field value of a parent (or self) data element. Defaults to the label + The tool tip of the element. Use ${C1:Data:[TypeName]:[FieldName]} to get a field value of a parent (or self) data element. Defaults to the label. Use ${C1:Data:[TypeName]:[FieldName]:[Format]} to format dates, decimals and ints using a format supported by .NET ToString() for the type. From f3ef1432232339134f5901590fadc4527be44983 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Thu, 1 Jun 2017 17:25:54 +0200 Subject: [PATCH 040/145] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2934e67b4e..7474778182 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Visit http://docs.c1.orckestra.com/Getting-started/Guide Download binaries from https://github.com/Orckestra/CMS-Foundation/releases/latest ## Forums ## -Head over to https://gitter.im/Orckestra/C1-CMS or https://c1cms.codeplex.com/discussions to ask questions +Head over to https://gitter.im/Orckestra/C1-CMS or add an issue ## Who are we? ## Orckestra is the company driving the development of C1 CMS Foundation. We have a team working full time on this CMS and on other cool stuff you can add to it. We are situated in Austin, Montreal, Copenhagen and Kiev. We specialize in enterprise commerce software. From aae89f979e77d837692d1778b9f8cbac0d6a1af0 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 2 Jun 2017 10:42:51 +0200 Subject: [PATCH 041/145] Fixing Dispose() not being called on layout razor pages --- Composite/AspNet/Razor/CompositeC1WebPage.cs | 43 +++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/Composite/AspNet/Razor/CompositeC1WebPage.cs b/Composite/AspNet/Razor/CompositeC1WebPage.cs index 7dc2b3036d..ad458023d9 100644 --- a/Composite/AspNet/Razor/CompositeC1WebPage.cs +++ b/Composite/AspNet/Razor/CompositeC1WebPage.cs @@ -5,6 +5,8 @@ using Composite.Data; using System.Threading; using System.Collections.Generic; +using System.Linq; +using Composite.Core.Extensions; using Composite.Functions; namespace Composite.AspNet.Razor @@ -17,6 +19,8 @@ public abstract class CompositeC1WebPage : WebPage, IDisposable private bool _disposed; private DataConnection _data; + private List _childPages = new List(); + /// /// Initializes a new instance of the class. /// @@ -84,25 +88,16 @@ public IHtmlString Markup(XNode xNode) /// /// Gets to letter ISO Language Name representing the pages language - use this like <html lang="@Lang" /> /// - public string Lang - { - get - { - return Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName; - } - } + public string Lang => Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName; - /// + /// /// Gets the function context container. /// - public FunctionContextContainer FunctionContextContainer - { - get { return GetFunctionContext(); } - } + public FunctionContextContainer FunctionContextContainer => GetFunctionContext(); - /// + /// /// Executes a C1 Function. /// /// Function name. @@ -156,7 +151,23 @@ private FunctionContextContainer GetFunctionContext() return PageData[RazorHelper.PageContext_FunctionContextContainer]; } - /// + /// + protected override void ConfigurePage(WebPageBase parentPage) + { + base.ConfigurePage(parentPage); + + if (parentPage is CompositeC1WebPage parentC1Page) + { + if (parentC1Page._childPages == null) + { + parentC1Page._childPages = new List(); + } + + parentC1Page._childPages.Add(this); + } + } + + /// public override void ExecutePageHierarchy() { base.ExecutePageHierarchy(); @@ -182,6 +193,8 @@ protected virtual void Dispose(bool disposing) if (disposing) { + (_childPages as IEnumerable)?.Reverse().ForEach(c => c.Dispose()); + _data?.Dispose(); } @@ -196,6 +209,6 @@ protected virtual void Dispose(bool disposing) Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } -#endif +#endif } } From f5be9ef6d931b4e40bb4217bbaaee3983e855e04 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Fri, 2 Jun 2017 17:15:34 +0200 Subject: [PATCH 042/145] Enabling #LeakCheck --- Composite/Composite.csproj | 2 +- Composite/Core/Instrumentation/DisposableResourceTracer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 7a111b4d12..a266318a7e 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -42,7 +42,7 @@ full false bin\Debug\ - TRACE;DEBUG + TRACE;DEBUG;LeakCheck prompt diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs index 92abf751c6..9ae611c897 100644 --- a/Composite/Core/Instrumentation/DisposableResourceTracer.cs +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -115,7 +115,7 @@ private static string GetMinimalStackTop(string stack) { string line = stackLines[lineNum]; sb.AppendLine(line); - foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_"); + foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_") && !line.Contains(".Threading."); lineNum++; } From 2a12b34e216092c3385feb1d23f773e285afdd38 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Tue, 6 Jun 2017 10:38:41 +0200 Subject: [PATCH 043/145] Fixing RoutedData<> not returning the list of elements when there are extra query string parameters --- .../Functions/AttributeBasedRoutedDataUrlMapper.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Composite/Functions/AttributeBasedRoutedDataUrlMapper.cs b/Composite/Functions/AttributeBasedRoutedDataUrlMapper.cs index 3fdf179765..ffd5ada8ca 100644 --- a/Composite/Functions/AttributeBasedRoutedDataUrlMapper.cs +++ b/Composite/Functions/AttributeBasedRoutedDataUrlMapper.cs @@ -52,22 +52,23 @@ public RoutedDataModel GetRouteDataModel(PageUrlData pageUrlData) string pathInfo = pageUrlData.PathInfo; bool pathIsEmpty = string.IsNullOrEmpty(pathInfo); - if (pathIsEmpty && !pageUrlData.HasQueryParameters) + int pathInfoSegmentsExpected = _mapper.PathSegmentsCount; + + if (pathIsEmpty && (pathInfoSegmentsExpected > 0 || !pageUrlData.HasQueryParameters)) { return new RoutedDataModel(GetDataQueryable); } - int totalSegments = _mapper.PathSegmentsCount; - if (pathIsEmpty != (totalSegments == 0)) + if (pathIsEmpty != (pathInfoSegmentsExpected == 0)) { return null; } string[] segments = !pathIsEmpty ? pathInfo.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries) - : new string[0]; + : Array.Empty(); - if (segments.Length != totalSegments) + if (segments.Length != pathInfoSegmentsExpected) { return null; } From de81a3b2a652f20687cd4b1bb10c096e0bd06798 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 6 Jun 2017 16:30:21 +0200 Subject: [PATCH 044/145] Fix #192 - media file upload, moving "refresh tree and focus new element" code to point after transaction completion, since "client refreshed before transaction is marked complete" is currently the only logical explanation for this issue. I'm unable to repro though. --- .../AddNewMediaFileWorkflow.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/AddNewMediaFileWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/AddNewMediaFileWorkflow.cs index 81f4852a3a..73e547897a 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/AddNewMediaFileWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/AddNewMediaFileWorkflow.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Workflow.Activities; @@ -195,6 +195,7 @@ private void ValidateStep2Bindings(object sender, ConditionalEventArgs e) private void finalizeCodeActivity_Finalize_ExecuteCode(object sender, EventArgs e) { AddNewTreeRefresher addNewTreeRefresher = this.CreateAddNewTreeRefresher(this.EntityToken); + DataEntityToken focusEntityToken; UploadedFile uploadedFile = this.GetBinding("UploadedFile"); string filename; @@ -235,9 +236,7 @@ private void finalizeCodeActivity_Finalize_ExecuteCode(object sender, EventArgs IMediaFile addedFile = DataFacade.AddNew(mediaFile, store.DataSourceId.ProviderName); - addNewTreeRefresher.PostRefreshMesseges(addedFile.GetDataEntityToken()); - - SelectElement(addedFile.GetDataEntityToken()); + focusEntityToken = addedFile.GetDataEntityToken(); } else { @@ -261,13 +260,14 @@ private void finalizeCodeActivity_Finalize_ExecuteCode(object sender, EventArgs DataFacade.Update(existingFile); DataFacade.Update(fileData); - addNewTreeRefresher.PostRefreshMesseges(existingFile.GetDataEntityToken()); - - SelectElement(existingFile.GetDataEntityToken()); + focusEntityToken = existingFile.GetDataEntityToken(); } transactionScope.Complete(); } - } + + addNewTreeRefresher.PostRefreshMesseges(focusEntityToken); + SelectElement(focusEntityToken); + } } } From d8ab81ff378fed1d91e94e60bbc0ea0b5cdfd1de Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 6 Jun 2017 16:32:43 +0200 Subject: [PATCH 045/145] Revert "Enabling #LeakCheck" This reverts commit f5be9ef6d931b4e40bb4217bbaaee3983e855e04. --- Composite/Composite.csproj | 2 +- Composite/Core/Instrumentation/DisposableResourceTracer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index a266318a7e..7a111b4d12 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -42,7 +42,7 @@ full false bin\Debug\ - TRACE;DEBUG;LeakCheck + TRACE;DEBUG prompt diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs index 9ae611c897..92abf751c6 100644 --- a/Composite/Core/Instrumentation/DisposableResourceTracer.cs +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -115,7 +115,7 @@ private static string GetMinimalStackTop(string stack) { string line = stackLines[lineNum]; sb.AppendLine(line); - foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_") && !line.Contains(".Threading."); + foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_"); lineNum++; } From 221184dda08cb6d60f10da507f8ae1411e7e797f Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 6 Jun 2017 16:35:57 +0200 Subject: [PATCH 046/145] DisposableResourceTracer - grabbing our own stacks deeper when calling up through our threading related code. --- Composite/Core/Instrumentation/DisposableResourceTracer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs index 92abf751c6..9ae611c897 100644 --- a/Composite/Core/Instrumentation/DisposableResourceTracer.cs +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -115,7 +115,7 @@ private static string GetMinimalStackTop(string stack) { string line = stackLines[lineNum]; sb.AppendLine(line); - foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_"); + foundCaller = line.TrimStart().StartsWith("at Composite.") && !line.Contains("..ctor") && !line.Contains(".get_") && !line.Contains(".Threading."); lineNum++; } From 7dad46f636c893761ed3d731332aedcfdfd58236 Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 6 Jun 2017 16:36:58 +0200 Subject: [PATCH 047/145] web project build action - robocopy phantomjs.exe path fix --- Website/WebSite.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Website/WebSite.csproj b/Website/WebSite.csproj index 4417785d55..b109d9c829 100644 --- a/Website/WebSite.csproj +++ b/Website/WebSite.csproj @@ -2808,7 +2808,7 @@ .\node_modules\.bin\jspm install --quick - robocopy "$(ProjectDir)\..\Packages\PhantomJS.2.1.1\tools\phantomjs" "$(ProjectDir)App_Data\Composite\PhantomJs" "phantomjs.exe" + robocopy "$(ProjectDir)\..\Packages\PhantomJS.2.1.1\tools\phantomjs" "$(ProjectDir)\App_Data\Composite\PhantomJs" "phantomjs.exe" set rce=%25errorlevel%25 if not %25rce%25==1 exit %25rce%25 else exit 0 From 435d4dd7082da0c9ee020915d503fbe531291e0d Mon Sep 17 00:00:00 2001 From: Marcus Wendt Date: Tue, 6 Jun 2017 17:27:19 +0200 Subject: [PATCH 048/145] Updating version moniker to C1 CMS 6.2 --- Composite/Properties/SharedAssemblyInfo.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Composite/Properties/SharedAssemblyInfo.cs b/Composite/Properties/SharedAssemblyInfo.cs index ca68172bcb..6c5ade29e1 100644 --- a/Composite/Properties/SharedAssemblyInfo.cs +++ b/Composite/Properties/SharedAssemblyInfo.cs @@ -2,9 +2,9 @@ // General Information about the assemblies Composite and Composite.Workflows #if !InternalBuild -[assembly: AssemblyTitle("C1 CMS 6.1")] +[assembly: AssemblyTitle("C1 CMS 6.2")] #else -[assembly: AssemblyTitle("C1 CMS 6.1 (Internal Build)")] +[assembly: AssemblyTitle("C1 CMS 6.2 (Internal Build)")] #endif [assembly: AssemblyCompany("Orckestra Inc")] @@ -13,4 +13,4 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("6.1.*")] +[assembly: AssemblyVersion("6.2.*")] From e69eb0677dd63c243bd10c2462726464171b2656 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 7 Jun 2017 12:12:39 +0200 Subject: [PATCH 049/145] Adding DocumentFieldFacet.ExpandSelection property --- Composite/Search/DocumentField.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Composite/Search/DocumentField.cs b/Composite/Search/DocumentField.cs index 93b014209e..776859d2e9 100644 --- a/Composite/Search/DocumentField.cs +++ b/Composite/Search/DocumentField.cs @@ -33,6 +33,11 @@ public class DocumentFieldFacet /// public FacetValuePreviewDelegate PreviewFunction { get; set; } + /// + /// When true, information about not selected facet values should be returned when querying the facet. + /// + public bool ExpandSelection { get; set; } = true; + //public int FieldOrder { get; set; } } From ed4670f73e204eeb04b5a1425a81477b0c9b7d82 Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Wed, 7 Jun 2017 12:16:33 +0200 Subject: [PATCH 050/145] Refactoring --- .../C1Console/Actions/ActionExecutorFacade.cs | 117 ++++++++---------- .../PackageSystem/LicenseDefinitionManager.cs | 8 +- Composite/Core/Xml/XhtmlDocument.cs | 39 ++---- 3 files changed, 66 insertions(+), 98 deletions(-) diff --git a/Composite/C1Console/Actions/ActionExecutorFacade.cs b/Composite/C1Console/Actions/ActionExecutorFacade.cs index 7648877052..81af54eb0a 100644 --- a/Composite/C1Console/Actions/ActionExecutorFacade.cs +++ b/Composite/C1Console/Actions/ActionExecutorFacade.cs @@ -1,19 +1,19 @@ -//#warning REMARK THIS!!! +//#warning REMARK THIS!!! //#define NO_SECURITY using System; using System.Collections.Generic; using Composite.C1Console.Actions.Foundation; using Composite.C1Console.Actions.Workflows; using Composite.C1Console.Events; -using Composite.Core.Logging; using Composite.C1Console.Security; using Composite.C1Console.Tasks; using Composite.C1Console.Workflow; +using Composite.Core; namespace Composite.C1Console.Actions { - /// + /// /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] @@ -42,7 +42,7 @@ public static FlowToken Execute(EntityToken entityToken, ActionToken actionToken IEnumerable userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(username); IEnumerable userGroupPermissionDefinitions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(username); SecurityResult securityResult = SecurityResolver.Resolve(UserValidationFacade.GetUserToken(), actionToken, entityToken, userPermissionDefinitions, userGroupPermissionDefinitions); - if ((securityResult != SecurityResult.Allowed) && (entityToken.GetType() != typeof(SecurityViolationWorkflowEntityToken))) + if (securityResult != SecurityResult.Allowed && !(entityToken is SecurityViolationWorkflowEntityToken)) { return ExecuteSecurityViolation(actionToken, entityToken, flowControllerServicesContainer); } @@ -50,72 +50,71 @@ public static FlowToken Execute(EntityToken entityToken, ActionToken actionToken bool ignoreLocking = actionToken.IsIgnoreEntityTokenLocking(); - if ((ignoreLocking) || - (ActionLockingFacade.IsLocked(entityToken) == false)) + if (!ignoreLocking && ActionLockingFacade.IsLocked(entityToken)) { - IActionExecutor actionExecutor = ActionExecutorCache.GetActionExecutor(actionToken); + return ExecuteEntityTokenLocked(actionToken, entityToken, flowControllerServicesContainer); + } + + IActionExecutor actionExecutor = ActionExecutorCache.GetActionExecutor(actionToken); + + ActionEventSystemFacade.FireOnBeforeActionExecution(entityToken, actionToken); - ActionEventSystemFacade.FireOnBeforeActionExecution(entityToken, actionToken); + FlowToken flowToken; + using (TaskContainer taskContainer = TaskManagerFacade.CreateNewTasks(entityToken, actionToken, taskManagerEvent)) + { + ITaskManagerFlowControllerService taskManagerService = null; + if (flowControllerServicesContainer.GetService(typeof(ITaskManagerFlowControllerService)) == null) + { + taskManagerService = new TaskManagerFlowControllerService(taskContainer); + flowControllerServicesContainer.AddService(taskManagerService); + } - FlowToken flowToken; - using (TaskContainer taskContainer = TaskManagerFacade.CreateNewTasks(entityToken, actionToken, taskManagerEvent)) + try { - ITaskManagerFlowControllerService taskManagerService = null; - if (flowControllerServicesContainer.GetService(typeof(ITaskManagerFlowControllerService)) == null) + if (actionExecutor is IActionExecutorSerializedParameters) { - taskManagerService = new TaskManagerFlowControllerService(taskContainer); - flowControllerServicesContainer.AddService(taskManagerService); - } + string serializedEntityToken = EntityTokenSerializer.Serialize(entityToken); + string serializedActionToken = ActionTokenSerializer.Serialize(actionToken); - try + flowToken = Execute(actionExecutor as IActionExecutorSerializedParameters, + serializedEntityToken, serializedActionToken, actionToken, + flowControllerServicesContainer); + } + else { - if ((actionExecutor is IActionExecutorSerializedParameters)) - { - string serializedEntityToken = EntityTokenSerializer.Serialize(entityToken); - string serializedActionToken = ActionTokenSerializer.Serialize(actionToken); - - flowToken = Execute(actionExecutor as IActionExecutorSerializedParameters, - serializedEntityToken, serializedActionToken, actionToken, - flowControllerServicesContainer); - } - else - { - flowToken = Execute(actionExecutor, entityToken, actionToken, - flowControllerServicesContainer); - } + flowToken = Execute(actionExecutor, entityToken, actionToken, + flowControllerServicesContainer); } - finally + } + finally + { + if (taskManagerService != null) { - if (taskManagerService != null) - { - flowControllerServicesContainer.RemoveService(taskManagerService); - } + flowControllerServicesContainer.RemoveService(taskManagerService); } - - taskContainer.SetOnIdleTaskManagerEvent(new FlowTaskManagerEvent(flowToken)); - taskContainer.UpdateTasksWithFlowToken(flowToken); - - taskContainer.SaveTasks(); } - ActionEventSystemFacade.FireOnAfterActionExecution(entityToken, actionToken, flowToken); + taskContainer.SetOnIdleTaskManagerEvent(new FlowTaskManagerEvent(flowToken)); + taskContainer.UpdateTasksWithFlowToken(flowToken); - IManagementConsoleMessageService managementConsoleMessageService = flowControllerServicesContainer.GetService(); - if (managementConsoleMessageService != null) - { - FlowControllerFacade.RegisterNewFlowInformation(flowToken, entityToken, actionToken, managementConsoleMessageService.CurrentConsoleId); - } - else - { - LoggingService.LogWarning("ActionExecutorFacade", "Missing ManagementConsoleMessageService, can not register the flow"); - } + taskContainer.SaveTasks(); + } - return flowToken; + ActionEventSystemFacade.FireOnAfterActionExecution(entityToken, actionToken, flowToken); + + IManagementConsoleMessageService managementConsoleMessageService = flowControllerServicesContainer + .GetService(); + if (managementConsoleMessageService != null) + { + FlowControllerFacade.RegisterNewFlowInformation(flowToken, entityToken, actionToken, + managementConsoleMessageService.CurrentConsoleId); } else { - return ExecuteEntityTokenLocked(actionToken, entityToken, flowControllerServicesContainer); + Log.LogWarning(nameof(ActionExecutorFacade), "Missing ManagementConsoleMessageService, can not register the flow"); } + + return flowToken; } @@ -152,12 +151,7 @@ private static FlowToken Execute(IActionExecutorSerializedParameters actionExecu { FlowToken result = actionExecutor.Execute(serializedEntityToken, serializedActionToken, actionToken, flowControllerServicesContainer); - if (result == null) - { - result = new NullFlowToken(); - } - - return result; + return result ?? new NullFlowToken(); } @@ -166,12 +160,7 @@ private static FlowToken Execute(IActionExecutor actionExecutor, EntityToken ent { FlowToken result = actionExecutor.Execute(entityToken, actionToken, flowControllerServicesContainer); - if (result == null) - { - result = new NullFlowToken(); - } - - return result; + return result ?? new NullFlowToken(); } } } diff --git a/Composite/Core/PackageSystem/LicenseDefinitionManager.cs b/Composite/Core/PackageSystem/LicenseDefinitionManager.cs index fe35ab21fc..44d63435ec 100644 --- a/Composite/Core/PackageSystem/LicenseDefinitionManager.cs +++ b/Composite/Core/PackageSystem/LicenseDefinitionManager.cs @@ -106,15 +106,15 @@ private static PackageLicenseDefinition TryLoadLicenseFile(string filePath) InstallationId = (Guid)doc.Descendants("InstallationId").Single(), ProductId = (Guid)doc.Descendants("ProductId").Single(), Permanent = (bool)doc.Descendants("Permanent").Single(), - Expires = (doc.Descendants("Expires").Any() ? (DateTime)doc.Descendants("Expires").Single() : DateTime.MaxValue), + Expires = (DateTime?)doc.Descendants("Expires").SingleOrDefault() ?? DateTime.MaxValue, LicenseKey = doc.Descendants("LicenseKey").Single().Value, - PurchaseUrl = (doc.Descendants("PurchaseUrl").Any() ? doc.Descendants("PurchaseUrl").Single().Value : ""), + PurchaseUrl = doc.Descendants("PurchaseUrl").SingleOrDefault()?.Value ?? "", LicenseFileName = filePath }; if (licenseDefinition.InstallationId != InstallationInformationFacade.InstallationId) { - Log.LogError(LogTitle, string.Format("The license for the product '{0}' ({1}) does not match the current installation", licenseDefinition.ProductId, licenseDefinition.ProductName)); + Log.LogError(LogTitle, $"The license for the product '{licenseDefinition.ProductId}' ({licenseDefinition.ProductName}) does not match the current installation"); return null; } @@ -145,7 +145,7 @@ private static string GetLicenseFilename(PackageLicenseDefinition packageLicense private static string GetObsoleteLicenseFilename(Guid productId) { - return Path.Combine(_packageLicenseDirectory, string.Format("{0}.xml", productId)); + return Path.Combine(_packageLicenseDirectory, $"{productId}.xml"); } } } diff --git a/Composite/Core/Xml/XhtmlDocument.cs b/Composite/Core/Xml/XhtmlDocument.cs index ad39840ab5..a0ce51fa7b 100644 --- a/Composite/Core/Xml/XhtmlDocument.cs +++ b/Composite/Core/Xml/XhtmlDocument.cs @@ -89,27 +89,14 @@ public XhtmlDocument(XDocument other) /// /// The head element for the XHTML Document /// - public XElement Head - { - get - { - return this.Root.Element(_head_XName); - } - } + public XElement Head => this.Root.Element(_head_XName); /// /// The body element for the XHTML Document /// - public XElement Body - { - get - { - return this.Root.Element(_body_XName); - } - } - + public XElement Body => this.Root.Element(_body_XName); /// @@ -225,9 +212,9 @@ public static XhtmlDocument ParseXhtmlFragment(string fragment) } } - if (nodes.Count == 1 && nodes[0] is XElement && (nodes[0] as XElement).Name.LocalName == "html") + if (nodes.Count == 1 && nodes[0] is XElement element && element.Name.LocalName == "html") { - return new XhtmlDocument(nodes[0] as XElement); + return new XhtmlDocument(element); } var document = new XhtmlDocument(); @@ -315,24 +302,16 @@ public override bool TryConvert(object value, Type targetType, out object target { Verify.ArgumentNotNull(value, "value"); - if (targetType == typeof(XhtmlDocument) && value is XElement) - { - XElement valueCasted = (XElement)value; - targetValue = new XhtmlDocument(valueCasted); - return true; - } - - if (targetType == typeof(XElement) && value is XhtmlDocument) + if (targetType == typeof(XhtmlDocument) && value is XElement element) { - XhtmlDocument valueCasted = (XhtmlDocument)value; - targetValue = valueCasted.Root; + targetValue = new XhtmlDocument(element); return true; } - if (targetType == typeof(XNode) && value is XhtmlDocument) + if ((targetType == typeof(XElement) || targetType == typeof(XNode)) + && value is XhtmlDocument document) { - XhtmlDocument valueCasted = (XhtmlDocument)value; - targetValue = valueCasted.Root; + targetValue = document.Root; return true; } From 8cf22885c1731f52c088357c78715f56acccf4da Mon Sep 17 00:00:00 2001 From: Dmitry Dzygin Date: Fri, 9 Jun 2017 10:32:03 +0200 Subject: [PATCH 051/145] A better preview text for IEnumerable function parameter --- .../WysiwygEditor/XhtmlTransformations.asmx | 1665 +++++++++-------- 1 file changed, 839 insertions(+), 826 deletions(-) diff --git a/Website/Composite/services/WysiwygEditor/XhtmlTransformations.asmx b/Website/Composite/services/WysiwygEditor/XhtmlTransformations.asmx index f773f4ce4a..bffdad4cb4 100644 --- a/Website/Composite/services/WysiwygEditor/XhtmlTransformations.asmx +++ b/Website/Composite/services/WysiwygEditor/XhtmlTransformations.asmx @@ -1,4 +1,4 @@ -<%@ WebService Language="C#" Class="Composite.Services.XhtmlTransformations" %> +<%@ WebService Language="C#" Class="Composite.Services.XhtmlTransformations" %> using System; using System.Collections.Concurrent; @@ -31,410 +31,410 @@ using Composite.Core.Types; namespace Composite.Services { - public class XhtmlTransformationResult - { - public string XhtmlFragment { get; set; } - public string Warnings { get; set; } - } - - - public class FunctionInfo - { - public string FunctionMarkup { get; set; } - public bool RequireConfiguration { get; set; } - } - - - - [WebService(Namespace = "http://www.composite.net/ns/management")] - [SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)] - public class XhtmlTransformations : System.Web.Services.WebService - { - private const string _markupWysiwygRepresentationAlt = "\n\n\n\n\n\n "; // like this so IE will make loading images have some width and height - - [WebMethod] - public XhtmlTransformationResult TinyContentToStructuredContent(string htmlFragment) - { - try - { - string warnings = ""; - string xsltPath = Server.MapPath("..\\..\\transformations\\WysiwygEditor_TinyContentToStructuredContent.xsl"); - - XDocument structuredResult; - try - { - var xsltParameters = new Dictionary - { - {"requesthostname", HttpContext.Current.Request.Url.Host}, - {"requestport", HttpContext.Current.Request.Url.Port.ToString()}, - {"requestscheme", HttpContext.Current.Request.Url.Scheme}, - {"requestapppath", UrlUtils.PublicRootPath} - }; - - structuredResult = MarkupTransformationServices.RepairXhtmlAndTransform(WrapInnerBody(htmlFragment), xsltPath, xsltParameters, out warnings); - } - catch (Exception ex) - { - throw new InvalidOperationException("Parse failed for \n" + htmlFragment, ex); - } - - List htmlWysiwygImages = structuredResult - .Descendants(Namespaces.Xhtml + "img") - .Where(e => HasMarkup(e) - && e.Attribute("class") != null - && e.Attribute("class").Value.Contains("compositeHtmlWysiwygRepresentation")).ToList(); - - foreach (var htmlWysiwygImageElement in htmlWysiwygImages) - { - try - { - string html = GetMarkupValue(htmlWysiwygImageElement); - XElement functionElement = XElement.Parse(html); - - if (IsFunctionAloneInParagraph(htmlWysiwygImageElement)) - { - htmlWysiwygImageElement.Parent.ReplaceWith(functionElement); - } - else - { - htmlWysiwygImageElement.ReplaceWith(functionElement); - } - } - catch (Exception ex) - { - htmlWysiwygImageElement.ReplaceWith(new XText("HTML PARSE FAILED: " + ex.Message)); - } - } - - - - List functionImages = - structuredResult - .Descendants() - .Where(e => e.Name.LocalName == "img" - && HasMarkup(e) - && e.Attribute("class") != null - && e.Attribute("class").Value.Contains("compositeFunctionWysiwygRepresentation")).ToList(); - - foreach (var functionImageElement in functionImages) - { - var nextNode = functionImageElement.NextNode; - - // Removing " " symbols that may appear between function call images - if (nextNode != null - && nextNode.NextNode != null - && nextNode is XText - && nextNode.NextNode is XElement - && string.IsNullOrWhiteSpace((nextNode as XText).Value.Replace(" ", "")) - && functionImages.Contains(nextNode.NextNode as XElement)) - { - nextNode.Remove(); - } - - // Replacing function call images with function markup - try - { - string functionMarkup = GetMarkupValue(functionImageElement); - XElement functionElement = XElement.Parse(functionMarkup); - - if (IsFunctionAloneInParagraph(functionImageElement)) - { - functionImageElement.Parent.ReplaceWith(functionElement); - } - else - { - functionImageElement.ReplaceWith(functionElement); - } - } - catch (Exception ex) - { - functionImageElement.ReplaceWith(new XText("FUNCTION MARKUP PARSE FAILED: " + ex.Message)); - } - } - - - IEnumerable dataFieldReferenceImages = - structuredResult.Descendants(Namespaces.Xhtml + "img") - .Where(f => f.Attribute("class") != null - && f.Attribute("class").Value.Contains("compositeFieldReferenceWysiwygRepresentation")); - - foreach (var referenceImageElement in dataFieldReferenceImages.ToList()) - { - try - { - string[] parts = HttpUtility.UrlDecode(referenceImageElement.Attribute("data-markup").Value).Split('\\'); - string typeName = parts[0]; - string fieldName = parts[1]; - - referenceImageElement.ReplaceWith(DynamicTypeMarkupServices.GetReferenceElement(fieldName, typeName)); - } - catch (Exception ex) - { - referenceImageElement.ReplaceWith(new XText("FIELD REFERENCE MARKUP PARSE FAILED: " + ex.Message)); - } - } - - FixTinyMceMalEncodingOfInternationalUrlHostNames(structuredResult); - - string bodyInnerXhtml = MarkupTransformationServices.OutputBodyDescendants(structuredResult); - - return new XhtmlTransformationResult - { - Warnings = warnings, - XhtmlFragment = FixXhtmlFragment(bodyInnerXhtml) - }; - } - catch (Exception ex) - { - LoggingService.LogError("XhtmlTransformation", ex.ToString()); - - throw; - } - } - - private static readonly List paragraphList = new List(){ - Namespaces.Xhtml + "p", - Namespaces.Xhtml + "h1", - Namespaces.Xhtml + "h2", - Namespaces.Xhtml + "h3", - Namespaces.Xhtml + "h4", - Namespaces.Xhtml + "h5", - Namespaces.Xhtml + "h6"}; - - private static bool IsFunctionAloneInParagraph(XElement element) - { - if (element.ElementsBeforeSelf().Any(d => d.Name != Namespaces.Xhtml + "br") - || element.ElementsAfterSelf().Any(d => d.Name != Namespaces.Xhtml + "br") - || !paragraphList.Contains(element.Parent.Name) - || element.Parent.Value.Replace(" ", "").Trim() != string.Empty) - return false; - - return true; - } - - private static string FixXhtmlFragment(string xhtmlFragment) - { - xhtmlFragment = Regex.Replace(xhtmlFragment, @"(\s)\r\n", "$1", RegexOptions.Multiline); - return xhtmlFragment.Replace("\xA0", " ").Replace(" ", " "); - } - - // Fixing issue where tiny - private void FixTinyMceMalEncodingOfInternationalUrlHostNames(XDocument xhtmlDoc) - { - var urlAttributes = xhtmlDoc.Descendants().Attributes().Where(f => f.Value.StartsWith("http://") || f.Value.StartsWith("https://")); - foreach (XAttribute urlAttribute in urlAttributes) - { - string url = urlAttribute.Value; - string urlWithoutProtocol = url.Substring(url.IndexOf("//", StringComparison.Ordinal) + 2); - string urlHostWithPort = (urlWithoutProtocol.Contains("/") ? urlWithoutProtocol.Substring(0, urlWithoutProtocol.IndexOf('/')) : urlWithoutProtocol); - string urlHost = (urlHostWithPort.Contains(":") ? urlHostWithPort.Substring(0, urlHostWithPort.IndexOf(':')) : urlHostWithPort); - if (urlHost != HttpUtility.UrlDecode(urlHost)) - { - urlAttribute.Value = urlAttribute.Value.Replace(urlHost, HttpUtility.UrlDecode(urlHost)); - } - } - } - - - [WebMethod] - public XhtmlTransformationResult StructuredContentToTinyContent(string htmlFragment) - { - return StructuredContentToTinyContentMultiTemplate(htmlFragment, Guid.Empty, Guid.Empty, null, 0); - } - - - [WebMethod] - public XhtmlTransformationResult StructuredContentToTinyContentMultiTemplate(string htmlFragment, Guid pageId, Guid pageTemplateId, string functionPreviewPlaceholderName, int width) - { - try - { - string warnings = ""; - string XhtmlPassXsltPath = Server.MapPath("..\\..\\transformations\\WysiwygEditor_StructuredContentToTinyContent.xsl"); - - string html = WrapInnerBody(htmlFragment); - - XDocument xml = XDocument.Parse(html, LoadOptions.PreserveWhitespace); - - IEnumerable functionRoots = xml - .Descendants(Namespaces.Function10 + "function") - .Where(f => f.Ancestors(Namespaces.Function10 + "function").Any() == false); - - foreach (var functionElement in functionRoots.ToList()) - { - string cssSelector = BuildAncestorCssSelector(functionElement); - - functionElement.ReplaceWith(GetImageTagForFunctionCall(functionElement, pageId, pageTemplateId, functionPreviewPlaceholderName, cssSelector, width)); - } - - IEnumerable dataFieldReferences = xml.Descendants(Namespaces.DynamicData10 + "fieldreference"); - foreach (var referenceElement in dataFieldReferences.ToList()) - { - referenceElement.ReplaceWith(GetImageTagForDynamicDataFieldReference(referenceElement)); - } - - var unHandledHtmlElementNames = new List - { - Namespaces.Xhtml + "audio", - Namespaces.Xhtml + "canvas", - Namespaces.Xhtml + "embed", - Namespaces.Xhtml + "iframe", - Namespaces.Xhtml + "map", - Namespaces.Xhtml + "object", - Namespaces.Xhtml + "script", - Namespaces.Xhtml + "noscript", - Namespaces.Xhtml + "video", - Namespaces.Xhtml + "svg" - }; - - IEnumerable langElements = xml.Descendants().Where(f => f.Name.Namespace == Namespaces.Localization10); - foreach (var langElement in langElements.ToList()) - { - langElement.ReplaceWith(GetImageTagForLangElement(langElement)); - } - - IEnumerable unHandledHtmlElements = xml.Descendants().Where(f => f.Name.Namespace != Namespaces.Xhtml || unHandledHtmlElementNames.Contains(f.Name)); - foreach (var unHandledHtmlElement in unHandledHtmlElements.ToList()) - { - unHandledHtmlElement.ReplaceWith(GetImageTagForHtmlElement(unHandledHtmlElement)); - } - - var xsltParameters = new Dictionary - { - {"requestapppath", UrlUtils.PublicRootPath} - }; - - XDocument structuredResult = MarkupTransformationServices.RepairXhtmlAndTransform(xml.ToString(), XhtmlPassXsltPath, xsltParameters, out warnings); - - string bodyInnerXhtml = MarkupTransformationServices.OutputBodyDescendants(structuredResult); - - return new XhtmlTransformationResult - { - Warnings = warnings, - XhtmlFragment = FixXhtmlFragment(bodyInnerXhtml) - }; - } - catch (Exception ex) - { - Log.LogError("XhtmlTransformation", ex.ToString()); - throw; - } - } - - private string BuildAncestorCssSelector(XElement element) - { - var sb = new StringBuilder(); - - foreach (var ancestor in element.Ancestors().Reverse()) - { - if(ancestor.Name.LocalName == "html" || ancestor.Name.LocalName == "body") continue; - - if (sb.Length > 0) - { - sb.Append(" "); - } - - sb.Append(ancestor.Name.LocalName); - string cssClasses = (string) ancestor.Attribute("class") ?? ""; - - foreach (var cssClass in cssClasses.Split(new [] {" "}, StringSplitOptions.RemoveEmptyEntries)) - { - sb.Append(".").Append(cssClass); - } - } - - return sb.ToString(); - } - - [WebMethod] - public string GetImageTagForFunctionCall(string functionMarkup) - { - return GetImageTagForFunctionCall2(functionMarkup, Guid.Empty, Guid.Empty, null, 0); - } - - [WebMethod] - public string GetImageTagForFunctionCall2(string functionMarkup, Guid functionPreviewPageId, Guid functionPreviewTemplateId, string functionPreviewPlaceholderName, int width) - { - XElement functionElement; - - try - { - functionElement = XElement.Parse(functionMarkup); - } - catch (Exception ex) - { - throw new ArgumentException("Unable to parse functionMarkup as XML", ex); - } - - return GetImageTagForFunctionCall(functionElement, functionPreviewPageId, functionPreviewTemplateId, functionPreviewPlaceholderName, null, width) - .ToString(SaveOptions.DisableFormatting); - } - - - - [WebMethod] - public FunctionInfo GetFunctionInfo(string functionName) - { - IFunction function = FunctionFacade.GetFunction(functionName); - - var functionRuntimeTreeNode = new FunctionRuntimeTreeNode(function); - - return new FunctionInfo - { - FunctionMarkup = functionRuntimeTreeNode.Serialize().ToString(), - RequireConfiguration = function.ParameterProfiles.Any(p => !p.IsInjectedValue) - }; - } - - - - private XElement GetImageTagForDynamicDataFieldReference(XElement fieldReferenceElement) - { - string typeName = fieldReferenceElement.Attribute("typemanagername").Value; - - Type type = TypeManager.GetType(typeName); - if (!typeof(IData).IsAssignableFrom(type)) - { - string fieldName = fieldReferenceElement.Attribute("fieldname").Value; - - return GetImageTagForDynamicDataFieldReference(fieldName, fieldName, type.AssemblyQualifiedName, type.AssemblyQualifiedName); - } - - DataTypeDescriptor typeDescriptor; - DataFieldDescriptor fieldDescriptor; - - if (!DynamicTypeMarkupServices.TryGetDescriptors(fieldReferenceElement, out typeDescriptor, out fieldDescriptor)) - { - return null; - } - - return GetImageTagForDynamicDataFieldReference(fieldDescriptor, typeDescriptor); - } - - - private XElement GetImageTagForHtmlElement(XElement element) - { - string description = element.ToString().Replace(" xmlns=\"http://www.w3.org/1999/xhtml\"", ""); - string title = "HTML block"; - - var descriptionLines = description.Split('\n'); - if (descriptionLines.Length > 6) - { - description = string.Format("{0}\n{1}\n{2}\n...\n{3}", descriptionLines[0], descriptionLines[1], - descriptionLines[2], descriptionLines.Last()); - } + public class XhtmlTransformationResult + { + public string XhtmlFragment { get; set; } + public string Warnings { get; set; } + } + + + public class FunctionInfo + { + public string FunctionMarkup { get; set; } + public bool RequireConfiguration { get; set; } + } + + + + [WebService(Namespace = "http://www.composite.net/ns/management")] + [SoapDocumentService(RoutingStyle = SoapServiceRoutingStyle.RequestElement)] + public class XhtmlTransformations : System.Web.Services.WebService + { + private const string _markupWysiwygRepresentationAlt = "\n\n\n\n\n\n "; // like this so IE will make loading images have some width and height + + [WebMethod] + public XhtmlTransformationResult TinyContentToStructuredContent(string htmlFragment) + { + try + { + string warnings = ""; + string xsltPath = Server.MapPath("..\\..\\transformations\\WysiwygEditor_TinyContentToStructuredContent.xsl"); + + XDocument structuredResult; + try + { + var xsltParameters = new Dictionary + { + {"requesthostname", HttpContext.Current.Request.Url.Host}, + {"requestport", HttpContext.Current.Request.Url.Port.ToString()}, + {"requestscheme", HttpContext.Current.Request.Url.Scheme}, + {"requestapppath", UrlUtils.PublicRootPath} + }; + + structuredResult = MarkupTransformationServices.RepairXhtmlAndTransform(WrapInnerBody(htmlFragment), xsltPath, xsltParameters, out warnings); + } + catch (Exception ex) + { + throw new InvalidOperationException("Parse failed for \n" + htmlFragment, ex); + } + + List htmlWysiwygImages = structuredResult + .Descendants(Namespaces.Xhtml + "img") + .Where(e => HasMarkup(e) + && e.Attribute("class") != null + && e.Attribute("class").Value.Contains("compositeHtmlWysiwygRepresentation")).ToList(); + + foreach (var htmlWysiwygImageElement in htmlWysiwygImages) + { + try + { + string html = GetMarkupValue(htmlWysiwygImageElement); + XElement functionElement = XElement.Parse(html); + + if (IsFunctionAloneInParagraph(htmlWysiwygImageElement)) + { + htmlWysiwygImageElement.Parent.ReplaceWith(functionElement); + } + else + { + htmlWysiwygImageElement.ReplaceWith(functionElement); + } + } + catch (Exception ex) + { + htmlWysiwygImageElement.ReplaceWith(new XText("HTML PARSE FAILED: " + ex.Message)); + } + } + + + + List functionImages = + structuredResult + .Descendants() + .Where(e => e.Name.LocalName == "img" + && HasMarkup(e) + && e.Attribute("class") != null + && e.Attribute("class").Value.Contains("compositeFunctionWysiwygRepresentation")).ToList(); + + foreach (var functionImageElement in functionImages) + { + var nextNode = functionImageElement.NextNode; + + // Removing " " symbols that may appear between function call images + if (nextNode != null + && nextNode.NextNode != null + && nextNode is XText + && nextNode.NextNode is XElement + && string.IsNullOrWhiteSpace((nextNode as XText).Value.Replace(" ", "")) + && functionImages.Contains(nextNode.NextNode as XElement)) + { + nextNode.Remove(); + } + + // Replacing function call images with function markup + try + { + string functionMarkup = GetMarkupValue(functionImageElement); + XElement functionElement = XElement.Parse(functionMarkup); + + if (IsFunctionAloneInParagraph(functionImageElement)) + { + functionImageElement.Parent.ReplaceWith(functionElement); + } + else + { + functionImageElement.ReplaceWith(functionElement); + } + } + catch (Exception ex) + { + functionImageElement.ReplaceWith(new XText("FUNCTION MARKUP PARSE FAILED: " + ex.Message)); + } + } + + + IEnumerable dataFieldReferenceImages = + structuredResult.Descendants(Namespaces.Xhtml + "img") + .Where(f => f.Attribute("class") != null + && f.Attribute("class").Value.Contains("compositeFieldReferenceWysiwygRepresentation")); + + foreach (var referenceImageElement in dataFieldReferenceImages.ToList()) + { + try + { + string[] parts = HttpUtility.UrlDecode(referenceImageElement.Attribute("data-markup").Value).Split('\\'); + string typeName = parts[0]; + string fieldName = parts[1]; + + referenceImageElement.ReplaceWith(DynamicTypeMarkupServices.GetReferenceElement(fieldName, typeName)); + } + catch (Exception ex) + { + referenceImageElement.ReplaceWith(new XText("FIELD REFERENCE MARKUP PARSE FAILED: " + ex.Message)); + } + } + + FixTinyMceMalEncodingOfInternationalUrlHostNames(structuredResult); + + string bodyInnerXhtml = MarkupTransformationServices.OutputBodyDescendants(structuredResult); + + return new XhtmlTransformationResult + { + Warnings = warnings, + XhtmlFragment = FixXhtmlFragment(bodyInnerXhtml) + }; + } + catch (Exception ex) + { + LoggingService.LogError("XhtmlTransformation", ex.ToString()); + + throw; + } + } + + private static readonly List paragraphList = new List(){ + Namespaces.Xhtml + "p", + Namespaces.Xhtml + "h1", + Namespaces.Xhtml + "h2", + Namespaces.Xhtml + "h3", + Namespaces.Xhtml + "h4", + Namespaces.Xhtml + "h5", + Namespaces.Xhtml + "h6"}; + + private static bool IsFunctionAloneInParagraph(XElement element) + { + if (element.ElementsBeforeSelf().Any(d => d.Name != Namespaces.Xhtml + "br") + || element.ElementsAfterSelf().Any(d => d.Name != Namespaces.Xhtml + "br") + || !paragraphList.Contains(element.Parent.Name) + || element.Parent.Value.Replace(" ", "").Trim() != string.Empty) + return false; + + return true; + } + + private static string FixXhtmlFragment(string xhtmlFragment) + { + xhtmlFragment = Regex.Replace(xhtmlFragment, @"(\s)\r\n", "$1", RegexOptions.Multiline); + return xhtmlFragment.Replace("\xA0", " ").Replace(" ", " "); + } + + // Fixing issue where tiny + private void FixTinyMceMalEncodingOfInternationalUrlHostNames(XDocument xhtmlDoc) + { + var urlAttributes = xhtmlDoc.Descendants().Attributes().Where(f => f.Value.StartsWith("http://") || f.Value.StartsWith("https://")); + foreach (XAttribute urlAttribute in urlAttributes) + { + string url = urlAttribute.Value; + string urlWithoutProtocol = url.Substring(url.IndexOf("//", StringComparison.Ordinal) + 2); + string urlHostWithPort = (urlWithoutProtocol.Contains("/") ? urlWithoutProtocol.Substring(0, urlWithoutProtocol.IndexOf('/')) : urlWithoutProtocol); + string urlHost = (urlHostWithPort.Contains(":") ? urlHostWithPort.Substring(0, urlHostWithPort.IndexOf(':')) : urlHostWithPort); + if (urlHost != HttpUtility.UrlDecode(urlHost)) + { + urlAttribute.Value = urlAttribute.Value.Replace(urlHost, HttpUtility.UrlDecode(urlHost)); + } + } + } + + + [WebMethod] + public XhtmlTransformationResult StructuredContentToTinyContent(string htmlFragment) + { + return StructuredContentToTinyContentMultiTemplate(htmlFragment, Guid.Empty, Guid.Empty, null, 0); + } + + + [WebMethod] + public XhtmlTransformationResult StructuredContentToTinyContentMultiTemplate(string htmlFragment, Guid pageId, Guid pageTemplateId, string functionPreviewPlaceholderName, int width) + { + try + { + string warnings = ""; + string XhtmlPassXsltPath = Server.MapPath("..\\..\\transformations\\WysiwygEditor_StructuredContentToTinyContent.xsl"); + + string html = WrapInnerBody(htmlFragment); + + XDocument xml = XDocument.Parse(html, LoadOptions.PreserveWhitespace); + + IEnumerable functionRoots = xml + .Descendants(Namespaces.Function10 + "function") + .Where(f => f.Ancestors(Namespaces.Function10 + "function").Any() == false); + + foreach (var functionElement in functionRoots.ToList()) + { + string cssSelector = BuildAncestorCssSelector(functionElement); + + functionElement.ReplaceWith(GetImageTagForFunctionCall(functionElement, pageId, pageTemplateId, functionPreviewPlaceholderName, cssSelector, width)); + } + + IEnumerable dataFieldReferences = xml.Descendants(Namespaces.DynamicData10 + "fieldreference"); + foreach (var referenceElement in dataFieldReferences.ToList()) + { + referenceElement.ReplaceWith(GetImageTagForDynamicDataFieldReference(referenceElement)); + } + + var unHandledHtmlElementNames = new List + { + Namespaces.Xhtml + "audio", + Namespaces.Xhtml + "canvas", + Namespaces.Xhtml + "embed", + Namespaces.Xhtml + "iframe", + Namespaces.Xhtml + "map", + Namespaces.Xhtml + "object", + Namespaces.Xhtml + "script", + Namespaces.Xhtml + "noscript", + Namespaces.Xhtml + "video", + Namespaces.Xhtml + "svg" + }; + + IEnumerable langElements = xml.Descendants().Where(f => f.Name.Namespace == Namespaces.Localization10); + foreach (var langElement in langElements.ToList()) + { + langElement.ReplaceWith(GetImageTagForLangElement(langElement)); + } + + IEnumerable unHandledHtmlElements = xml.Descendants().Where(f => f.Name.Namespace != Namespaces.Xhtml || unHandledHtmlElementNames.Contains(f.Name)); + foreach (var unHandledHtmlElement in unHandledHtmlElements.ToList()) + { + unHandledHtmlElement.ReplaceWith(GetImageTagForHtmlElement(unHandledHtmlElement)); + } + + var xsltParameters = new Dictionary + { + {"requestapppath", UrlUtils.PublicRootPath} + }; + + XDocument structuredResult = MarkupTransformationServices.RepairXhtmlAndTransform(xml.ToString(), XhtmlPassXsltPath, xsltParameters, out warnings); + + string bodyInnerXhtml = MarkupTransformationServices.OutputBodyDescendants(structuredResult); + + return new XhtmlTransformationResult + { + Warnings = warnings, + XhtmlFragment = FixXhtmlFragment(bodyInnerXhtml) + }; + } + catch (Exception ex) + { + Log.LogError("XhtmlTransformation", ex.ToString()); + throw; + } + } + + private string BuildAncestorCssSelector(XElement element) + { + var sb = new StringBuilder(); + + foreach (var ancestor in element.Ancestors().Reverse()) + { + if(ancestor.Name.LocalName == "html" || ancestor.Name.LocalName == "body") continue; + + if (sb.Length > 0) + { + sb.Append(" "); + } + + sb.Append(ancestor.Name.LocalName); + string cssClasses = (string) ancestor.Attribute("class") ?? ""; + + foreach (var cssClass in cssClasses.Split(new [] {" "}, StringSplitOptions.RemoveEmptyEntries)) + { + sb.Append(".").Append(cssClass); + } + } + + return sb.ToString(); + } + + [WebMethod] + public string GetImageTagForFunctionCall(string functionMarkup) + { + return GetImageTagForFunctionCall2(functionMarkup, Guid.Empty, Guid.Empty, null, 0); + } + + [WebMethod] + public string GetImageTagForFunctionCall2(string functionMarkup, Guid functionPreviewPageId, Guid functionPreviewTemplateId, string functionPreviewPlaceholderName, int width) + { + XElement functionElement; + + try + { + functionElement = XElement.Parse(functionMarkup); + } + catch (Exception ex) + { + throw new ArgumentException("Unable to parse functionMarkup as XML", ex); + } + + return GetImageTagForFunctionCall(functionElement, functionPreviewPageId, functionPreviewTemplateId, functionPreviewPlaceholderName, null, width) + .ToString(SaveOptions.DisableFormatting); + } + + + + [WebMethod] + public FunctionInfo GetFunctionInfo(string functionName) + { + IFunction function = FunctionFacade.GetFunction(functionName); + + var functionRuntimeTreeNode = new FunctionRuntimeTreeNode(function); + + return new FunctionInfo + { + FunctionMarkup = functionRuntimeTreeNode.Serialize().ToString(), + RequireConfiguration = function.ParameterProfiles.Any(p => !p.IsInjectedValue) + }; + } + + + + private XElement GetImageTagForDynamicDataFieldReference(XElement fieldReferenceElement) + { + string typeName = fieldReferenceElement.Attribute("typemanagername").Value; + + Type type = TypeManager.GetType(typeName); + if (!typeof(IData).IsAssignableFrom(type)) + { + string fieldName = fieldReferenceElement.Attribute("fieldname").Value; + + return GetImageTagForDynamicDataFieldReference(fieldName, fieldName, type.AssemblyQualifiedName, type.AssemblyQualifiedName); + } + + DataTypeDescriptor typeDescriptor; + DataFieldDescriptor fieldDescriptor; + + if (!DynamicTypeMarkupServices.TryGetDescriptors(fieldReferenceElement, out typeDescriptor, out fieldDescriptor)) + { + return null; + } + + return GetImageTagForDynamicDataFieldReference(fieldDescriptor, typeDescriptor); + } + + + private XElement GetImageTagForHtmlElement(XElement element) + { + string description = element.ToString().Replace(" xmlns=\"http://www.w3.org/1999/xhtml\"", ""); + string title = "HTML block"; + + var descriptionLines = description.Split('\n'); + if (descriptionLines.Length > 6) + { + description = string.Format("{0}\n{1}\n{2}\n...\n{3}", descriptionLines[0], descriptionLines[1], + descriptionLines[2], descriptionLines.Last()); + } - int diplayImageHashCode; - string imageUrl = GetFunctionBoxImageUrl("html", title, description, out diplayImageHashCode); + int diplayImageHashCode; + string imageUrl = GetFunctionBoxImageUrl("html", title, description, out diplayImageHashCode); - return new XElement(Namespaces.Xhtml + "img", - new XAttribute("alt", _markupWysiwygRepresentationAlt), - new XAttribute("src", imageUrl), - new XAttribute("class", "compositeHtmlWysiwygRepresentation"), - GetMarkupAttribute(element.ToString()) - ); - } + return new XElement(Namespaces.Xhtml + "img", + new XAttribute("alt", _markupWysiwygRepresentationAlt), + new XAttribute("src", imageUrl), + new XAttribute("class", "compositeHtmlWysiwygRepresentation"), + GetMarkupAttribute(element.ToString()) + ); + } - /* + /* DK EN @@ -442,428 +442,441 @@ namespace Composite.Services */ - private XElement GetImageTagForLangElement(XElement element) - { - XName switchName = Namespaces.Localization10 + "switch"; - XName stringName = Namespaces.Localization10 + "string"; - - string title = "Language specific block"; - StringBuilder description = new StringBuilder(); - - try - { - - if (element.Name == stringName) - { - description.AppendLine(element.Attribute("key").Value); - } - - if (element.Name == switchName) - { - foreach (var option in element.Elements().Where(e => e.Name.Namespace == Namespaces.Localization10)) - { - int toGrab = Math.Min(35, option.Value.Length); - string ellipsis = (option.Value.Length > 35 ? "..." : ""); - string descriptionContent = string.Format("{0}{1}", - option.Value.Substring(0, toGrab), - ellipsis); - - if (String.IsNullOrWhiteSpace(option.Value) && option.Nodes().Any()) - { - description.Append("(html code)"); - } - - switch (option.Name.LocalName) - { - case "when": - description.AppendFormat("{0}: {1}", - option.Attribute("culture").Value, - descriptionContent); - break; - case "default": - description.AppendLine(); - description.AppendFormat("Default: {0}", - descriptionContent); - break; - } - description.AppendLine("\n\n\n\n\n\n"); /* wtf - do I need this? */ - } - } - } - catch (Exception) - { - description.AppendLine("[ ERROR PARSING MARKUP ]"); - } - - int diplayImageHashCode; - string imageUrl = GetFunctionBoxImageUrl("html", title, description.ToString(), out diplayImageHashCode); - - return new XElement(Namespaces.Xhtml + "img", - new XAttribute("alt", _markupWysiwygRepresentationAlt), - new XAttribute("src", imageUrl), - new XAttribute("class", "compositeHtmlWysiwygRepresentation"), - GetMarkupAttribute(element.ToString()) - ); - } - - - private static string GetFunctionBoxImageUrl(string type, string title, string description, out int previewImageHashCode) - { - string imageUrl = "~/Renderers/FunctionBox?type={0}&title={1}&description={2}&lang={3}".FormatWith( - HttpUtility.UrlEncode(type, Encoding.UTF8), - HttpUtility.UrlEncode(title, Encoding.UTF8), - UrlUtils.ZipContent(description.Trim()), // ZIPping description as it may contain xml tags f.e.