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); + } } } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/DeleteMediaFolderWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/DeleteMediaFolderWorkflow.cs index 35493644cb..003e761aa1 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/DeleteMediaFolderWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/MediaFileProviderElementProvider/DeleteMediaFolderWorkflow.cs @@ -1,19 +1,18 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Transactions; using System.Workflow.Activities; using Composite.C1Console.Actions; using Composite.C1Console.Events; using Composite.Data; using Composite.Data.Types; using Composite.Data.Types.StoreIdFilter; -using Composite.C1Console.Elements.ElementProviderHelpers.AssociatedDataElementProviderHelper; using Composite.Core.Linq; -using Composite.Core.ResourceSystem; using Composite.Data.Transactions; using Composite.C1Console.Workflow; +using Texts = Composite.Core.ResourceSystem.LocalizationFiles.Composite_Management; + namespace Composite.Plugins.Elements.ElementProviders.MediaFileProviderElementProvider { @@ -28,13 +27,13 @@ public DeleteMediaFolderWorkflow() private void HasDataReferences(object sender, ConditionalEventArgs e) { - DataEntityToken token = (DataEntityToken)this.EntityToken; - IMediaFileFolder folder = (IMediaFileFolder)token.Data; + var token = (DataEntityToken)this.EntityToken; + var folder = (IMediaFileFolder)token.Data; string storeId = folder.StoreId; string parentPath = folder.Path; - string innerElementsPathPrefix = string.Format("{0}/", parentPath); + string innerElementsPathPrefix = $"{parentPath}/"; var fileQueryable = new StoreIdFilterQueryable(DataFacade.GetData(), storeId); IEnumerable childFiles = @@ -72,7 +71,7 @@ private void deleteCodeActivity_ExecuteCode(object sender, EventArgs e) DeleteTreeRefresher treeRefresher = this.CreateDeleteTreeRefresher(this.EntityToken); DataEntityToken token = (DataEntityToken)this.EntityToken; - using (TransactionScope transactionScope = TransactionsFacade.CreateNewScope()) + using (var transactionScope = TransactionsFacade.CreateNewScope()) { IMediaFileFolder folder = DataFacade.GetDataFromDataSourceId(token.DataSourceId, false) as IMediaFileFolder; @@ -81,11 +80,9 @@ private void deleteCodeActivity_ExecuteCode(object sender, EventArgs e) { if (!DataFacade.WillDeleteSucceed(folder)) { - this.ShowMessage( - DialogType.Error, - StringResourceSystemFacade.GetString("Composite.Management", "DeleteMediaFolderWorkflow.CascadeDeleteErrorTitle"), - StringResourceSystemFacade.GetString("Composite.Management", "DeleteMediaFolderWorkflow.CascadeDeleteErrorMessage") - ); + this.ShowMessage(DialogType.Error, + Texts.DeleteMediaFolderWorkflow_CascadeDeleteErrorTitle, + Texts.DeleteMediaFolderWorkflow_CascadeDeleteErrorMessage); return; } @@ -101,16 +98,16 @@ private void deleteCodeActivity_ExecuteCode(object sender, EventArgs e) private void codeActivity1_ExecuteCode(object sender, EventArgs e) { - DataEntityToken token = (DataEntityToken)this.EntityToken; - IMediaFileFolder folder = (IMediaFileFolder)token.Data; + var token = (DataEntityToken)this.EntityToken; + var folder = (IMediaFileFolder)token.Data; string storeId = folder.StoreId; string parentPath = folder.Path; - StoreIdFilterQueryable folderQueryable = new StoreIdFilterQueryable(DataFacade.GetData(), storeId); - StoreIdFilterQueryable fileQueryable = new StoreIdFilterQueryable(DataFacade.GetData(), storeId); + var folderQueryable = new StoreIdFilterQueryable(DataFacade.GetData(), storeId); + var fileQueryable = new StoreIdFilterQueryable(DataFacade.GetData(), storeId); - string innerElementsPathPrefix = string.Format("{0}/", parentPath); + string innerElementsPathPrefix = $"{parentPath}/"; bool anyFiles = (from item in fileQueryable @@ -122,15 +119,11 @@ where item.FolderPath.StartsWith(innerElementsPathPrefix) || item.FolderPath == where item.Path.StartsWith(innerElementsPathPrefix) select item).Any(); + var message = !anyFiles && !anyFolders + ? Texts.Website_Forms_Administrative_DeleteMediaFolder_Text + : Texts.Website_Forms_Administrative_DeleteMediaFolder_HasChildringText; - if ((anyFiles == false) && (anyFolders == false)) - { - this.Bindings.Add("MessageText", StringResourceSystemFacade.GetString("Composite.Management", "Website.Forms.Administrative.DeleteMediaFolder.Text")); - } - else - { - this.Bindings.Add("MessageText", StringResourceSystemFacade.GetString("Composite.Management", "Website.Forms.Administrative.DeleteMediaFolder.HasChildringText")); - } + this.Bindings.Add("MessageText", message); } } } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/EditPageWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/EditPageWorkflow.cs index 02f53550b1..2a79d34d6c 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/EditPageWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/EditPageWorkflow.cs @@ -269,7 +269,9 @@ private void editStateCodeActivity_ExecuteCode(object sender, EventArgs e) } - var contents = DataFacade.GetData(f => f.PageId == selectedPage.Id && f.VersionId == selectedPage.VersionId).ToList(); + var contents = DataFacade.GetData(false) + .Where(f => f.PageId == selectedPage.Id && f.VersionId == selectedPage.VersionId) + .ToList(); var namedXhtmlFragments = contents.ToDictionary(content => content.PlaceHolderId, content => content.Content ?? ""); @@ -355,8 +357,8 @@ private void saveCodeActivity_ExecuteCode(object sender, EventArgs e) } var contentDictionary = GetBinding>("NamedXhtmlFragments"); - var existingContents = DataFacade.GetData( - f => f.PageId == selectedPage.Id && f.VersionId == selectedPage.VersionId).ToList(); + var existingContents = DataFacade.GetData(false) + .Where(f => f.PageId == selectedPage.Id && f.VersionId == selectedPage.VersionId).ToList(); foreach (var existingContent in existingContents) { diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/UndoUnpublishedChangesWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/UndoUnpublishedChangesWorkflow.cs index 2fc4a8a5e8..d941475d88 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/UndoUnpublishedChangesWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/PageElementProvider/UndoUnpublishedChangesWorkflow.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Linq; using Composite.Data; using Composite.Data.Types; @@ -38,7 +38,7 @@ private void undoCodeActivity_Undo_ExecuteCode(object sender, EventArgs e) using (new DataScope(DataScopeIdentifier.Administrated)) { administrativePlaceholders = - (from ph in DataFacade.GetData() + (from ph in DataFacade.GetData(false) where ph.PageId == pageId && ph.VersionId == versionId select ph).ToList(); } @@ -47,7 +47,7 @@ private void undoCodeActivity_Undo_ExecuteCode(object sender, EventArgs e) using (new DataScope(DataScopeIdentifier.Public)) { publicPlaceholders = - (from ph in DataFacade.GetData() + (from ph in DataFacade.GetData(false) where ph.PageId == pageId && ph.VersionId == versionId select ph).ToList(); } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/AddNewUserWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/AddNewUserWorkflow.cs index be42a19bd2..4434ae41c0 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/AddNewUserWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/AddNewUserWorkflow.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Workflow.Runtime; @@ -73,7 +74,7 @@ where string.Compare(user.Username, newUser.Username, StringComparison.Invariant if(usersWithTheSameName.Any()) { ShowFieldMessage(BindingNames.Username, - StringResourceSystemFacade.GetString("Composite.Management", "UserElementProvider.UserLoginIsAlreadyUsed")); + LocalizationFiles.Composite_Management.UserElementProvider_UserLoginIsAlreadyUsed); isValid = false; } @@ -104,8 +105,8 @@ private void MissingActiveLanguageCodeActivity_ExecuteCode(object sender, EventA managementConsoleMessageService.ShowMessage( DialogType.Message, - StringResourceSystemFacade.GetString("Composite.Management", "UserElementProvider.MissingActiveLanguageTitle"), - StringResourceSystemFacade.GetString("Composite.Management", "UserElementProvider.MissingActiveLanguageMessage")); + LocalizationFiles.Composite_Management.UserElementProvider_MissingActiveLanguageTitle, + LocalizationFiles.Composite_Management.UserElementProvider_MissingActiveLanguageMessage); } @@ -155,7 +156,7 @@ private void step1CodeActivity_ExecuteCode(object sender, EventArgs e) foreach (ValidationResult result in validationResults) { - this.ShowFieldMessage(string.Format("{0}.{1}", BindingNames.NewUser, result.Key), result.Message); + this.ShowFieldMessage($"{BindingNames.NewUser}.{result.Key}", result.Message); } IQueryable usersWithTheSameName = @@ -165,7 +166,9 @@ where string.Compare(user.Username, newUser.Username, StringComparison.Invariant if (usersWithTheSameName.Any()) { - this.ShowFieldMessage(BindingNames.Username, StringResourceSystemFacade.GetString("Composite.Management", "AddNewUserWorkflow.UsernameDuplicateError")); + this.ShowFieldMessage(BindingNames.Username, + LocalizationFiles.Composite_Management.AddNewUserWorkflow_UsernameDuplicateError); + } } @@ -202,7 +205,10 @@ private void finalizeCodeActivity_ExecuteCode(object sender, EventArgs e) addNewTreeRefresher.PostRefreshMesseges(newUser.GetDataEntityToken()); - LoggingService.LogVerbose("UserManagement", String.Format("New C1 Console user '{0}' created by '{1}'.", newUser.Username, UserValidationFacade.GetUsername()), LoggingService.Category.Audit); + LoggingService.LogEntry("UserManagement", + $"New C1 Console user '{newUser.Username}' created by '{UserValidationFacade.GetUsername()}'.", + LoggingService.Category.Audit, + TraceEventType.Information); this.ExecuteWorklow(newUser.GetDataEntityToken(), typeof(EditUserWorkflow)); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/DeleteUserWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/DeleteUserWorkflow.cs index 8436d74e12..9065349209 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/DeleteUserWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/DeleteUserWorkflow.cs @@ -1,6 +1,6 @@ -using System; +using System; +using System.Diagnostics; using System.Workflow.Activities; -using Composite.C1Console.Actions; using Composite.C1Console.Events; using Composite.Data; using Composite.Data.Types; @@ -12,7 +12,7 @@ namespace Composite.Plugins.Elements.ElementProviders.UserElementProvider { - [EntityTokenLock()] + [EntityTokenLock] [AllowPersistingWorkflow(WorkflowPersistingType.Idle)] public sealed partial class DeleteUserWorkflow : Composite.C1Console.Workflow.Activities.FormsWorkflow { @@ -36,7 +36,7 @@ private void IsDeleteSelf(object sender, ConditionalEventArgs e) private void initializeCodeActivity_Initialize_ExecuteCode(object sender, EventArgs e) { - DataEntityToken dataEntityToken = (DataEntityToken)this.EntityToken; + var dataEntityToken = (DataEntityToken)this.EntityToken; IUser user = (IUser)dataEntityToken.Data; @@ -55,8 +55,8 @@ private void finalizeCodeActivity_ExecuteCode(object sender, EventArgs e) { this.ShowMessage( DialogType.Error, - StringResourceSystemFacade.GetString("Composite.Management", "DeleteUserWorkflow.CascadeDeleteErrorTitle"), - StringResourceSystemFacade.GetString("Composite.Management", "DeleteUserWorkflow.CascadeDeleteErrorMessage")); + LocalizationFiles.Composite_Management.DeleteUserWorkflow_CascadeDeleteErrorTitle, + LocalizationFiles.Composite_Management.DeleteUserWorkflow_CascadeDeleteErrorMessage); return; } @@ -64,11 +64,12 @@ private void finalizeCodeActivity_ExecuteCode(object sender, EventArgs e) DataFacade.Delete(user); - LoggingService.LogVerbose("UserManagement", + LoggingService.LogEntry("UserManagement", $"C1 Console user '{user.Username}' deleted by '{UserValidationFacade.GetUsername()}'.", - LoggingService.Category.Audit); + LoggingService.Category.Audit, + TraceEventType.Information); this.CreateParentTreeRefresher().PostRefreshMessages(dataEntityToken, 2); - } + } } } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs index 5383bb4f92..a553f6003a 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Workflow.Runtime; @@ -191,7 +192,7 @@ private void saveCodeActivity_ExecuteCode(object sender, EventArgs e) foreach (ValidationResult result in validationResults) { - this.ShowFieldMessage(string.Format("{0}.{1}", BindingNames.User, result.Key), result.Message); + this.ShowFieldMessage($"{BindingNames.User}.{result.Key}", result.Message); userValidated = false; } @@ -396,7 +397,10 @@ from r in oldRelations DataFacade.AddNew(userUserGroupRelation); } - LoggingService.LogVerbose("UserManagement", String.Format("C1 Console user '{0}' updated by '{1}'.", user.Username, UserValidationFacade.GetUsername()), LoggingService.Category.Audit); + LoggingService.LogEntry("UserManagement", + $"C1 Console user '{user.Username}' updated by '{UserValidationFacade.GetUsername()}'.", + LoggingService.Category.Audit, + TraceEventType.Information); transactionScope.Complete(); } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/AddNewUserGroupWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/AddNewUserGroupWorkflow.cs index 51405d5d9d..74b26f8d90 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/AddNewUserGroupWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/AddNewUserGroupWorkflow.cs @@ -2,6 +2,7 @@ using System.ComponentModel; using System.ComponentModel.Design; using System.Collections; +using System.Diagnostics; using System.Drawing; using System.Linq; using System.Workflow.ComponentModel.Compiler; @@ -77,7 +78,10 @@ private void finalizeCodeActivity_Finalize_ExecuteCode(object sender, EventArgs this.CloseCurrentView(); - LoggingService.LogVerbose("UserManagement", String.Format("New C1 Console user group '{0}' created by '{1}'.", userGroup.Name, UserValidationFacade.GetUsername()), LoggingService.Category.Audit); + LoggingService.LogEntry("UserManagement", + $"New C1 Console user group '{userGroup.Name}' created by '{UserValidationFacade.GetUsername()}'.", + LoggingService.Category.Audit, + TraceEventType.Information); addNewTreeRefresher.PostRefreshMesseges(userGroup.GetDataEntityToken()); diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/DeleteUserGroupWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/DeleteUserGroupWorkflow.cs index 2d7508dd32..0b2b21566e 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/DeleteUserGroupWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/DeleteUserGroupWorkflow.cs @@ -1,4 +1,5 @@ -using System; +using System; +using System.Diagnostics; using System.Linq; using Composite.Data; using Composite.Data.Types; @@ -51,7 +52,10 @@ private void finalizeCodeActivity_Delete_ExecuteCode(object sender, EventArgs e) DataFacade.Delete(userGroup); - LoggingService.LogVerbose("UserManagement", String.Format("C1 Console user group '{0}' deleted by '{1}'.", userGroup.Name, UserValidationFacade.GetUsername()), LoggingService.Category.Audit); + LoggingService.LogEntry("UserManagement", + $"C1 Console user group '{userGroup.Name}' deleted by '{UserValidationFacade.GetUsername()}'.", + LoggingService.Category.Audit, + TraceEventType.Information); deleteTreeRefresher.PostRefreshMesseges(); } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs index e03dd5dd51..0eea5156fb 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs @@ -1,5 +1,6 @@ -using System; +using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Workflow.Activities; using System.Xml.Linq; @@ -140,7 +141,10 @@ private void saveCodeActivity_Save_ExecuteCode(object sender, EventArgs e) SetSaveStatus(true); - LoggingService.LogVerbose("UserManagement", $"C1 Console user group '{userGroup.Name}' updated by '{UserValidationFacade.GetUsername()}'.", LoggingService.Category.Audit); + LoggingService.LogEntry("UserManagement", + $"C1 Console user group '{userGroup.Name}' updated by '{UserValidationFacade.GetUsername()}'.", + LoggingService.Category.Audit, + TraceEventType.Information); if (userGroup.Name != this.GetBinding("OldName")) { diff --git a/Composite/AspNet/Razor/CompositeC1WebPage.cs b/Composite/AspNet/Razor/CompositeC1WebPage.cs index de375c995f..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(); @@ -170,8 +181,10 @@ public void Dispose() { Dispose(true); - GC.SuppressFinalize(this); - } +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } /// protected virtual void Dispose(bool disposing) @@ -180,16 +193,22 @@ protected virtual void Dispose(bool disposing) if (disposing) { + (_childPages as IEnumerable)?.Reverse().ForEach(c => c.Dispose()); + _data?.Dispose(); } _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/Security/FileBasedFunctionEntityToken.cs b/Composite/AspNet/Security/FileBasedFunctionEntityToken.cs index 16b654717a..b81db186ba 100644 --- a/Composite/AspNet/Security/FileBasedFunctionEntityToken.cs +++ b/Composite/AspNet/Security/FileBasedFunctionEntityToken.cs @@ -1,5 +1,6 @@ using System; using Composite.C1Console.Security; +using Newtonsoft.Json; namespace Composite.AspNet.Security { @@ -36,6 +37,7 @@ public override string Type /// /// The name of the function provider. /// + [JsonIgnore] public string FunctionProviderName { get { return Source; } @@ -47,6 +49,7 @@ public string FunctionProviderName /// /// The name of the function. /// + [JsonIgnore] public string FunctionName { get { return Id; } 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/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/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/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/Actions/DuplicateActionToken.cs b/Composite/C1Console/Actions/DuplicateActionToken.cs index f535b9cf7d..c118d9a995 100644 --- a/Composite/C1Console/Actions/DuplicateActionToken.cs +++ b/Composite/C1Console/Actions/DuplicateActionToken.cs @@ -202,8 +202,8 @@ internal void CopyPageData(IPage sourcePage, IPage newPage) var newPlaceholders = new List(); var placeholders = - DataFacade.GetData(). - Where(ph => ph.PageId == sourcePageId + DataFacade.GetData(false) + .Where(ph => ph.PageId == sourcePageId && ph.VersionId == sourceVersionId) .ToList(); diff --git a/Composite/C1Console/Actions/Workflow/EntityTokenLockedEntityToken.cs b/Composite/C1Console/Actions/Workflow/EntityTokenLockedEntityToken.cs index e5eee1f9e7..66e884192a 100644 --- a/Composite/C1Console/Actions/Workflow/EntityTokenLockedEntityToken.cs +++ b/Composite/C1Console/Actions/Workflow/EntityTokenLockedEntityToken.cs @@ -1,4 +1,5 @@ using Composite.C1Console.Security; +using Newtonsoft.Json; namespace Composite.C1Console.Actions.Workflows @@ -28,6 +29,7 @@ public EntityTokenLockedEntityToken(string lockedByUsername, string serializedLo /// + [JsonIgnore] public string LockedByUsername { get { return _lockedByUsername; } @@ -35,6 +37,7 @@ public string LockedByUsername /// + [JsonIgnore] public ActionToken LockedActionToken { get @@ -50,6 +53,7 @@ public ActionToken LockedActionToken /// + [JsonIgnore] public EntityToken LockedEntityToken { get 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/Elements/ElementActionSecurityExtensions.cs b/Composite/C1Console/Elements/ElementActionSecurityExtensions.cs new file mode 100644 index 0000000000..d6fb8b65f9 --- /dev/null +++ b/Composite/C1Console/Elements/ElementActionSecurityExtensions.cs @@ -0,0 +1,135 @@ +//#warning REMARK THIS!!! +//#define NO_SECURITY +using System; +using System.Collections.Generic; +using Composite.C1Console.Security; +using Composite.Core.Linq; +using Composite.Plugins.Elements.ElementProviders.PageElementProvider; + + +namespace Composite.C1Console.Elements +{ + /// + /// Extension methods related to security on element actions. + /// + public static class ElementActionSecurityExtensions + { + /// + /// Will filter actions (according to current users permissions) attached on the elements + /// + /// Elements to filder + /// New sequence of elements, with actions filtered + public static IEnumerable FilterElementsAndActions(this IEnumerable elements) + { + Verify.ArgumentNotNull(elements, nameof(elements)); + +#if NO_SECURITY + return elements; +#else + UserToken userToken = UserValidationFacade.GetUserToken(); + + var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username).Evaluate(); + var userGroupPermissionDefinition = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username).Evaluate(); + + return elements + .FilterElements(userToken, userPermissionDefinitions, userGroupPermissionDefinition) + .FilterActions(userToken, userPermissionDefinitions, userGroupPermissionDefinition); +#endif + } + + internal static IEnumerable FilterElements(this IEnumerable elements) + { +#if NO_SECURITY + return elements; +#endif + + var userToken = UserValidationFacade.GetUserToken(); + + var userPermissions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username).Evaluate(); + var userGroupPermissions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username).Evaluate(); + + return FilterElements(elements, userToken, userPermissions, userGroupPermissions); + } + + internal static IEnumerable FilterElements(this IEnumerable elements, + UserToken userToken, + ICollection userPermissionDefinitions, + ICollection userGroupPermissionDefinition) + { + Verify.ArgumentNotNull(elements, nameof(elements)); + +#if NO_SECURITY + return elements; +#else + foreach (Element element in elements) + { + if (PermissionTypeFacade.IsSubBrachContainingPermissionTypes(userToken, + element.ElementHandle.EntityToken, userPermissionDefinitions, userGroupPermissionDefinition)) + { + yield return element; + } + } +#endif + } + + + internal static IEnumerable FilterActions(this IEnumerable elements) + { +#if NO_SECURITY + return elements; +#endif + + var userToken = UserValidationFacade.GetUserToken(); + + var userPermissions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username).Evaluate(); + var userGroupPermissions = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username).Evaluate(); + + return FilterActions(elements, userToken, userPermissions, userGroupPermissions); + } + + + internal static IEnumerable FilterActions(this IEnumerable elements, + UserToken userToken, + ICollection userPermissionDefinitions, + ICollection userGroupPermissionDefinition) + { + Verify.ArgumentNotNull(elements, nameof(elements)); + +#if NO_SECURITY + return elements; +#else + foreach (var element in elements) + { + var actionsToRemove = new List(); + foreach (ElementAction elementAction in element.Actions) + { + if (SecurityResolver.Resolve(userToken, elementAction.ActionHandle.ActionToken, + element.ElementHandle.EntityToken, userPermissionDefinitions, + userGroupPermissionDefinition) == SecurityResult.Disallowed) + { + actionsToRemove.Add(elementAction); + } + } + + foreach (ElementAction elementAction in actionsToRemove) + { + element.RemoveAction(elementAction); + } + + // Drag & drop security + if (element.MovabilityInfo != null) + { + if (SecurityResolver.Resolve(userToken, new DragAndDropActionToken(), + element.ElementHandle.EntityToken, userPermissionDefinitions, + userGroupPermissionDefinition) == SecurityResult.Disallowed) + { + element.RemoveMovabilityInfo(); + } + } + + yield return element; + } +#endif + } + } +} \ No newline at end of file diff --git a/Composite/C1Console/Elements/ElementFacade.cs b/Composite/C1Console/Elements/ElementFacade.cs index 42b880d11f..f3c15785ef 100644 --- a/Composite/C1Console/Elements/ElementFacade.cs +++ b/Composite/C1Console/Elements/ElementFacade.cs @@ -7,7 +7,6 @@ using Composite.C1Console.Elements.Foundation; using Composite.C1Console.Elements.Foundation.PluginFacades; using Composite.C1Console.Elements.Plugins.ElementProvider; -using Composite.C1Console.Elements.Security; using Composite.C1Console.Forms.DataServices; using Composite.C1Console.Forms.Flows; using Composite.C1Console.Security; @@ -24,7 +23,7 @@ namespace Composite.C1Console.Elements [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class ElementFacade { - private static IEnumerable _perspectiveEntityTokens = null; + private static IEnumerable _perspectiveEntityTokens; /// public static IEnumerable GetRoots(SearchToken searchToken) @@ -159,8 +158,6 @@ public static SearchToken GetNewSearchToken(ElementHandle elementHandle) { if (elementHandle == null) throw new ArgumentNullException("elementHandle"); - if (elementHandle == null) throw new ArgumentNullException("elementHandle"); - SearchToken searchToken; if (ElementProviderPluginFacade.GetNewSearchToken(elementHandle.ProviderName, elementHandle.EntityToken, out searchToken) == false) @@ -283,7 +280,7 @@ private static IEnumerable GetRoots(string providerName, SearchToken se if (providerName == null) throw new ArgumentNullException("providerName"); IEnumerable roots; - if ((useForeign == false) || (ElementProviderPluginFacade.IsLocaleAwareElementProvider(providerName) == false)) + if (!useForeign || !ElementProviderPluginFacade.IsLocaleAwareElementProvider(providerName)) { roots = ElementProviderPluginFacade.GetRoots(providerName, searchToken).ToList(); } @@ -292,58 +289,70 @@ private static IEnumerable GetRoots(string providerName, SearchToken se roots = ElementProviderPluginFacade.GetForeignRoots(providerName, searchToken).ToList(); } + if (performSecurityCheck) + { + roots = roots.FilterElements(); + } + ElementActionProviderFacade.AddActions(roots, providerName); if (performSecurityCheck) { return roots.FilterActions(); } - else - { - return roots; - } - } + return roots; + } - private static IEnumerable GetChildren(ElementHandle elementHandle, SearchToken searchToken, bool performSecurityCheck, bool useForeign) + private static IEnumerable GetChildrenFromProvider(ElementHandle elementHandle, SearchToken searchToken, bool useForeign) { - if (elementHandle == null) throw new ArgumentNullException("elementHandle"); - - IPerformanceCounterToken performanceToken = PerformanceCounterFacade.BeginElementCreation(); - - IEnumerable children; if (ElementProviderRegistry.ElementProviderNames.Contains(elementHandle.ProviderName)) { - if ((useForeign == false) || (ElementProviderPluginFacade.IsLocaleAwareElementProvider(elementHandle.ProviderName) == false)) - { - children = ElementProviderPluginFacade.GetChildren(elementHandle.ProviderName, elementHandle.EntityToken, searchToken).Evaluate(); - } - else + if (!useForeign || !ElementProviderPluginFacade.IsLocaleAwareElementProvider(elementHandle.ProviderName)) { - children = ElementProviderPluginFacade.GetForeignChildren(elementHandle.ProviderName, elementHandle.EntityToken, searchToken).Evaluate(); + return ElementProviderPluginFacade.GetChildren(elementHandle.ProviderName, elementHandle.EntityToken, searchToken); } + + return ElementProviderPluginFacade.GetForeignChildren(elementHandle.ProviderName, elementHandle.EntityToken, searchToken); } - else if (ElementAttachingProviderRegistry.ElementAttachingProviderNames.Contains(elementHandle.ProviderName)) + + if (ElementAttachingProviderRegistry.ElementAttachingProviderNames.Contains(elementHandle.ProviderName)) { - children = ElementAttachingProviderPluginFacade.GetChildren(elementHandle.ProviderName, elementHandle.EntityToken, elementHandle.Piggyback).Evaluate(); + return ElementAttachingProviderPluginFacade.GetChildren(elementHandle.ProviderName, elementHandle.EntityToken, elementHandle.Piggyback); } - else + + throw new InvalidOperationException($"No element provider named '{elementHandle.ProviderName}' found"); + } + + + private static IEnumerable GetChildren(ElementHandle elementHandle, SearchToken searchToken, bool performSecurityCheck, bool useForeign) + { + Verify.ArgumentNotNull(elementHandle, nameof(elementHandle)); + + var performanceToken = PerformanceCounterFacade.BeginElementCreation(); + + var children = GetChildrenFromProvider(elementHandle, searchToken, useForeign).Evaluate(); + + var originalChildren = children; + + children = ElementAttachingProviderFacade.AttachElements(elementHandle.EntityToken, elementHandle.Piggyback, children).Evaluate(); + + int totalElementCount = children.Count; + + if (performSecurityCheck) { - throw new InvalidOperationException(string.Format("No element provider named '{0}' found", elementHandle.ProviderName)); + children = children.FilterElements().Evaluate(); } - foreach (Element element in children) + // Evaluating HasChildren for not attached elements + foreach (Element element in originalChildren) { - if (element.VisualData.HasChildren == false) + if (children.Contains(element) && !element.VisualData.HasChildren) { element.VisualData.HasChildren = ElementAttachingProviderFacade.HaveCustomChildElements(element.ElementHandle.EntityToken, element.ElementHandle.Piggyback); } } - - children = ElementAttachingProviderFacade.AttachElements(elementHandle.EntityToken, elementHandle.Piggyback, children).Evaluate(); - - int totalElementCount = children.Count(); ElementActionProviderFacade.AddActions(children, elementHandle.ProviderName); @@ -352,7 +361,7 @@ private static IEnumerable GetChildren(ElementHandle elementHandle, Sea children = children.FilterActions().Evaluate(); } - PerformanceCounterFacade.EndElementCreation(performanceToken, children.Count(), totalElementCount); + PerformanceCounterFacade.EndElementCreation(performanceToken, children.Count, totalElementCount); return children; } diff --git a/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelper.cs b/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelper.cs index 5ffc6a9caf..f52ffc5acc 100644 --- a/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelper.cs +++ b/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelper.cs @@ -80,11 +80,8 @@ static AssociatedDataElementProviderHelper() public AssociatedDataElementProviderHelper(ElementProviderContext elementProviderContext, EntityToken rootEntityToken, bool addVisualFunctionActions) { - if (elementProviderContext == null) throw new ArgumentNullException("elementProviderContext"); - if (rootEntityToken == null) throw new ArgumentNullException("rootEntityToken"); - - _elementProviderContext = elementProviderContext; - _rootEntityToken = rootEntityToken; + _elementProviderContext = elementProviderContext ?? throw new ArgumentNullException(nameof(elementProviderContext)); + _rootEntityToken = rootEntityToken ?? throw new ArgumentNullException(nameof(rootEntityToken)); _addVisualFunctionActions = addVisualFunctionActions; _dataGroupingProviderHelper = new DataGroupingProviderHelper.DataGroupingProviderHelper(elementProviderContext) @@ -115,8 +112,7 @@ private Func GetLeafsFilter(EntityToken parentEntityToken) private Guid GetPageId(EntityToken entityToken) { - var dataGroupingEntityToken = entityToken as DataGroupingProviderHelperEntityToken; - if (dataGroupingEntityToken != null) + if (entityToken is DataGroupingProviderHelperEntityToken dataGroupingEntityToken) { if(dataGroupingEntityToken.Payload.IsNullOrEmpty()) { @@ -126,19 +122,17 @@ private Guid GetPageId(EntityToken entityToken) return new Guid(dataGroupingEntityToken.Payload); } - var pageFolderElementEntityToken = entityToken as AssociatedDataElementProviderHelperEntityToken; - if( pageFolderElementEntityToken != null) + if(entityToken is AssociatedDataElementProviderHelperEntityToken pageFolderElementEntityToken) { return new Guid(pageFolderElementEntityToken.Id); } - var dataEntityToken = entityToken as DataEntityToken; - if(dataEntityToken != null) + if(entityToken is DataEntityToken dataEntityToken) { return (dataEntityToken.Data as IPageRelatedData).PageId; } - throw new InvalidOperationException("Unexpected entity token type '{0}'".FormatWith(entityToken.GetType().FullName)); + throw new InvalidOperationException($"Unexpected entity token type '{entityToken.GetType().FullName}'"); } private EntityToken GetParentEntityToken(Type interfaceType, EntityToken entityToken) @@ -373,9 +367,9 @@ private Element CreateElement(IData data) Label = label, ToolTip = label, HasChildren = false, - Icon = (data is IPublishControlled - ? DataIconLookup[((IPublishControlled)data).PublicationStatus] - : DataIconFacade.DataPublishedIcon) + Icon = data is IPublishControlled publishedControlled + ? DataIconLookup[publishedControlled.PublicationStatus] + : DataIconFacade.DataPublishedIcon } }; diff --git a/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelperEntityToken.cs b/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelperEntityToken.cs index c22cf8a156..537f2ed55b 100644 --- a/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelperEntityToken.cs +++ b/Composite/C1Console/Elements/ElementProviderHelpers/AssociatedDataElementProviderHelper/AssociatedDataElementProviderHelperEntityToken.cs @@ -1,58 +1,48 @@ using System; using System.Collections.Generic; -using System.Text; using Composite.Data; using Composite.C1Console.Security; +using Composite.Core; using Composite.Core.Serialization; using Composite.Core.Types; +using Newtonsoft.Json; namespace Composite.C1Console.Elements.ElementProviderHelpers.AssociatedDataElementProviderHelper { - /// - /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityAncestorProvider(typeof(AssociatedDataElementProviderHelperSecurityAncestorProvider))] public sealed class AssociatedDataElementProviderHelperEntityToken : EntityToken { - private string _type; - private string _providerName; - private string _id; + private readonly string _providerName; private int _hashCode = 0; /// + [JsonConstructor] public AssociatedDataElementProviderHelperEntityToken(string type, string providerName, string id, string payload) { - _type = type; + Type = type; _providerName = providerName; - _id = id; + Id = id; this.Payload = payload; } /// - public override string Type - { - get { return _type; } - } + public override string Type { get; } /// - public override string Source - { - get { return _providerName; } - } + [JsonProperty(PropertyName = "providerName")] + public override string Source => _providerName; /// - public override string Id - { - get { return _id; } - } + public override string Id { get; } /// @@ -99,25 +89,36 @@ public IEnumerable GetDataList() /// public override string Serialize() { - StringBuilder sb = new StringBuilder(); - - DoSerialize(sb); - - StringConversionServices.SerializeKeyValuePair(sb, "_Payload_", this.Payload); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = CompositeJsonSerializer + .Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(AssociatedDataElementProviderHelperEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; Dictionary dic; DoDeserialize(serializedEntityToken, out type, out source, out id, out dic); - if (dic.ContainsKey("_Payload_") == false) + if (dic.ContainsKey("_Payload_") == false) { throw new ArgumentException("The serializedEntityToken is not a serialized entity token", "serializedEntityToken"); } @@ -127,7 +128,6 @@ public static EntityToken Deserialize(string serializedEntityToken) return new AssociatedDataElementProviderHelperEntityToken(type, source, id, payload); } - /// public override bool Equals(object obj) { diff --git a/Composite/C1Console/Elements/ElementProviderHelpers/DataGroupingProviderHelper/DataGroupingProviderHelper.cs b/Composite/C1Console/Elements/ElementProviderHelpers/DataGroupingProviderHelper/DataGroupingProviderHelper.cs index c3767d6cae..b9896edcec 100644 --- a/Composite/C1Console/Elements/ElementProviderHelpers/DataGroupingProviderHelper/DataGroupingProviderHelper.cs +++ b/Composite/C1Console/Elements/ElementProviderHelpers/DataGroupingProviderHelper/DataGroupingProviderHelper.cs @@ -41,6 +41,24 @@ public DataGroupingProviderHelper(ElementProviderContext elementProviderContext) AuxiliarySecurityAncestorFacade.AddAuxiliaryAncestorProvider(this); AuxiliarySecurityAncestorFacade.AddAuxiliaryAncestorProvider(this); + + DataEventSystemFacade.SubscribeToDataAfterUpdate(typeof(IData), (sender, args) => + { + if (!OnOwnsType(args.DataType)) return; + + var dataTypeDescriptor = DynamicTypeManager.GetDataTypeDescriptor(args.DataType); + + IEnumerable groupingDataFieldDescriptors = + from dfd in dataTypeDescriptor.Fields + where dfd.GroupByPriority != 0 + orderby dfd.GroupByPriority + select dfd; + + if (groupingDataFieldDescriptors.Any()) + { + EntityTokenCacheFacade.ClearCache(args.Data.GetDataEntityToken()); + } + }, false); } @@ -63,8 +81,7 @@ public Dictionary> GetParents(IEnumerable< foreach (EntityToken entityToken in entityTokens) { - var groupingEntityToken = entityToken as DataGroupingProviderHelperEntityToken; - if (groupingEntityToken != null) + if (entityToken is DataGroupingProviderHelperEntityToken groupingEntityToken) { var parent = GetGroupingEntityTokenParent(groupingEntityToken); @@ -76,8 +93,7 @@ public Dictionary> GetParents(IEnumerable< } - var dataEntityToken = entityToken as DataEntityToken; - if (dataEntityToken != null) + if (entityToken is DataEntityToken dataEntityToken) { var parent = GetDataEntityTokenParent(dataEntityToken); if (parent != null) @@ -99,9 +115,11 @@ private EntityToken GetGroupingEntityTokenParent(DataGroupingProviderHelperEntit return OnGetRootParentEntityToken(type, groupingEntityToken); } - var newGroupingParentEntityToken = new DataGroupingProviderHelperEntityToken(groupingEntityToken.Type); - newGroupingParentEntityToken.Payload = this.OnGetPayload(groupingEntityToken); - newGroupingParentEntityToken.GroupingValues = new Dictionary(); + var newGroupingParentEntityToken = new DataGroupingProviderHelperEntityToken(groupingEntityToken.Type) + { + Payload = this.OnGetPayload(groupingEntityToken), + GroupingValues = new Dictionary() + }; foreach (var kvp in groupingEntityToken.GroupingValues.Take(groupingEntityToken.GroupingValues.Count - 1)) { newGroupingParentEntityToken.GroupingValues.Add(kvp.Key, NormalizeGroupingValue(kvp.Value)); @@ -133,9 +151,11 @@ orderby dfd.GroupByPriority IData data = dataEntityToken.Data; - var parentToken = new DataGroupingProviderHelperEntityToken(dataEntityToken.Type); - parentToken.Payload = this.OnGetPayload(dataEntityToken); - parentToken.GroupingValues = new Dictionary(); + var parentToken = new DataGroupingProviderHelperEntityToken(dataEntityToken.Type) + { + Payload = this.OnGetPayload(dataEntityToken), + GroupingValues = new Dictionary() + }; foreach (DataFieldDescriptor dfd in groupingDataFieldDescriptors) { PropertyInfo propertyInfo = interfaceType.GetPropertiesRecursively().Single(f => f.Name == dfd.Name); @@ -149,7 +169,7 @@ orderby dfd.GroupByPriority private static object NormalizeGroupingValue(object value) { - return value is DateTime ? ((DateTime)value).Date : value; + return (value as DateTime?)?.Date ?? value; } @@ -212,7 +232,7 @@ orderby dfd.GroupByPriority bool listingLimitReached = elements.Count == MaxElementsToShow; var labelFieldDescriptor = dataTypeDescriptor.Fields.FirstOrDefault(f => f.Name == dataTypeDescriptor.LabelFieldName); - if (labelFieldDescriptor != null && labelFieldDescriptor.ForeignKeyReferenceTypeName != null && labelFieldDescriptor.TreeOrderingProfile.OrderPriority.HasValue) + if (labelFieldDescriptor?.ForeignKeyReferenceTypeName != null && labelFieldDescriptor.TreeOrderingProfile.OrderPriority.HasValue) { elements = (labelFieldDescriptor.TreeOrderingProfile.OrderDescending ? elements.OrderByDescending(f => f.VisualData.Label) : @@ -243,7 +263,7 @@ orderby dfd.GroupByPriority private IEnumerable GetRootGroupFolders(Type interfaceType, EntityToken parentEntityToken, DataFieldDescriptor firstDataFieldDescriptor, PropertyInfo propertyInfo) { - Func filter = (this.OnGetLeafsFilter != null) ? this.OnGetLeafsFilter(parentEntityToken) : null; + Func filter = OnGetLeafsFilter?.Invoke(parentEntityToken); IQueryable queryable = GetFilteredData(interfaceType, filter); @@ -295,7 +315,7 @@ public static IQueryable OrderData(IQueryable source, Type interfaceType) private IEnumerable GetRootGroupFoldersFoldersLeafs(Type interfaceType, Func filter, bool isForeign) { - Func func = (isForeign ? OnCreateGhostedLeafElement : OnCreateLeafElement); + Func func = isForeign ? OnCreateGhostedLeafElement : OnCreateLeafElement; IQueryable source = DataFacade.GetData(interfaceType); @@ -372,7 +392,7 @@ public IEnumerable GetGroupChildren(DataGroupingProviderHelperEntityTok { // Grouping ordering has ben changed, at the moment the best thing we can do its to return no elements // TODO: This class and the whole attach element provider stuff should be redone - return new Element[] { }; + return Enumerable.Empty(); } Func filter = null; @@ -415,7 +435,7 @@ public IEnumerable GetGroupChildren(DataGroupingProviderHelperEntityTok if (!dataTypeDescriptor.Fields.Any(f => f.TreeOrderingProfile.OrderPriority.HasValue && f.ForeignKeyReferenceTypeName == null)) { var labelFieldDescriptor = dataTypeDescriptor.Fields.FirstOrDefault(f => f.Name == dataTypeDescriptor.LabelFieldName); - if (labelFieldDescriptor != null && labelFieldDescriptor.ForeignKeyReferenceTypeName != null) + if (labelFieldDescriptor?.ForeignKeyReferenceTypeName != null) { elements = (labelFieldDescriptor.TreeOrderingProfile.OrderDescending ? elements.OrderByDescending(f => f.VisualData.Label) : @@ -492,10 +512,10 @@ private static IQueryable GetFilteredData(Type interfaceType, Func if (filter == null) return queryable; - var dataQueryable = (queryable as IQueryable).Where(filter).AsQueryable(); + var dataQueryable = ((IQueryable) queryable).Where(filter).AsQueryable(); return GenericCastMethodInfo - .MakeGenericMethod(new[] { interfaceType }) + .MakeGenericMethod(interfaceType) .Invoke(null, new object[] { dataQueryable }) as IQueryable; } @@ -530,9 +550,11 @@ private IEnumerable CreateGroupFolderElements(Type interfaceType, DataF foreach (object obj in queryable) { - var entityToken = new DataGroupingProviderHelperEntityToken(TypeManager.SerializeType(interfaceType)); - entityToken.Payload = this.OnGetPayload(parentEntityToken); - entityToken.GroupingValues = new Dictionary(); + var entityToken = new DataGroupingProviderHelperEntityToken(TypeManager.SerializeType(interfaceType)) + { + Payload = this.OnGetPayload(parentEntityToken), + GroupingValues = new Dictionary() + }; foreach (var kvp in propertyInfoValueCollection.PropertyValues) { @@ -541,13 +563,12 @@ private IEnumerable CreateGroupFolderElements(Type interfaceType, DataF entityToken.GroupingValues.Add(propertyInfo.Name, obj); - Element element = new Element(_elementProviderContext.CreateElementHandle(entityToken)); + var element = new Element(_elementProviderContext.CreateElementHandle(entityToken)); - string label = (obj == null ? string.Format(_undefinedLabelValue, dataFieldDescriptor.Name) : obj.ToString()); - if (obj is DateTime) + string label = obj?.ToString() ?? string.Format(_undefinedLabelValue, dataFieldDescriptor.Name); + if (obj is DateTime dt) { - DateTime dt = (DateTime)obj; label = dt.ToString("yyyy-MM-dd"); } @@ -589,7 +610,7 @@ private static void ValidateGroupByPriorities(Type interfaceType, IEnumerable - public override string Type - { - get { return _serializedParentEntityToken; } - } + public override string Type => _serializedParentEntityToken; /// - public override string Source - { - get { return ""; } - } + public override string Source => ""; /// - public override string Id - { - get { return ""; } - } + public override string Id => ""; /// + [JsonIgnore] public EntityToken ParentEntityToken { get diff --git a/Composite/C1Console/Elements/Foundation/ElementAttachingProviderFacade.cs b/Composite/C1Console/Elements/Foundation/ElementAttachingProviderFacade.cs index 8fd59f03ac..a52d0d5544 100644 --- a/Composite/C1Console/Elements/Foundation/ElementAttachingProviderFacade.cs +++ b/Composite/C1Console/Elements/Foundation/ElementAttachingProviderFacade.cs @@ -1,10 +1,9 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Text; using Composite.C1Console.Security; using Composite.C1Console.Elements.Foundation.PluginFacades; using Composite.C1Console.Elements.Plugins.ElementAttachingProvider; +using Composite.Core.Extensions; namespace Composite.C1Console.Elements.Foundation { @@ -28,16 +27,16 @@ public static bool HaveCustomChildElements(EntityToken parentEntityToken, Dictio public static IEnumerable AttachElements(EntityToken parentEntityToken, Dictionary piggybag, IEnumerable currentElements) { - List topResults = new List(); - List bottomResults = new List(); + var topResults = new List(); + var bottomResults = new List(); foreach (string providerName in ElementAttachingProviderRegistry.ElementAttachingProviderNames) { - if (ElementAttachingProviderPluginFacade.IsMultibleResultElementAttachingProvider(providerName) == false) + if (!ElementAttachingProviderPluginFacade.IsMultipleResultElementAttachingProvider(providerName)) { - ElementAttachingProviderResult result = ElementAttachingProviderPluginFacade.GetAlternateElementList(providerName, parentEntityToken, piggybag); + var result = ElementAttachingProviderPluginFacade.GetAlternateElementList(providerName, parentEntityToken, piggybag); - if ((result == null) || (result.Elements == null)) continue; + if (result?.Elements == null) continue; if (result.Position == ElementAttachingProviderPosition.Top) { @@ -50,13 +49,13 @@ public static IEnumerable AttachElements(EntityToken parentEntityToken, } else { - IEnumerable results = ElementAttachingProviderPluginFacade.GetAlternateElementLists(providerName, parentEntityToken, piggybag); + var results = ElementAttachingProviderPluginFacade.GetAlternateElementLists(providerName, parentEntityToken, piggybag); if (results == null) continue; foreach (ElementAttachingProviderResult result in results) { - if ((result == null) || (result.Elements == null)) continue; + if (result?.Elements == null) continue; if (result.Position == ElementAttachingProviderPosition.Top) { @@ -70,55 +69,30 @@ public static IEnumerable AttachElements(EntityToken parentEntityToken, } } - Comparison sortMethod = delegate(ElementAttachingProviderResult r1, ElementAttachingProviderResult r2) { return r2.PositionPriority - r1.PositionPriority; }; + Comparison sortMethod = + (r1, r2) => r2.PositionPriority - r1.PositionPriority; + topResults.Sort(sortMethod); bottomResults.Sort(sortMethod); IEnumerable topElements = null; - foreach (ElementAttachingProviderResult result in topResults) + foreach (var result in topResults) { - if (topElements == null) - { - topElements = result.Elements; - } - else - { - topElements = topElements.Concat(result.Elements); - } + topElements = topElements.ConcatOrDefault(result.Elements); } IEnumerable bottomElements = null; - foreach (ElementAttachingProviderResult result in bottomResults) + foreach (var result in bottomResults) { - if (bottomElements == null) - { - bottomElements = result.Elements; - } - else - { - bottomElements = bottomElements.Concat(result.Elements); - } + bottomElements = bottomElements.ConcatOrDefault(result.Elements); } - IEnumerable resultElements = topElements; - - if (resultElements == null) - { - resultElements = currentElements; - } - else - { - resultElements = resultElements.Concat(currentElements); - } - - if (bottomElements != null) - { - resultElements = resultElements.Concat(bottomElements); - } - return resultElements; + return topElements + .ConcatOrDefault(currentElements) + .ConcatOrDefault(bottomElements); } } } diff --git a/Composite/C1Console/Elements/Foundation/PluginFacades/ElementAttachingProviderPluginFacade.cs b/Composite/C1Console/Elements/Foundation/PluginFacades/ElementAttachingProviderPluginFacade.cs index 1d82d563f3..8ba44fe9f2 100644 --- a/Composite/C1Console/Elements/Foundation/PluginFacades/ElementAttachingProviderPluginFacade.cs +++ b/Composite/C1Console/Elements/Foundation/PluginFacades/ElementAttachingProviderPluginFacade.cs @@ -52,7 +52,7 @@ public static IEnumerable GetAlternateElementLis { Verify.ArgumentNotNullOrEmpty(providerName, "providerName"); - IMultibleResultElementAttachingProvider elementAttachingProvider = (IMultibleResultElementAttachingProvider)GetElementAttachingProvider(providerName); + var elementAttachingProvider = (IMultipleResultElementAttachingProvider)GetElementAttachingProvider(providerName); IEnumerable result = elementAttachingProvider.GetAlternateElementLists(parentEntityToken, piggybag); @@ -74,9 +74,9 @@ public static IEnumerable GetChildren(string providerName, EntityToken - public static bool IsMultibleResultElementAttachingProvider(string providerName) + public static bool IsMultipleResultElementAttachingProvider(string providerName) { - return GetElementAttachingProvider(providerName) is IMultibleResultElementAttachingProvider; + return GetElementAttachingProvider(providerName) is IMultipleResultElementAttachingProvider; } @@ -135,7 +135,7 @@ private static void HandleConfigurationError(Exception ex) { Flush(); - throw new ConfigurationErrorsException(string.Format("Failed to load the configuration section '{0}' from the configuration.", ElementAttachingProviderSettings.SectionName), ex); + throw new ConfigurationErrorsException($"Failed to load the configuration section '{ElementAttachingProviderSettings.SectionName}' from the configuration.", ex); } diff --git a/Composite/C1Console/Elements/Foundation/PluginFacades/ElementProviderPluginFacade.cs b/Composite/C1Console/Elements/Foundation/PluginFacades/ElementProviderPluginFacade.cs index 3c1f463d84..7c0e8bb00f 100644 --- a/Composite/C1Console/Elements/Foundation/PluginFacades/ElementProviderPluginFacade.cs +++ b/Composite/C1Console/Elements/Foundation/PluginFacades/ElementProviderPluginFacade.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Configuration; +using System.Linq; using System.Xml; using Composite.C1Console.Actions; using Composite.Core.Collections.Generic; @@ -20,52 +21,45 @@ internal static class ElementProviderPluginFacade static ElementProviderPluginFacade() { - GlobalEventSystemFacade.SubscribeToFlushEvent(OnFlushEvent); + GlobalEventSystemFacade.SubscribeToFlushEvent(args => Flush()); } public static IEnumerable GetRoots(string providerName, SearchToken seachToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); IEnumerable roots = GetElementProvider(providerName).GetRoots(seachToken); - if (roots == null) return new List(); - - return roots; + return roots ?? Enumerable.Empty(); } public static IEnumerable GetChildren(string providerName, EntityToken entityToken, SearchToken seachToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); IEnumerable children = GetElementProvider(providerName).GetChildren(entityToken, seachToken); - if (children == null) return new List(); - - return children; + return children ?? Enumerable.Empty(); } public static IEnumerable GetLabeledProperties(string providerName, EntityToken entityToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); ILabeledPropertiesElementProvider labledElementProvider = GetElementProvider(providerName) as ILabeledPropertiesElementProvider; - if (labledElementProvider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(ILabeledPropertiesElementProvider))); - + if (labledElementProvider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(ILabeledPropertiesElementProvider)}"); IEnumerable properties = labledElementProvider.GetLabeledProperties(entityToken); - if (properties == null) return new List(); - - return properties; + return properties ?? Enumerable.Empty(); } @@ -73,17 +67,15 @@ public static IEnumerable GetLabeledProperties(string providerN #pragma warning disable 612 public static List GetHooks(string providerName) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); IElementProvider provider = GetElementProvider(providerName) as IElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(IElementProvider))); + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(IElementProvider)}"); List hooks = provider.GetHooks(); - if (hooks == null) return new List(); - - return hooks; + return hooks ?? new List(); } #pragma warning restore 612 @@ -91,10 +83,10 @@ public static List GetHooks(string providerName) public static bool ContainsLocalizedData(string providerName) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); - ILocaleAwareElementProvider provider = GetElementProvider(providerName) as ILocaleAwareElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(ILocaleAwareElementProvider))); + var provider = GetElementProvider(providerName) as ILocaleAwareElementProvider; + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(ILocaleAwareElementProvider)}"); return provider.ContainsLocalizedData; } @@ -103,10 +95,10 @@ public static bool ContainsLocalizedData(string providerName) public static IEnumerable GetForeignRoots(string providerName, SearchToken seachToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); ILocaleAwareElementProvider provider = GetElementProvider(providerName) as ILocaleAwareElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(ILocaleAwareElementProvider))); + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(ILocaleAwareElementProvider)}"); return provider.GetForeignRoots(seachToken); } @@ -115,11 +107,11 @@ public static IEnumerable GetForeignRoots(string providerName, SearchTo public static IEnumerable GetForeignChildren(string providerName, EntityToken entityToken, SearchToken seachToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); - ILocaleAwareElementProvider provider = GetElementProvider(providerName) as ILocaleAwareElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(ILocaleAwareElementProvider))); + var provider = GetElementProvider(providerName) as ILocaleAwareElementProvider; + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(ILocaleAwareElementProvider)}"); return provider.GetForeignChildren(entityToken, seachToken); } @@ -128,10 +120,10 @@ public static IEnumerable GetForeignChildren(string providerName, Entit public static IEnumerable GetForeignLabeledProperties(string providerName, EntityToken entityToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); ILocaleAwareLabeledPropertiesElementProvider provider = GetElementProvider(providerName) as ILocaleAwareLabeledPropertiesElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(ILocaleAwareElementProvider))); + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(ILocaleAwareElementProvider)}"); return provider.GetForeignLabeledProperties(entityToken); } @@ -140,11 +132,11 @@ public static IEnumerable GetForeignLabeledProperties(string pr public static object GetData(string providerName, string dataName) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (string.IsNullOrEmpty(dataName)) throw new ArgumentNullException("dataName"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNullOrEmpty(dataName, nameof(dataName)); IDataExchangingElementProvider provider = GetElementProvider(providerName) as IDataExchangingElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified by the specified provider name does not implement {0}", typeof(IDataExchangingElementProvider))); + if (provider == null) throw new ArgumentException($"The Element Provider identified by the specified provider name does not implement {typeof(IDataExchangingElementProvider)}"); return provider.GetData(dataName); } @@ -153,10 +145,10 @@ public static object GetData(string providerName, string dataName) public static bool GetNewSearchToken(string providerName, EntityToken entityToken, out SearchToken searchToken) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); - ICustomSearchElementProvider provider = GetElementProvider(providerName) as ICustomSearchElementProvider; + var provider = GetElementProvider(providerName) as ICustomSearchElementProvider; if (provider == null) { @@ -172,10 +164,10 @@ public static bool GetNewSearchToken(string providerName, EntityToken entityToke public static bool GetSearchFormDefinition(string providerName, EntityToken entityToken, out XmlReader formDefinition) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); - ICustomSearchElementProvider provider = GetElementProvider(providerName) as ICustomSearchElementProvider; + var provider = GetElementProvider(providerName) as ICustomSearchElementProvider; if (provider == null) { @@ -191,10 +183,10 @@ public static bool GetSearchFormDefinition(string providerName, EntityToken enti public static bool GetSearchFormBindings(string providerName, EntityToken entityToken, out Dictionary bindings) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); - ICustomSearchElementProvider provider = GetElementProvider(providerName) as ICustomSearchElementProvider; + var provider = GetElementProvider(providerName) as ICustomSearchElementProvider; if (provider == null) { @@ -211,13 +203,13 @@ public static bool GetSearchFormBindings(string providerName, EntityToken entity public static bool OnElementDraggedAndDropped(string providerName, EntityToken draggedEntityToken, EntityToken newParentEntityToken, int dropIndex, DragAndDropType dragAndDropType, FlowControllerServicesContainer draggedElementFlowControllerServicesContainer) { - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (draggedEntityToken == null) throw new ArgumentNullException("draggedEntityToken"); - if (newParentEntityToken == null) throw new ArgumentNullException("newParentEntityToken"); - if (draggedElementFlowControllerServicesContainer == null) throw new ArgumentNullException("draggedElementFlowControllerServicesContainer"); + Verify.ArgumentNotNullOrEmpty(providerName, nameof(providerName)); + Verify.ArgumentNotNull(draggedEntityToken, nameof(draggedEntityToken)); + Verify.ArgumentNotNull(newParentEntityToken, nameof(newParentEntityToken)); + Verify.ArgumentNotNull(draggedElementFlowControllerServicesContainer, nameof(draggedElementFlowControllerServicesContainer)); - IDragAndDropElementProvider provider = GetElementProvider(providerName) as IDragAndDropElementProvider; - if (provider == null) throw new ArgumentException(string.Format("The Element Provider identified byu the specified provider name does not implement {0}", typeof(IDragAndDropElementProvider))); + var provider = GetElementProvider(providerName) as IDragAndDropElementProvider; + if (provider == null) throw new ArgumentException($"The Element Provider identified byu the specified provider name does not implement {typeof(IDragAndDropElementProvider)}"); return provider.OnElementDraggedAndDropped(draggedEntityToken, newParentEntityToken, dropIndex, dragAndDropType, draggedElementFlowControllerServicesContainer); } @@ -226,7 +218,7 @@ public static bool OnElementDraggedAndDropped(string providerName, EntityToken d public static bool IsLocaleAwareElementProvider(string providerName) { - ILocaleAwareElementProvider elementProvider = GetElementProvider(providerName) as ILocaleAwareElementProvider; + var elementProvider = GetElementProvider(providerName) as ILocaleAwareElementProvider; return elementProvider != null; } @@ -237,7 +229,7 @@ internal static IHooklessElementProvider GetElementProvider(string providerName) { IHooklessElementProvider provider; - if (_resourceLocker.Resources.ProviderCache.TryGetValue(providerName, out provider) == false) + if (!_resourceLocker.Resources.ProviderCache.TryGetValue(providerName, out provider)) { try { @@ -254,7 +246,7 @@ internal static IHooklessElementProvider GetElementProvider(string providerName) using (_resourceLocker.Locker) { - if (_resourceLocker.Resources.ProviderCache.ContainsKey(providerName) == false) + if (!_resourceLocker.Resources.ProviderCache.ContainsKey(providerName)) { _resourceLocker.Resources.ProviderCache.Add(providerName, provider); } @@ -283,28 +275,21 @@ private static void Flush() - private static void OnFlushEvent(FlushEventArgs args) - { - Flush(); - } - - - private static void HandleConfigurationError(Exception ex) { Flush(); - throw new ConfigurationErrorsException(string.Format("Failed to load the configuration section '{0}' from the configuration.", ElementProviderSettings.SectionName), ex); + throw new ConfigurationErrorsException($"Failed to load the configuration section '{ElementProviderSettings.SectionName}' from the configuration.", ex); } private sealed class Resources { - public ElementProviderFactory Factory { get; set; } - public HooklessElementProviderFactory HooklessFactory { get; set; } + public ElementProviderFactory Factory { get; private set; } + public HooklessElementProviderFactory HooklessFactory { get; private set; } - public Dictionary ProviderCache { get; set; } + public Dictionary ProviderCache { get; private set; } public static void Initialize(Resources resources) { diff --git a/Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultibleResultElementAttachingProvider.cs b/Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultipleResultElementAttachingProvider.cs similarity index 87% rename from Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultibleResultElementAttachingProvider.cs rename to Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultipleResultElementAttachingProvider.cs index 912bfe8b5c..728ef84aad 100644 --- a/Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultibleResultElementAttachingProvider.cs +++ b/Composite/C1Console/Elements/Plugins/ElementAttachingProvider/IMultipleResultElementAttachingProvider.cs @@ -4,7 +4,7 @@ namespace Composite.C1Console.Elements.Plugins.ElementAttachingProvider { - internal interface IMultibleResultElementAttachingProvider : IElementAttachingProvider + internal interface IMultipleResultElementAttachingProvider : IElementAttachingProvider { /// /// If null is returned, the result is ignored diff --git a/Composite/C1Console/Elements/SearchToken.cs b/Composite/C1Console/Elements/SearchToken.cs index ac3363d656..05d7d47be3 100644 --- a/Composite/C1Console/Elements/SearchToken.cs +++ b/Composite/C1Console/Elements/SearchToken.cs @@ -1,4 +1,4 @@ -using System; +using System; using Composite.Core.Extensions; using Composite.Core.Serialization; using Composite.Core.Types; @@ -22,12 +22,7 @@ public class SearchToken /// String representation public string Serialize() { - string serializedSearchToken = SerializationFacade.Serialize(this); - string serializedClassName = TypeManager.SerializeType(this.GetType()); - - string serializedSearchTokenWithClass = string.Format("{0}|{1}", serializedClassName, serializedSearchToken); - - return serializedSearchTokenWithClass; + return CompositeJsonSerializer.SerializeObject(this); } @@ -38,8 +33,20 @@ public string Serialize() /// Deserialized SearchToken public static SearchToken Deserialize( string serializedSearchToken ) { - Verify.ArgumentNotNullOrEmpty("serializedSearchToken", serializedSearchToken); - Verify.ArgumentCondition(serializedSearchToken.IndexOf('|') > -1, "serializedSearchToken", "Malformed serializedSearchToken - must be formated like '|'"); + Verify.ArgumentNotNullOrEmpty(serializedSearchToken, nameof(serializedSearchToken)); + + if (serializedSearchToken.StartsWith("{")) + { + return CompositeJsonSerializer.Deserialize(serializedSearchToken); + } + + return DeserializeLegacy(serializedSearchToken); + } + + private static SearchToken DeserializeLegacy(string serializedSearchToken) + { + Verify.ArgumentNotNullOrEmpty(serializedSearchToken, nameof(serializedSearchToken)); + Verify.ArgumentCondition(serializedSearchToken.IndexOf('|') > -1, nameof(serializedSearchToken), "Malformed serializedSearchToken - must be formated like '|'"); string[] parts = serializedSearchToken.Split('|'); diff --git a/Composite/C1Console/Elements/Security/ElementSecurityFacade.cs b/Composite/C1Console/Elements/Security/ElementSecurityFacade.cs deleted file mode 100644 index d6190b7583..0000000000 --- a/Composite/C1Console/Elements/Security/ElementSecurityFacade.cs +++ /dev/null @@ -1,60 +0,0 @@ -//#warning REMARK THIS!!! -//#define NO_SECURITY -using System; -using System.Collections.Generic; -using Composite.C1Console.Security; -using Composite.Core.Logging; -using Composite.Plugins.Elements.ElementProviders.PageElementProvider; - - -namespace Composite.C1Console.Elements.Security -{ - internal static class ElementSecurityFacade - { - public static IEnumerable FilterActions(this IEnumerable elements) - { - if (elements == null) throw new ArgumentNullException("elements"); - -#if NO_SECURITY - return elements; -#else - UserToken userToken = UserValidationFacade.GetUserToken(); - - IEnumerable userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(userToken.Username); - IEnumerable userGroupPermissionDefinition = PermissionTypeFacade.GetUserGroupPermissionDefinitions(userToken.Username); - - foreach (Element element in elements) - { - if (PermissionTypeFacade.IsSubBrachContainingPermissionTypes(userToken, element.ElementHandle.EntityToken, userPermissionDefinitions, userGroupPermissionDefinition)) - { - - List actionsToRemove = new List(); - foreach (ElementAction elementAction in element.Actions) - { - if (SecurityResolver.Resolve(userToken, elementAction.ActionHandle.ActionToken, element.ElementHandle.EntityToken, userPermissionDefinitions, userGroupPermissionDefinition) == SecurityResult.Disallowed) - { - actionsToRemove.Add(elementAction); - } - } - - foreach (ElementAction elementAction in actionsToRemove) - { - element.RemoveAction(elementAction); - } - - // Drag & drop security - if (element.MovabilityInfo != null) - { - if (SecurityResolver.Resolve(userToken, new DragAndDropActionToken(), element.ElementHandle.EntityToken, userPermissionDefinitions, userGroupPermissionDefinition) == SecurityResult.Disallowed) - { - element.RemoveMovabilityInfo(); - } - } - - yield return element; - } - } -#endif - } - } -} 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/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/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/AuxiliarySecurityAncestorFacade.cs b/Composite/C1Console/Security/AuxiliarySecurityAncestorFacade.cs index 7e3c062075..0cc50fd7c8 100644 --- a/Composite/C1Console/Security/AuxiliarySecurityAncestorFacade.cs +++ b/Composite/C1Console/Security/AuxiliarySecurityAncestorFacade.cs @@ -16,7 +16,7 @@ public static class AuxiliarySecurityAncestorFacade static AuxiliarySecurityAncestorFacade() { - GlobalEventSystemFacade.SubscribeToFlushEvent(OnFlushEvent); + GlobalEventSystemFacade.SubscribeToFlushEvent(args => _implementation.Flush()); } @@ -27,7 +27,7 @@ static AuxiliarySecurityAncestorFacade() /// public static IEnumerable GetParents(EntityToken entityToken) { - return _implementation.GetParents(entityToken); + return _implementation.GetParents(entityToken); } @@ -90,19 +90,5 @@ public static IEnumerable GetAuxiliaryAncest { return _implementation.GetAuxiliaryAncestorProviders(entityTokenType); } - - - - private static void Flush() - { - _implementation.Flush(); - } - - - - private static void OnFlushEvent(FlushEventArgs args) - { - Flush(); - } } } diff --git a/Composite/C1Console/Security/AuxiliarySecurityAncestorFacadeImpl.cs b/Composite/C1Console/Security/AuxiliarySecurityAncestorFacadeImpl.cs index 96f5c4ddec..7dedcca632 100644 --- a/Composite/C1Console/Security/AuxiliarySecurityAncestorFacadeImpl.cs +++ b/Composite/C1Console/Security/AuxiliarySecurityAncestorFacadeImpl.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Composite.Core.Extensions; -using Composite.Core.Linq; namespace Composite.C1Console.Security @@ -15,7 +14,7 @@ internal sealed class AuxiliarySecurityAncestorFacadeImpl : IAuxiliarySecurityAn public IEnumerable GetParents(EntityToken entityToken) { - if (entityToken == null) throw new ArgumentNullException("entityToken"); + Verify.ArgumentNotNull(entityToken, nameof(entityToken)); List auxiliarySecurityAncestorProviders; @@ -32,20 +31,11 @@ public IEnumerable GetParents(EntityToken entityToken) IEnumerable totalResult = null; foreach (IAuxiliarySecurityAncestorProvider auxiliarySecurityAncestorProvider in resultSecurityAncestorProviders) { - Dictionary> result = auxiliarySecurityAncestorProvider.GetParents(new EntityToken[] { entityToken }); + var result = auxiliarySecurityAncestorProvider.GetParents(new [] { entityToken }); if (result.Count > 0) { - var firstValue = result.Values.First(); - - if (totalResult == null) - { - totalResult = firstValue; - } - else - { - totalResult = totalResult.Concat(firstValue); - } + totalResult = totalResult.ConcatOrDefault(result.Values.First()); } } @@ -56,19 +46,13 @@ public IEnumerable GetParents(EntityToken entityToken) public void AddAuxiliaryAncestorProvider(Type entityTokenType, IAuxiliarySecurityAncestorProvider auxiliarySecurityAncestorProvider, bool flushPersistent) { - Dictionary> providers; - if (!flushPersistent) - { - providers = _auxiliarySecurityAncestorProviders; - } - else - { - providers = _flushPersistentAuxiliarySecurityAncestorProviders; - } + var providers = !flushPersistent + ? _auxiliarySecurityAncestorProviders + : _flushPersistentAuxiliarySecurityAncestorProviders; List auxiliarySecurityAncestorProviders; - if (providers.TryGetValue(entityTokenType, out auxiliarySecurityAncestorProviders) == false) + if (!providers.TryGetValue(entityTokenType, out auxiliarySecurityAncestorProviders)) { auxiliarySecurityAncestorProviders = new List(); providers.Add(entityTokenType, auxiliarySecurityAncestorProviders); @@ -76,7 +60,7 @@ public void AddAuxiliaryAncestorProvider(Type entityTokenType, IAuxiliarySecurit if (auxiliarySecurityAncestorProviders.Contains(auxiliarySecurityAncestorProvider)) { - throw new ArgumentNullException("The given auxiliarySecurityAncestorProvider has already been added with the given entity token"); + throw new ArgumentException("The given provider has already been added with the given entity token", nameof(auxiliarySecurityAncestorProvider)); } auxiliarySecurityAncestorProviders.Add(auxiliarySecurityAncestorProvider); @@ -121,8 +105,6 @@ public IEnumerable GetAuxiliaryAncestorProvi yield return auxiliarySecurityAncestorProvider; } } - - yield break; } diff --git a/Composite/C1Console/Security/Compatibility/LegacySerializedEntityTokenUpgrader.cs b/Composite/C1Console/Security/Compatibility/LegacySerializedEntityTokenUpgrader.cs new file mode 100644 index 0000000000..4259c18d18 --- /dev/null +++ b/Composite/C1Console/Security/Compatibility/LegacySerializedEntityTokenUpgrader.cs @@ -0,0 +1,208 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml.Linq; +using Composite.Core.Application; +using Composite.Core.Configuration; +using Composite.Core.IO; +using Composite.Core.Logging; +using Composite.Data; +using Composite.Data.DynamicTypes; + +namespace Composite.C1Console.Security.Compatibility +{ + /// + /// Will auto upgrade serialized entity tokens and data ids, stored in c1 data. Needed because serialization has changed. + /// + [ApplicationStartup(AbortStartupOnException = false)] + public static class LegacySerializedEntityTokenUpgrader + { + const string _configFilename = "LegacySerializedEntityTokenUpgrader.config"; + static readonly XName _docRoot = "UpgradeSettings"; + private static ILog _log; + + /// + /// See class description - this allow us to run on startup + /// + /// + public static void OnInitialized(ILog log) + { + if (ShouldRun) + { + _log = log; + try + { + Run(); + } + catch (Exception ex) + { + _log.LogError(nameof(LegacySerializedEntityTokenUpgrader), ex); + + throw; + } + } + + } + + private static void Run() + { + UpgradeStoredData(); + + var config = GetConfigElement(); + + config.SetAttributeValue("last-run", DateTime.Now); + config.SetAttributeValue("completed", true); + + config.Save(ConfigFilePath); + } + + private static bool ShouldRun + { + get + { + var _xConfig = GetConfigElement(); + return true == (bool)_xConfig.Attribute("enabled") && + (false == (bool)_xConfig.Attribute("completed") || + true == (bool)_xConfig.Attribute("force") || + (DateTime)_xConfig.Attribute("last-run") - (DateTime)_xConfig.Attribute("inception") < TimeSpan.FromMinutes(5)); + } + } + + + private static string ConfigFilePath + { + get + { + var configDir = PathUtil.Resolve(GlobalSettingsFacade.ConfigurationDirectory); + var configFilePath = Path.Combine(configDir, _configFilename); + + return configFilePath; + } + } + + private static XElement GetConfigElement() + { + XDocument config = null; + + if (File.Exists(ConfigFilePath)) + { + config = XDocument.Load(ConfigFilePath); + } + else + { + config = new XDocument( + new XElement(_docRoot, + new XAttribute("inception", DateTime.Now), + new XAttribute("force", false), + new XAttribute("completed", false), + new XAttribute("enabled", true))); + } + + return config.Root; + } + + + private static void UpgradeStoredData() + { + const string _ET = "EntityToken"; + const string _DSI = "DataSourceId"; + + List magicPropertyNames = new List { _ET, _DSI }; + Func isSerializedFieldFunc = g => magicPropertyNames.Any(s => g.Name.Contains(s)); + var descriptors = DataMetaDataFacade.AllDataTypeDescriptors.Where(f => f.Fields.Any(isSerializedFieldFunc)); + + foreach (var descriptor in descriptors) + { + Type dataType = descriptor.GetInterfaceType(); + + if (dataType == null) + { + continue; + } + + var propertiesToUpdate = new List(); + foreach (var tokenField in descriptor.Fields.Where(isSerializedFieldFunc)) + { + var tokenProperty = dataType.GetProperty(tokenField.Name); + propertiesToUpdate.Add(tokenProperty); + } + + using (var dc = new DataConnection(PublicationScope.Unpublished)) + { + var allRows = DataFacade.GetData(dataType).ToDataList(); + + foreach (var rowItem in allRows) + { + bool rowChange = false; + + foreach (var tokenProperty in propertiesToUpdate) + { + string token = tokenProperty.GetValue(rowItem) as string; + + if (tokenProperty.Name.Contains(_ET)) + { + try + { + var entityToken = EntityTokenSerializer.Deserialize(token); + var tokenReserialized = EntityTokenSerializer.Serialize(entityToken); + + if (tokenReserialized != token) + { + tokenProperty.SetValue(rowItem, tokenReserialized); + rowChange = true; + } + } + catch (Exception ex) + { + _log.LogError(nameof(LegacySerializedEntityTokenUpgrader), "Failed to upgrade old token {0} from data type {1} as EntityToken.\n{2}", token, dataType.FullName, ex); + } + } + + if (tokenProperty.Name.Contains(_DSI)) + { + try + { + token = EnsureValidDataSourceId(token); + var dataSourceId = DataSourceId.Deserialize(token); + var dataSourceIdReserialized = dataSourceId.Serialize(); + + if (dataSourceIdReserialized != token) + { + tokenProperty.SetValue(rowItem, dataSourceIdReserialized); + rowChange = true; + } + } + catch (Exception ex) + { + _log.LogError(nameof(LegacySerializedEntityTokenUpgrader), "Failed to upgrade old token {0} from data type {1} as DataSourceId.\n{2}", token, dataType.FullName, ex); + } + } + + if (rowChange) DataFacade.Update(rowItem); + } + } + } + } + } + + private static string EnsureValidDataSourceId(string token) + { + try + { + // fixing specific data inconsistency, where old serialized data id's for versioned data do not reflect versionid property added in a later version + if (token.Contains("Composite_Data_Types_IPagePlaceholderContentDataId") && !token.Contains("VersionId")) + { + var pageId = Guid.Parse(token.Substring(19, 36)); + token = token.Replace("'_dataIdType_", String.Format(",\\ VersionId=\\'{0}\\''_dataIdType_", pageId)); + } + } + catch(Exception) { + // if we have an issue, caller will act + } + + return token; + } + } +} diff --git a/Composite/C1Console/Security/EntityToken.cs b/Composite/C1Console/Security/EntityToken.cs index e1a1a14a29..9c1696c816 100644 --- a/Composite/C1Console/Security/EntityToken.cs +++ b/Composite/C1Console/Security/EntityToken.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using Composite.Core.Serialization; +using Newtonsoft.Json; namespace Composite.C1Console.Security @@ -70,8 +71,6 @@ public abstract class EntityToken /// a string representation of the entity token public abstract string Serialize(); - - /// protected void DoSerialize(StringBuilder stringBuilder) { @@ -80,16 +79,12 @@ protected void DoSerialize(StringBuilder stringBuilder) StringConversionServices.SerializeKeyValuePair(stringBuilder, "_EntityToken_Id_", Id); } - - /// protected string DoSerialize() { - StringBuilder sb = new StringBuilder(); - - DoSerialize(sb); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize( + new Dictionary() {{nameof(Type), Type}, + { nameof(Source), Source}, {nameof(Id), Id}}); } @@ -97,13 +92,32 @@ protected string DoSerialize() /// protected static void DoDeserialize(string serializedEntityToken, out string type, out string source, out string id) { - Dictionary dic; + if(CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + var entityToken = + CompositeJsonSerializer.Deserialize>(serializedEntityToken); + if (entityToken.ContainsKey(nameof(Type)) && + entityToken.ContainsKey(nameof(Source)) && + entityToken.ContainsKey(nameof(Id))) + { + type = entityToken[nameof(Type)]; + source = entityToken[nameof(Source)]; + id = entityToken[nameof(Id)]; + } + else + { + throw new ArgumentException("Is not a serialized entity token", nameof(serializedEntityToken)); + } + } + else + { + Dictionary dic; - DoDeserialize(serializedEntityToken, out type, out source, out id, out dic); + DoDeserialize(serializedEntityToken, out type, out source, out id, out dic); + } + } - - /// protected static void DoDeserialize(string serializedEntityToken, out string type, out string source, out string id, out Dictionary dic) { @@ -187,6 +201,7 @@ public bool EqualsWithVersionIgnore(object obj) } /// + [JsonIgnore] public virtual string VersionId { get; } = ""; diff --git a/Composite/C1Console/Security/EntityTokenSerializer.cs b/Composite/C1Console/Security/EntityTokenSerializer.cs index 29c93a3d6b..a9de2f7666 100644 --- a/Composite/C1Console/Security/EntityTokenSerializer.cs +++ b/Composite/C1Console/Security/EntityTokenSerializer.cs @@ -1,10 +1,9 @@ using System; using System.Reflection; using System.Security; -using System.Text; using Composite.Core.Serialization; using Composite.Core.Types; - +using Composite.Core; namespace Composite.C1Console.Security { @@ -27,29 +26,10 @@ public static string Serialize(EntityToken entityToken, bool includeHashValue) { Verify.ArgumentNotNull(entityToken, "entityToken"); - var sb = new StringBuilder(); - - StringConversionServices.SerializeKeyValuePair(sb, "entityTokenType", TypeManager.SerializeType(entityToken.GetType())); - - string serializedEntityToken = entityToken.Serialize(); - - if (serializedEntityToken == null) - { - throw new InvalidCastException($"'{entityToken.GetType()}' Serialize returned null"); - } - - StringConversionServices.SerializeKeyValuePair(sb, "entityToken", serializedEntityToken); - - if (includeHashValue) - { - StringConversionServices.SerializeKeyValuePair(sb, "entityTokenHash", HashSigner.GetSignedHash(serializedEntityToken).Serialize()); - } - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(entityToken, includeHashValue); } - - + /// public static EntityToken Deserialize(string serializedEntityToken) { @@ -63,9 +43,35 @@ public static EntityToken Deserialize(string serializedEntityToken, bool include { if (string.IsNullOrEmpty(serializedEntityToken)) throw new ArgumentNullException(nameof(serializedEntityToken)); + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = + CompositeJsonSerializer + .Deserialize(serializedEntityToken, + includeHashValue); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken, includeHashValue); + Log.LogVerbose(nameof(EntityTokenSerializer), entityToken.GetType().FullName); + } + + if (entityToken == null) + { + throw new EntityTokenSerializerException($"Deserialization function returned null value. EntityToken: '{serializedEntityToken}'"); + } + + return entityToken; + + + } + + private static EntityToken DeserializeLegacy(string serializedEntityToken, bool includeHashValue) + { var dic = StringConversionServices.ParseKeyValueCollection(serializedEntityToken); - if (!dic.ContainsKey("entityTokenType") || + if (!dic.ContainsKey("entityTokenType") || !dic.ContainsKey("entityToken") || (includeHashValue && !dic.ContainsKey("entityTokenHash"))) { @@ -94,7 +100,6 @@ public static EntityToken Deserialize(string serializedEntityToken, bool include throw new InvalidOperationException($"The entity token {entityType} is missing a public static Deserialize method taking a string as parameter and returning an {typeof(EntityToken)}"); } - EntityToken entityToken; try { @@ -102,19 +107,18 @@ public static EntityToken Deserialize(string serializedEntityToken, bool include } catch (Exception ex) { - throw new EntityTokenSerializerException($"Failed to deserialize entity token '{entityTokenString}'", ex); + throw new EntityTokenSerializerException($"Failed to deserialize entity token '{serializedEntityToken}'", ex); } if (entityToken == null) { - throw new EntityTokenSerializerException($"Deserialization function returned null value. EntityToken: '{entityTokenString}'"); + throw new EntityTokenSerializerException($"Deserialization function returned null value. EntityToken: '{serializedEntityToken}'"); } return entityToken; } - /// public static T Deserialize(string serializedEntityToken) where T : EntityToken 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/Security/RelationshipGraph.cs b/Composite/C1Console/Security/RelationshipGraph.cs index 153118b8b7..d81b41486e 100644 --- a/Composite/C1Console/Security/RelationshipGraph.cs +++ b/Composite/C1Console/Security/RelationshipGraph.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -6,6 +6,7 @@ using Composite.Core.Extensions; using Composite.C1Console.Security.Foundation; using Composite.C1Console.Users; +using Composite.Core.Linq; namespace Composite.C1Console.Security @@ -392,12 +393,12 @@ public override string ToString() sb.AppendLine("Level: " + level.Level); foreach (EntityToken entityToken in level.Entities) { - sb.AppendLine("Native: Type = " + entityToken.Type + " Source = " + entityToken.Source + " Id = " + entityToken.Id); + sb.AppendLine($"Native: Type = {entityToken.Type} Source = {entityToken.Source} Id = {entityToken.Id}"); } foreach (EntityToken entityToken in level.HookedEntities) { - sb.AppendLine("Hooked: Type = " + entityToken.Type + " Source = " + entityToken.Source + " Id = " + entityToken.Id); + sb.AppendLine($"Hooked: Type = {entityToken.Type} Source = {entityToken.Source} Id = {entityToken.Id}"); } sb.AppendLine("---------"); @@ -407,21 +408,14 @@ public override string ToString() } - internal int LevelCount - { - get - { - return _levels.Count; - } - } - + internal int LevelCount => _levels.Count; internal RelationshipGraphLevel GetLevel(int level) { string userName = UserValidationFacade.IsLoggedIn() ? UserSettings.Username : null; - while ((_levels.Count - 1 < level) && (_moreLevelsToExpend)) + while (_levels.Count - 1 < level && _moreLevelsToExpend) { ExpandNextLevel(userName); } @@ -462,7 +456,7 @@ private void ExpandNextLevel(string userName) IEnumerable parentEntityTokens; if (!EntityTokenCacheFacade.GetCachedNativeParents(node.EntityToken, out parentEntityTokens, userName)) { - parentEntityTokens = SecurityAncestorFacade.GetParents(node.EntityToken); + parentEntityTokens = SecurityAncestorFacade.GetParents(node.EntityToken)?.Evaluate(); EntityTokenCacheFacade.AddNativeCache(node.EntityToken, parentEntityTokens); } @@ -482,7 +476,7 @@ private void ExpandNextLevel(string userName) IEnumerable auxiliaryParentEntityTokens = AuxiliarySecurityAncestorFacade.GetParents(node.EntityToken); IEnumerable hookingParentEntityTokens = HookingFacade.GetHookies(node.EntityToken); - parentEntityTokens = auxiliaryParentEntityTokens.ConcatOrDefault(hookingParentEntityTokens); + parentEntityTokens = auxiliaryParentEntityTokens.ConcatOrDefault(hookingParentEntityTokens)?.Evaluate(); EntityTokenCacheFacade.AddHookingCache(node.EntityToken, parentEntityTokens); } @@ -490,7 +484,7 @@ private void ExpandNextLevel(string userName) if (parentEntityTokens != null) { AddNewParentEntityTokens(node, parentEntityTokens, RelationshipGraphNodeType.Hooking, levelNumber); - } + } } } } diff --git a/Composite/C1Console/Tasks/TaskContainer.cs b/Composite/C1Console/Tasks/TaskContainer.cs index 20c84098f8..48db38a686 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; @@ -93,18 +93,31 @@ public void SaveTasks() } +#if LeakCheck + private string stack = Environment.StackTrace; + /// ~TaskContainer() { - Dispose(); + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + Dispose(false); } +#endif + /// + public void Dispose() + { + Dispose(true); +#if LeakCheck + GC.SuppressFinalize(this); +#endif + } /// - public void Dispose() + public void Dispose(bool disposing) { - if (_disposed == false) + if (disposing && !_disposed) { _disposed = true; 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/Composite/C1Console/Trees/DataFolderElementsTreeNode.cs b/Composite/C1Console/Trees/DataFolderElementsTreeNode.cs index 0c89f65147..a35916095f 100644 --- a/Composite/C1Console/Trees/DataFolderElementsTreeNode.cs +++ b/Composite/C1Console/Trees/DataFolderElementsTreeNode.cs @@ -193,7 +193,7 @@ protected override IEnumerable OnGetElements(EntityToken parentEntityTo if (dataEntityToken != null) { var data = dataEntityToken.Data; - Verify.IsNotNull(data, "data is null, " + dataEntityToken); + if (data == null) return Enumerable.Empty(); referenceType = this.ChildGeneratingParentIdFilterNode.KeyPropertyInfo.DeclaringType; referenceValue = this.ChildGeneratingParentIdFilterNode.KeyPropertyInfo.GetValue(data, null); @@ -816,7 +816,7 @@ private Expression CreateFirstLetterOnlyFilterExpression(object value, Expressio this.ToUpperCompareMethodInfo ), this.StringStartsWithMethodInfo, - Expression.Constant(castedValue) + Expression.Constant(castedValue.ToUpperInvariant()) ), Expression.Constant(false) ); diff --git a/Composite/C1Console/Trees/Foundation/TreePerspectiveEntityToken.cs b/Composite/C1Console/Trees/Foundation/TreePerspectiveEntityToken.cs index 6426c34b8b..e24967b701 100644 --- a/Composite/C1Console/Trees/Foundation/TreePerspectiveEntityToken.cs +++ b/Composite/C1Console/Trees/Foundation/TreePerspectiveEntityToken.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using Composite.Core.Serialization; using System.Text; +using Composite.Core; +using Newtonsoft.Json; namespace Composite.C1Console.Trees.Foundation @@ -15,6 +17,7 @@ public class TreePerspectiveEntityToken : EntityToken /// + [JsonConstructor] public TreePerspectiveEntityToken(string id) { _id = id; @@ -29,6 +32,7 @@ public override string Id /// + [JsonIgnore] public override string Type { get { return "C1Trees"; } @@ -36,6 +40,7 @@ public override string Type /// + [JsonIgnore] public override string Source { get { return "C1Trees"; } @@ -52,7 +57,7 @@ public void AddChildTree(string treeId) /// public override string Serialize() { - return Id; + return CompositeJsonSerializer.Serialize(this); /*StringBuilder sb = new StringBuilder(); StringConversionServices.SerializeKeyValuePair(sb, "Id", Id); @@ -71,16 +76,22 @@ public override string Serialize() /// public static EntityToken Deserialize(string serializedEntityToken) { - /* Dictionary dic = StringConversionServices.ParseKeyValueCollection(serializedEntityToken); - - string id = StringConversionServices.DeserializeValueString(dic["Id"]); - - int count = 0; - while (true) + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) { - string key = "TreeId" + (counter++); - }*/ + entityToken = CompositeJsonSerializer.Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(TreePerspectiveEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) + { return new TreePerspectiveEntityToken(serializedEntityToken); } } diff --git a/Composite/C1Console/Trees/SimpleElementTreeNode.cs b/Composite/C1Console/Trees/SimpleElementTreeNode.cs index d1eae090a3..024b497910 100644 --- a/Composite/C1Console/Trees/SimpleElementTreeNode.cs +++ b/Composite/C1Console/Trees/SimpleElementTreeNode.cs @@ -23,16 +23,9 @@ internal class SimpleElementTreeNode : TreeNode public override IEnumerable GetEntityTokens(EntityToken childEntityToken, TreeNodeDynamicContext dynamicContext) { - EntityToken possibleCurrentEntityToken; - - TreeSimpleElementEntityToken treeSimpleElementEntityToken = childEntityToken as TreeSimpleElementEntityToken; - if (treeSimpleElementEntityToken != null) { - possibleCurrentEntityToken = treeSimpleElementEntityToken.ParentEntityToken; - } - else - { - possibleCurrentEntityToken = childEntityToken; - } + var possibleCurrentEntityToken = childEntityToken is TreeSimpleElementEntityToken seEntityToken + ? seEntityToken.ParentEntityToken + : childEntityToken; foreach (EntityToken entityToken in this.ParentNode.GetEntityTokens(possibleCurrentEntityToken, dynamicContext)) { @@ -45,8 +38,8 @@ internal override IEnumerable FilterParentGeneretedEntityTokens(Ent { // Check below ensures that the parent EntityToken actually present in a tree and not been filtered out - TreeSimpleElementEntityToken treeSimpleElementEntityToken = (TreeSimpleElementEntityToken) selfEntityToken; - EntityToken parentEntityToken = treeSimpleElementEntityToken.ParentEntityToken; + var treeSimpleElementEntityToken = (TreeSimpleElementEntityToken) selfEntityToken; + var parentEntityToken = treeSimpleElementEntityToken.ParentEntityToken; foreach (EntityToken entityToken in parentGeneretedEntityTokens) { if (parentEntityToken.Equals(entityToken)) @@ -54,15 +47,15 @@ internal override IEnumerable FilterParentGeneretedEntityTokens(Ent return new[] { parentEntityToken }; } - TreeSimpleElementEntityToken castedEntityToken = entityToken as TreeSimpleElementEntityToken; - if ((castedEntityToken != null) && - (parentEntityToken.Equals(castedEntityToken.ParentEntityToken))) + var castedEntityToken = entityToken as TreeSimpleElementEntityToken; + if (castedEntityToken != null && + parentEntityToken.Equals(castedEntityToken.ParentEntityToken)) { return new [] { parentEntityToken }; } } - return new EntityToken[0]; + return Array.Empty(); } @@ -78,9 +71,9 @@ protected override IEnumerable OnGetElements(EntityToken parentEntityTo var entityToken = new TreeSimpleElementEntityToken( this.Id, this.Tree.TreeId, - EntityTokenSerializer.Serialize(parentEntityToken)); + EntityTokenSerializer.Serialize(parentEntityToken)); - Element element = new Element(new ElementHandle( + var element = new Element(new ElementHandle( dynamicContext.ElementProviderName, entityToken, dynamicContext.Piggybag.PreparePiggybag(this.ParentNode, parentEntityToken) @@ -117,10 +110,6 @@ protected override void OnInitialize() } - public override string ToString() - { - - return string.Format("SimpleElementTreeNode, Id = {0}, Label = {1}", this.Id, this.Label); - } + public override string ToString() => $"SimpleElementTreeNode, Id = {Id}, Label = {Label}"; } } diff --git a/Composite/C1Console/Trees/TreeAuxiliaryAncestorProvider.cs b/Composite/C1Console/Trees/TreeAuxiliaryAncestorProvider.cs index ba7de90573..8865293ce1 100644 --- a/Composite/C1Console/Trees/TreeAuxiliaryAncestorProvider.cs +++ b/Composite/C1Console/Trees/TreeAuxiliaryAncestorProvider.cs @@ -14,11 +14,11 @@ internal sealed class TreeAuxiliaryAncestorProvider : IAuxiliarySecurityAncestor { public Dictionary> GetParents(IEnumerable entityTokens) { - Dictionary> result = new Dictionary>(); + var result = new Dictionary>(); foreach (EntityToken entityToken in entityTokens) { - TreeNodeDynamicContext dynamicContext = new TreeNodeDynamicContext(TreeNodeDynamicContextDirection.Up) + var dynamicContext = new TreeNodeDynamicContext(TreeNodeDynamicContextDirection.Up) { CurrentEntityToken = entityToken }; @@ -37,37 +37,36 @@ public Dictionary> GetParents(IEnumerable< { string treeId; - if (entityToken is TreeSimpleElementEntityToken) + if (entityToken is TreeSimpleElementEntityToken simpleElementEntityToken) { - treeId = (entityToken as TreeSimpleElementEntityToken).TreeNodeId; + treeId = simpleElementEntityToken.TreeNodeId; } - else if (entityToken is TreeFunctionElementGeneratorEntityToken) + else if (entityToken is TreeFunctionElementGeneratorEntityToken functionGenEntityToken) { - treeId = (entityToken as TreeFunctionElementGeneratorEntityToken).TreeNodeId; + treeId = functionGenEntityToken.TreeNodeId; } else { throw new InvalidOperationException("This code should not be reachable."); } - Log.LogError("TreeFacade", "The tree '{0}' failed to return parent entity tokens and are ignored", treeId); + Log.LogError("TreeFacade", $"The tree '{treeId}' failed to return parent entity tokens and are ignored"); Log.LogError("TreeFacade", ex); } } - else if (entityToken is TreeDataFieldGroupingElementEntityToken) + else if (entityToken is TreeDataFieldGroupingElementEntityToken dataFieldGroupingEntityToken) { - TreeDataFieldGroupingElementEntityToken treeDataFieldGroupingElementEntityToken = entityToken as TreeDataFieldGroupingElementEntityToken; string treeId = entityToken.Source; try { Tree tree = TreeFacade.GetTree(treeId); - string treeNodeId = treeDataFieldGroupingElementEntityToken.TreeNodeId; + string treeNodeId = dataFieldGroupingEntityToken.TreeNodeId; TreeNode treeNode = tree.GetTreeNode(treeNodeId); - dynamicContext.FieldGroupingValues = treeDataFieldGroupingElementEntityToken.GroupingValues; - dynamicContext.FieldFolderRangeValues = treeDataFieldGroupingElementEntityToken.FolderRangeValues; + dynamicContext.FieldGroupingValues = dataFieldGroupingEntityToken.GroupingValues; + dynamicContext.FieldFolderRangeValues = dataFieldGroupingEntityToken.FolderRangeValues; dynamicContext.CurrentTreeNode = treeNode; result.Add(entityToken, treeNode.ParentNode.GetEntityTokens(entityToken, dynamicContext)); @@ -78,10 +77,8 @@ public Dictionary> GetParents(IEnumerable< Log.LogError("TreeFacade", ex); } } - else if (entityToken is DataEntityToken) + else if (entityToken is DataEntityToken dataEntityToken) { - DataEntityToken dataEntityToken = entityToken as DataEntityToken; - Type interfaceType = dataEntityToken.InterfaceType; foreach (Tree tree in TreeFacade.AllTrees) @@ -89,12 +86,12 @@ public Dictionary> GetParents(IEnumerable< List treeNodes; if (!tree.BuildProcessContext.DataInteraceToTreeNodes.TryGetValue(interfaceType, out treeNodes)) continue; - IEnumerable concatList = null; + IEnumerable concatList = null; foreach (TreeNode treeNode in treeNodes) { try - { + { dynamicContext.CurrentTreeNode = treeNode; concatList = concatList.ConcatOrDefault(treeNode.ParentNode.GetEntityTokens(entityToken, dynamicContext)); diff --git a/Composite/C1Console/Trees/TreeDataFieldGroupingElementEntityToken.cs b/Composite/C1Console/Trees/TreeDataFieldGroupingElementEntityToken.cs index 246bba4d00..520f058ab2 100644 --- a/Composite/C1Console/Trees/TreeDataFieldGroupingElementEntityToken.cs +++ b/Composite/C1Console/Trees/TreeDataFieldGroupingElementEntityToken.cs @@ -231,43 +231,45 @@ public Dictionary FolderRangeValues /// public override int GetHashCode() { - if (_hashCode == 0) + if (_hashCode != 0) { - _hashCode = base.GetHashCode(); + return _hashCode; + } - if (this.GroupingValues != null) - { - foreach (var kvp in this.GroupingValues.SortByKeys()) - { - _hashCode ^= kvp.Key.GetHashCode(); - if (kvp.Value != null) - { - _hashCode ^= kvp.Value.GetHashCode(); - } - } - } + int hashCode = base.GetHashCode(); - if (this.FolderRangeValues != null) + if (this.GroupingValues != null) + { + foreach (var kvp in this.GroupingValues/*.SortByKeys()*/) { - foreach (var kvp in this.FolderRangeValues.SortByKeys()) + hashCode ^= kvp.Key.GetHashCode(); + if (kvp.Value != null) { - _hashCode ^= kvp.Key.GetHashCode(); - _hashCode ^= kvp.Value.GetHashCode(); + hashCode ^= kvp.Value.GetHashCode(); } } + } - if (this.ChildGeneratingDataElementsReferenceType != null) + if (this.FolderRangeValues != null) + { + foreach (var kvp in this.FolderRangeValues/*.SortByKeys()*/) { - _hashCode ^= ChildGeneratingDataElementsReferenceType.GetHashCode(); + hashCode ^= kvp.Key.GetHashCode(); + hashCode ^= kvp.Value.GetHashCode(); } + } - if (this.ChildGeneratingDataElementsReferenceValue != null) - { - _hashCode ^= ChildGeneratingDataElementsReferenceValue.GetHashCode(); - } + if (this.ChildGeneratingDataElementsReferenceType != null) + { + hashCode ^= ChildGeneratingDataElementsReferenceType.GetHashCode(); + } + + if (this.ChildGeneratingDataElementsReferenceValue != null) + { + hashCode ^= ChildGeneratingDataElementsReferenceValue.GetHashCode(); } - return _hashCode; + return _hashCode = hashCode; } diff --git a/Composite/C1Console/Trees/TreeElementAttachingProvider.cs b/Composite/C1Console/Trees/TreeElementAttachingProvider.cs index fa3dd89532..5aed056b2f 100644 --- a/Composite/C1Console/Trees/TreeElementAttachingProvider.cs +++ b/Composite/C1Console/Trees/TreeElementAttachingProvider.cs @@ -17,7 +17,7 @@ namespace Composite.C1Console.Trees { [ConfigurationElementType(typeof(NonConfigurableElementAttachingProvider))] - internal class TreeElementAttachingProvider : IMultibleResultElementAttachingProvider + internal class TreeElementAttachingProvider : IMultipleResultElementAttachingProvider { public ElementProviderContext Context { 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 { diff --git a/Composite/C1Console/Trees/TreeFunctionElementGeneratorEntityToken.cs b/Composite/C1Console/Trees/TreeFunctionElementGeneratorEntityToken.cs index 105ee6574d..bb4d1cd701 100644 --- a/Composite/C1Console/Trees/TreeFunctionElementGeneratorEntityToken.cs +++ b/Composite/C1Console/Trees/TreeFunctionElementGeneratorEntityToken.cs @@ -1,10 +1,9 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; using Composite.C1Console.Security; using Composite.C1Console.Security.SecurityAncestorProviders; +using Composite.Core; using Composite.Core.Serialization; +using Newtonsoft.Json; namespace Composite.C1Console.Trees @@ -13,9 +12,9 @@ namespace Composite.C1Console.Trees internal sealed class TreeFunctionElementGeneratorEntityToken : EntityToken, IEntityTokenContainingParentEntityToken { private EntityToken _parentEntityToken; - private string _treeNodeId; - private string _treeId; - private string _serializedParentEntityToken; + private readonly string _treeNodeId; + private readonly string _treeId; + private readonly string _serializedParentEntityToken; public TreeFunctionElementGeneratorEntityToken(string treeNodeId, string treeId, string serializedParentEntityToken, string elementId) @@ -26,23 +25,22 @@ public TreeFunctionElementGeneratorEntityToken(string treeNodeId, string treeId, this.ElementId = elementId; } - - public override string Type + [JsonConstructor] + private TreeFunctionElementGeneratorEntityToken(string id, string source, EntityToken parentEntityToken, string elementId) { - get { return _serializedParentEntityToken; } + _treeNodeId = id; + _treeId = source; + _parentEntityToken = parentEntityToken; + this.ElementId = elementId; } + public override string Type => _serializedParentEntityToken; - public override string Source - { - get { return _treeId; } - } + public override string Source => _treeId; - public override string Id - { - get { return _treeNodeId; } - } + + public override string Id => _treeNodeId; public string ElementId @@ -52,16 +50,10 @@ public string ElementId } - public string TreeNodeId - { - get - { - return this.Id; - } - } - + public string TreeNodeId => this.Id; + [JsonIgnore] public EntityToken ParentEntityToken { get @@ -86,16 +78,29 @@ public EntityToken GetParentEntityToken() public override string Serialize() { - StringBuilder sb = new StringBuilder(); - DoSerialize(sb); + return CompositeJsonSerializer.Serialize(this); + } + - StringConversionServices.SerializeKeyValuePair(sb, "ElementId", this.ElementId); - return sb.ToString(); + public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = CompositeJsonSerializer.Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(TreeFunctionElementGeneratorEntityToken), entityToken.GetType().FullName); + } + return entityToken; } - public static EntityToken Deserialize(string serializedEntityToken) + + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; Dictionary dic; @@ -107,8 +112,6 @@ public static EntityToken Deserialize(string serializedEntityToken) return new TreeFunctionElementGeneratorEntityToken(id, source, type, elementId); } - - public override int GetHashCode() { return base.GetHashCode() ^ this.ElementId.GetHashCode(); diff --git a/Composite/C1Console/Trees/TreeSimpleElementEntityToken.cs b/Composite/C1Console/Trees/TreeSimpleElementEntityToken.cs index 2409cc5533..b10d59e9af 100644 --- a/Composite/C1Console/Trees/TreeSimpleElementEntityToken.cs +++ b/Composite/C1Console/Trees/TreeSimpleElementEntityToken.cs @@ -1,18 +1,21 @@ using System.Diagnostics; using Composite.C1Console.Security; using Composite.C1Console.Security.SecurityAncestorProviders; - +using Composite.Core.Serialization; +using Newtonsoft.Json; +using Composite.Core; +using Newtonsoft.Json.Linq; namespace Composite.C1Console.Trees { /// /// /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityAncestorProvider(typeof(NoAncestorSecurityAncestorProvider))] [DebuggerDisplay("Id = {Id}, TreeId = {Source}, ParentEntityToken = {Type}")] public sealed class TreeSimpleElementEntityToken : EntityToken, IEntityTokenContainingParentEntityToken - { + { private EntityToken _parentEntityToken; private readonly string _treeNodeId; private readonly string _treeId; @@ -26,45 +29,52 @@ public TreeSimpleElementEntityToken(string treeNodeId, string treeId, string ser _serializedParentEntityToken = serializedParentEntityToken; } - /// - public override string Type + [JsonConstructor] + private TreeSimpleElementEntityToken(string treeNodeId, string treeId, JRaw parentEntityToken, string serializedParentEntityToken) { - get { return _serializedParentEntityToken; } + _treeNodeId = treeNodeId; + _treeId = treeId; + _serializedParentEntityToken = parentEntityToken?.Value.ToString() ?? serializedParentEntityToken; } + /// + [JsonProperty(PropertyName = "serializedParentEntityToken")] + public override string Type => _serializedParentEntityToken; /// - public override string Source + public bool ShouldSerializeType() { - get { return _treeId; } + return !CompositeJsonSerializer.IsJsonSerialized(_serializedParentEntityToken); } + [JsonProperty(PropertyName = "parentEntityToken")] + private JRaw rawSerializedParentEntityToken => new JRaw(_serializedParentEntityToken); /// - public override string Id + public bool ShouldSerializerawSerializedParentEntityToken() { - get { return _treeNodeId; } + return CompositeJsonSerializer.IsJsonSerialized(_serializedParentEntityToken); } /// - public string TreeNodeId - { - get - { - return this.Id; - } - } + [JsonProperty(PropertyName = "treeId")] + public override string Source => _treeId; + /// - public string SerializedParentEntityToken - { - get - { - return _serializedParentEntityToken; - } - } + [JsonProperty(PropertyName = "treeNodeId")] + public override string Id => _treeNodeId; /// + [JsonIgnore] + public string TreeNodeId => this.Id; + + /// + [JsonIgnore] + public string SerializedParentEntityToken => _serializedParentEntityToken; + + /// + [JsonIgnore] public EntityToken ParentEntityToken { get @@ -89,11 +99,29 @@ public EntityToken GetParentEntityToken() /// public override string Serialize() { - return DoSerialize(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = + CompositeJsonSerializer.Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(TreeSimpleElementEntityToken), entityToken.GetType().FullName); + } + return entityToken; + + } + + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; @@ -122,11 +150,11 @@ public override string OnGetTypePrettyHtml() type = string.Format(@"
{0}
", parentEntityToken.OnGetTypePrettyHtml()); } else - { + { type = parentEntityToken.Type; } return string.Format("ParentEntityToken:
Type: {0}
Source: {1}
Id:{2}
", type, parentEntityToken.Source, parentEntityToken.Id); - } + } } } 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/Composite.csproj b/Composite/Composite.csproj index 62a2d7d06f..dde66947de 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 @@ -228,7 +229,6 @@ - ASPXCodeBehind @@ -244,15 +244,22 @@ + + + + + + + @@ -804,7 +811,7 @@ - + @@ -1778,7 +1785,7 @@ - + @@ -2459,7 +2466,6 @@ - @@ -2469,7 +2475,6 @@ - diff --git a/Composite/Core/Application/AppDomainLocker.cs b/Composite/Core/Application/AppDomainLocker.cs index 2f9a2f2d41..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 } @@ -215,11 +217,14 @@ void Dispose(bool disposing) } - +#if LeakCheck + private string stack = Environment.StackTrace; ~DisposableLock() { + 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 8767185ac9..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 } @@ -170,11 +172,14 @@ protected virtual void Dispose(bool disposing) } - +#if LeakCheck + private string stack = Environment.StackTrace; ~DisposableLock() { + 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/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinGlobalSettingsProvider.cs b/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinGlobalSettingsProvider.cs index 267babe9a6..ba4480c67b 100644 --- a/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinGlobalSettingsProvider.cs +++ b/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinGlobalSettingsProvider.cs @@ -25,7 +25,6 @@ internal sealed class BuildinGlobalSettingsProvider : IGlobalSettingsProvider private string _dataMetaDataDirectory = "~/DataMetaData"; private string _inlineCSharpFunctionDirectory = "~/InlineCSharpFunctions"; private string _packageLicenseDirectory = "~/PackageLicenses"; - private readonly IResourceCacheSettings _resourceCacheSettings = new BuildinResourceCacheSettings(); private readonly ICachingSettings _cachingSettings = new BuildinCachingSettings(); private readonly List _nonProbableAssemblyNames = new List(); private readonly int _consoleMessageQueueSecondToLive = TimeSpan.FromMinutes(10).Seconds; @@ -102,9 +101,6 @@ public string SerializedWorkflowsDirectory public string PackageLicenseDirectory => _packageLicenseDirectory; - public IResourceCacheSettings ResourceCacheSettings => _resourceCacheSettings; - - public IEnumerable NonProbableAssemblyNames => _nonProbableAssemblyNames; diff --git a/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinResourceCacheSettings.cs b/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinResourceCacheSettings.cs deleted file mode 100644 index c49624aad7..0000000000 --- a/Composite/Core/Configuration/BuildinPlugins/GlobalSettingsProvider/BuildinResourceCacheSettings.cs +++ /dev/null @@ -1,31 +0,0 @@ - - -namespace Composite.Core.Configuration.BuildinPlugins.GlobalSettingsProvider -{ - internal sealed class BuildinResourceCacheSettings : IResourceCacheSettings - { - private string _resourceCacheDirectory = "~"; - private int _serverCacheMinutes = 0; - private int _clientCacheMinutes = 0; - - - public string ResourceCacheDirectory - { - get { return _resourceCacheDirectory; } - set { _resourceCacheDirectory = value; } - } - - - public int ServerCacheMinutes - { - get { return _serverCacheMinutes; } - set { _serverCacheMinutes = value; } - } - - public int ClientCacheMinutes - { - get { return _clientCacheMinutes; } - set { _clientCacheMinutes = value; } - } - } -} diff --git a/Composite/Core/Configuration/Foundation/PluginFacades/GlobalSettingsProviderPluginFacade.cs b/Composite/Core/Configuration/Foundation/PluginFacades/GlobalSettingsProviderPluginFacade.cs index 42870c161d..2bd6a1985b 100644 --- a/Composite/Core/Configuration/Foundation/PluginFacades/GlobalSettingsProviderPluginFacade.cs +++ b/Composite/Core/Configuration/Foundation/PluginFacades/GlobalSettingsProviderPluginFacade.cs @@ -214,16 +214,6 @@ public static string PackageLicenseDirectory return UseReaderLock(provider => provider.PackageLicenseDirectory); } } - - - - public static IResourceCacheSettings ResourceCacheSettings - { - get - { - return UseReaderLock(provider => provider.ResourceCacheSettings); - } - } diff --git a/Composite/Core/Configuration/GlobalSettingsFacade.cs b/Composite/Core/Configuration/GlobalSettingsFacade.cs index 9d66798395..5ed1639cb6 100644 --- a/Composite/Core/Configuration/GlobalSettingsFacade.cs +++ b/Composite/Core/Configuration/GlobalSettingsFacade.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Collections.Generic; using System.Globalization; -using Composite.Core.ResourceSystem; namespace Composite.Core.Configuration @@ -28,7 +27,7 @@ internal CachingSettings(bool enabled, int size) /// - public bool Enabled { get; private set; } + public bool Enabled { get; } /// @@ -38,7 +37,7 @@ internal CachingSettings(bool enabled, int size) /// All other values (> 0): The caller should use this size /// See /// - public int Size { get; private set; } + public int Size { get; } /// @@ -66,30 +65,22 @@ public static class GlobalSettingsFacade private static IGlobalSettingsFacade _globalSettingsFacade = new GlobalSettingsFacadeImpl(); - internal static IGlobalSettingsFacade Implementation { get { return _globalSettingsFacade; } set { _globalSettingsFacade = value; } } + internal static IGlobalSettingsFacade Implementation + { + get => _globalSettingsFacade; + set => _globalSettingsFacade = value; + } /// /// The name of the application to be displayed in the UI. /// - public static string ApplicationName - { - get - { - return _globalSettingsFacade.ApplicationName; - } - } + public static string ApplicationName => _globalSettingsFacade.ApplicationName; /// /// The short name of the application to be displayed in the UI. /// - public static string ApplicationShortName - { - get - { - return _globalSettingsFacade.ApplicationShortName; - } - } + public static string ApplicationShortName => _globalSettingsFacade.ApplicationShortName; /// /// Name of an assembly file, which version should displayed as a product version in UI. @@ -98,210 +89,75 @@ public static string ApplicationShortName /// - public static CultureInfo DefaultCultureInfo - { - get - { - return _globalSettingsFacade.DefaultCultureInfo; - } - } - + public static CultureInfo DefaultCultureInfo => _globalSettingsFacade.DefaultCultureInfo; /// - public static string DefaultCultureName - { - get - { - return _globalSettingsFacade.DefaultCultureName; - } - } - + public static string DefaultCultureName => _globalSettingsFacade.DefaultCultureName; /// - public static string ConfigurationDirectory - { - get - { - return _globalSettingsFacade.ConfigurationDirectory; - } - } - + public static string ConfigurationDirectory => _globalSettingsFacade.ConfigurationDirectory; /// - public static string GeneratedAssembliesDirectory - { - get - { - return _globalSettingsFacade.GeneratedAssembliesDirectory; - } - } - + public static string GeneratedAssembliesDirectory => _globalSettingsFacade.GeneratedAssembliesDirectory; /// - public static string SerializedWorkflowsDirectory - { - get - { - return _globalSettingsFacade.SerializedWorkflowsDirectory; - } - } - + public static string SerializedWorkflowsDirectory => _globalSettingsFacade.SerializedWorkflowsDirectory; /// - public static string SerializedConsoleMessagesDirectory - { - get - { - return _globalSettingsFacade.SerializedConsoleMessagesDirectory; - } - } - + public static string SerializedConsoleMessagesDirectory => _globalSettingsFacade.SerializedConsoleMessagesDirectory; /// - public static string AppCodeDirectory - { - get - { - return _globalSettingsFacade.AppCodeDirectory; - } - } - + public static string AppCodeDirectory => _globalSettingsFacade.AppCodeDirectory; /// - public static string BinDirectory - { - get - { - return _globalSettingsFacade.BinDirectory; - } - } - + public static string BinDirectory => _globalSettingsFacade.BinDirectory; /// - public static string TempDirectory - { - get - { - return _globalSettingsFacade.TempDirectory; - } - } + public static string TempDirectory => _globalSettingsFacade.TempDirectory; /// - public static string CacheDirectory - { - get - { - return _globalSettingsFacade.CacheDirectory; - } - } - - - /// - public static string PackageDirectory - { - get - { - return _globalSettingsFacade.PackageDirectory; - } - } - + public static string CacheDirectory => _globalSettingsFacade.CacheDirectory; /// - public static string AutoPackageInstallDirectory - { - get - { - return _globalSettingsFacade.AutoPackageInstallDirectory; - } - } - + public static string PackageDirectory => _globalSettingsFacade.PackageDirectory; /// - public static string TreeDefinitionsDirectory - { - get - { - return _globalSettingsFacade.TreeDefinitionsDirectory; - } - } - + public static string AutoPackageInstallDirectory => _globalSettingsFacade.AutoPackageInstallDirectory; /// - public static string PageTemplateFeaturesDirectory - { - get - { - return _globalSettingsFacade.PageTemplateFeaturesDirectory; - } - } - + public static string TreeDefinitionsDirectory => _globalSettingsFacade.TreeDefinitionsDirectory; /// - public static string DataMetaDataDirectory - { - get - { - return _globalSettingsFacade.DataMetaDataDirectory; - } - } - + public static string PageTemplateFeaturesDirectory => _globalSettingsFacade.PageTemplateFeaturesDirectory; /// - public static string InlineCSharpFunctionDirectory - { - get - { - return _globalSettingsFacade.InlineCSharpFunctionDirectory; - } - } - + public static string DataMetaDataDirectory => _globalSettingsFacade.DataMetaDataDirectory; /// - public static string PackageLicenseDirectory - { - get - { - return _globalSettingsFacade.PackageLicenseDirectory; - } - } - + public static string InlineCSharpFunctionDirectory => _globalSettingsFacade.InlineCSharpFunctionDirectory; /// - public static IResourceCacheSettings ResourceCacheSettings - { - get - { - return _globalSettingsFacade.ResourceCacheSettings; - } - } - + public static string PackageLicenseDirectory => _globalSettingsFacade.PackageLicenseDirectory; /// - public static IEnumerable NonProbableAssemblyNames - { - get - { - return _globalSettingsFacade.NonProbableAssemblyNames; - } - } - + public static IEnumerable NonProbableAssemblyNames => _globalSettingsFacade.NonProbableAssemblyNames; /// @@ -321,158 +177,66 @@ public static void RemoveNonProbableAssemblyName(string assemblyNamePatern) /// - public static int ConsoleMessageQueueItemSecondToLive - { - get - { - return _globalSettingsFacade.ConsoleMessageQueueItemSecondToLive; - } - } - + public static int ConsoleMessageQueueItemSecondToLive => _globalSettingsFacade.ConsoleMessageQueueItemSecondToLive; /// - public static bool EnableDataTypesAutoUpdate - { - get - { - return _globalSettingsFacade.EnableDataTypesAutoUpdate; - } - } - + public static bool EnableDataTypesAutoUpdate => _globalSettingsFacade.EnableDataTypesAutoUpdate; /// - public static bool BroadcastConsoleElementChanges - { - get - { - return _globalSettingsFacade.BroadcastConsoleElementChanges; - } - } - + public static bool BroadcastConsoleElementChanges => _globalSettingsFacade.BroadcastConsoleElementChanges; /// - public static string AutoCreatedAdministratorUserName - { - get - { - return _globalSettingsFacade.AutoCreatedAdministratorUserName; - } - } - + public static string AutoCreatedAdministratorUserName => _globalSettingsFacade.AutoCreatedAdministratorUserName; /// - public static TimeSpan WorkflowTimeout - { - get - { - return _globalSettingsFacade.WorkflowTimeout; - } - } - + public static TimeSpan WorkflowTimeout => _globalSettingsFacade.WorkflowTimeout; /// - public static TimeSpan ConsoleTimeout - { - get - { - return _globalSettingsFacade.ConsoleTimeout; - } - } - + public static TimeSpan ConsoleTimeout => _globalSettingsFacade.ConsoleTimeout; /// - public static TimeSpan DefaultReaderLockWaitTimeout - { - get - { - return TimeSpan.FromMinutes(5); - } - } + public static TimeSpan DefaultReaderLockWaitTimeout => TimeSpan.FromMinutes(5); /// - public static TimeSpan DefaultWriterLockWaitTimeout - { - get - { - return TimeSpan.FromMinutes(5); - } - } - + public static TimeSpan DefaultWriterLockWaitTimeout => TimeSpan.FromMinutes(5); /// - public static ICachingSettings Caching - { - get - { - return _globalSettingsFacade.Caching; - } - } - + public static ICachingSettings Caching => _globalSettingsFacade.Caching; /// - public static int ImageQuality - { - get - { - return _globalSettingsFacade.ImageQuality; - } - } + public static int ImageQuality => _globalSettingsFacade.ImageQuality; /// /// When true only pages that are published or awaiting publication can be translated in console. /// - public static bool OnlyTranslateWhenApproved - { - get - { - return _globalSettingsFacade.OnlyTranslateWhenApproved; - } - } + public static bool OnlyTranslateWhenApproved => _globalSettingsFacade.OnlyTranslateWhenApproved; /// /// The maximum number of characters the path to the application root (like 'C:\InetPub\MySite') can contain. /// C1 CMS create files below this path, some of which have very long paths - if the root path is long enough the combined length /// can exceed a limitation in Microsoft Windows - see http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx#paths /// - public static int MaximumRootPathLength - { - get - { - return 90; - } - } + public static int MaximumRootPathLength => 90; /// /// When true the output XHTML markup will be formatted. /// - public static bool PrettifyPublicMarkup - { - get - { - return _globalSettingsFacade.PrettifyPublicMarkup; - } - } + public static bool PrettifyPublicMarkup => _globalSettingsFacade.PrettifyPublicMarkup; /// /// When true exceptions thrown by C1 Functions during a page rendering will be prettified - ensuring the rest of the page render okay. /// For unauthenticated requests this will become "error", while authenticated users get error info. /// - public static bool PrettifyRenderFunctionExceptions - { - get - { - return _globalSettingsFacade.PrettifyRenderFunctionExceptions; - } - } + public static bool PrettifyRenderFunctionExceptions => _globalSettingsFacade.PrettifyRenderFunctionExceptions; /// public static bool FunctionPreviewEnabled => _globalSettingsFacade.FunctionPreviewEnabled; @@ -492,15 +256,15 @@ public static CachingSettings GetNamedCaching(string name) bool enabled = Caching.Enabled; int size = CachingSettings.DefaultCacheSize; - if ((enabled) && (cacheSettings != null)) + if (enabled && cacheSettings != null) { enabled = cacheSettings.Enabled && cacheSettings.Size != CachingSettings.NoCacheSize; size = cacheSettings.Size; } - CachingSettings cachingSettings = new CachingSettings(enabled, size); + var cachingSettings = new CachingSettings(enabled, size); return cachingSettings; } } -} +} \ No newline at end of file diff --git a/Composite/Core/Configuration/GlobalSettingsFacadeImpl.cs b/Composite/Core/Configuration/GlobalSettingsFacadeImpl.cs index e70be27e43..f5f07a068b 100644 --- a/Composite/Core/Configuration/GlobalSettingsFacadeImpl.cs +++ b/Composite/Core/Configuration/GlobalSettingsFacadeImpl.cs @@ -12,26 +12,11 @@ internal sealed class GlobalSettingsFacadeImpl : IGlobalSettingsFacade private readonly object _lock = new object(); - public string ApplicationName - { - get - { - return GlobalSettingsProviderPluginFacade.ApplicationName; - } - } + public string ApplicationName => GlobalSettingsProviderPluginFacade.ApplicationName; - public string ApplicationShortName - { - get - { - return GlobalSettingsProviderPluginFacade.ApplicationShortName; - } - } + public string ApplicationShortName => GlobalSettingsProviderPluginFacade.ApplicationShortName; - public string BrandedVersionAssemblySource - { - get { return GlobalSettingsProviderPluginFacade.BrandedVersionAssemblySource; } - } + public string BrandedVersionAssemblySource => GlobalSettingsProviderPluginFacade.BrandedVersionAssemblySource; public CultureInfo DefaultCultureInfo @@ -52,7 +37,7 @@ public string DefaultCultureName { string defaultCultureName = GlobalSettingsProviderPluginFacade.DefaultCultureName; - CultureInfo defaultCulture = CultureInfo.CreateSpecificCulture(defaultCultureName); + var defaultCulture = CultureInfo.CreateSpecificCulture(defaultCultureName); return defaultCulture.Name; } @@ -60,159 +45,49 @@ public string DefaultCultureName - public string ConfigurationDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.ConfigurationDirectory; - } - } - - - - public string GeneratedAssembliesDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.GeneratedAssembliesDirectory; - } - } - - - - public string SerializedWorkflowsDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.SerializedWorkflowsDirectory; - } - } - - - - public string SerializedConsoleMessagesDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.SerializedConsoleMessagesDirectory; - } - } - - - public string AppCodeDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.AppCodeDirectory; - } - } - + public string ConfigurationDirectory => GlobalSettingsProviderPluginFacade.ConfigurationDirectory; - public string BinDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.BinDirectory; - } - } + public string GeneratedAssembliesDirectory => GlobalSettingsProviderPluginFacade.GeneratedAssembliesDirectory; - public string TempDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.TempDirectory; - } - } + public string SerializedWorkflowsDirectory => GlobalSettingsProviderPluginFacade.SerializedWorkflowsDirectory; + public string SerializedConsoleMessagesDirectory => GlobalSettingsProviderPluginFacade.SerializedConsoleMessagesDirectory; - public string CacheDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.CacheDirectory; - } - } + public string AppCodeDirectory => GlobalSettingsProviderPluginFacade.AppCodeDirectory; - public string PackageDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.PackageDirectory; - } - } + public string BinDirectory => GlobalSettingsProviderPluginFacade.BinDirectory; - public string AutoPackageInstallDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.AutoPackageInstallDirectory; - } - } + public string TempDirectory => GlobalSettingsProviderPluginFacade.TempDirectory; + public string CacheDirectory => GlobalSettingsProviderPluginFacade.CacheDirectory; - public string TreeDefinitionsDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.TreeDefinitionsDirectory; - } - } + public string PackageDirectory => GlobalSettingsProviderPluginFacade.PackageDirectory; - public string PageTemplateFeaturesDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.PageTemplateFeaturesDirectory; - } - } + public string AutoPackageInstallDirectory => GlobalSettingsProviderPluginFacade.AutoPackageInstallDirectory; - public string DataMetaDataDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.DataMetaDataDirectory; - } - } + public string TreeDefinitionsDirectory => GlobalSettingsProviderPluginFacade.TreeDefinitionsDirectory; + public string PageTemplateFeaturesDirectory => GlobalSettingsProviderPluginFacade.PageTemplateFeaturesDirectory; - public string InlineCSharpFunctionDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.InlineCSharpFunctionDirectory; - } - } + public string DataMetaDataDirectory => GlobalSettingsProviderPluginFacade.DataMetaDataDirectory; - public string PackageLicenseDirectory - { - get - { - return GlobalSettingsProviderPluginFacade.PackageLicenseDirectory; - } - } - + public string InlineCSharpFunctionDirectory => GlobalSettingsProviderPluginFacade.InlineCSharpFunctionDirectory; - public IResourceCacheSettings ResourceCacheSettings - { - get - { - return GlobalSettingsProviderPluginFacade.ResourceCacheSettings; - } - } + public string PackageLicenseDirectory => GlobalSettingsProviderPluginFacade.PackageLicenseDirectory; public IEnumerable NonProbableAssemblyNames @@ -247,108 +122,43 @@ public void RemoveNonProbableAssemblyName(string assemblyNamePatern) { lock (_lock) { - if (_addedNonProbableAssemblyNames.Contains(assemblyNamePatern) == false) throw new InvalidOperationException("The assembly name pattern has not been added"); + if (!_addedNonProbableAssemblyNames.Contains(assemblyNamePatern)) + { + throw new InvalidOperationException("The assembly name pattern has not been added"); + } _addedNonProbableAssemblyNames.Remove(assemblyNamePatern); } } - public int ConsoleMessageQueueItemSecondToLive - { - get - { - return GlobalSettingsProviderPluginFacade.ConsoleMessageQueueItemSecondToLive; - } - } - - - public bool EnableDataTypesAutoUpdate - { - get - { - return GlobalSettingsProviderPluginFacade.EnableDataTypesAutoUpdate; - } - } + public int ConsoleMessageQueueItemSecondToLive => GlobalSettingsProviderPluginFacade.ConsoleMessageQueueItemSecondToLive; - public bool BroadcastConsoleElementChanges - { - get - { - return GlobalSettingsProviderPluginFacade.BroadcastConsoleElementChanges; - } - } + public bool EnableDataTypesAutoUpdate => GlobalSettingsProviderPluginFacade.EnableDataTypesAutoUpdate; - public string AutoCreatedAdministratorUserName - { - get - { - return GlobalSettingsProviderPluginFacade.AutoCreatedAdministratorUserName; - } - } + public bool BroadcastConsoleElementChanges => GlobalSettingsProviderPluginFacade.BroadcastConsoleElementChanges; + public string AutoCreatedAdministratorUserName => GlobalSettingsProviderPluginFacade.AutoCreatedAdministratorUserName; - public TimeSpan WorkflowTimeout - { - get - { - return TimeSpan.Parse(GlobalSettingsProviderPluginFacade.WorkflowTimeout); - } - } + public TimeSpan WorkflowTimeout => TimeSpan.Parse(GlobalSettingsProviderPluginFacade.WorkflowTimeout); - public TimeSpan ConsoleTimeout - { - get - { - return TimeSpan.Parse(GlobalSettingsProviderPluginFacade.ConsoleTimeout); - } - } + public TimeSpan ConsoleTimeout => TimeSpan.Parse(GlobalSettingsProviderPluginFacade.ConsoleTimeout); - public bool OnlyTranslateWhenApproved - { - get - { - return GlobalSettingsProviderPluginFacade.OnlyTranslateWhenApproved; - } - } + public bool OnlyTranslateWhenApproved => GlobalSettingsProviderPluginFacade.OnlyTranslateWhenApproved; - public ICachingSettings Caching - { - get - { - return GlobalSettingsProviderPluginFacade.Caching; - } - } + public ICachingSettings Caching => GlobalSettingsProviderPluginFacade.Caching; - public int ImageQuality - { - get - { - return GlobalSettingsProviderPluginFacade.ImageQuality; - } - } + public int ImageQuality => GlobalSettingsProviderPluginFacade.ImageQuality; - public bool PrettifyPublicMarkup - { - get - { - return GlobalSettingsProviderPluginFacade.PrettifyPublicMarkup; - } - } + public bool PrettifyPublicMarkup => GlobalSettingsProviderPluginFacade.PrettifyPublicMarkup; - public bool PrettifyRenderFunctionExceptions - { - get - { - return GlobalSettingsProviderPluginFacade.PrettifyRenderFunctionExceptions; - } - } + public bool PrettifyRenderFunctionExceptions => GlobalSettingsProviderPluginFacade.PrettifyRenderFunctionExceptions; public bool FunctionPreviewEnabled => GlobalSettingsProviderPluginFacade.FunctionPreviewEnabled; @@ -357,4 +167,4 @@ public bool PrettifyRenderFunctionExceptions public bool InheritGlobalReadPermissionOnHiddenPerspectives => GlobalSettingsProviderPluginFacade.InheritGlobalReadPermissionOnHiddenPerspectives; } -} +} \ No newline at end of file diff --git a/Composite/Core/Configuration/IGlobalSettingsFacade.cs b/Composite/Core/Configuration/IGlobalSettingsFacade.cs index 2b1d173045..ee3495bb83 100644 --- a/Composite/Core/Configuration/IGlobalSettingsFacade.cs +++ b/Composite/Core/Configuration/IGlobalSettingsFacade.cs @@ -27,7 +27,6 @@ internal interface IGlobalSettingsFacade string DataMetaDataDirectory { get; } string InlineCSharpFunctionDirectory { get; } string PackageLicenseDirectory { get; } - IResourceCacheSettings ResourceCacheSettings { get; } IEnumerable NonProbableAssemblyNames { get; } void AddNonProbableAssemblyName(string assemblyNamePatern); void RemoveNonProbableAssemblyName(string assemblyNamePatern); diff --git a/Composite/Core/Configuration/IResourceCacheSettings.cs b/Composite/Core/Configuration/IResourceCacheSettings.cs deleted file mode 100644 index 25b9d3c2a4..0000000000 --- a/Composite/Core/Configuration/IResourceCacheSettings.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Composite.Core.Configuration -{ - /// - /// - /// - [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] - public interface IResourceCacheSettings - { - /// - string ResourceCacheDirectory { get; set; } - - /// - int ServerCacheMinutes { get; set; } - - /// - int ClientCacheMinutes { get; set; } - } -} diff --git a/Composite/Core/Configuration/Plugins/GlobalSettingsProvider/IGlobalSettingsProvider.cs b/Composite/Core/Configuration/Plugins/GlobalSettingsProvider/IGlobalSettingsProvider.cs index d86c4a9b47..1540c192b3 100644 --- a/Composite/Core/Configuration/Plugins/GlobalSettingsProvider/IGlobalSettingsProvider.cs +++ b/Composite/Core/Configuration/Plugins/GlobalSettingsProvider/IGlobalSettingsProvider.cs @@ -48,8 +48,6 @@ internal interface IGlobalSettingsProvider string PackageLicenseDirectory { get; } - IResourceCacheSettings ResourceCacheSettings { get; } - /// /// List of assembly names to exclude from type probing. Use "*" as wildcard, like. "System.*" /// 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/IO/C1FileSystemWatcher.cs b/Composite/Core/IO/C1FileSystemWatcher.cs index 9bdf3f9904..722ae06270 100644 --- a/Composite/Core/IO/C1FileSystemWatcher.cs +++ b/Composite/Core/IO/C1FileSystemWatcher.cs @@ -268,16 +268,22 @@ public C1WaitForChangedResult WaitForChanged(WatcherChangeTypes changeType, int void IDisposable.Dispose() { Dispose(true); +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; /// /// Destructor. /// ~C1FileSystemWatcher() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// /// Disposes the stream. diff --git a/Composite/Core/IO/C1StreamReader.cs b/Composite/Core/IO/C1StreamReader.cs index 237cb6d711..214d966735 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.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/IO/C1StreamWriter.cs b/Composite/Core/IO/C1StreamWriter.cs index 3eaf09907a..0eb6a2e85a 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.RegisterFinalizerExecution(stack); Dispose(false); } - - +#endif /// /// Disposes the stream. diff --git a/Composite/Core/IO/MimeTypeInfo.cs b/Composite/Core/IO/MimeTypeInfo.cs index 3a31e36b22..f6148db8c5 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); @@ -413,13 +414,24 @@ public static string GetMimeType(UploadedFile uploadedFile) /// /// /// - internal static bool IsTextFile(string mimeType) + public static bool IsTextFile(string mimeType) { string canonicalMimeType = GetCanonical(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. + /// + /// + /// + public static bool IsBrowserPreviewableFile(string mimeType) + { + return mimeType == Pdf + || mimeType == Html + || mimeType == Text; + } internal static string TryGetLocalizedName(string mimeType) { 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 5e383f0b65..e3da3dbd9d 100644 --- a/Composite/Core/Implementation/C1FileStreamImplementation.cs +++ b/Composite/Core/Implementation/C1FileStreamImplementation.cs @@ -221,16 +221,22 @@ public virtual void Close() public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; /// ~C1FileStreamImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } +#endif diff --git a/Composite/Core/Implementation/C1StreamReaderImplementation.cs b/Composite/Core/Implementation/C1StreamReaderImplementation.cs index be51bd7f84..257bc16ed1 100644 --- a/Composite/Core/Implementation/C1StreamReaderImplementation.cs +++ b/Composite/Core/Implementation/C1StreamReaderImplementation.cs @@ -171,19 +171,22 @@ public virtual Encoding CurrentEncoding public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } - /// - /// See . - /// +#if LeakCheck + private string stack = Environment.StackTrace; + /// ~C1StreamReaderImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Implementation/C1StreamWriterImplementation.cs b/Composite/Core/Implementation/C1StreamWriterImplementation.cs index 0b94d93995..2446869aff 100644 --- a/Composite/Core/Implementation/C1StreamWriterImplementation.cs +++ b/Composite/Core/Implementation/C1StreamWriterImplementation.cs @@ -544,17 +544,22 @@ public virtual Encoding Encoding public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; /// ~C1StreamWriterImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Core/Implementation/DataConnectionImplementation.cs b/Composite/Core/Implementation/DataConnectionImplementation.cs index 3a98273486..4c0ac58939 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,23 +14,10 @@ namespace Composite.Core.Implementation /// public class DataConnectionImplementation : DataConnectionBase, IDisposable { -#if ConnectionLeakCheck - private string _allocationCallStack; -#endif - private IDisposable _threadDataManager; 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(); - } /// @@ -51,10 +36,6 @@ public DataConnectionImplementation(PublicationScope scope, CultureInfo locale) private void InitializeThreadData() { _threadDataManager = ThreadDataManager.EnsureInitialize(); - -#if ConnectionLeakCheck - _allocationCallStack = new StackTrace().ToString(); -#endif } /// @@ -171,21 +152,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 +164,6 @@ public virtual TData New() public virtual CultureInfo CurrentLocale => this.Locale; - /// - /// Documentation pending - /// - public virtual IEnumerable AllLocales => DataLocalizationFacade.ActiveLocalizationCultures; /// @@ -210,21 +172,22 @@ public virtual TData New() public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } - /// +#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.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// 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/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/PageDataConnectionImplementation.cs b/Composite/Core/Implementation/PageDataConnectionImplementation.cs index fb762721a1..fbe473834f 100644 --- a/Composite/Core/Implementation/PageDataConnectionImplementation.cs +++ b/Composite/Core/Implementation/PageDataConnectionImplementation.cs @@ -88,17 +88,20 @@ public IQueryable GetPageData(SitemapScope scope, Guid sourcePageI public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } - - +#if LeakCheck + private string stack = Environment.StackTrace; /// ~PageDataConnectionImplementation() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// 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; + } +} diff --git a/Composite/Core/Instrumentation/DisposableResourceTracer.cs b/Composite/Core/Instrumentation/DisposableResourceTracer.cs new file mode 100644 index 0000000000..9ae611c897 --- /dev/null +++ b/Composite/Core/Instrumentation/DisposableResourceTracer.cs @@ -0,0 +1,139 @@ +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. + /// 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 + { + private static Dictionary stacks = new Dictionary(); + 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; + + static DisposableResourceTracer() + { + GlobalEventSystemFacade.SubscribeToShutDownEvent(OnShutDownEvent); + } + + private static void OnShutDownEvent(ShutDownEventArgs args) + { + dumpAlways = true; + DumpStacks(); + } + + /// 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) + { + string topStack = GetMinimalStackTop(stack); + int count = 0; + if (stacks.TryGetValue(topStack, out count)) + { + stacks[topStack] = count + 1; + } + else + { + stacks.Add(topStack, 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 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)) + ); + + lock (_dumpLock) + { + dumpDoc.Save(fullPath); + } + } + + 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_") && !line.Contains(".Threading."); + 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... + /// + /// + public static Dictionary GetStacks() + { + lock (_lock) + { + return new Dictionary(stacks); + } + } + } +#endif +} + diff --git a/Composite/Core/Instrumentation/Foundation/NoopTimerProfiler.cs b/Composite/Core/Instrumentation/Foundation/NoopTimerProfiler.cs index c65562966c..c39ce04377 100644 --- a/Composite/Core/Instrumentation/Foundation/NoopTimerProfiler.cs +++ b/Composite/Core/Instrumentation/Foundation/NoopTimerProfiler.cs @@ -1,9 +1,11 @@ -namespace Composite.Core.Instrumentation.Foundation +namespace Composite.Core.Instrumentation.Foundation { internal sealed class NoopTimerProfiler : TimerProfiler { + public static NoopTimerProfiler Instance { get; } = new NoopTimerProfiler(); + public override void Dispose() - { + { } } } 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/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/Instrumentation/TimerProfilerFacade.cs b/Composite/Core/Instrumentation/TimerProfilerFacade.cs index 0ebbb62a6d..eda65d5461 100644 --- a/Composite/Core/Instrumentation/TimerProfilerFacade.cs +++ b/Composite/Core/Instrumentation/TimerProfilerFacade.cs @@ -1,10 +1,10 @@ -//#define PROFILE_MODE +//#define PROFILE_MODE using Composite.Core.Instrumentation.Foundation; namespace Composite.Core.Instrumentation { - /// + /// /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] @@ -13,36 +13,26 @@ public static class TimerProfilerFacade /// public static TimerProfiler CreateTimerProfiler() { +#if PROFILE_MODE if (RuntimeInformation.IsDebugBuild) { -#if PROFILE_MODE return new XmlTimerProfiler(); -#else - return new NoopTimerProfiler(); -#endif - } - else - { - return new NoopTimerProfiler(); } +#endif + return NoopTimerProfiler.Instance; } /// public static TimerProfiler CreateTimerProfiler(string message) { +#if PROFILE_MODE if (RuntimeInformation.IsDebugBuild) { -#if PROFILE_MODE return new XmlTimerProfiler(message); -#else - return new NoopTimerProfiler(); -#endif - } - else - { - return new NoopTimerProfiler(); } +#endif + return NoopTimerProfiler.Instance; } } -} +} \ No newline at end of file diff --git a/Composite/Core/Linq/Extensions.cs b/Composite/Core/Linq/Extensions.cs index 945b02d170..002cec294c 100644 --- a/Composite/Core/Linq/Extensions.cs +++ b/Composite/Core/Linq/Extensions.cs @@ -42,14 +42,16 @@ public static class IEnumerableExtensions /// Evaluated collection. public static ICollection Evaluate(this IEnumerable enumerable) { - if (enumerable is T[]) + Verify.ArgumentNotNull(enumerable, nameof(enumerable)); + + if (enumerable is T[] array) { - return enumerable as T[]; + return array; } - if (enumerable is ICollection) + if (enumerable is ICollection collection) { - return enumerable as ICollection; + return collection; } return new List(enumerable); diff --git a/Composite/Core/Localization/LocalizationParser.cs b/Composite/Core/Localization/LocalizationParser.cs index 193098ac5b..920607e5df 100644 --- a/Composite/Core/Localization/LocalizationParser.cs +++ b/Composite/Core/Localization/LocalizationParser.cs @@ -16,7 +16,7 @@ namespace Composite.Core.Localization [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class LocalizationParser { - private static Regex _attributRegex = new Regex(@"\$\((?[^:]+):(?[^\)]+)\)"); + private static readonly Regex _attributRegex = new Regex(@"\$\((?[^:]+):(?[^\)]+)\)"); /// @@ -35,16 +35,16 @@ public static void Parse(XContainer container) else if (element.Name.LocalName == "switch") { HandleSwitchElement(element); - } + } } IEnumerable attributes = element.Attributes().ToList(); foreach (XAttribute attribute in attributes) { Match match = _attributRegex.Match(attribute.Value); - if ((match.Success) && (match.Groups["type"].Value == "lang")) + if (match.Success && match.Groups["type"].Value == "lang") { - string newValue = StringResourceSystemFacade.ParseString(string.Format("${{{0}}}", match.Groups["id"].Value)); + string newValue = StringResourceSystemFacade.ParseString($"${{{match.Groups["id"].Value}}}"); attribute.SetValue(newValue); } } @@ -56,9 +56,9 @@ public static void Parse(XContainer container) private static void HandleStringElement(XElement element) { XAttribute attribute = element.Attribute("key"); - if (attribute == null) throw new InvalidOperationException(string.Format("Missing attibute named 'key' at {0}", element)); + if (attribute == null) throw new InvalidOperationException($"Missing attibute named 'key' at {element}"); - string newValue = StringResourceSystemFacade.ParseString(string.Format("${{{0}}}", attribute.Value)); + string newValue = StringResourceSystemFacade.ParseString($"${{{attribute.Value}}}"); element.ReplaceWith(newValue); } @@ -67,14 +67,14 @@ private static void HandleStringElement(XElement element) private static void HandleSwitchElement(XElement element) { - XElement defaultElement = element.Element(((XNamespace)LocalizationXmlConstants.XmlNamespace) + "default"); + XElement defaultElement = element.Element((XNamespace)LocalizationXmlConstants.XmlNamespace + "default"); Verify.IsNotNull(defaultElement, "Missing element named 'default' at {0}", element); XElement newValueParent = defaultElement; CultureInfo currentCultureInfo = LocalizationScopeManager.CurrentLocalizationScope; - foreach (XElement whenElement in element.Elements(((XNamespace)LocalizationXmlConstants.XmlNamespace) + "when")) + foreach (XElement whenElement in element.Elements((XNamespace)LocalizationXmlConstants.XmlNamespace + "when")) { XAttribute cultureAttribute = whenElement.Attribute("culture"); Verify.IsNotNull(cultureAttribute, "Missing attriubte named 'culture' at {0}", whenElement); 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/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/PageTemplates/PageTemplateEntityToken.cs b/Composite/Core/PageTemplates/PageTemplateEntityToken.cs index c60ad31c69..494aa6c732 100644 --- a/Composite/Core/PageTemplates/PageTemplateEntityToken.cs +++ b/Composite/Core/PageTemplates/PageTemplateEntityToken.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using Composite.C1Console.Security; using Composite.Plugins.Elements.ElementProviders.PageTemplateElementProvider; +using Newtonsoft.Json; namespace Composite.Core.PageTemplates { @@ -28,6 +29,7 @@ public PageTemplateEntityToken(Guid templateId) /// /// Gets the template id. /// + [JsonIgnore] public Guid TemplateId { get { return new Guid(_id); } diff --git a/Composite/Core/PageTemplates/TemplateDefinitionHelper.cs b/Composite/Core/PageTemplates/TemplateDefinitionHelper.cs index 75a31d6d20..946b95e455 100644 --- a/Composite/Core/PageTemplates/TemplateDefinitionHelper.cs +++ b/Composite/Core/PageTemplates/TemplateDefinitionHelper.cs @@ -125,19 +125,22 @@ public static void BindPlaceholders(IPageTemplate template, if (functionContextContainer != null) { - using (Profiler.Measure("Evaluating placeholder '{0}'".FormatWith(placeholderId))) + using (Profiler.Measure($"Evaluating placeholder '{placeholderId}'")) { PageRenderer.ExecuteEmbeddedFunctions(placeholderXhtml.Root, functionContextContainer); } - PageRenderer.NormalizeXhtmlDocument(placeholderXhtml); + using (Profiler.Measure("Normalizing XHTML document")) + { + PageRenderer.NormalizeXhtmlDocument(placeholderXhtml); + } } PageRenderer.ResolveRelativePaths(placeholderXhtml); - + PropertyInfo property = placeholderProperties[placeholderId]; - if (!property.ReflectedType.IsAssignableFrom(template.GetType())) + if (!property.ReflectedType.IsInstanceOfType(template)) { string propertyName = property.Name; property = template.GetType().GetProperty(property.Name); 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/ResourceSystem/LocalizationFiles.cs b/Composite/Core/ResourceSystem/LocalizationFiles.cs index b5c02771af..e5be4a0b8a 100644 --- a/Composite/Core/ResourceSystem/LocalizationFiles.cs +++ b/Composite/Core/ResourceSystem/LocalizationFiles.cs @@ -236,10 +236,10 @@ public static class Composite_C1Console_Trees { public static string TreeValidationError_FieldOrderBy_UnknownField_Template=>T("TreeValidationError.FieldOrderBy.UnknownField"); ///"The type '{0}' does not contain a field named '{1}'" public static string TreeValidationError_FieldOrderBy_UnknownField(object parameter0,object parameter1)=>string.Format(T("TreeValidationError.FieldOrderBy.UnknownField"), parameter0,parameter1); -///"'{0}' is in wrong format, use the format: {1}" +///"'{0}' is in wrong format, use the format {1} for raw values or {2} for formatted values. For format options, see the .ToString() oprions for the field type, ex 'yyyy MMM' for DateTime" public static string TreeValidationError_DataFieldValueHelper_WrongFormat_Template=>T("TreeValidationError.DataFieldValueHelper.WrongFormat"); -///"'{0}' is in wrong format, use the format: {1}" -public static string TreeValidationError_DataFieldValueHelper_WrongFormat(object parameter0,object parameter1)=>string.Format(T("TreeValidationError.DataFieldValueHelper.WrongFormat"), parameter0,parameter1); +///"'{0}' is in wrong format, use the format {1} for raw values or {2} for formatted values. For format options, see the .ToString() oprions for the field type, ex 'yyyy MMM' for DateTime" +public static string TreeValidationError_DataFieldValueHelper_WrongFormat(object parameter0,object parameter1,object parameter2)=>string.Format(T("TreeValidationError.DataFieldValueHelper.WrongFormat"), parameter0,parameter1,parameter2); ///"The interface '{0}' is not contained in the current element or any of its parents" public static string TreeValidationError_DataFieldValueHelper_InterfaceNotInParentTree_Template=>T("TreeValidationError.DataFieldValueHelper.InterfaceNotInParentTree"); ///"The interface '{0}' is not contained in the current element or any of its parents" @@ -2522,6 +2522,10 @@ public static class Composite_Management { public static string Website_Forms_Administrative_AddNewMediaFile_FileExists_Message=>T("Website.Forms.Administrative.AddNewMediaFile.FileExists.Message"); ///"The total length of the filename (folder and filename) is too long" public static string Website_Forms_Administrative_AddNewMediaFile_TotalFilenameToLong_Message=>T("Website.Forms.Administrative.AddNewMediaFile.TotalFilenameToLong.Message"); +///"Add tags to your media item seperated by a comma (,)" +public static string Website_Forms_Administrative_AddNewMediaFile_TagsTextBox_Help=>T("Website.Forms.Administrative.AddNewMediaFile.TagsTextBox.Help"); +///"Tags" +public static string Website_Forms_Administrative_AddNewMediaFile_TagsTextBox_Label=>T("Website.Forms.Administrative.AddNewMediaFile.TagsTextBox.Label"); ///"Add New Media Folder" public static string Website_Forms_Administrative_AddNewMediaFolder_Label_AddNewMediaFolder=>T("Website.Forms.Administrative.AddNewMediaFolder.Label.AddNewMediaFolder"); ///"Folder Name" @@ -2606,6 +2610,14 @@ public static class Composite_Management { public static string Website_Forms_Administrative_EditMediaFile_LabelDescription=>T("Website.Forms.Administrative.EditMediaFile.LabelDescription"); ///"A description of the media file content" public static string Website_Forms_Administrative_EditMediaFile_HelpDescription=>T("Website.Forms.Administrative.EditMediaFile.HelpDescription"); +///"Tags" +public static string Website_Forms_Administrative_EditMediaFile_LabelTags=>T("Website.Forms.Administrative.EditMediaFile.LabelTags"); +///"Provide tags for the media file content (Delimited by commas (,))" +public static string Website_Forms_Administrative_EditMediaFile_HelpTags=>T("Website.Forms.Administrative.EditMediaFile.HelpTags"); +///"URL" +public static string Website_Forms_Administrative_EditMediaFile_LabelMediaURL=>T("Website.Forms.Administrative.EditMediaFile.LabelMediaURL"); +///"This is the URL for your media File" +public static string Website_Forms_Administrative_EditMediaFile_HelpMediaURL=>T("Website.Forms.Administrative.EditMediaFile.HelpMediaURL"); ///"The total length of the filename (folder and filename) is too long" public static string Website_Forms_Administrative_EditMediaFile_TotalFilenameToLong_Message=>T("Website.Forms.Administrative.EditMediaFile.TotalFilenameToLong.Message"); ///"A file with the same name already exists in this folder." @@ -7211,6 +7223,22 @@ public static class Composite_Web_SourceEditor { public static string Insert_FrontendURL_Label=>T("Insert.FrontendURL.Label"); ///"Function Markup" public static string Insert_FunctionMarkup_Label=>T("Insert.FunctionMarkup.Label"); +///"Translate To {0}" +public static string ResxEditor_TranslateTo_Label_Template=>T("ResxEditor.TranslateTo.Label"); +///"Translate To {0}" +public static string ResxEditor_TranslateTo_Label(object parameter0)=>string.Format(T("ResxEditor.TranslateTo.Label"), parameter0); +///"Translate To {0}" +public static string ResxEditor_TranslateTo_Tooltip_Template=>T("ResxEditor.TranslateTo.Tooltip"); +///"Translate To {0}" +public static string ResxEditor_TranslateTo_Tooltip(object parameter0)=>string.Format(T("ResxEditor.TranslateTo.Tooltip"), parameter0); +///"Label" +public static string ResxEditor_Label=>T("ResxEditor.Label"); +///"Original Text" +public static string ResxEditor_OriginalText=>T("ResxEditor.OriginalText"); +///"Translated Text" +public static string ResxEditor_TranslatedText=>T("ResxEditor.TranslatedText"); +///"Save" +public static string ResxEditor_Save=>T("ResxEditor.Save"); private static string T(string key) => StringResourceSystemFacade.GetString("Composite.Web.SourceEditor", key); } diff --git a/Composite/Core/Serialization/CodeGeneration/Foundation/ISerializer.cs b/Composite/Core/Serialization/CodeGeneration/Foundation/ISerializer.cs index 5be8274b46..3d064e99da 100644 --- a/Composite/Core/Serialization/CodeGeneration/Foundation/ISerializer.cs +++ b/Composite/Core/Serialization/CodeGeneration/Foundation/ISerializer.cs @@ -1,4 +1,4 @@ -using System.Text; +using System.Text; using System.Collections.Generic; @@ -10,9 +10,6 @@ namespace Composite.Core.Serialization.CodeGeneration.Foundation [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public interface ISerializer { - /// - void Serialize(object propertyClass, StringBuilder serializedValues, IEnumerable propertyNames=null); - /// object Deserialize(Dictionary objectState); } diff --git a/Composite/Core/Serialization/CodeGeneration/PropertySerializerTypeCodeGenerator.cs b/Composite/Core/Serialization/CodeGeneration/PropertySerializerTypeCodeGenerator.cs index bc2b127850..ab923ca6c4 100644 --- a/Composite/Core/Serialization/CodeGeneration/PropertySerializerTypeCodeGenerator.cs +++ b/Composite/Core/Serialization/CodeGeneration/PropertySerializerTypeCodeGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; @@ -81,7 +81,6 @@ internal static CodeTypeDeclaration CreateCodeTypeDeclaration(string propertyCla ) ); - AddSerializeMethod(declaration, propertyClassTypeName, properties); AddDeserializeMethod(declaration, propertyClassTypeName, properties); return declaration; @@ -89,109 +88,6 @@ internal static CodeTypeDeclaration CreateCodeTypeDeclaration(string propertyCla - private static void AddSerializeMethod(CodeTypeDeclaration declaration, string propertyClassTypeName, IList> properties) - { - CodeMemberMethod method = new CodeMemberMethod(); - method.Name = "Serialize"; - method.ReturnType = new CodeTypeReference(typeof(void)); - method.Attributes = MemberAttributes.Public | MemberAttributes.Final; - method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "propertyClass")); - method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(StringBuilder), "serializedValues")); - method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(IEnumerable), "propertyNames")); - - - method.Statements.Add(new CodeVariableDeclarationStatement( - propertyClassTypeName, - "classToSerialize" - )); - - method.Statements.Add(new CodeTryCatchFinallyStatement( - new CodeStatement[] { - new CodeAssignStatement( - new CodeVariableReferenceExpression("classToSerialize"), - new CodeCastExpression( - propertyClassTypeName, - new CodeVariableReferenceExpression("propertyClass")) - ) - }, - new CodeCatchClause[] { - new CodeCatchClause("e", new CodeTypeReference(typeof(Exception)), new CodeStatement[] { - new CodeThrowExceptionStatement( - new CodeObjectCreateExpression( - new CodeTypeReference(typeof(ArgumentException)), - new CodeExpression[] { - new CodePrimitiveExpression( - string.Format("The supplied propertyClass is not of type '{0}'", propertyClassTypeName) - ), - new CodeVariableReferenceExpression("e") - } - ) - ) - }) - } - )); - - - bool isFirst = true; - foreach (var property in properties) - { - if (isFirst) - { - isFirst = false; - } - else - { - method.Statements.Add(new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression("serializedValues"), - "Append", - new CodeExpression[] { - new CodePrimitiveExpression(", ") - } - ) - )); - } - - Type propertyType; - string methodName; - if (!property.Item2.IsArray) - { - propertyType = property.Item2; - methodName = "SerializeKeyValuePair"; - } - else - { - propertyType = property.Item2.GetElementType(); - methodName = "SerializeKeyValueArrayPair"; - } - - method.Statements.Add(new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression( - new CodeTypeReferenceExpression(typeof(StringConversionServices)), - methodName, - new CodeTypeReference[] { - new CodeTypeReference(propertyType), - } - ), - new CodeExpression[] { - new CodeVariableReferenceExpression("serializedValues"), - new CodePrimitiveExpression(property.Item1), - new CodePropertyReferenceExpression( - new CodeVariableReferenceExpression("classToSerialize"), - property.Item1 - ),new CodeVariableReferenceExpression("propertyNames") - } - ) - )); - } - - - declaration.Members.Add(method); - } - - - private static void AddDeserializeMethod(CodeTypeDeclaration declaration, string propertyClassTypeName, IList> properties) { CodeMemberMethod method = new CodeMemberMethod(); diff --git a/Composite/Core/Serialization/CompositeJsonSerializer.cs b/Composite/Core/Serialization/CompositeJsonSerializer.cs new file mode 100644 index 0000000000..3dc82191e6 --- /dev/null +++ b/Composite/Core/Serialization/CompositeJsonSerializer.cs @@ -0,0 +1,321 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security; +using Composite.C1Console.Security; +using Composite.Core.Types; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Composite.Core.Serialization +{ + /// + /// Use this class to serialize and deserialize objects with json serializer + /// + public static class CompositeJsonSerializer + { + private const string ObjectKeyString = "meta:obj"; + private const string TypeKeyString = "meta:type"; + private const string HashKeyString = "meta:hash"; + + /// + /// Check if string is serilized with JsonSerializer + /// + /// + /// + public static bool IsJsonSerialized(string str) + { + return str.StartsWith("{"); + } + + /// + /// Serialize with automatic type name handling + /// + /// Object to serialize + /// seialized string + public static string Serialize(object obj) + { + var serializedData = JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameHandling = TypeNameHandling.Auto, + Converters = {new JsonTypeConverter()} + }); + + return serializedData; + } + + /// + /// Serialize with json object structure type name handling + /// + /// Object to serialize + /// seialized string + public static string SerializeObject(object obj) + { + var serializedData = JsonConvert.SerializeObject(obj, new JsonSerializerSettings + { + TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameHandling = TypeNameHandling.Objects, + Formatting = Formatting.None, + Converters = {new JsonTypeConverter()} + }); + + return serializedData; + } + + /// + /// Serialize only the property names that is specified + /// + /// Object to serialize + /// List of properties to be serialized + /// seialized string + public static string SerializePartial(object obj, IEnumerable propertyNames) + { + if (propertyNames == null) + { + return SerializeObject(obj); + } + + var serializedData = + JsonConvert.SerializeObject(obj, new PartialJsonConvertor(propertyNames, obj.GetType())); + + return serializedData; + } + + + + /// + /// Serialize with a wrapper containing the serialized object, its hash sign and its type + /// + /// Object to serialize + /// To calculate the hash sign + /// seialized string + public static string Serialize(object obj, bool shouldSign) + { + var type = obj.GetType(); + var methodInfo = type.GetMethod("Serialize"); + string serializedData; + + if (methodInfo == null) + { + serializedData = Serialize(obj); + } + else + { + serializedData = (string)methodInfo.Invoke(obj, null); + } + + var hash = shouldSign ? HashSigner.GetSignedHash(serializedData).GetHashCode() : 0; + + var serializedProperties = IsJsonSerialized(serializedData) + ? serializedData.Substring(1, serializedData.Length - 2) + : $@"""{ObjectKeyString}"":""{serializedData}"""; + + return "{" + serializedProperties + + $@",""{TypeKeyString}"":""{GetSerializedTypeName(type)}""" + + (shouldSign ? $@",""{HashKeyString}"":""{hash}""" : "") + + "}"; + } + + /// + /// Deserialize string into object with specified type + /// + /// Serilaized string + /// Type of returned object + /// The object + public static T Deserialize(string str) + { + var obj = JsonConvert.DeserializeObject(str, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + return obj; + } + + /// + /// Deserialize strings into object with specified type by merging them together + /// + /// Serilaized string + /// Type of returned object + /// The object + public static T Deserialize(params string[] strs) + { + var combinedObj = new JObject(); + var mergeSettings = new JsonMergeSettings + { + MergeArrayHandling = MergeArrayHandling.Union + }; + try + { + foreach (var s in strs) + { + combinedObj.Merge(JObject.Parse(s), mergeSettings); + } + } + catch (Exception) + { + throw new ArgumentException("Cannot merge arguments into one"); + } + var obj = JsonConvert.DeserializeObject(combinedObj.ToString(), new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + return obj; + } + + /// + /// Deserialize string into object + /// + /// Serilaized string + /// The object + public static object Deserialize(string str) + { + var obj = JsonConvert.DeserializeObject(str, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + return obj; + } + + /// + /// Deserialize strings into object with specified type from a hash signed wrapper + /// + /// Serilaized string + /// Is signed + /// Type of returned object + /// The object + public static T Deserialize(string str, bool isSigned) + { + var legacyStyleSerilized = str.StartsWith("{\"" + ObjectKeyString + "\":\""); + + string obj; + var hash = 0; + var type = TypeManager.TryGetType(str.GetValue(TypeKeyString)); + + if (type==null) + throw new SerializationException(); + + if (isSigned) + { + if(!int.TryParse(str.GetValue(HashKeyString),out hash)) + throw new SerializationException(); + } + + if (legacyStyleSerilized) + { + obj = str.GetValue(ObjectKeyString); + } + else + { + obj = "{" + str.Substring(1, str.LastIndexOf(TypeKeyString, StringComparison.InvariantCulture) - 3) + "}"; + } + + if (isSigned) + { + if (!HashSigner.ValidateSignedHash(obj, new HashValue(hash))) + { + throw new SecurityException($"Serialized {typeof(T).FullName} is tampered"); + } + } + MethodInfo methodInfo = type.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); + if (methodInfo == null) + { + return Deserialize(obj); + } + return (T)methodInfo.Invoke(null, new object[] { obj }); + } + + private static string GetValue(this string str, string key) + { + var searchterm = "\"" + key + "\":\""; + var valueStartIndex = str.LastIndexOf(searchterm,StringComparison.InvariantCulture) + searchterm.Length; + var valueLength = str.IndexOf("\"", valueStartIndex,StringComparison.InvariantCulture) - valueStartIndex; + var value = str.Substring(valueStartIndex, valueLength); + return value; + } + + private static string GetSerializedTypeName(Type type) + { + return $"{type.FullName}, {type.Assembly.GetName().Name}"; + } + + private class JsonTypeConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var type = (Type) value; + writer.WriteValue(GetSerializedTypeName(type)); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanRead => false; + + public override bool CanConvert(Type objectType) + { + return typeof(Type).IsAssignableFrom(objectType); + } + } + + + private class PartialJsonConvertor : JsonConverter + { + private readonly IEnumerable _propertyNames; + private readonly Type _type; + + public PartialJsonConvertor(IEnumerable propertyNames, Type type) + { + _propertyNames = propertyNames; + _type = type; + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var o = new JObject(); + + o.AddFirst(new JProperty("$type", GetSerializedTypeName(_type))); + + var jsonSerializer = new JsonSerializer + { + TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple, + TypeNameHandling = TypeNameHandling.Objects, + Converters = {new JsonTypeConverter()} + }; + + foreach (var property in _propertyNames) + { + o.Add(property, JToken.FromObject(GetPropValue(value, property), jsonSerializer)); + } + + o.WriteTo(writer); + } + + private static object GetPropValue(object src, string propName) + { + var pinf = src.GetType().GetProperty(propName); + if (pinf == null) + { + throw new ArgumentException($"There is no {propName} in {src.GetType().FullName}"); + } + return pinf.GetValue(src, null); + + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, + JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanConvert(Type objectType) + { + return true; + } + } + } +} diff --git a/Composite/Core/Serialization/SerializationFacade.cs b/Composite/Core/Serialization/SerializationFacade.cs index e3ed0aeb5c..2f66016e79 100644 --- a/Composite/Core/Serialization/SerializationFacade.cs +++ b/Composite/Core/Serialization/SerializationFacade.cs @@ -1,13 +1,10 @@ -using System; +using System; using System.Collections.Generic; -using System.Text; using Composite.Core.Collections.Generic; using Composite.C1Console.Events; -using Composite.Core.Extensions; using Composite.Core.Serialization.CodeGeneration; using Composite.Core.Serialization.CodeGeneration.Foundation; using Composite.Core.Types; -using Composite.Core.Logging; namespace Composite.Core.Serialization @@ -24,28 +21,6 @@ static SerializationFacade() } - public static string Serialize(object propertyClass) - { - ISerializer serializer = GetSerializer(propertyClass.GetType()); - - StringBuilder sb = new StringBuilder(); - - serializer.Serialize(propertyClass, sb); - - return sb.ToString(); - } - - public static string Serialize(object propertyClass, IEnumerable propertyNames) - { - ISerializer serializer = GetSerializer(propertyClass.GetType()); - - StringBuilder sb = new StringBuilder(); - - serializer.Serialize(propertyClass, sb, propertyNames); - - return sb.ToString(); - } - public static object Deserialize(Type propertyClassType, string serializedProperties) { ISerializer serializer = GetSerializer(propertyClassType); diff --git a/Composite/Core/ServiceLocator.cs b/Composite/Core/ServiceLocator.cs index 5c27455ec2..6768385221 100644 --- a/Composite/Core/ServiceLocator.cs +++ b/Composite/Core/ServiceLocator.cs @@ -108,7 +108,7 @@ public static void SetServiceProvider(Func /// /// Gets an application service provider /// - internal static IServiceProvider ServiceProvider + public static IServiceProvider ServiceProvider { get { 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 47d132e9d1..ae2c530a4c 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,69 @@ 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; - } } +#if LeakCheck + private string stack = Environment.StackTrace; +#endif ~ThreadDataManagerScope() { - if (_disposed || !_disposeData) - { - return; - } - - - try - { - _threadData.Dispose(); - } - catch (Exception) - { - // silent... - } +#if LeakCheck + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); +#endif + Dispose(false); } } } diff --git a/Composite/Core/Threading/ThreadDataManagerData.cs b/Composite/Core/Threading/ThreadDataManagerData.cs index ac2f992e41..cf3c0d9d37 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,13 +103,22 @@ public void CheckNotDisposed() /// public void Dispose() { - if (OnDispose != null) - { - OnDispose(); - } + 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/Types/CodeCompatibilityChecker.cs b/Composite/Core/Types/CodeCompatibilityChecker.cs index 0d74b04e25..19d1696f01 100644 --- a/Composite/Core/Types/CodeCompatibilityChecker.cs +++ b/Composite/Core/Types/CodeCompatibilityChecker.cs @@ -66,17 +66,17 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da if (filesToCompile.Count == 0) return new CompatibilityCheckResult(); - CSharpCodeProvider csCompiler = new CSharpCodeProvider(); + var csCompiler = new CSharpCodeProvider(); List referencedAssemblies = new List(); - Dictionary> codeTypeDeclarations = new Dictionary>(); + var codeTypeDeclarations = new Dictionary>(); - foreach (DataTypeDescriptor dataTypeDescriptor in DataMetaDataFacade.GeneratedTypeDataTypeDescriptors) + foreach (var dataTypeDescriptor in DataMetaDataFacade.GeneratedTypeDataTypeDescriptors) { - if (!includeDataTypeDescriptor && (dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId)) continue; + if (!includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) continue; DataTypeDescriptor dataTypeDescriptorToUse = dataTypeDescriptor; - if (includeDataTypeDescriptor && (dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId)) dataTypeDescriptorToUse = dataTypeDescriptorToTest; + if (includeDataTypeDescriptor && dataTypeDescriptor.DataTypeId == dataTypeDescriptorToTest.DataTypeId) dataTypeDescriptorToUse = dataTypeDescriptorToTest; referencedAssemblies.AddRange(InterfaceCodeGenerator.GetReferencedAssemblies(dataTypeDescriptorToUse)); CodeTypeDeclaration codeTypeDeclaration = InterfaceCodeGenerator.CreateCodeTypeDeclaration(dataTypeDescriptorToUse); @@ -96,12 +96,12 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da { using (var sw = new StreamWriter(file)) { - CodeNamespace codeNamespace = new CodeNamespace(dataTypeDescriptorToUse.Namespace); + var codeNamespace = new CodeNamespace(dataTypeDescriptorToUse.Namespace); codeNamespace.Types.Add(codeTypeDeclaration); csCompiler.GenerateCodeFromNamespace(codeNamespace, sw, new CodeGeneratorOptions()); } - StringBuilder sb = new StringBuilder(); + var sb = new StringBuilder(); using (var sw = new StringWriter(sb)) { csCompiler.GenerateCodeFromMember(codeTypeDeclaration, sw, new CodeGeneratorOptions()); @@ -112,27 +112,30 @@ private static CompatibilityCheckResult CheckAgainsAppCode(DataTypeDescriptor da filesToCompile.Sort(); - CompilerParameters compilerParameters = new CompilerParameters(); - compilerParameters.GenerateExecutable = false; - compilerParameters.GenerateInMemory = true; + var compilerParameters = new CompilerParameters + { + GenerateExecutable = false, + GenerateInMemory = true + }; compilerParameters.ReferencedAssemblies.AddRangeIfNotContained(referencedAssemblies.Select(f => f.Location).ToArray()); compilerParameters.ReferencedAssemblies.AddRangeIfNotContained(CodeGenerationManager.CompiledAssemblies.Select(f => f.Location).ToArray()); - compilerParameters.AddAssemblyLocationsFromBin(); compilerParameters.AddLoadedAssemblies(false); + compilerParameters.AddAssemblyLocationsFromBin(); compilerParameters.AddCommonAssemblies(); compilerParameters.RemoveGeneratedAssemblies(); - CodeCompileUnit codeCompileUnit = new CodeCompileUnit(); + + var codeCompileUnit = new CodeCompileUnit(); foreach (var kvp in codeTypeDeclarations) { - CodeNamespace codeNamespace = new CodeNamespace(kvp.Key); + var codeNamespace = new CodeNamespace(kvp.Key); codeNamespace.Types.AddRange(kvp.Value.ToArray()); codeCompileUnit.Namespaces.Add(codeNamespace); } - CSharpCodeProvider compiler = new CSharpCodeProvider(); - CompilerResults compileResult = compiler.CompileAssemblyFromFile(compilerParameters, filesToCompile.ToArray()); + var compiler = new CSharpCodeProvider(); + var compileResult = compiler.CompileAssemblyFromFile(compilerParameters, filesToCompile.ToArray()); if (compileResult.Errors.Count == 0) return new CompatibilityCheckResult(); diff --git a/Composite/Core/Types/CodeGenerationCommon.cs b/Composite/Core/Types/CodeGenerationCommon.cs index b9704d5a93..8ee1e61775 100644 --- a/Composite/Core/Types/CodeGenerationCommon.cs +++ b/Composite/Core/Types/CodeGenerationCommon.cs @@ -1,7 +1,6 @@ using System; using System.CodeDom.Compiler; using System.Collections.Generic; -using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; @@ -35,7 +34,7 @@ public static void AddAssemblyLocationsFromBin(this CompilerParameters compilerP /// if set to true reference to App_Code will be included to results. public static void AddLoadedAssemblies(this CompilerParameters compilerParameters, bool includeAppCode) { - Dictionary foundAssemblyLocations = new Dictionary(); + var foundAssemblyLocations = new Dictionary(); IEnumerable assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(AssemblyHasLocation); @@ -51,7 +50,7 @@ public static void AddLoadedAssemblies(this CompilerParameters compilerParameter string locationKey = Path.GetFileName(location).ToLowerInvariant(); - if (foundAssemblyLocations.ContainsKey(locationKey) == false) + if (!foundAssemblyLocations.ContainsKey(locationKey)) { foundAssemblyLocations.Add(locationKey, location); } @@ -81,7 +80,7 @@ public static void AddLoadedAssemblies(this CompilerParameters compilerParameter /// public static void AddCommonAssemblies(this CompilerParameters compilerParameters) { - List commonAssemblies = new List() + var commonAssemblies = new List { typeof(System.Linq.Expressions.Expression).Assembly.Location, typeof(System.Xml.Linq.XElement).Assembly.Location, @@ -109,15 +108,11 @@ public static void RemoveGeneratedAssemblies(this CompilerParameters compilerPar Select(f => f). ToList(); - foreach (string assemblyToRemove in assembliesToRemove) - { - compilerParameters.ReferencedAssemblies.Remove(assemblyToRemove); - } + assembliesToRemove.ForEach(compilerParameters.ReferencedAssemblies.Remove); } - [DebuggerStepThrough] private static bool AssemblyHasLocation(Assembly assembly) { @@ -136,7 +131,7 @@ private static bool AssemblyHasLocation(Assembly assembly) return assembly.ManifestModule.Name != "" && assembly.ManifestModule.FullyQualifiedName != "" && assembly.ManifestModule.ScopeName != "RefEmit_InMemoryManifestModule" && - string.IsNullOrEmpty(assembly.Location) == false; + !string.IsNullOrEmpty(assembly.Location); } catch (Exception) { 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/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/ApplicationLevelEventHandlers.cs b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs index b2b19cc099..57abdaef53 100644 --- a/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs +++ b/Composite/Core/WebClient/ApplicationLevelEventHandlers.cs @@ -94,7 +94,9 @@ 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()); + UrlToEntityTokenFacade.Register(new WebsiteFileUrlToEntityTokenMapper()); var services = ServiceLocator.ServiceCollection; services.AddLogging(); 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/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/FlowMediators/TreeServicesFacade.cs b/Composite/Core/WebClient/FlowMediators/TreeServicesFacade.cs index 0781f82a90..13c8b76057 100644 --- a/Composite/Core/WebClient/FlowMediators/TreeServicesFacade.cs +++ b/Composite/Core/WebClient/FlowMediators/TreeServicesFacade.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Composite.C1Console.Events; @@ -207,23 +207,19 @@ public static List GetMultipleChildren(List public static List GetLabeledProperties(string providerName, string serializedEntityToken, string piggybag) { - EntityToken elementEntityToken = EntityTokenSerializer.Deserialize(serializedEntityToken); - ElementHandle elementHandle = new ElementHandle(providerName, elementEntityToken, piggybag); + var elementEntityToken = EntityTokenSerializer.Deserialize(serializedEntityToken); + var elementHandle = new ElementHandle(providerName, elementEntityToken, piggybag); - IEnumerable labeledProperties; - if (UserSettings.ForeignLocaleCultureInfo == null || UserSettings.ForeignLocaleCultureInfo.Equals(UserSettings.ActiveLocaleCultureInfo)) - { - labeledProperties = ElementFacade.GetLabeledProperties(elementHandle); - } - else - { - labeledProperties = ElementFacade.GetForeignLabeledProperties(elementHandle); - } + bool showForeign = UserSettings.ForeignLocaleCultureInfo != null + && UserSettings.ForeignLocaleCultureInfo.Equals(UserSettings.ActiveLocaleCultureInfo); + var labeledProperties = showForeign + ? ElementFacade.GetForeignLabeledProperties(elementHandle) + : ElementFacade.GetLabeledProperties(elementHandle); - return - (from property in labeledProperties - select new ClientLabeledProperty(property)).ToList(); + return + (from property in labeledProperties + select new ClientLabeledProperty(property)).ToList(); } @@ -282,8 +278,14 @@ public static List FindEntityToken(string serializedAncesto } - internal static List FindEntityToken(EntityToken ancestorEntityToken, EntityToken entityToken, List openedNodes) + internal static List FindEntityToken(EntityToken ancestorEntityToken, EntityToken entityToken, List nodesToRefresh) { + var openedNodes = nodesToRefresh.Select(node => new + { + EntityToken = EntityTokenSerializer.Deserialize(node.EntityToken), + ElementData = node + }).ToList(); + foreach (List ancestorChain in GetAncestorChains(ancestorEntityToken, entityToken)) { if (ancestorChain == null || ancestorChain.Count == 0) @@ -295,12 +297,12 @@ internal static List FindEntityToken(EntityToken ancestorEn int lastAlreadyOpenedNodeIndex = 0; while (lastAlreadyOpenedNodeIndex + 1 < ancestorChain.Count - && openedNodes.Any(node => EntityTokenSerializer.Deserialize(node.EntityToken).Equals(ancestorEntityTokens[lastAlreadyOpenedNodeIndex + 1]))) + && openedNodes.Any(node => node.EntityToken.Equals(ancestorEntityTokens[lastAlreadyOpenedNodeIndex + 1]))) { lastAlreadyOpenedNodeIndex++; } - var openNode = openedNodes.FirstOrDefault(node => EntityTokenSerializer.Deserialize(node.EntityToken).Equals(ancestorEntityTokens[lastAlreadyOpenedNodeIndex])); + var openNode = openedNodes.FirstOrDefault(node => node.EntityToken.Equals(ancestorEntityTokens[lastAlreadyOpenedNodeIndex])); if (openNode == null) { return null; @@ -311,15 +313,15 @@ internal static List FindEntityToken(EntityToken ancestorEn nodesToBeExpanded.AddRange(ancestorEntityTokens.Skip(lastAlreadyOpenedNodeIndex)); nodesToBeExpanded.RemoveAt(nodesToBeExpanded.Count - 1); // Last node is a target one, so doesn't have to be expanded - nodesToBeExpanded.AddRange(openedNodes.Select(s => EntityTokenSerializer.Deserialize(s.EntityToken))); + nodesToBeExpanded.AddRange(openedNodes.Select(node => node.EntityToken)); var result = new List(); // Expanding all the nodes, and checking if all of the nodes in the ancestor chain is marked // as seen in TreeLockBehaviour bool success = - ExpandNodesRec(openNode.EntityToken, - openNode.ProviderName, - openNode.Piggybag, + ExpandNodesRec(openNode.ElementData.EntityToken, + openNode.ElementData.ProviderName, + openNode.ElementData.Piggybag, nodesToBeExpanded, result, ancestorEntityTokens); @@ -432,7 +434,7 @@ private static IEnumerable> GetAncestorChains(EntityToken desc } visitedParents.Add(descendant); - List parents = ParentsFacade.GetAllParents(descendant); + List parents = ParentsFacade.GetAllParents(descendant); if (parents.Count == 0) { var newChain = new List {descendant}; diff --git a/Composite/Core/WebClient/PhantomJs/PhantomServer.cs b/Composite/Core/WebClient/PhantomJs/PhantomServer.cs index 811753ef56..6aa995b81e 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.RegisterFinalizerExecution(stack); +#endif // Finalizer calls Dispose(false) Dispose(false); } diff --git a/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs b/Composite/Core/WebClient/Presentation/OutputTransformationManager.cs index 87ac48d995..6a9a465535 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/Renderings/Page/PageRenderer.cs b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs index 7f185d3771..6f578f7235 100644 --- a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs +++ b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs @@ -7,7 +7,6 @@ using System.Web.UI.HtmlControls; using System.Xml.Linq; using Composite.Core.Caching; -using Composite.Core.Extensions; using Composite.Core.Routing; using Composite.Core.Routing.Pages; using Composite.Data; @@ -54,8 +53,8 @@ public static Control Render(this IPage page, IEnumerable{1}".FormatWith(Namespaces.Xhtml, placeholderContent.Content)); + return XhtmlDocument.Parse($"{placeholderContent.Content}"); } private static void ResolvePlaceholders(XDocument document, IEnumerable placeholderContents) { using (TimerProfilerFacade.CreateTimerProfiler()) { - List placeHolders = document.Descendants(RenderingElementNames.PlaceHolder).ToList(); + var placeHolders = + (from placeholder in document.Descendants(RenderingElementNames.PlaceHolder) + let idAttribute = placeholder.Attribute(RenderingElementNames.PlaceHolderIdAttribute) + where idAttribute != null + select new { Element = placeholder, IdAttribute = idAttribute}).ToList(); - if (placeHolders.Any()) + foreach (var placeholder in placeHolders) { - foreach (XElement placeHolder in placeHolders.Where(f => f.Attribute(RenderingElementNames.PlaceHolderIdAttribute) != null)) - { - IPagePlaceholderContent placeHolderContent = - placeholderContents - .FirstOrDefault(f => f.PlaceHolderId == placeHolder.Attribute(RenderingElementNames.PlaceHolderIdAttribute).Value); - - string placeHolderId = null; - - XAttribute idAttribute = placeHolder.Attribute("id"); - if (idAttribute != null) - { - placeHolderId = idAttribute.Value; - idAttribute.Remove(); - } - - XhtmlDocument xhtmlDocument = ParsePlaceholderContent(placeHolderContent); - placeHolder.ReplaceWith(xhtmlDocument.Root); - - - if (placeHolderId != null) - { - try - { - placeHolder.Add(new XAttribute("id", placeHolderId)); - } - catch (Exception ex) - { - throw new InvalidOperationException($"Failed to set id '{placeHolderId}' on element", ex); - } - } - } + string placeHolderId = placeholder.IdAttribute.Value; + placeholder.IdAttribute.Remove(); + + IPagePlaceholderContent placeHolderContent = + placeholderContents.FirstOrDefault(f => f.PlaceHolderId == placeHolderId); + + XhtmlDocument xhtmlDocument = ParsePlaceholderContent(placeHolderContent); + placeholder.Element.ReplaceWith(xhtmlDocument.Root); + + //try + //{ + // placeholder.Element.Add(new XAttribute(RenderingElementNames.PlaceHolderIdAttribute, placeHolderId)); + //} + //catch (Exception ex) + //{ + // throw new InvalidOperationException($"Failed to set id '{placeHolderId}' on element", ex); + //} } } } @@ -247,40 +236,83 @@ public static IEnumerable GetCurrentPageAssociatedData() where T : IDa } - /// - public static Control Render(XDocument document, FunctionContextContainer contextContainer, IXElementToControlMapper mapper, IPage page) + internal static void ProcessPageDocument( + XDocument document, + FunctionContextContainer contextContainer, + IPage page) { - using (TimerProfilerFacade.CreateTimerProfiler()) + using (Profiler.Measure("Executing embedded functions")) { ExecuteEmbeddedFunctions(document.Root, contextContainer); + } + using (Profiler.Measure("Resolving page fields")) + { ResolvePageFields(document, page); + } + using (Profiler.Measure("Normalizing ASP.NET forms")) + { NormalizeAspNetForms(document); + } + } - if (document.Root.Name != Namespaces.Xhtml + "html") - { - return new LiteralControl(document.ToString()); - } - - XhtmlDocument xhtmlDocument = new XhtmlDocument(document); + internal static void ProcessXhtmlDocument(XhtmlDocument xhtmlDocument, IPage page) + { + using (Profiler.Measure("Normalizing XHTML document")) + { NormalizeXhtmlDocument(xhtmlDocument); + } + using (Profiler.Measure("Resolving relative paths")) + { ResolveRelativePaths(xhtmlDocument); + } - PrioritizeHeadNodex(xhtmlDocument); + using (Profiler.Measure("Sorting elements")) + { + PrioritizeHeadNodes(xhtmlDocument); + } + using (Profiler.Measure("Appending C1 meta tags")) + { AppendC1MetaTags(page, xhtmlDocument); + } + using (Profiler.Measure("Parsing localization strings")) + { LocalizationParser.Parse(xhtmlDocument); + } + } - return xhtmlDocument.AsAspNetControl(mapper); + + /// + public static Control Render(XDocument document, FunctionContextContainer contextContainer, IXElementToControlMapper mapper, IPage page) + { + using (TimerProfilerFacade.CreateTimerProfiler()) + { + ProcessPageDocument(document, contextContainer, page); + + if (document.Root.Name != RenderingElementNames.Html) + { + return new LiteralControl(document.ToString()); + } + + var xhtmlDocument = new XhtmlDocument(document); + + ProcessXhtmlDocument(xhtmlDocument, page); + + using (Profiler.Measure("Converting XHTML document into an ASP.NET control")) + { + return xhtmlDocument.AsAspNetControl(mapper); + } } } - private static void PrioritizeHeadNodex(XhtmlDocument xhtmlDocument) + + private static void PrioritizeHeadNodes(XhtmlDocument xhtmlDocument) { - List> prioritizedHeadNodes = new List>(); + var prioritizedHeadNodes = new List>(); foreach (var node in xhtmlDocument.Head.Nodes().ToList()) { int p = GetHeadNodePriority(node); @@ -397,19 +429,18 @@ public static void ResolveRelativePaths(XhtmlDocument xhtmlDocument) hardRootedPathAttribute.Value = applicationVirtualPath + hardRootedPathAttribute.Value; } } - - } private static void NormalizeAspNetForms(XDocument document) { - List aspNetFormElements = document.Descendants(Namespaces.AspNetControls + "form").Reverse().ToList(); + var aspNetFormXName = Namespaces.AspNetControls + "form"; + List aspNetFormElements = document.Descendants(aspNetFormXName).Reverse().ToList(); foreach (XElement aspNetFormElement in aspNetFormElements) { - if (aspNetFormElement.Ancestors(Namespaces.AspNetControls + "form").Any()) + if (aspNetFormElement.Ancestors(aspNetFormXName).Any()) { aspNetFormElement.ReplaceWith(aspNetFormElement.Nodes()); } @@ -444,7 +475,6 @@ public static void ResolvePageFields(XDocument document, IPage page) new XAttribute("name", "description"), new XAttribute("content", page.Description))); } - } @@ -584,22 +614,29 @@ public static void NormalizeXhtmlDocument(XhtmlDocument rootDocument) { using (TimerProfilerFacade.CreateTimerProfiler()) { - XElement nestedDocument = rootDocument.Root.Descendants(Namespaces.Xhtml + "html").FirstOrDefault(); - - while (nestedDocument != null) + while (true) { - XhtmlDocument nestedXhtml = new XhtmlDocument(nestedDocument); + XElement nestedDocument = rootDocument.Root.Descendants(XhtmlDocument.XName_html).FirstOrDefault(); + + if (nestedDocument == null) break; + + var nestedHead = nestedDocument.Element(XhtmlDocument.XName_head); + var nestedBody = nestedDocument.Element(XhtmlDocument.XName_body); + + Verify.IsNotNull(nestedHead, "XHTML document is missing element"); + Verify.IsNotNull(nestedBody, "XHTML document is missing element"); + + rootDocument.Root.Add(nestedDocument.Attributes().Except(rootDocument.Root.Attributes(), _nameBasedAttributeComparer)); - rootDocument.Root.Add(nestedXhtml.Root.Attributes().Except(rootDocument.Root.Attributes(), _nameBasedAttributeComparer)); // making from nested documents appear first. We will not filter them later and this ensure desired precedence - rootDocument.Head.AddFirst(nestedXhtml.Head.Elements().Where(f=>f.Name.LocalName=="meta" && f.Attribute("property")!=null)); - rootDocument.Head.Add(nestedXhtml.Head.Nodes().Where(f => !(f is XElement) || !(((XElement)f).Name.LocalName == "meta" && ((XElement)f).Attribute("property") != null))); - rootDocument.Head.Add(nestedXhtml.Head.Attributes().Except(rootDocument.Head.Attributes(), _nameBasedAttributeComparer)); - rootDocument.Body.Add(nestedXhtml.Body.Attributes().Except(rootDocument.Body.Attributes(), _nameBasedAttributeComparer)); + bool IsMetaProperty(XElement e) => e.Name.LocalName == "meta" && e.Attribute("property") != null; - nestedDocument.ReplaceWith(nestedXhtml.Body.Nodes()); + rootDocument.Head.AddFirst(nestedHead.Elements().Where(IsMetaProperty)); + rootDocument.Head.Add(nestedHead.Nodes().Where(f => !(f is XElement e && IsMetaProperty(e)))); + rootDocument.Head.Add(nestedHead.Attributes().Except(rootDocument.Head.Attributes(), _nameBasedAttributeComparer)); + rootDocument.Body.Add(nestedBody.Attributes().Except(rootDocument.Body.Attributes(), _nameBasedAttributeComparer)); - nestedDocument = rootDocument.Root.Descendants(Namespaces.Xhtml + "html").FirstOrDefault(); + nestedDocument.ReplaceWith(nestedBody.Nodes()); } } } diff --git a/Composite/Core/WebClient/Renderings/Page/XElementToAspNetExtensions.cs b/Composite/Core/WebClient/Renderings/Page/XElementToAspNetExtensions.cs index 05b54520f5..2689a5be51 100644 --- a/Composite/Core/WebClient/Renderings/Page/XElementToAspNetExtensions.cs +++ b/Composite/Core/WebClient/Renderings/Page/XElementToAspNetExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; @@ -15,12 +14,17 @@ namespace Composite.Core.WebClient.Renderings.Page { - /// + /// /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class XElementToAspNetExtensions { + private static readonly XName XName_Id = "id"; + private static readonly XName XName_Xmlns = "xmlns"; + private static readonly XName XName_Title = Namespaces.Xhtml + "title"; + private static readonly XName XName_Meta = Namespaces.Xhtml + "meta"; + /// public static Control AsAspNetControl(this XhtmlDocument xhtmlDocument) { @@ -34,7 +38,7 @@ public static Control AsAspNetControl(this XhtmlDocument xhtmlDocument, IXElemen { using (TimerProfilerFacade.CreateTimerProfiler()) { - HtmlGenericControl htmlControl = new HtmlGenericControl("html"); + var htmlControl = new HtmlGenericControl("html"); CopyAttributes(xhtmlDocument.Root, htmlControl); HtmlHead headControl = xhtmlDocument.BuildHtmlHeadControl(controlMapper); @@ -48,9 +52,10 @@ public static Control AsAspNetControl(this XhtmlDocument xhtmlDocument, IXElemen if (xhtmlDocument.DocumentType != null) { string docType = xhtmlDocument.DocumentType.ToString(); - if (docType.Contains("[]")) + var offset = docType.IndexOf("[]", StringComparison.Ordinal); + if (offset >= 0) { - docType = docType.Remove(docType.IndexOf("[]"), 2); + docType = docType.Remove(offset, 2); } pageHolder.Controls.Add(new LiteralControl(docType)); @@ -74,13 +79,13 @@ public static Control AsAspNetControl(this XNode xnode) /// public static Control AsAspNetControl(this XNode xnode, IXElementToControlMapper controlMapper) { - if (xnode is XElement) return ((XElement)xnode).AsAspNetControl(controlMapper); - if (xnode is XDocument) return ((XDocument)xnode).Root.AsAspNetControl(controlMapper); + if (xnode is XElement element) return element.AsAspNetControl(controlMapper); + if (xnode is XDocument document) return document.Root.AsAspNetControl(controlMapper); - if (xnode is XText) return new LiteralControl(((XText)xnode).Value); - if (xnode is XComment) return new LiteralControl(string.Format("", ((XComment)xnode).Value)); + if (xnode is XText text) return new LiteralControl(text.Value); + if (xnode is XComment comment) return new LiteralControl($""); - throw new NotImplementedException(string.Format("Type '{0}' not handled", xnode.GetType().Name)); + throw new NotImplementedException($"Type '{xnode.GetType().Name}' not handled"); } @@ -98,12 +103,14 @@ public static Control AsAspNetControl(this XElement element, IXElementToControlM { Control control; - if (controlMapper.TryGetControlFromXElement(element, out control) == false) + if (!controlMapper.TryGetControlFromXElement(element, out control)) { - if (IsHtmlControlElement(element) || element.Attribute("id") != null) + if (IsHtmlControlElement(element) || element.Attribute(XName_Id) != null) { - control = new HtmlGenericControl(element.Name.LocalName); - control.ClientIDMode = ClientIDMode.Static; + control = new HtmlGenericControl(element.Name.LocalName) + { + ClientIDMode = ClientIDMode.Static + }; CopyAttributes(element, (HtmlControl)control); ExportChildNodes(element.Nodes(), control, controlMapper); } @@ -127,17 +134,18 @@ private static XElement CopyWithoutNamespace(XElement source, XNamespace namespa if (!sourceNs.Equals(namespaceToRemove) && sourceNs != source.Parent.Name.Namespace - && source.Attribute("xmlns") == null + && source.Attribute(XName_Xmlns) == null && (sourceNs == Namespaces.Xhtml.NamespaceName || sourceNs == Namespaces.Svg.NamespaceName)) { - copy.Add(new XAttribute("xmlns", source.Name.Namespace)); + copy.Add(new XAttribute(XName_Xmlns, source.Name.Namespace)); } copy.Add(source.Attributes().Where(a => a.Name.Namespace == namespaceToRemove) .Select(a => new XAttribute(a.Name.LocalName, a.Value))); Func isNotHtmlRelatedNsDeclaration = - ns => !ns.IsNamespaceDeclaration || (ns.Value != Namespaces.Xhtml.NamespaceName && ns.Value != Namespaces.Svg.NamespaceName); + ns => !ns.IsNamespaceDeclaration + || (ns.Value != Namespaces.Xhtml.NamespaceName && ns.Value != Namespaces.Svg.NamespaceName); copy.Add(source.Attributes().Where(a => a.Name.Namespace != namespaceToRemove && isNotHtmlRelatedNsDeclaration(a)) .Select(a => new XAttribute(a.Name, a.Value))); @@ -160,7 +168,6 @@ private static XElement CopyWithoutNamespace(XElement source, XNamespace namespa private static bool IsHtmlControlElement(XElement element) { - var name = element.Name; string xnamespace = element.Name.Namespace.NamespaceName; if (xnamespace == Namespaces.Xhtml.NamespaceName || xnamespace == string.Empty) @@ -187,18 +194,17 @@ private static void ExportChildNodes(IEnumerable nodes, Control container { foreach (var childNode in nodes) { - if (childNode is XElement) + if (childNode is XElement element) { - containerControl.Controls.Add(((XElement)childNode).AsAspNetControl(controlMapper)); + containerControl.Controls.Add(element.AsAspNetControl(controlMapper)); continue; } - if (childNode is XCData) + if (childNode is XCData cdata) { if (!childNode.Ancestors().Any(f => f.Name.LocalName == "script")) { - XCData cdata = (XCData)childNode; - LiteralControl literal = new LiteralControl(cdata.Value); + var literal = new LiteralControl(cdata.Value); containerControl.Controls.Add(literal); continue; } @@ -206,18 +212,18 @@ private static void ExportChildNodes(IEnumerable nodes, Control container if (childNode is XText) { - LiteralControl literal = new LiteralControl(childNode.ToString()); + var literal = new LiteralControl(childNode.ToString()); containerControl.Controls.Add(literal); continue; } if (childNode is XComment) { - containerControl.Controls.Add(new LiteralControl(childNode.ToString() + "\n")); + containerControl.Controls.Add(new LiteralControl(childNode + "\n")); continue; } - throw new NotImplementedException(string.Format("Unhandled XNode type '{0}'", childNode.GetType())); + throw new NotImplementedException($"Unhandled XNode type '{childNode.GetType()}'"); } } @@ -246,14 +252,14 @@ public static void CopyAttributes(this XElement source, HtmlControl target, bool if (namespaceName != "http://www.w3.org/1999/xhtml" && !namespaceName.StartsWith("http://www.composite.net/ns")) { - target.Attributes.Add(string.Format("xmlns:{0}", attribute.Name.LocalName), attribute.Value); + target.Attributes.Add($"xmlns:{attribute.Name.LocalName}", attribute.Value); } continue; } string localName = attribute.Name.LocalName; - if (localName != "xmlns" + if (localName != XName_Xmlns || (copyXmlnsAttribute && (source.Parent == null || source.Name.Namespace != source.Parent.Name.Namespace))) { @@ -285,7 +291,7 @@ internal static void MergeToHeadControl(this XhtmlDocument xhtmlDocument, HtmlHe CopyAttributes(headSource, headControl); - XElement titleElement = headSource.Elements(Namespaces.Xhtml + "title").LastOrDefault(); + XElement titleElement = headSource.Elements(XName_Title).LastOrDefault(); if (titleElement != null) { HtmlTitle existingControl = headControl.Controls.OfType().FirstOrDefault(); @@ -299,11 +305,11 @@ internal static void MergeToHeadControl(this XhtmlDocument xhtmlDocument, HtmlHe headControl.Controls.AddAt(0, new HtmlTitle { Text = HttpUtility.HtmlEncode(titleElement.Value) }); } - var metaTags = headSource.Elements().Where(f => f.Name == Namespaces.Xhtml + "meta"); + var metaTags = headSource.Elements().Where(f => f.Name == XName_Meta); int metaTagPosition = Math.Min(1, headControl.Controls.Count); foreach (var metaTag in metaTags) { - HtmlMeta metaControl = new HtmlMeta(); + var metaControl = new HtmlMeta(); foreach (var attribute in metaTag.Attributes()) { metaControl.Attributes.Add(attribute.Name.LocalName, attribute.Value); @@ -311,7 +317,9 @@ internal static void MergeToHeadControl(this XhtmlDocument xhtmlDocument, HtmlHe headControl.Controls.AddAt(metaTagPosition++, metaControl); } - ExportChildNodes(headSource.Nodes().Where(f => ((f is XElement) == false || ((XElement)f).Name != Namespaces.Xhtml + "title" && ((XElement)f).Name != Namespaces.Xhtml + "meta")), headControl, controlMapper); + ExportChildNodes(headSource.Nodes().Where(f => + !(f is XElement element) || (element.Name != XName_Title && element.Name != XName_Meta)), + headControl, controlMapper); headControl.RemoveDuplicates(); } @@ -322,7 +330,6 @@ private static void RemoveDuplicates(this HtmlHead headControl) { HashSet uniqueIdValues = new HashSet(); HashSet uniqueMetaNameValues = new HashSet(); - HashSet uniqueMetaPropertyValues = new HashSet(); HashSet uniqueScriptAttributes = new HashSet(); HashSet uniqueLinkAttributes = new HashSet(); @@ -330,8 +337,10 @@ private static void RemoveDuplicates(this HtmlHead headControl) // Leaving last instances of each meta tag, and first instances of script/link tags var priorityOrderedControls = new List(); - priorityOrderedControls.AddRange(controls.Where(c => c.TagName.ToLowerInvariant() == "meta").Reverse()); - priorityOrderedControls.AddRange(controls.Where(c => c.TagName.ToLowerInvariant() != "meta")); + + var ignoreCase = StringComparison.OrdinalIgnoreCase; + priorityOrderedControls.AddRange(controls.Where(c => c.TagName.Equals("meta", ignoreCase)).Reverse()); + priorityOrderedControls.AddRange(controls.Where(c => !c.TagName.Equals("meta", ignoreCase))); foreach (HtmlControl c in priorityOrderedControls) { @@ -363,15 +372,10 @@ private static void RemoveDuplicates(this HtmlHead headControl) private static string AttributesAsString(this HtmlControl c) { - List keys = new List(); - IEnumerator keysEnum = c.Attributes.Keys.GetEnumerator(); - - while (keysEnum.MoveNext()) - keys.Add((string)keysEnum.Current); - - StringBuilder str = new StringBuilder(c.ClientID); + var str = new StringBuilder(c.ClientID); + var keys = c.Attributes.Keys.Cast().OrderBy(f => f); - foreach (string key in keys.OrderBy(f => f)) + foreach (string key in keys) { str.Append(key); str.Append("=\""); @@ -386,12 +390,13 @@ private static bool IsDuplicate(HashSet uniqueList, string uniqueString) { if (!string.IsNullOrEmpty(uniqueString)) { - if (uniqueList.Contains(uniqueString.ToLowerInvariant())) + var lowered = uniqueString.ToLowerInvariant(); + if (uniqueList.Contains(lowered)) { return true; } - uniqueList.Add(uniqueString.ToLowerInvariant()); + uniqueList.Add(lowered); } return false; @@ -399,7 +404,7 @@ private static bool IsDuplicate(HashSet uniqueList, string uniqueString) private static HtmlHead BuildHtmlHeadControl(this XhtmlDocument xhtmlDocument, IXElementToControlMapper controlMapper) { - HtmlHead headControl = new HtmlHead(); + var headControl = new HtmlHead(); xhtmlDocument.MergeToHeadControl(headControl, controlMapper); @@ -410,7 +415,7 @@ private static HtmlHead BuildHtmlHeadControl(this XhtmlDocument xhtmlDocument, I #region Private helper class private class NoMappingMapper : IXElementToControlMapper { - private static NoMappingMapper _instance = new NoMappingMapper(); + private static readonly NoMappingMapper _instance = new NoMappingMapper(); public static IXElementToControlMapper GetInstance() { diff --git a/Composite/Core/WebClient/Renderings/RenderingContext.cs b/Composite/Core/WebClient/Renderings/RenderingContext.cs index f349517b85..81b5360349 100644 --- a/Composite/Core/WebClient/Renderings/RenderingContext.cs +++ b/Composite/Core/WebClient/Renderings/RenderingContext.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Text; using System.Web; -using System.Xml.Linq; using Composite.C1Console.Security; using Composite.Core.Extensions; using Composite.Core.Instrumentation; @@ -43,14 +42,14 @@ public sealed class RenderingContext: IDisposable /// The page. public IPage Page { get; private set; } + internal PageContentToRender PageContentToRender { get; private set; } + /// /// Indicates whether page caching is disabled. /// /// 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; @@ -125,10 +124,8 @@ public string ConvertInternalLinks(string xhtml) { using (Profiler.Measure("Converting internal urls to public")) { - xhtml = InternalUrls.ConvertInternalUrlsToPublic(xhtml); + return InternalUrls.ConvertInternalUrlsToPublic(xhtml); } - - return xhtml; } /// @@ -158,7 +155,7 @@ public string FormatXhtml(string xhtml) Log.LogWarning(LogTitle, "Failed to format output xhtml in a pretty way - your page output is likely not strict xml. Url: " + (HttpUtility.UrlDecode(_cachedUrl) ?? "undefined")); if (maxWarningsToShow == _prettifyErrorCount) { - Log.LogInformation(LogTitle, "{0} xhtml format errors logged since startup. No more will be logged until next startup.", maxWarningsToShow); + Log.LogInformation(LogTitle, $"{maxWarningsToShow} xhtml format errors logged since startup. No more will be logged until next startup."); } } } @@ -176,18 +173,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); } @@ -252,14 +243,22 @@ private void InitializeFromHttpContextInternal() _dataScope = new DataScope(Page.DataSourceId.PublicationScope, Page.DataSourceId.LocaleScope); var pagePlaceholderContents = GetPagePlaceholderContents(); - var pageRenderingJob = new PageContentToRender(Page, pagePlaceholderContents, PreviewMode); + PageContentToRender = new PageContentToRender(Page, pagePlaceholderContents, PreviewMode); - Verify.IsNotNull(httpContext.Handler, "HttpHandler isn't defined"); + AttachRendererToAspNetPage(httpContext); + } + + private void AttachRendererToAspNetPage(HttpContext context) + { + Verify.IsNotNull(context.Handler, "HttpHandler isn't defined"); + var aspnetPage = context.Handler as System.Web.UI.Page; + if (aspnetPage == null) + { + return; + } - var aspnetPage = (System.Web.UI.Page)httpContext.Handler; - var pageRenderer = PageTemplateFacade.BuildPageRenderer(Page.TemplateId); - pageRenderer.AttachToPage(aspnetPage, pageRenderingJob); + pageRenderer.AttachToPage(aspnetPage, PageContentToRender); } private void ValidateViewUnpublishedRequest(HttpContext httpContext) @@ -318,10 +317,7 @@ public void Dispose() { PageRenderingHistory.MarkPageAsRendered(this.Page); - if (_dataScope != null) - { - _dataScope.Dispose(); - } + _dataScope?.Dispose(); if (PreviewMode) { diff --git a/Composite/Core/WebClient/Renderings/RenderingElementNames.cs b/Composite/Core/WebClient/Renderings/RenderingElementNames.cs index 222d2771c1..511857f881 100644 --- a/Composite/Core/WebClient/Renderings/RenderingElementNames.cs +++ b/Composite/Core/WebClient/Renderings/RenderingElementNames.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Xml.Linq; +using System.Xml.Linq; using Composite.Core.Xml; namespace Composite.Core.WebClient.Renderings @@ -12,33 +8,29 @@ namespace Composite.Core.WebClient.Renderings /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static class RenderingElementNames - { + { /// - public static XName PlaceHolder { get { return _placeholder; } } + public static XName Html { get; } = Namespaces.Xhtml + "html"; /// - public static XName PlaceHolderIdAttribute { get { return "id"; } } + public static XName PlaceHolder { get; } = Namespaces.Rendering10 + "placeholder"; /// - public static XName PlaceHolderTitleAttribute { get { return "title"; } } + public static XName PlaceHolderIdAttribute { get; } = "id"; /// - public static XName PlaceHolderDefaultAttribute { get { return "default"; } } - + public static XName PlaceHolderTitleAttribute { get; } = "title"; /// - public static XName PageTitle { get { return _pageTitle; } } + public static XName PlaceHolderDefaultAttribute { get; } = "default"; /// - public static XName PageAbstract { get { return _pageAbstract; } } + public static XName PageTitle { get; } = Namespaces.Rendering10 + "page.title"; /// - public static XName PageMetaTagDescription { get { return _pageMetaTagDescription; } } - + public static XName PageAbstract { get; } = Namespaces.Rendering10 + "page.description"; - private static XName _placeholder = Namespaces.Rendering10 + "placeholder"; - private static XName _pageTitle = Namespaces.Rendering10 + "page.title"; - private static XName _pageAbstract = Namespaces.Rendering10 + "page.description"; - private static XName _pageMetaTagDescription = Namespaces.Rendering10 + "page.metatag.description"; + /// + public static XName PageMetaTagDescription { get; } = Namespaces.Rendering10 + "page.metatag.description"; } } diff --git a/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs b/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs index 55488d1f50..4a2cb99ec8 100644 --- a/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs +++ b/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs @@ -32,28 +32,50 @@ void context_BeginRequest(object sender, EventArgs e) return; } + IHostnameBinding hostnameBinding = HostnameBindingsFacade.GetBindingForCurrentRequest(); + + if (hostnameBinding != null + && hostnameBinding.EnforceHttps + && !httpContext.Request.IsSecureConnection) + { + RedirectToHttps(httpContext); + return; + } + if (HandleMediaRequest(httpContext)) { return; } - SetCultureByHostname(); + SetCultureByHostname(hostnameBinding); PrettifyPublicMarkup(httpContext); HandleRootRequestInClassicMode(httpContext); } - static void SetCultureByHostname() + private void RedirectToHttps(HttpContext context) { - IHostnameBinding hostnameBinding = HostnameBindingsFacade.GetBindingForCurrentRequest(); - if(hostnameBinding != null && !hostnameBinding.Culture.IsNullOrEmpty()) + var url = context.Request.Url.ToString(); + + const string expectedPrefix = "http:"; + Verify.That(url.StartsWith(expectedPrefix, StringComparison.OrdinalIgnoreCase), "Unexpected protocol, url: '{0}'", url); + + var redirectUrl = "https:" + url.Substring(expectedPrefix.Length); + context.Response.Redirect(redirectUrl, false); + } + + static void SetCultureByHostname(IHostnameBinding hostnameBinding) + { + if ((hostnameBinding?.Culture).IsNullOrEmpty()) { - var cultureInfo = new CultureInfo(hostnameBinding.Culture); - var thread = System.Threading.Thread.CurrentThread; - thread.CurrentCulture = cultureInfo; - thread.CurrentUICulture = cultureInfo; + return; } + + var cultureInfo = new CultureInfo(hostnameBinding.Culture); + var thread = System.Threading.Thread.CurrentThread; + thread.CurrentCulture = cultureInfo; + thread.CurrentUICulture = cultureInfo; } 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) { 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; diff --git a/Composite/Core/WebClient/Services/WysiwygEditor/PageTemplatePreview.cs b/Composite/Core/WebClient/Services/WysiwygEditor/PageTemplatePreview.cs index f372ef422c..3d90ba0a4a 100644 --- a/Composite/Core/WebClient/Services/WysiwygEditor/PageTemplatePreview.cs +++ b/Composite/Core/WebClient/Services/WysiwygEditor/PageTemplatePreview.cs @@ -60,7 +60,7 @@ public static bool GetPreviewInformation(HttpContext context, Guid pageId, Guid if (result.Status != RenderingResultStatus.Success) { Log.LogWarning("PageTemplatePreview", "Failed to build preview for page template '{0}'. Reason: {1}; Output:\r\n{2}", - templateId, result.Status, result.Output); + templateId, result.Status, string.Join(Environment.NewLine, result.Output)); imageFilePath = null; placeholders = null; diff --git a/Composite/Core/WebClient/UiControlLib/DataInput.cs b/Composite/Core/WebClient/UiControlLib/DataInput.cs index c83be63440..99f38c7c80 100644 --- a/Composite/Core/WebClient/UiControlLib/DataInput.cs +++ b/Composite/Core/WebClient/UiControlLib/DataInput.cs @@ -1,7 +1,7 @@ using System.ComponentModel; using System.Web.UI; using System.Web.UI.WebControls; - +using Castle.Core.Internal; using Composite.Core.WebClient.UiControlLib.Foundation; @@ -50,6 +50,10 @@ public DataInput() [Category("Appearance"), DefaultValue(""), Description("The type of data expected in this field")] public virtual DataInputType InputType { get; set; } + /// + [Category("Appearance"), DefaultValue(""), Description("Language to use for spell check for input field")] + public virtual string Language { get; set; } + /// [Category("Appearance"), DefaultValue(0), Description("The required minimum length of this field. Default is 0 (no minimum).")] public virtual int MinLength { get; set; } @@ -88,6 +92,11 @@ protected override void AddAttributesToRender(HtmlTextWriter writer) writer.AddAttribute("onvaluechange", "this.dispatchAction ( PageBinding.ACTION_DOPOSTBACK )"); } + if (!this.Language.IsNullOrEmpty()) + { + writer.AddAttribute("lang", Language); + } + if (!Enabled) { writer.AddAttribute("isdisabled", "true"); diff --git a/Composite/Core/WebClient/UiControlLib/Feedback.cs b/Composite/Core/WebClient/UiControlLib/Feedback.cs index 6db27df1de..811c700c39 100644 --- a/Composite/Core/WebClient/UiControlLib/Feedback.cs +++ b/Composite/Core/WebClient/UiControlLib/Feedback.cs @@ -58,6 +58,7 @@ public enum Status private Generic _requestTag; private Generic _responseTag; private Generic _consoleIdField; + private Generic _viewIdField; private string _consoleId; @@ -79,6 +80,11 @@ public Feedback(string emptyParameter) : base("ui:feedbackset") _consoleIdField.Attributes["clientid"] = "__CONSOLEID"; _consoleIdField.Attributes["name"] = "__CONSOLEID"; + _viewIdField = new Generic("input"); + _viewIdField.Attributes["type"] = "hidden"; + _viewIdField.Attributes["clientid"] = "__VIEWID"; + _viewIdField.Attributes["name"] = "__VIEWID"; + // Persisting "__CONSOLEID" field value _consoleId = GetConsoleId(); if (!_consoleId.IsNullOrEmpty()) @@ -89,6 +95,7 @@ public Feedback(string emptyParameter) : base("ui:feedbackset") this.Controls.Add(_requestTag); this.Controls.Add(_responseTag); _requestTag.Controls.Add(_consoleIdField); + _requestTag.Controls.Add(_viewIdField); } /// diff --git a/Composite/Core/Xml/XhtmlDocument.cs b/Composite/Core/Xml/XhtmlDocument.cs index ad39840ab5..5c6eba5bc9 100644 --- a/Composite/Core/Xml/XhtmlDocument.cs +++ b/Composite/Core/Xml/XhtmlDocument.cs @@ -21,11 +21,11 @@ namespace Composite.Core.Xml [XhtmlDocumentConverter] public sealed class XhtmlDocument : XDocument { - private static readonly XName _html_XName = Namespaces.Xhtml + "html"; - private static readonly XName _head_XName = Namespaces.Xhtml + "head"; - private static readonly XName _body_XName = Namespaces.Xhtml + "body"; + internal static readonly XName XName_html = Namespaces.Xhtml + "html"; + internal static readonly XName XName_head = Namespaces.Xhtml + "head"; + internal static readonly XName XName_body = Namespaces.Xhtml + "body"; - private static readonly string XhtmlFragmentDtdInternalSubset = null; + private static readonly string XhtmlFragmentDtdInternalSubset; private static readonly string EntityFileName = PathUtil.Resolve("~/App_Data/Composite/Configuration/Entities.xml"); static XhtmlDocument() @@ -55,9 +55,9 @@ static XhtmlDocument() /// Constructs an empty XhtmlDocument /// public XhtmlDocument() - : base(new XElement(_html_XName, - new XElement(_head_XName), - new XElement(_body_XName))) + : base(new XElement(XName_html, + new XElement(XName_head), + new XElement(XName_body))) { } @@ -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(XName_head); /// /// The body element for the XHTML Document /// - public XElement Body - { - get - { - return this.Root.Element(_body_XName); - } - } - + public XElement Body => this.Root.Element(XName_body); /// @@ -173,7 +160,7 @@ private void Validate() { if (this.Root != null) { - Verify.That(this.Root.Name == _html_XName, "Supplied XDocument must have a root named html belonging to the namespace xmlns=\"{0}\"", Namespaces.Xhtml); + Verify.That(this.Root.Name == XName_html, "Supplied XDocument must have a root named html belonging to the namespace xmlns=\"{0}\"", Namespaces.Xhtml); Verify.IsNotNull(this.Head, "XHTML document is missing element"); Verify.IsNotNull(this.Body, "XHTML document is missing element"); } @@ -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; } 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); + } } } diff --git a/Composite/Core/Xml/XhtmlPrettifier.cs b/Composite/Core/Xml/XhtmlPrettifier.cs index 4354d8778f..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 = "" }, @@ -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 = "" }, @@ -694,7 +695,7 @@ private class XmlAttribute - private sealed class NamespaceName + internal sealed class NamespaceName { public string Name; public string Namespace; diff --git a/Composite/Data/Caching/CachedTable.cs b/Composite/Data/Caching/CachedTable.cs index 72929cb189..e66695ace2 100644 --- a/Composite/Data/Caching/CachedTable.cs +++ b/Composite/Data/Caching/CachedTable.cs @@ -13,7 +13,11 @@ internal abstract class CachedTable /// public abstract IQueryable Queryable { get; } - internal abstract void Remove(IEnumerable dataset); + internal abstract bool Add(IEnumerable dataset); + + internal abstract bool Update(IEnumerable dataset); + + internal abstract bool Remove(IEnumerable dataset); /// /// Row by key table @@ -21,7 +25,7 @@ internal abstract class CachedTable public abstract IReadOnlyDictionary> RowsByKey { get; } } - internal class CachedTable : CachedTable + internal class CachedTable : CachedTable { private IReadOnlyCollection _items; private IReadOnlyDictionary> _rowsByKey; @@ -33,18 +37,88 @@ public CachedTable(IReadOnlyCollection items) public override IQueryable Queryable => _items.AsQueryable(); - internal override void Remove(IEnumerable dataset) + + internal override bool Add(IEnumerable dataset) + { + var toAdd = dataset.Cast().ToList(); + + lock (this) + { + var existingRows = _items; + + var newTable = new List(existingRows.Count + toAdd.Count); + newTable.AddRange(existingRows); + newTable.AddRange(toAdd); + + _items = newTable; + + return true; + } + } + + + internal override bool Update(IEnumerable dataset) + { + var toUpdate = dataset.ToDictionary(_ => _.DataSourceId); + + lock (this) + { + var existingRows = _items; + + + var updated = new List(existingRows.Count); + + int updatedTotal = 0; + foreach (var data in existingRows) + { + bool matched = toUpdate.TryGetValue(((IData)data).DataSourceId, out IData updatedDataItem); + + if (matched) + { + updatedTotal++; + } + + updated.Add(matched ? (T)updatedDataItem : data); + } + + _items = updated.AsReadOnly(); + _rowsByKey = null; // Can be optimized as well + + return updatedTotal == toUpdate.Count; + } + } + + + internal override bool Remove(IEnumerable dataset) { var toRemove = new HashSet(dataset.Select(_ => _.DataSourceId)); lock (this) { var existingRows = _items; - _items = existingRows.Where(data => !toRemove.Contains((data as IData).DataSourceId)).ToList(); + + if (existingRows.Count < toRemove.Count) return false; + + var newTable = new List(existingRows.Count - toRemove.Count); + + int removed = 0; + foreach (var item in existingRows) + { + if (toRemove.Contains(((IData)item).DataSourceId)) + { + removed++; + continue; + } + newTable.Add(item); + } + _items = newTable; _rowsByKey = null; // Can be optimized as well + + return removed == toRemove.Count; } } + public override IReadOnlyDictionary> RowsByKey { get 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/Caching/DataCachingFacade.cs b/Composite/Data/Caching/DataCachingFacade.cs index 550fc63034..a2b1c73a73 100644 --- a/Composite/Data/Caching/DataCachingFacade.cs +++ b/Composite/Data/Caching/DataCachingFacade.cs @@ -8,6 +8,7 @@ using Composite.Data.Foundation; using Composite.Data.Foundation.PluginFacades; using Composite.C1Console.Events; +using Composite.Core; using Composite.Core.Configuration; using ScopeKey = System.Tuple; @@ -211,10 +212,34 @@ public static void ClearCache(Type interfaceType, DataScopeIdentifier dataScopeI /// /// Removes the specified data collection from the cache. - /// The items should belong to the same interface type and the same data scope. /// /// - internal static void RemoveFromCache(IReadOnlyCollection dataset) + internal static void RemoveDataFromCache(IReadOnlyCollection dataset) + { + UpdateCachedTables(dataset, (table, data) => table.Remove(data)); + } + + /// + /// Removes the specified data collection from the cache. + /// + /// + internal static void UpdateCachedData(IReadOnlyCollection dataset) + { + UpdateCachedTables(dataset, (table, data) => table.Update(data)); + } + + /// + /// Adds the specified data collection to the cache. + /// + /// + internal static void AddDataToCache(IReadOnlyCollection dataset) + { + UpdateCachedTables(dataset, (table, data) => table.Add(data)); + } + + internal static void UpdateCachedTables( + IReadOnlyCollection dataset, + Func, bool> action) { if (dataset.Count == 0) return; @@ -242,7 +267,14 @@ internal static void RemoveFromCache(IReadOnlyCollection dataset) continue; } - cachedTable.Remove(group); + bool success = action(cachedTable, group); + if (!success) + { + var key = group.Key; + Log.LogError(nameof(DataCachingFacade), $"Cache out of sync for type '{key.InterfaceType}' scope '{key.DataScopeIdentifier}' culture '{key.LocaleScope}'"); + + ClearCache(key.InterfaceType, key.DataScopeIdentifier, key.LocaleScope); + } } // TODO: clear cache on transaction rollback? diff --git a/Composite/Data/DataConnection.cs b/Composite/Data/DataConnection.cs index c3ea89ba42..fe77b05724 100644 --- a/Composite/Data/DataConnection.cs +++ b/Composite/Data/DataConnection.cs @@ -515,17 +515,22 @@ public SitemapNavigator SitemapNavigator public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; /// ~DataConnection() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } - +#endif /// diff --git a/Composite/Data/DataEntityToken.cs b/Composite/Data/DataEntityToken.cs index a5745e0049..1ba9a62a62 100644 --- a/Composite/Data/DataEntityToken.cs +++ b/Composite/Data/DataEntityToken.cs @@ -5,6 +5,8 @@ using System.Text; using Composite.C1Console.Security; using Composite.Core.Types; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace Composite.Data @@ -14,6 +16,7 @@ namespace Composite.Data /// to move between data items and EntityToken. /// [SecurityAncestorProvider(typeof(DataSecurityAncestorProvider))] + [JsonObject(MemberSerialization.OptIn)] public sealed class DataEntityToken : EntityToken { private IData _data; @@ -40,7 +43,6 @@ internal DataEntityToken(IData data) } - private DataEntityToken(string serializedDataSourceId) { _data = null; @@ -49,7 +51,14 @@ private DataEntityToken(string serializedDataSourceId) _dataSourceId = null; } - + [JsonConstructor] + private DataEntityToken(JRaw dataSourceId) + { + _data = null; + _dataInitialized = false; + _serializedDataSourceId = dataSourceId.Value.ToString(); + _dataSourceId = null; + } /// public override string Type @@ -183,14 +192,15 @@ public override void OnGetPrettyHtml(EntityTokenHtmlPrettyfier prettifier) foreach (PropertyInfo propertyInfo in dataId.GetType().GetPropertiesRecursively()) { - sb.Append("" + propertyInfo.Name + ": " + propertyInfo.GetValue(dataId, null).ToString() + "
"); + sb.Append($"{propertyInfo.Name}: {propertyInfo.GetValue(dataId, null)}
"); } helper.AddFullRow(new [] { "Id", sb.ToString() }); }; } - + [JsonProperty(PropertyName = "dataSourceId")] + private JRaw rawSerializedDataSourceId => new JRaw(_serializedDataSourceId); private string SerializedDataSourceId { 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/DataFacadeImpl.cs b/Composite/Data/DataFacadeImpl.cs index c4f31aa1e0..571819ad79 100644 --- a/Composite/Data/DataFacadeImpl.cs +++ b/Composite/Data/DataFacadeImpl.cs @@ -298,7 +298,7 @@ public void Update(IEnumerable dataset, bool suppressEventing, bool perfo if (DataCachingFacade.IsTypeCacheable(interfaceTypePair.Key)) { - List newDataToUpdate = new List(); + var newDataToUpdate = new List(); foreach (IData d in interfaceTypePair.Value) { @@ -312,7 +312,7 @@ public void Update(IEnumerable dataset, bool suppressEventing, bool perfo if (DataCachingFacade.IsTypeCacheable(interfaceTypePair.Key)) { - DataCachingFacade.ClearCache(interfaceTypePair.Key); + DataCachingFacade.UpdateCachedData(dataToUpdate); } } } @@ -431,7 +431,7 @@ private static List AddNew_AddingMethod(string providerName, IEnumerable addedDataset = DataProviderPluginFacade.AddNew(providerName, dataset); - DataCachingFacade.ClearCache(typeof(T), DataScopeManager.MapByType(typeof(T)), LocalizationScopeManager.MapByType(typeof(T))); + DataCachingFacade.AddDataToCache(addedDataset); if (!suppressEventing) { @@ -537,7 +537,7 @@ private void Delete(IEnumerable dataset, bool suppressEventing, CascadeDel if (DataCachingFacade.IsTypeCacheable(interfaceTypePair.Key)) { - DataCachingFacade.RemoveFromCache(interfaceTypePair.Value); + DataCachingFacade.RemoveDataFromCache(interfaceTypePair.Value); } } } diff --git a/Composite/Data/DataIdSerializer.cs b/Composite/Data/DataIdSerializer.cs index d687425f8a..5a2366c68d 100644 --- a/Composite/Data/DataIdSerializer.cs +++ b/Composite/Data/DataIdSerializer.cs @@ -1,12 +1,9 @@ using System; using System.Collections.Generic; -using System.Text; using Composite.Core.Serialization; using Composite.Core.Types; - using static Composite.Core.Serialization.StringConversionServices; - namespace Composite.Data { internal static class DataIdSerializer @@ -15,55 +12,69 @@ public static string Serialize(this IDataId dataId, IEnumerable property { if (dataId == null) throw new ArgumentNullException(nameof(dataId)); - var sb = new StringBuilder(); - SerializeKeyValuePair(sb, "_dataIdType_", TypeManager.SerializeType(dataId.GetType())); - SerializeKeyValuePair(sb, "_dataId_", SerializationFacade.Serialize(dataId, propertyNames)); - - return sb.ToString(); + return CompositeJsonSerializer.SerializePartial(dataId,propertyNames); } - public static IDataId Deserialize(string serializedId, string serializedVersionId) - { - Dictionary dataIdValues = ParseKeyValueCollection(serializedId); - - if (!dataIdValues.ContainsKey("_dataIdType_") || - !dataIdValues.ContainsKey("_dataId_")) - { - throw new ArgumentException("The serializedId is not a serialized id", nameof(serializedId)); - } + public static IDataId Deserialize(string serializedId, string serializedVersionId) + { + if (CompositeJsonSerializer.IsJsonSerialized(serializedId) && + CompositeJsonSerializer.IsJsonSerialized(serializedVersionId)) + { + return CompositeJsonSerializer.Deserialize(serializedId, serializedVersionId); + } + else if (!(CompositeJsonSerializer.IsJsonSerialized(serializedId) && + CompositeJsonSerializer.IsJsonSerialized(serializedVersionId))) + { + return DeserializeLegacy(serializedId, serializedVersionId); + } + else + { + throw new ArgumentException($"{nameof(IDataId)} is not serialized properly.",nameof(serializedId)+" and "+ nameof(serializedVersionId)); + } + } - string dataIdType = DeserializeValueString(dataIdValues["_dataIdType_"]); - string serializedIdString = DeserializeValueString(dataIdValues["_dataId_"]); + public static IDataId DeserializeLegacy(string serializedId, string serializedVersionId) + { + Dictionary dataIdValues = ParseKeyValueCollection(serializedId); - string serializedVersionIdString = ""; + if (!dataIdValues.ContainsKey("_dataIdType_") || + !dataIdValues.ContainsKey("_dataId_")) + { + throw new ArgumentException("The serializedId is not a serialized id", nameof(serializedId)); + } - if (!string.IsNullOrEmpty(serializedVersionId)) - { - Dictionary versionValues = ParseKeyValueCollection(serializedVersionId); + string dataIdType = DeserializeValueString(dataIdValues["_dataIdType_"]); + string serializedIdString = DeserializeValueString(dataIdValues["_dataId_"]); - if (!versionValues.ContainsKey("_dataIdType_") || - !versionValues.ContainsKey("_dataId_")) - { - throw new ArgumentException("The serializedVersionId is not a serialized version id", nameof(serializedVersionId)); - } + string serializedVersionIdString = ""; - if (dataIdValues["_dataIdType_"] != versionValues["_dataIdType_"]) - { - throw new ArgumentException("Serialized id and version id have diffrent types", nameof(serializedId)); - } + if (!string.IsNullOrEmpty(serializedVersionId)) + { + Dictionary versionValues = ParseKeyValueCollection(serializedVersionId); - serializedVersionIdString = DeserializeValueString(versionValues["_dataId_"]); - } + if (!versionValues.ContainsKey("_dataIdType_") || + !versionValues.ContainsKey("_dataId_")) + { + throw new ArgumentException("The serializedVersionId is not a serialized version id", nameof(serializedVersionId)); + } - Type type = TypeManager.TryGetType(dataIdType); - if (type == null) - { - throw new InvalidOperationException($"The type {dataIdType} could not be found"); - } + if (dataIdValues["_dataIdType_"] != versionValues["_dataIdType_"]) + { + throw new ArgumentException("Serialized id and version id have diffrent types", nameof(serializedId)); + } - IDataId dataId = SerializationFacade.Deserialize(type, string.Join("", serializedIdString, serializedVersionIdString)); + serializedVersionIdString = DeserializeValueString(versionValues["_dataId_"]); + } - return dataId; - } - } + Type type = TypeManager.TryGetType(dataIdType); + if (type == null) + { + throw new InvalidOperationException($"The type {dataIdType} could not be found"); + } + + IDataId dataId = SerializationFacade.Deserialize(type, string.Join("", serializedIdString, serializedVersionIdString)); + + return dataId; + } + } } diff --git a/Composite/Data/DataScope.cs b/Composite/Data/DataScope.cs index 681d915e8e..bc79a1bd9c 100644 --- a/Composite/Data/DataScope.cs +++ b/Composite/Data/DataScope.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; @@ -92,17 +92,23 @@ public DataScope(PublicationScope publicationScope, CultureInfo cultureInfo) { } +#if LeakCheck + private string stack = Environment.StackTrace; /// ~DataScope() { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); Dispose(false); } +#endif /// public void Dispose() { Dispose(true); +#if LeakCheck GC.SuppressFinalize(this); +#endif } void Dispose(bool disposing) diff --git a/Composite/Data/DataScopeIdentifier.cs b/Composite/Data/DataScopeIdentifier.cs index bf98c42043..800f28e94c 100644 --- a/Composite/Data/DataScopeIdentifier.cs +++ b/Composite/Data/DataScopeIdentifier.cs @@ -1,5 +1,6 @@ using System; using System.Diagnostics; +using Newtonsoft.Json; namespace Composite.Data @@ -29,13 +30,12 @@ public static DataScopeIdentifier GetDefault() return DataScopeIdentifier.Public; } - - private DataScopeIdentifier(string dataScope) + [JsonConstructor] + private DataScopeIdentifier(string name) { - this.Name = dataScope; + this.Name = name; } - /// public string Name { diff --git a/Composite/Data/DataSourceId.cs b/Composite/Data/DataSourceId.cs index 56087da730..e341019e36 100644 --- a/Composite/Data/DataSourceId.cs +++ b/Composite/Data/DataSourceId.cs @@ -5,6 +5,7 @@ using Composite.Core.Types; using Composite.Data.Foundation; using Composite.Data.Types; +using Newtonsoft.Json; namespace Composite.Data @@ -14,8 +15,6 @@ namespace Composite.Data ///
public sealed class DataSourceId { - private DataScopeIdentifier _dataScopeIdentifier; - private CultureInfo _localeScope; private string _serializedData; private Dictionary _tagInformation; @@ -24,16 +23,12 @@ public sealed class DataSourceId ///
public DataSourceId(IDataId dataId, string providerName, Type interfaceType) { - if (null == dataId) throw new ArgumentNullException("dataId"); - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (null == interfaceType) throw new ArgumentNullException("interfaceType"); - - - this.DataId = dataId; + if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException(nameof(providerName)); + this.DataId = dataId ?? throw new ArgumentNullException(nameof(dataId)); ProviderName = providerName; - InterfaceType = interfaceType; - _dataScopeIdentifier = DataScopeManager.MapByType(interfaceType); - _localeScope = LocalizationScopeManager.MapByType(interfaceType); + InterfaceType = interfaceType ?? throw new ArgumentNullException(nameof(interfaceType)); + DataScopeIdentifier = DataScopeManager.MapByType(interfaceType); + LocaleScope = LocalizationScopeManager.MapByType(interfaceType); this.ExistsInStore = true; } @@ -42,23 +37,30 @@ public DataSourceId(IDataId dataId, string providerName, Type interfaceType) /// /// This is for internal use only! /// - public DataSourceId(IDataId dataId, string providerName, Type interfaceType, DataScopeIdentifier dataScope, CultureInfo localeScope) + public DataSourceId(IDataId dataId, string providerName, Type interfaceType, DataScopeIdentifier dataScopeIdentifier, CultureInfo localeScope) { // This constructor has to be extremely fast, we have up to 100.000 objects related while some requests - if (dataId == null) throw new ArgumentNullException("dataId"); - if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException("providerName"); - if (interfaceType == null) throw new ArgumentNullException("interfaceType"); - if (dataScope == null) throw new ArgumentNullException("dataScope"); - if (localeScope == null) throw new ArgumentNullException("localeScope"); - - this.DataId = dataId; + if (string.IsNullOrEmpty(providerName)) throw new ArgumentNullException(nameof(providerName)); + this.DataId = dataId ?? throw new ArgumentNullException(nameof(dataId)); ProviderName = providerName; - InterfaceType = interfaceType; - _dataScopeIdentifier = dataScope; - _localeScope = localeScope; + InterfaceType = interfaceType ?? throw new ArgumentNullException(nameof(interfaceType)); + DataScopeIdentifier = dataScopeIdentifier ?? throw new ArgumentNullException(nameof(dataScopeIdentifier)); + LocaleScope = localeScope ?? throw new ArgumentNullException(nameof(localeScope)); this.ExistsInStore = true; } + [JsonConstructor] + private DataSourceId(IDataId dataId, string providerName, Type interfaceType, DataScopeIdentifier dataScopeIdentifier, string localeScope) + { + // This constructor has to be extremely fast, we have up to 100.000 objects related while some requests + + this.DataId = dataId ?? throw new ArgumentNullException(nameof(dataId)); + ProviderName = providerName ?? DataProviderRegistry.DefaultDynamicTypeDataProviderName; + InterfaceType = interfaceType ?? throw new ArgumentNullException(nameof(interfaceType)); + DataScopeIdentifier = dataScopeIdentifier ?? throw new ArgumentNullException(nameof(dataScopeIdentifier)); + LocaleScope = CultureInfo.CreateSpecificCulture(localeScope); + this.ExistsInStore = true; + } /// @@ -83,6 +85,12 @@ internal DataSourceId(Type interfaceType) /// public string ProviderName { get; } + /// + public bool ShouldSerializeProviderName() + { + // don't serialize ProviderName if it is default + return ProviderName != DataProviderRegistry.DefaultDynamicTypeDataProviderName; + } /// /// The interface used for the data element. This is expected to be implementing IData. @@ -93,71 +101,44 @@ internal DataSourceId(Type interfaceType) /// /// The data scope (language and published/unpublished) from which the data element originate. /// - public DataScopeIdentifier DataScopeIdentifier - { - get { return _dataScopeIdentifier; } - internal set { _dataScopeIdentifier = value; } - } - - + public DataScopeIdentifier DataScopeIdentifier { get; internal set; } /// /// The publication scope (published or unpublished) from which the data element originate. /// - public PublicationScope PublicationScope => _dataScopeIdentifier.ToPublicationScope(); + [JsonIgnore] + public PublicationScope PublicationScope => DataScopeIdentifier.ToPublicationScope(); /// /// The language from which the data element originate. /// - public CultureInfo LocaleScope - { - get { return _localeScope; } - internal set { _localeScope = value; } - } - + [JsonIgnore] + public CultureInfo LocaleScope { get; internal set; } + [JsonProperty(PropertyName = "localeScope")] + private string LocalScopeName => LocaleScope.Name; /// /// True when the data element represents a physically stored element /// + [JsonIgnore] public bool ExistsInStore { get; } - - /// /// Serialize to string /// /// Serialized as string public string Serialize() { - if (_serializedData == null) - { - string s = SerializationFacade.Serialize(this.DataId); - - System.Text.StringBuilder sb = new System.Text.StringBuilder(); - StringConversionServices.SerializeKeyValuePair(sb, "_dataId_", s); - StringConversionServices.SerializeKeyValuePair(sb, "_dataIdType_", TypeManager.SerializeType(this.DataId.GetType())); - - if (ProviderName != DataProviderRegistry.DefaultDynamicTypeDataProviderName) - { - StringConversionServices.SerializeKeyValuePair(sb, "_providerName_", ProviderName); - } - - StringConversionServices.SerializeKeyValuePair(sb, "_interfaceType_", TypeManager.SerializeType(InterfaceType)); - StringConversionServices.SerializeKeyValuePair(sb, "_dataScope_", DataScopeIdentifier.Serialize()); - StringConversionServices.SerializeKeyValuePair(sb, "_localeScope_", LocaleScope.Name); - - _serializedData = sb.ToString(); - } - - return _serializedData; + return _serializedData ?? (_serializedData = CompositeJsonSerializer.Serialize(this)); } + /// /// Recreate a DataSourceId based on a serialized string representation of it. @@ -189,6 +170,17 @@ public static bool TryDeserialize(string serializedDataSourceId, out DataSourceI private static bool Deserialize(string serializedDataSourceId, out DataSourceId dataSourceId, bool throwException) + { + if(CompositeJsonSerializer.IsJsonSerialized(serializedDataSourceId)) + { + dataSourceId = CompositeJsonSerializer.Deserialize(serializedDataSourceId); + return true; + } + + return DeserializeLegacy(serializedDataSourceId, out dataSourceId, throwException); + } + + private static bool DeserializeLegacy(string serializedDataSourceId, out DataSourceId dataSourceId, bool throwException) { dataSourceId = null; @@ -210,8 +202,8 @@ private static bool Deserialize(string serializedDataSourceId, out DataSourceId string serializedDataId = StringConversionServices.DeserializeValueString(dic["_dataId_"]); string dataIdTypeName = StringConversionServices.DeserializeValueString(dic["_dataIdType_"]); - string providerName = dic.ContainsKey("_providerName_") - ? StringConversionServices.DeserializeValueString(dic["_providerName_"]) + string providerName = dic.ContainsKey("_providerName_") + ? StringConversionServices.DeserializeValueString(dic["_providerName_"]) : DataProviderRegistry.DefaultDynamicTypeDataProviderName; string interfaceTypeName = StringConversionServices.DeserializeValueString(dic["_interfaceType_"]); @@ -251,15 +243,13 @@ private static bool Deserialize(string serializedDataSourceId, out DataSourceId private static string FixSerializedDataId(string serializedDataId, Type interfaceType) { - if (interfaceType == typeof (IPage) && !serializedDataId.Contains(nameof(IPage.VersionId))) + if (interfaceType == typeof(IPage) && !serializedDataId.Contains(nameof(IPage.VersionId))) { return serializedDataId + "," + serializedDataId.Replace(nameof(IPage.Id), nameof(IPage.VersionId)); } return serializedDataId; } - - /// public override string ToString() => Serialize(); @@ -267,11 +257,7 @@ private static string FixSerializedDataId(string serializedDataId, Type interfac /// public override bool Equals(object obj) { - if (obj == null) return false; - - DataSourceId dataSourceId = obj as DataSourceId; - - return Equals(dataSourceId); + return obj is DataSourceId dataSourceId && Equals(dataSourceId); } @@ -279,13 +265,21 @@ public override bool Equals(object obj) /// public bool Equals(DataSourceId dataSourceId) { - return dataSourceId != null && dataSourceId.ToString() == ToString(); + return dataSourceId != null + && dataSourceId.ProviderName == ProviderName + && dataSourceId.DataId.Equals(DataId) + && dataSourceId.InterfaceType == InterfaceType + && dataSourceId.DataScopeIdentifier.Equals(DataScopeIdentifier) + && dataSourceId.LocaleScope.Name.Equals(LocaleScope.Name); } /// - public override int GetHashCode() => ToString().GetHashCode(); + public override int GetHashCode() + => DataId.GetHashCode() ^ InterfaceType.GetHashCode() ^ ProviderName.GetHashCode() + ^ DataScopeIdentifier.GetHashCode() ^ LocaleScope.GetHashCode(); + internal void SetTag(string id, object value) 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, diff --git a/Composite/Data/PageManager.cs b/Composite/Data/PageManager.cs index b3dcec1d3f..5162982708 100644 --- a/Composite/Data/PageManager.cs +++ b/Composite/Data/PageManager.cs @@ -204,7 +204,7 @@ public static ReadOnlyCollection GetPlaceholderContent( return cachedValue; } - var list = DataFacade.GetData() + var list = DataFacade.GetData(false) .Where(f => f.PageId == pageId && f.VersionId == versionId).ToList(); 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 - { - } - } - } } } 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/Data/Types/Foundation/PagePublishControlledAuxiliary.cs b/Composite/Data/Types/Foundation/PagePublishControlledAuxiliary.cs index 2b450d9f61..c758f840ef 100644 --- a/Composite/Data/Types/Foundation/PagePublishControlledAuxiliary.cs +++ b/Composite/Data/Types/Foundation/PagePublishControlledAuxiliary.cs @@ -18,7 +18,7 @@ public void OnAfterDataUpdated(IData data) using (new DataScope(DataScopeIdentifier.Administrated)) { pagePlaceholderContents = - (from content in DataFacade.GetData() + (from content in DataFacade.GetData(false) where content.PageId == page.Id && content.VersionId == page.VersionId select content).ToList(); } diff --git a/Composite/Data/Types/IHostnameBinding.cs b/Composite/Data/Types/IHostnameBinding.cs index 976d43684c..2b1723ee14 100644 --- a/Composite/Data/Types/IHostnameBinding.cs +++ b/Composite/Data/Types/IHostnameBinding.cs @@ -14,7 +14,7 @@ namespace Composite.Data.Types [KeyPropertyName("Id")] [LabelPropertyName("Hostname")] [DataScope(DataScopeIdentifier.PublicName)] - [CachingAttribute(CachingType.Full)] + [Caching(CachingType.Full)] [DataAncestorProvider(typeof(NoAncestorDataAncestorProvider))] public interface IHostnameBinding : IData { @@ -61,5 +61,14 @@ public interface IHostnameBinding : IData [StoreFieldType(PhysicalStoreFieldType.Boolean)] [ImmutableFieldId("{A554BD68-C2F5-6F6F-4D3B-1FC2760BDE38}")] bool IncludeCultureInUrl { get; set; } + + /// + /// When set to true, the system will generate HTTPS links to the hostname and + /// will make automatic redirects from HTTP to HTTPS for the given hostname. + /// + [StoreFieldType(PhysicalStoreFieldType.Boolean)] + [DefaultFieldBoolValue(false)] + [ImmutableFieldId("{1F45F2E7-2FAF-4F33-ACCA-BFF32EF7818A}")] + bool EnforceHttps { get; set; } } } 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; } diff --git a/Composite/GlobalInitializerFacade.cs b/Composite/GlobalInitializerFacade.cs index d962b088e8..5965aa4030 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 } @@ -820,6 +832,9 @@ where sf.GetMethod().DeclaringType.Assembly.FullName.Contains("Composite.Test") public void Dispose() { +#if LeakCheck + GC.SuppressFinalize(this); +#endif if (!_isWriterLock) { ReleaseReaderLock(); @@ -850,6 +865,15 @@ where sf.GetMethod().DeclaringType.Assembly.FullName.Contains("Composite.Test") ReleaseWriterLock(); } + +#if LeakCheck + private string stack = Environment.StackTrace; + /// + ~LockerToken() + { + Composite.Core.Instrumentation.DisposableResourceTracer.RegisterFinalizerExecution(stack); + } +#endif } #endregion diff --git a/Composite/Plugins/Data/DataProviders/FileSystemDataProvider/Foundation/FileSystemFileDataId.cs b/Composite/Plugins/Data/DataProviders/FileSystemDataProvider/Foundation/FileSystemFileDataId.cs index 296a6c3f22..a4cc470ee8 100644 --- a/Composite/Plugins/Data/DataProviders/FileSystemDataProvider/Foundation/FileSystemFileDataId.cs +++ b/Composite/Plugins/Data/DataProviders/FileSystemDataProvider/Foundation/FileSystemFileDataId.cs @@ -1,4 +1,4 @@ -using System; +using System; using Composite.Data; @@ -10,7 +10,7 @@ namespace Composite.Plugins.Data.DataProviders.FileSystemDataProvider.Foundation [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public sealed class FileSystemFileDataId : IDataId { - private string _fullPath = null; + private string _fullPath; /// @@ -33,5 +33,13 @@ public string FullPath } set { _fullPath = value; } } + + + /// + public override int GetHashCode() => _fullPath.GetHashCode(); + + + /// + public override bool Equals(object obj) => obj is FileSystemFileDataId dataId && dataId.FullPath == _fullPath; } } \ No newline at end of file diff --git a/Composite/Plugins/Data/DataProviders/FileSystemMediaFileProvider/FileSystemMediaFileProvider.cs b/Composite/Plugins/Data/DataProviders/FileSystemMediaFileProvider/FileSystemMediaFileProvider.cs index ec695c9a14..af1590e906 100644 --- a/Composite/Plugins/Data/DataProviders/FileSystemMediaFileProvider/FileSystemMediaFileProvider.cs +++ b/Composite/Plugins/Data/DataProviders/FileSystemMediaFileProvider/FileSystemMediaFileProvider.cs @@ -17,11 +17,11 @@ namespace Composite.Plugins.Data.DataProviders.FileSystemMediaFileProvider [ConfigurationElementType(typeof(MediaArchiveDataProviderData))] internal sealed class FileSystemMediaFileProvider : IWritableDataProvider { - private string _storeId; - private string _storeDescription; - private string _storeTitle; - private string _rootDir; - private string[] _excludedDirs; + private readonly string _storeId; + private readonly string _storeDescription; + private readonly string _storeTitle; + private readonly string _rootDir; + private readonly string[] _excludedDirs; private MediaArchiveStore _store; private DataProviderContext _context; private static readonly int _folderType = 1; @@ -45,7 +45,8 @@ private IMediaFileStore Store { if (_store == null) { - _store = new MediaArchiveStore(_storeId, _storeTitle, _storeDescription, _context.CreateDataSourceId(new MediaDataId() { MediaType = _storeType }, typeof(IMediaFileStore))); + _store = new MediaArchiveStore(_storeId, _storeTitle, _storeDescription, + _context.CreateDataSourceId(new MediaDataId(_storeType), typeof(IMediaFileStore))); } return _store; } @@ -62,7 +63,7 @@ public DataProviderContext Context public IEnumerable GetSupportedInterfaces() { - return new List() { typeof(IMediaFile), typeof(IMediaFileFolder), typeof(IMediaFileStore) }; + return new List { typeof(IMediaFile), typeof(IMediaFileFolder), typeof(IMediaFileStore) }; } @@ -133,7 +134,7 @@ public void Update(IEnumerable datas) public List AddNew(IEnumerable datas) where T : class, IData { - List result = new List(); + var result = new List(); foreach (IData data in datas) { @@ -214,36 +215,35 @@ public IQueryable GetData() where T : class, IData { CheckInterface(typeof(T)); - if (typeof(T) == typeof(IMediaFile)) - { - var excludePaths = - (from dir in _excludedDirs - select PathUtil.Resolve(_rootDir + dir.Replace('/', '\\')) + @"\").ToList(); - - var matches = - from filePath in C1Directory.GetFiles( _rootDir, "*", SearchOption.AllDirectories) - where excludePaths.Where( f=> filePath.StartsWith(f) ).Count()== 0 - select CreateFile( filePath ); - - return matches.Cast().AsQueryable(); - } - else if (typeof(T) == typeof(IMediaFileFolder)) - { - var excludePaths = - (from dir in _excludedDirs - select PathUtil.Resolve(_rootDir + dir.Replace('/','\\')) + @"\").ToList(); - - var matches = - from dirPath in C1Directory.GetDirectories(_rootDir, "*", SearchOption.AllDirectories) - where excludePaths.Where(f => (dirPath+@"\").StartsWith(f)).Count() == 0 - select CreateFolder(dirPath); - - return matches.Cast().AsQueryable(); - } - else - { - return new List() { Store as T}.AsQueryable(); - } + if (typeof(T) == typeof(IMediaFile)) + { + var excludePaths = + (from dir in _excludedDirs + select PathUtil.Resolve(_rootDir + dir.Replace('/', '\\')) + @"\").ToList(); + + var matches = + from filePath in C1Directory.GetFiles( _rootDir, "*", SearchOption.AllDirectories) + where !excludePaths.Where( filePath.StartsWith ).Any() + select CreateFile( filePath ); + + return matches.Cast().AsQueryable(); + } + + if (typeof(T) == typeof(IMediaFileFolder)) + { + var excludePaths = + (from dir in _excludedDirs + select PathUtil.Resolve(_rootDir + dir.Replace('/','\\')) + @"\").ToList(); + + var matches = + from dirPath in C1Directory.GetDirectories(_rootDir, "*", SearchOption.AllDirectories) + where !excludePaths.Any(f => (dirPath + @"\").StartsWith(f)) + select CreateFolder(dirPath); + + return matches.Cast().AsQueryable(); + } + + return new List { Store as T}.AsQueryable(); } @@ -273,7 +273,8 @@ where GetRelativePath(dirInfo) == mediaDataId.Path select CreateFolder(dirInfo)).FirstOrDefault(); return folder as T; } - else if (mediaDataId.MediaType == _fileType) + + if (mediaDataId.MediaType == _fileType) { if (typeof(T) != typeof(IMediaFile)) { @@ -286,19 +287,26 @@ where GetRelativePath(Path.GetDirectoryName(fileInfo)) == mediaDataId.Path && Pa return file as T; } - else - { - return Store as T; - } + + return Store as T; } internal sealed class MediaDataId : IDataId { - public int MediaType { get; set; } - public string Path { get; set; } - public string FileName { get; set; } + public MediaDataId(int type, string path = null, string fileName = null) + { + MediaType = type; + Path = path; + FileName = fileName; + } + + public int MediaType { get; } + public string Path { get; } + public string FileName { get; } + + public override int GetHashCode() => MediaType ^ (Path ?? "").GetHashCode() ^ (FileName ?? "").GetHashCode(); } @@ -319,14 +327,14 @@ private string GetAbsolutePath(IMediaFileFolder folder) private string GetAbsolutePath(MediaDataId mediaData) { + string folderPath = Path.Combine(_rootDir, mediaData.Path.Remove(0, 1)); + if (mediaData.MediaType == _fileType) { - return Path.Combine(Path.Combine(_rootDir, mediaData.Path.Remove(0, 1)), mediaData.FileName); - } - else - { - return Path.Combine(_rootDir, mediaData.Path.Remove(0, 1)); + return Path.Combine(folderPath, mediaData.FileName); } + + return folderPath; } @@ -336,7 +344,7 @@ private bool IsReadOnlyFolder(string folder) var folders = from item in _excludedDirs where folder == item || (item.StartsWith(folder) && item[folder.Length] == '/') select item; - return folders.Count() > 0; + return folders.Any(); } @@ -346,7 +354,7 @@ private bool IsExcludedFolder(string folder) var folders = from item in _excludedDirs where folder == item || (folder.StartsWith(item) && folder[item.Length] == '/') select item; - return folders.Count() > 0; + return folders.Any(); } @@ -354,11 +362,7 @@ private bool IsExcludedFolder(string folder) private string GetRelativePath(string path) { string result = path.Substring(_rootDir.Length).Replace("\\", @"/"); - if (result == string.Empty) - { - return "/"; - } - return result; + return result == string.Empty ? "/" : result; } @@ -366,8 +370,8 @@ private string GetRelativePath(string path) private FileSystemMediaFileFolder CreateFolder(string dir) { string relativeDir = GetRelativePath(dir); - FileSystemMediaFileFolder folder = new FileSystemMediaFileFolder(relativeDir, Store.Id, - _context.CreateDataSourceId(new MediaDataId() { Path = relativeDir, MediaType = _folderType }, typeof(IMediaFileFolder))); + var folder = new FileSystemMediaFileFolder(relativeDir, Store.Id, + _context.CreateDataSourceId(new MediaDataId(_folderType, relativeDir), typeof(IMediaFileFolder))); folder.IsReadOnly = IsReadOnlyFolder(folder.Path); return folder; } @@ -378,21 +382,19 @@ private FileSystemMediaFile CreateFile(string filePath) string relativeDir = GetRelativePath(Path.GetDirectoryName(filePath)); string fileName = Path.GetFileName(filePath); - DataSourceId dataSourceId = _context.CreateDataSourceId(new MediaDataId() { Path = relativeDir, FileName = fileName, MediaType = _fileType }, typeof(IMediaFile)); - FileSystemMediaFile file = new FileSystemMediaFile(filePath, fileName, relativeDir, this.Store.Id, dataSourceId); - - return file; + DataSourceId dataSourceId = _context.CreateDataSourceId(new MediaDataId (_fileType , relativeDir, fileName), typeof(IMediaFile)); + return new FileSystemMediaFile(filePath, fileName, relativeDir, this.Store.Id, dataSourceId); } private void CheckInterface(Type interfaceType) { - if (typeof(IMediaFile).IsAssignableFrom(interfaceType) == false && - typeof(IMediaFileFolder).IsAssignableFrom(interfaceType) == false && - typeof(IMediaFileStore).IsAssignableFrom(interfaceType) == false) + if (!typeof(IMediaFile).IsAssignableFrom(interfaceType) && + !typeof(IMediaFileFolder).IsAssignableFrom(interfaceType) && + !typeof(IMediaFileStore).IsAssignableFrom(interfaceType)) { - throw new ArgumentException(string.Format("Unexpected interface '{0}' - only '{1}, {2} and {3}' are supported", interfaceType, typeof(IMediaFile), typeof(IMediaFileFolder), typeof(IMediaFileStore))); + throw new ArgumentException($"Unexpected interface '{interfaceType}' - only '{typeof(IMediaFile)}, {typeof(IMediaFileFolder)} and {typeof(IMediaFileStore)}' are supported"); } } @@ -400,68 +402,27 @@ private void CheckInterface(Type interfaceType) private sealed class MediaArchiveStore : IMediaFileStore { - private DataSourceId _dataSourceId; - - - - public MediaArchiveStore(string id, string title, string description,DataSourceId dataSourceId) + public MediaArchiveStore(string id, string title, string description, DataSourceId dataSourceId) { Id = id; Title = title; Description = description; - _dataSourceId = dataSourceId; - } - - - - public string Id - { - get; - set; - } - - - - public string Title - { - get; - set; - } - - - - public string Description - { - get; - set; + DataSourceId = dataSourceId; } - public string Tags - { - get; - set; - } - - + public string Id { get; } - public bool IsReadOnly - { - get { return false; } - } + public string Title { get; } + public string Description { get; } + //public string Tags { get; set; } - public bool ProvidesMetadata - { - get { return false; } - } + public bool IsReadOnly => false; + public bool ProvidesMetadata => false; - - public DataSourceId DataSourceId - { - get { return _dataSourceId; } - } + public DataSourceId DataSourceId { get; } } } @@ -529,9 +490,9 @@ public IDataProvider Assemble(IBuilderContext context, DataProviderData objectCo if (configuration == null) throw new ArgumentException("Expected configuration to be of type MediaFileDataProviderData", "objectConfiguration"); string resolvedRootDirectory = PathUtil.Resolve(configuration.RootDirectory); - if (C1Directory.Exists(resolvedRootDirectory) == false) + if (!C1Directory.Exists(resolvedRootDirectory)) { - string directoryNotFoundMsg = string.Format("Directory '{0}' not found", configuration.RootDirectory); + string directoryNotFoundMsg = $"Directory '{configuration.RootDirectory}' not found"; throw new ConfigurationErrorsException(directoryNotFoundMsg, configuration.ElementInformation.Source, configuration.ElementInformation.LineNumber); } if (resolvedRootDirectory.EndsWith("\\")) @@ -542,7 +503,7 @@ public IDataProvider Assemble(IBuilderContext context, DataProviderData objectCo string[] excludedDirs; if (configuration.ExcludedDirectories == null) { - excludedDirs = new string[0]; + excludedDirs = Array.Empty(); } else { diff --git a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/CodeGeneration/DataIdClassGenerator.cs b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/CodeGeneration/DataIdClassGenerator.cs index c226d691b0..49912a6393 100644 --- a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/CodeGeneration/DataIdClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/CodeGeneration/DataIdClassGenerator.cs @@ -1,8 +1,10 @@ -using System; +using System; using System.CodeDom; +using System.Linq; using System.Reflection; using Composite.Data; using Composite.Data.DynamicTypes; +using Composite.Plugins.Data.DataProviders.XmlDataProvider.CodeGeneration; namespace Composite.Plugins.Data.DataProviders.MSSqlServerDataProvider.CodeGeneration @@ -16,7 +18,7 @@ internal sealed class DataIdClassGenerator public DataIdClassGenerator(DataTypeDescriptor dataTypeDescriptor, string dataIdClassName) { _dataTypeDescriptor = dataTypeDescriptor; - _dataIdClassName = dataIdClassName; + _dataIdClassName = dataIdClassName; } @@ -25,13 +27,15 @@ public CodeTypeDeclaration CreateClass() var codeTypeDeclaration = new CodeTypeDeclaration(_dataIdClassName) { IsClass = true, - TypeAttributes = (TypeAttributes.Public | TypeAttributes.Sealed) + TypeAttributes = TypeAttributes.Public | TypeAttributes.Sealed }; codeTypeDeclaration.BaseTypes.Add(typeof(IDataId)); AddDefaultConstructor(codeTypeDeclaration); AddConstructor(codeTypeDeclaration); - AddProperties(codeTypeDeclaration); + AddProperties(codeTypeDeclaration); + AddEqualsMethod(codeTypeDeclaration); + AddGetHashCodeMethod(codeTypeDeclaration); return codeTypeDeclaration; } @@ -109,17 +113,152 @@ private static void AddAsignment(CodeConstructor constructor, string name) } - - private static string MakeParamName(string name) + private void AddEqualsMethod(CodeTypeDeclaration declaration) { - return string.Format("parm{0}", name); + Verify.That(_dataTypeDescriptor.KeyPropertyNames.Count > 0, "A dynamic type should have at least one key property"); + + //Generates code like + + // public override bool Equals(object obj) + // { + // return obj != null && typeof(TestDataId).IsAssignableFrom(obj.GetType()) + // && object.Equals(this.Id, (obj as TestDataId).Id) && .....; + // } + + const string argumentName = "obj"; + + var method = new CodeMemberMethod + { + Attributes = MemberAttributes.Public | MemberAttributes.Override, + Name = nameof(object.Equals), + ReturnType = new CodeTypeReference(typeof(bool)) + }; + method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), argumentName)); + + + var argument = new CodeArgumentReferenceExpression(argumentName); + + // CODEGEN: obj != null && typeof(TestDataId).IsAssignableFrom(obj.GetType()) + + CodeExpression condition = + new CodeBinaryOperatorExpression( + new CodeBinaryOperatorExpression( + argument, + CodeBinaryOperatorType.IdentityInequality, + new CodePrimitiveExpression(null)), + CodeBinaryOperatorType.BooleanAnd, + new CodeMethodInvokeExpression( + new CodeTypeOfExpression(new CodeTypeReference(_dataIdClassName)), + nameof(Type.IsAssignableFrom), + new CodeMethodInvokeExpression(argument, nameof(GetType)))); + + + foreach (string keyPropertyName in _dataTypeDescriptor.PhysicalKeyPropertyNames) + { + string propertyFieldName = MakePropertyFieldName(_dataTypeDescriptor.Fields[keyPropertyName].Name); + + CodeExpression newCondition = + new CodeMethodInvokeExpression( + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), propertyFieldName), + nameof(object.Equals), new CodeFieldReferenceExpression( + new CodeCastExpression(this._dataIdClassName, argument), + propertyFieldName)); + + condition = new CodeBinaryOperatorExpression( + condition, + CodeBinaryOperatorType.BooleanAnd, + newCondition); + } + + method.Statements.Add(new CodeMethodReturnStatement(condition)); + + declaration.Members.Add(method); } - private static string MakePropertyFieldName(string name) + private void AddGetHashCodeMethod(CodeTypeDeclaration declaration) { - return string.Format("_property{0}", name); + // CODEGEN: private int _hashcode; + var hashcodeField = new CodeMemberField(typeof(int), "_hashcode"); + + // CODEGEN: + // public override int GetHashCode() + // { + // if(_hashcode == 0) + // { + // _hashcode = _fullPath.GetHashCode() ^ ....; + // if(_hashcode == 0) + // { + // _hashCode = -1; + // } + // } + // + // return _hashcode; + // } + + var method = new CodeMemberMethod + { + Attributes = MemberAttributes.Public | MemberAttributes.Override, + Name = nameof(GetHashCode), + ReturnType = new CodeTypeReference(typeof(int)) + }; + + Verify.That(_dataTypeDescriptor.KeyPropertyNames.Count > 0, "A dynamic type should have at least one key property"); + + CodeExpression hashCodeExpression = null; + +#warning We DO want IDataId classes to reflect both id and VersionId for data, right? + foreach (string keyPropertyName in _dataTypeDescriptor.PhysicalKeyFields.Select(f => f.Name)) + { + string propertyFieldName = MakePropertyFieldName(_dataTypeDescriptor.Fields[keyPropertyName].Name); + + CodeExpression hashCodePart = + new CodeMethodInvokeExpression( + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), propertyFieldName), + nameof(GetHashCode)); + + if (hashCodeExpression == null) + { + hashCodeExpression = hashCodePart; + } + else + { + hashCodeExpression = new CodeMethodInvokeExpression( + new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(DataProviderHelperBase)), + nameof(DataProviderHelperBase.Xor)), + hashCodeExpression, hashCodePart); + } + } + + // "this.__hashcode" + var hashCodeFieldReference = + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_hashcode"); + + method.Statements.Add(new CodeConditionStatement( + new CodeBinaryOperatorExpression( + hashCodeFieldReference, + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(0)), + new CodeAssignStatement(hashCodeFieldReference, hashCodeExpression), + new CodeConditionStatement( + new CodeBinaryOperatorExpression( + hashCodeFieldReference, + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(0)), + new CodeAssignStatement(hashCodeFieldReference, new CodePrimitiveExpression(-1))))); + + // "return __hashcode" + method.Statements.Add(new CodeMethodReturnStatement(hashCodeFieldReference)); + + declaration.Members.Add(hashcodeField); + declaration.Members.Add(method); } + + + private static string MakeParamName(string name) => $"parm{name}"; + + + private static string MakePropertyFieldName(string name) => $"_property{name}"; } } diff --git a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/NamesCreator.cs b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/NamesCreator.cs index db17dec539..e3153582b9 100644 --- a/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/NamesCreator.cs +++ b/Composite/Plugins/Data/DataProviders/MSSqlServerDataProvider/Foundation/NamesCreator.cs @@ -7,57 +7,67 @@ internal static class NamesCreator { internal static string MakeDataIdClassName(DataTypeDescriptor dataTypeDescriptor) { - return string.Format("{0}DataId", MakeNiceTypeFullName(dataTypeDescriptor)); + return $"{MakeNiceTypeFullName(dataTypeDescriptor)}DataId"; } internal static string MakeDataIdClassFullName(DataTypeDescriptor dataTypeDescriptor, string providerName) { - return string.Format("{0}.{1}", MakeNamespaceName(providerName), MakeDataIdClassName(dataTypeDescriptor)); + return $"{MakeNamespaceName(providerName)}.{MakeDataIdClassName(dataTypeDescriptor)}"; } internal static string MakeEntityBaseClassName(DataTypeDescriptor dataTypeDescriptor) { - return string.Format("{0}EntityBase", dataTypeDescriptor.GetFullInterfaceName().Replace('.', '_').Replace('+', '_')); + return $"{MakeNiceTypeFullName(dataTypeDescriptor)}EntityBase"; } internal static string MakeEntityBaseClassFullName(DataTypeDescriptor dataTypeDescriptor, string providerName) { - return string.Format("{0}.{1}", MakeNamespaceName(providerName), MakeEntityBaseClassName(dataTypeDescriptor)); + return $"{MakeNamespaceName(providerName)}.{MakeEntityBaseClassName(dataTypeDescriptor)}"; } internal static string MakeEntityClassName(DataTypeDescriptor dataTypeDescriptor, string dataScopeIdentifierName, string localeCultureName) { - if (string.IsNullOrEmpty(localeCultureName)) return string.Format("{0}_{1}Entity", MakeNiceTypeFullName(dataTypeDescriptor), dataScopeIdentifierName); + var typeName = MakeNiceTypeFullName(dataTypeDescriptor); - return string.Format("{0}_{1}_{2}Entity", MakeNiceTypeFullName(dataTypeDescriptor), dataScopeIdentifierName, localeCultureName.Replace("-", "")); + if (string.IsNullOrEmpty(localeCultureName)) + { + return $"{typeName}_{dataScopeIdentifierName}Entity"; + } + + + return $"{typeName}_{dataScopeIdentifierName}_{localeCultureName.Replace("-", "")}Entity"; } internal static string MakeEntityClassFullName(DataTypeDescriptor dataTypeDescriptor, string dataScopeIdentifierName, string localeCultureName, string providerName) { - return string.Format("{0}.{1}", MakeNamespaceName(providerName), MakeEntityClassName(dataTypeDescriptor, dataScopeIdentifierName, localeCultureName)); + return $"{MakeNamespaceName(providerName)}.{MakeEntityClassName(dataTypeDescriptor, dataScopeIdentifierName, localeCultureName)}"; } internal static string MakeSqlDataProviderHelperClassName(DataTypeDescriptor dataTypeDescriptor, string dataScopeIdentifierName, string localeCultureName) { - if (string.IsNullOrEmpty(localeCultureName)) return string.Format("{0}_{1}SqlDataProviderHelper", MakeNiceTypeFullName(dataTypeDescriptor), dataScopeIdentifierName); + var typeName = MakeNiceTypeFullName(dataTypeDescriptor); + + if (string.IsNullOrEmpty(localeCultureName)) + return $"{typeName}_{dataScopeIdentifierName}SqlDataProviderHelper"; - return string.Format("{0}_{1}_{2}SqlDataProviderHelper", MakeNiceTypeFullName(dataTypeDescriptor), dataScopeIdentifierName, localeCultureName.Replace("-", "")); + return $"{typeName}_{dataScopeIdentifierName}_{localeCultureName.Replace("-", "")}SqlDataProviderHelper"; } internal static string MakeSqlDataProviderHelperClassFullName(DataTypeDescriptor dataTypeDescriptor, string dataScopeIdentifierName, string localeCultureName, string providerName) { - return string.Format("{0}.{1}", MakeNamespaceName(providerName), MakeSqlDataProviderHelperClassName(dataTypeDescriptor, dataScopeIdentifierName, localeCultureName)); + var className = MakeSqlDataProviderHelperClassName(dataTypeDescriptor, dataScopeIdentifierName, localeCultureName); + return $"{MakeNamespaceName(providerName)}.{className}"; } @@ -69,13 +79,13 @@ internal static string MakeDataContextFieldName(string tableName) internal static string MakeDataContextClassName(string providerName) { - return string.Format("{0}DataContext", providerName); + return $"{providerName}DataContext"; } internal static string MakeDataContextClassFullName(string providerName) { - return string.Format("{0}.{1}", MakeNamespaceName(providerName), MakeDataContextClassName(providerName)); + return $"{MakeNamespaceName(providerName)}.{MakeDataContextClassName(providerName)}"; } 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/MediaFileProvider/MediaFile.cs b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFile.cs index afbb3ef48d..e50a6abffa 100644 --- a/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFile.cs +++ b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFile.cs @@ -3,6 +3,7 @@ using Composite.Data; using Composite.Data.Plugins.DataProvider.Streams; using Composite.Data.Streams; +using Newtonsoft.Json; namespace Composite.Plugins.Data.DataProviders.MediaFileProvider @@ -10,12 +11,11 @@ namespace Composite.Plugins.Data.DataProviders.MediaFileProvider [FileStreamManager(typeof(FileSystemFileStreamManager))] internal class MediaFile : FileSystemFileBase, IMediaFile { - private readonly DataSourceId _dataSourceId; - private string _keyPath; + private string _keyPath; public MediaFile(IMediaFileData file, string storeId, DataSourceId dataSourceId, string filePath) { - _dataSourceId = dataSourceId; + DataSourceId = dataSourceId; StoreId = storeId; this.Id = file.Id; @@ -34,18 +34,39 @@ public MediaFile(IMediaFileData file, string storeId, DataSourceId dataSourceId, this.SystemPath = filePath; } + [JsonConstructor] + private MediaFile(Guid id,string fileName,string folderPath,string title,string description, + string tags,string mimeType,int? length,bool isReadOnly,string culture,DateTime creationTime, + DateTime lastWriteTime,string storeId, DataSourceId dataSourceId, string filePath) + { + DataSourceId = dataSourceId; + StoreId = storeId; - public Guid Id - { - get; internal set; + this.Id = id; + this.FileName = fileName; + this.FolderPath = folderPath; + this.Title = title; + this.Description = description; + this.Tags = tags; + this.MimeType = mimeType; + this.Length = length; + this.IsReadOnly = isReadOnly; + this.Culture = culture; + this.CreationTime = creationTime; + this.LastWriteTime = lastWriteTime; + + this.SystemPath = filePath; + } - public string KeyPath + public Guid Id { - get { return _keyPath ?? (_keyPath = this.GetKeyPath()); } + get; internal set; } - public string CompositePath + public string KeyPath => _keyPath ?? (_keyPath = this.GetKeyPath()); + + public string CompositePath { get { return this.GetCompositePath(); } set { /* Do nothing. Used for deserialization purpouses */ } @@ -120,9 +141,6 @@ public bool IsReadOnly } - public DataSourceId DataSourceId - { - get { return _dataSourceId; } - } - } + public DataSourceId DataSourceId { get; } + } } diff --git a/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileFolder.cs b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileFolder.cs index e8295b36d8..2207477a4c 100644 --- a/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileFolder.cs +++ b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileFolder.cs @@ -1,17 +1,16 @@ using System; using Composite.Data.Types; using Composite.Data; +using Newtonsoft.Json; namespace Composite.Plugins.Data.DataProviders.MediaFileProvider { internal sealed class MediaFileFolder : IMediaFileFolder { - private readonly DataSourceId _dataSourceId; - public MediaFileFolder(IMediaFolderData folder, string storeId, DataSourceId dataSourceId) { - _dataSourceId = dataSourceId; + DataSourceId = dataSourceId; Id = folder.Id; Description = folder.Description; @@ -20,20 +19,30 @@ public MediaFileFolder(IMediaFolderData folder, string storeId, DataSourceId dat Path = folder.Path; } - public Guid Id + [JsonConstructor] + private MediaFileFolder(Guid id,string description,string title,string path, string storeId, DataSourceId dataSourceId) { - get; private set; + DataSourceId = dataSourceId; + + Id = id; + Description = description; + Title = title; + StoreId = storeId; + Path = path; } - public string KeyPath + public Guid Id { - get { return this.GetKeyPath(); } + get; private set; } + public string KeyPath => this.GetKeyPath(); + + [JsonIgnore] public string CompositePath { - get { return this.GetCompositePath(); } - set { throw new NotImplementedException(); } + get => this.GetCompositePath(); + set => throw new NotImplementedException(); } public string StoreId @@ -66,9 +75,6 @@ public bool IsReadOnly set; } - public DataSourceId DataSourceId - { - get { return _dataSourceId; } - } + public DataSourceId DataSourceId { get; } } } diff --git a/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileProvider.cs b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileProvider.cs index 45d3fc1a8d..c401165fda 100644 --- a/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileProvider.cs +++ b/Composite/Plugins/Data/DataProviders/MediaFileProvider/MediaFileProvider.cs @@ -19,7 +19,7 @@ namespace Composite.Plugins.Data.DataProviders.MediaFileProvider { - /// + /// /// /// [ConfigurationElementType(typeof(MediaFileDataProviderData))] @@ -113,7 +113,7 @@ public void Update(IEnumerable dataset) } else { - throw new InvalidOperationException("Unexpected media type '{0}'".FormatWith(dataId.MediaType)); + throw new InvalidOperationException($"Unexpected media type '{dataId.MediaType}'"); } } } @@ -230,7 +230,7 @@ private IMediaFile AddMediaFile(IMediaFile mediaFile) Verify.IsNotNull(readStream, "GetReadStream returned null for type '{0}'", mediaFile.GetType()); fileData.Length = (int)readStream.Length; - string internalPath = Path.Combine(_workingDirectory, fileData.Id.ToString()); + string internalPath = GetFilePath(fileData.Id); internalMediaFile = new MediaFile(fileData, Store.Id, _context.CreateDataSourceId( new MediaDataId { @@ -287,7 +287,7 @@ public void Delete(IEnumerable dataSourceIds) } else { - throw new InvalidOperationException("Unexpected media type '{0}'".FormatWith(dataId.MediaType)); + throw new InvalidOperationException($"Unexpected media type '{dataId.MediaType}'"); } } } @@ -298,19 +298,16 @@ private void DeleteMediaFolder(Guid mediaFolderId) IMediaFolderData folder = DataFacade.GetData(x => x.Id == mediaFolderId).First(); string folderPath = folder.Path; - string innerElementsPathPrefix = string.Format("{0}/", folderPath); + string innerElementsPathPrefix = $"{folderPath}/"; var files = (from item in DataFacade.GetData() where item.FolderPath.StartsWith(innerElementsPathPrefix) || item.FolderPath == folderPath select item).ToList(); + DeleteMediaFiles(files); + DataFacade.Delete(x => x.Path.StartsWith(innerElementsPathPrefix)); DataFacade.Delete(folder); - - foreach (IMediaFileData file in files) - { - DeleteMediaFile(file.Id); - } } @@ -362,7 +359,7 @@ private IQueryable GetMediaFiles() var publicDataScope = DataScopeIdentifier.Public; foreach (IMediaFileData file in files) { - string internalPath = Path.Combine(_workingDirectory, file.Id.ToString()); + string internalPath = GetFilePath(file.Id); fileItems.Add(new MediaFile(file, Store.Id, _context.CreateDataSourceId( new MediaDataId { @@ -411,10 +408,8 @@ private IQueryable GetMediaFileFolders() /// public T GetData(IDataId dataId) where T : class, IData { - if (dataId == null) - { - throw new ArgumentNullException("dataId"); - } + Verify.ArgumentNotNull(dataId, nameof(dataId)); + CheckInterface(typeof(T)); MediaDataId mediaDataId = dataId as MediaDataId; @@ -431,7 +426,7 @@ public T GetData(IDataId dataId) where T : class, IData throw new ArgumentException("The dataId specifies a IMediaFileFolder, but the generic method was invoked with different type"); } - IMediaFolderData folder = DataFacade.GetData().FirstOrDefault(x => x.Id == mediaDataId.Id); + var folder = DataFacade.GetData().FirstOrDefault(x => x.Id == mediaDataId.Id); if (folder == null) { return null; @@ -453,7 +448,7 @@ public T GetData(IDataId dataId) where T : class, IData return null; } - string internalPath = Path.Combine(_workingDirectory, file.Id.ToString()); + string internalPath = GetFilePath(file.Id); return new MediaFile(file, Store.Id, _context.CreateDataSourceId(new MediaDataId { MediaType = MediaElementType.File, Id = file.Id }, typeof(IMediaFile)), internalPath) as T; @@ -485,7 +480,7 @@ private void CheckInterface(Type interfaceType) !typeof(IMediaFileFolder).IsAssignableFrom(interfaceType) && !typeof(IMediaFileStore).IsAssignableFrom(interfaceType)) { - throw new ArgumentException(string.Format("Unexpected interface '{0}' - only '{1}, {2} and {3}' are supported", interfaceType, typeof(IMediaFile), typeof(IMediaFileFolder), typeof(IMediaFileStore))); + throw new ArgumentException($"Unexpected interface '{interfaceType}' - only '{typeof(IMediaFile)}, {typeof(IMediaFileFolder)} and {typeof(IMediaFileStore)}' are supported"); } } @@ -493,7 +488,7 @@ private void CheckInterface(Type interfaceType) private void DeleteMediaFile(Guid id) { - string fullPath = Path.Combine(_workingDirectory, id.ToString()); + string fullPath = GetFilePath(id); if (C1File.Exists(fullPath)) { C1File.Delete(fullPath); @@ -503,15 +498,25 @@ private void DeleteMediaFile(Guid id) } - - private bool DoesFolderExists(string path) + private void DeleteMediaFiles(IList mediaFiles) { - if (path == "/") + foreach (var mediaFile in mediaFiles) { - return true; + string fullPath = GetFilePath(mediaFile.Id); + if (C1File.Exists(fullPath)) + { + C1File.Delete(fullPath); + } } - return GetData().Any(item => item.Path == path); + DataFacade.Delete(mediaFiles); + } + + + + private bool DoesFolderExists(string path) + { + return path == "/" || GetData().Any(item => item.Path == path); } @@ -581,8 +586,10 @@ private void ValidateFolderData(IMediaFileFolder mediaFolder) } + private string GetFilePath(Guid mediaId) => Path.Combine(_workingDirectory, mediaId.ToString()); + - /// + /// /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] @@ -593,6 +600,12 @@ public sealed class MediaDataId : IDataId /// public Guid Id { get; set; } + + /// + public override bool Equals(object obj) => obj is MediaDataId mediaId && mediaId.Id == Id; + + /// + public override int GetHashCode() => Id.GetHashCode(); } @@ -613,30 +626,17 @@ public MediaFileStore(string id, string title, string description, DataSourceId - public string Id { get; set; } - - public string Title { get; set; } - - public string Description { get; set; } - - public bool IsReadOnly - { - get { return false; } - } - + public string Id { get; } + public string Title { get; } - public bool ProvidesMetadata - { - get { return true; } - } + public string Description { get; } + public bool IsReadOnly => false; + public bool ProvidesMetadata => true; - public DataSourceId DataSourceId - { - get { return _dataSourceId; } - } + public DataSourceId DataSourceId => _dataSourceId; } } diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataIdClassGenerator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataIdClassGenerator.cs index b117465885..1b167c5391 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataIdClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataIdClassGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.CodeDom; using System.ComponentModel; using System.Linq; @@ -126,20 +126,14 @@ private void AddConstructor(CodeTypeDeclaration declaration) new CodeVariableReferenceExpression(attributeVariableName), CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null) - ), - new CodeStatement[] { - new CodeThrowExceptionStatement( - new CodeObjectCreateExpression( - typeof(InvalidOperationException), - new CodeExpression[] { - new CodePrimitiveExpression( - string.Format("Missing '{0}' attribute in a data store file.", keyField.Name) - ) - } - ) - ) - } - )); + ), + new CodeThrowExceptionStatement( + new CodeObjectCreateExpression( + typeof(InvalidOperationException), + new CodePrimitiveExpression( + $"Missing '{keyField.Name}' attribute in a data store file." + )) + ))); // CODEGEN: // _propertyId = (Guid) attrId; @@ -204,41 +198,54 @@ private void AddEqualsMethod(CodeTypeDeclaration declaration) // public override bool Equals(object obj) // { - // return object.Equals(this.FullPath, (obj as FileSystemFileDataId1).FullPath) && .....; + // return obj != null + // && typeof(TestDataId).IsAssignableFrom(obj.GetType()) + // && obj.Equals(this.FullPath, (obj as FileSystemFileDataId1).FullPath) && .....; // } + const string argumentName = "obj"; + var argument = new CodeArgumentReferenceExpression(argumentName); + var method = new CodeMemberMethod { Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "Equals", + Name = nameof(object.Equals), ReturnType = new CodeTypeReference(typeof (bool)) }; - method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), "obj")); + method.Parameters.Add(new CodeParameterDeclarationExpression(typeof(object), argumentName)); Verify.That(_dataTypeDescriptor.KeyPropertyNames.Count > 0, "A dynamic type should have at least one key property"); - CodeExpression condition = null; + // CODEGEN: obj != null && typeof(TestDataId).IsAssignableFrom(obj.GetType()) + + CodeExpression condition = + new CodeBinaryOperatorExpression( + new CodeBinaryOperatorExpression( + argument, + CodeBinaryOperatorType.IdentityInequality, + new CodePrimitiveExpression(null)), + CodeBinaryOperatorType.BooleanAnd, + new CodeMethodInvokeExpression( + new CodeTypeOfExpression(new CodeTypeReference(_className)), + nameof(Type.IsAssignableFrom), + new CodeMethodInvokeExpression(argument, nameof(GetType)))); - foreach (string keyPropertyName in _dataTypeDescriptor.KeyPropertyNames) + foreach (string keyPropertyName in _dataTypeDescriptor.PhysicalKeyFields.Select(f => f.Name)) { string propertyFieldName = MakePropertyFieldName(_dataTypeDescriptor.Fields[keyPropertyName].Name); CodeExpression newCondition = new CodeMethodInvokeExpression( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), propertyFieldName), - "Equals", new CodeFieldReferenceExpression( - new CodeCastExpression(this._className, new CodeArgumentReferenceExpression("obj")), + nameof(object.Equals), new CodeFieldReferenceExpression( + new CodeCastExpression(this._className, argument), propertyFieldName)); - if (condition == null) - { - condition = newCondition; - } - else - { - condition = new CodeBinaryOperatorExpression(condition, CodeBinaryOperatorType.BooleanAnd, newCondition); - } + condition = new CodeBinaryOperatorExpression( + condition, + CodeBinaryOperatorType.BooleanAnd, + newCondition); } method.Statements.Add(new CodeMethodReturnStatement(condition)); @@ -250,24 +257,28 @@ private void AddGetHashCodeMethod(CodeTypeDeclaration declaration) { // Generates code like like - // private int? _hashcode; + // private int _hashcode; // // public override int GetHashCode() // { - // if(_hashcode == null) + // if(_hashcode == 0) // { // _hashcode = _fullPath.GetHashCode() ^ ....; - // } + // if(_hashcode == 0) + // { + // _hashcode == -1; + // } // - // return _hashcode.Value; + // return _hashcode; // } - var hashcodeField = new CodeMemberField(typeof(int?), "_hashcode"); + const string HashCodeFieldName = "_hashcode"; + declaration.Members.Add(new CodeMemberField(typeof(int), HashCodeFieldName)); var method = new CodeMemberMethod { Attributes = MemberAttributes.Public | MemberAttributes.Override, - Name = "GetHashCode", + Name = nameof(GetHashCode), ReturnType = new CodeTypeReference(typeof (int)) }; @@ -275,7 +286,6 @@ private void AddGetHashCodeMethod(CodeTypeDeclaration declaration) CodeExpression hashCodeExpression = null; -#warning We DO want IDataId classes to reflect both id and VersionId for data, right? foreach (string keyPropertyName in _dataTypeDescriptor.PhysicalKeyFields.Select(f=>f.Name)) { string propertyFieldName = MakePropertyFieldName(_dataTypeDescriptor.Fields[keyPropertyName].Name); @@ -283,7 +293,7 @@ private void AddGetHashCodeMethod(CodeTypeDeclaration declaration) CodeExpression hashCodePart = new CodeMethodInvokeExpression( new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), propertyFieldName), - "GetHashCode"); + nameof(GetHashCode)); if (hashCodeExpression == null) { @@ -293,37 +303,38 @@ private void AddGetHashCodeMethod(CodeTypeDeclaration declaration) { hashCodeExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression(new CodeTypeReferenceExpression(typeof(DataProviderHelperBase)), - "Xor"), + nameof(DataProviderHelperBase.Xor)), hashCodeExpression, hashCodePart); } } // "this.__hashcode" var hashCodeFieldReference = - new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "_hashcode"); + new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), HashCodeFieldName); - method.Statements.Add(new CodeConditionStatement( - new CodeBinaryOperatorExpression(hashCodeFieldReference, + method.Statements.Add( + new CodeConditionStatement( + new CodeBinaryOperatorExpression(hashCodeFieldReference, CodeBinaryOperatorType.ValueEquality, - new CodePrimitiveExpression(null)), - new CodeAssignStatement(hashCodeFieldReference, hashCodeExpression))); + new CodePrimitiveExpression(0)), + new CodeAssignStatement(hashCodeFieldReference, hashCodeExpression), + new CodeConditionStatement( + new CodeBinaryOperatorExpression(hashCodeFieldReference, + CodeBinaryOperatorType.ValueEquality, + new CodePrimitiveExpression(0)), + new CodeAssignStatement( + hashCodeFieldReference, + new CodePrimitiveExpression(-1))))); + + // "return __hashcode;" + method.Statements.Add(new CodeMethodReturnStatement(hashCodeFieldReference)); - // "return __hashcode.Value;" - method.Statements.Add(new CodeMethodReturnStatement(new CodePropertyReferenceExpression(hashCodeFieldReference, "Value"))); - - declaration.Members.Add(hashcodeField); declaration.Members.Add(method); } - private static string MakePropertyFieldName(string name) - { - return string.Format("_property{0}", name); - } + private static string MakePropertyFieldName(string name) => $"_property{name}"; - private static string MakeXNameFieldName(string name) - { - return string.Format("_{0}XName", name); - } + private static string MakeXNameFieldName(string name) => $"_{name}XName"; } } diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs index 01f522163f..5b81a0abe6 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/CodeGeneration/DataWrapperClassGenerator.cs @@ -1,7 +1,8 @@ -using System; +using System; using System.CodeDom; using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reflection; using System.Xml.Linq; using Composite.Core.Types; @@ -31,12 +32,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 +55,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 +136,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)) }; @@ -154,15 +160,15 @@ 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(), fieldName ), - "Value" + nameof(ExtendedNullable.Value) )); statments.Add(AddCommitDataMethodFinalHelper(dataFieldDescriptor, newStatements)); @@ -176,126 +182,128 @@ private void AddCommitDataMethod(CodeTypeDeclaration declaration) - private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor dataFieldDescriptor, List statements) + private static CodeStatement AddCommitDataMethodFinalHelper(DataFieldDescriptor dataFieldDescriptor, IEnumerable statements) { // CODEGEN: - // if(this._emailNullable != null) { + // if (this._isSet_email && this._emailNullable != null) { // [statements] + // _isSet_email = false; // } string fieldName = CreateNullableFieldName(dataFieldDescriptor); + // this._isSet_email + var fieldIsSetFieldReference = + 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) - ), - 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 CodeExpression[] { - new CodeVariableReferenceExpression(elementVariableName) - } - ) - ) - }, - new CodeStatement[] { - new CodeExpressionStatement( - new CodeMethodInvokeExpression( - new CodeVariableReferenceExpression(elementVariableName), - "SetValue", - new CodeExpression[] { - 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 CodeExpression[] {} - ) - ) - } - ) + new CodeExpressionStatement( + new CodeMethodInvokeExpression( + elementVariable, + "SetValue", + valueExpression) + ), } ) - }; - - - List statements = new List(); - - statements.Add(new CodeVariableDeclarationStatement( - typeof(XAttribute), - elementVariableName, - new CodeMethodInvokeExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - WrappedElementFieldName - ), - "Attribute", - new CodeExpression[] { - 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; + ) + } + ) + }; } @@ -305,12 +313,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 +340,64 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) { foreach (DataFieldDescriptor dataFieldDescriptor in _dataTypeDescriptor.Fields) { - string fieldName = CreateNullableFieldName(dataFieldDescriptor); + string nullableFieldName = CreateNullableFieldName(dataFieldDescriptor); + + var nullableFieldReference = new CodeFieldReferenceExpression( + new CodeThisReferenceExpression(), + nullableFieldName + ); + + var nullableFieldValueReference = new CodePropertyReferenceExpression( + nullableFieldReference, + nameof(ExtendedNullable.Value) + ); // 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) { @@ -372,21 +406,12 @@ private void AddInterfaceProperties(CodeTypeDeclaration declaration) property.GetStatements.Add( new CodeConditionStatement( new CodeBinaryOperatorExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - fieldName - ), + nullableFieldReference, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null) ), new CodeMethodReturnStatement( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - fieldName - ), - "Value" - ) + nullableFieldValueReference ) )); @@ -424,16 +449,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,67 +471,92 @@ 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 = value; // Using the implicit cast to ExtendedEnumerable<> + + property.GetStatements.Add( + new CodeAssignStatement( + nullableFieldReference, + new CodeVariableReferenceExpression("value"))); + + // CODEGEN: + // return value; + property.GetStatements.Add( + new CodeMethodReturnStatement( + new CodeVariableReferenceExpression("value"))); if (!dataFieldDescriptor.IsReadOnly) { // 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(), - fieldName - ), + nullableFieldReference, CodeBinaryOperatorType.IdentityEquality, new CodePrimitiveExpression(null) ), - new CodeAssignStatement( - new CodeFieldReferenceExpression( - new CodeThisReferenceExpression(), - fieldName - ), - new CodeObjectCreateExpression(nullableType, new CodeExpression[] { })))); + new CodeStatement[] + { + new CodeAssignStatement( + nullableFieldReference, + new CodePropertySetValueReferenceExpression()) + }, + new CodeStatement[] + { + new CodeAssignStatement( + nullableFieldValueReference, + new CodePropertySetValueReferenceExpression() + ) + })); // CODEGEN: - // this._emailNullable.Value = value; + // this._isSet_email = true; property.SetStatements.Add( new CodeAssignStatement( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression( + new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), - fieldName - ), - "Value" + IsSetFieldName(dataFieldDescriptor) ), - new CodePropertySetValueReferenceExpression() + new CodePrimitiveExpression(true) )); } @@ -520,16 +568,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 +592,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); } } diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/NamesCreator.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/NamesCreator.cs index 23f272cfa2..62196e5d87 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/NamesCreator.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/NamesCreator.cs @@ -22,7 +22,7 @@ internal static string MakeFileName(DataTypeDescriptor dataTypeDescriptor, DataS publicationScopePart = "_" + PublicationScope.Unpublished; break; default: - throw new InvalidOperationException("Unsupported data scope identifier: '{0}'".FormatWith(dataScopeIdentifier.Name)); + throw new InvalidOperationException($"Unsupported data scope identifier: '{dataScopeIdentifier.Name}'"); } string cultureNamePart = ""; @@ -44,7 +44,7 @@ internal static string MakeElementName(DataTypeDescriptor dataTypeDescriptor) if (name.StartsWith("I")) { name = name.Remove(0, 1); - name = string.Format("{0}{1}Elements", name.Substring(0, 1).ToUpper(), name.Remove(0, 1)); + name = $"{name.Substring(0, 1).ToUpper()}{name.Remove(0, 1)}Elements"; } return name; @@ -54,21 +54,21 @@ internal static string MakeElementName(DataTypeDescriptor dataTypeDescriptor) internal static string MakeWrapperClassName(DataTypeDescriptor dataTypeDescriptor) { - return string.Format("{0}Wrapper", dataTypeDescriptor.GetFullInterfaceName().Replace('.', '_').Replace('+', '_')); + return $"{MakeNiceTypeFullName(dataTypeDescriptor)}Wrapper"; } internal static string MakeDataIdClassName(DataTypeDescriptor dataTypeDescriptor) { - return string.Format("{0}DataId", dataTypeDescriptor.GetFullInterfaceName().Replace('.', '_').Replace('+', '_')); + return $"{MakeNiceTypeFullName(dataTypeDescriptor)}DataId"; } internal static string MakeDataProviderHelperClassName(DataTypeDescriptor dataTypeDescriptor) { - return string.Format("{0}DataProviderHelper", dataTypeDescriptor.GetFullInterfaceName().Replace('.', '_').Replace('+', '_')); + return $"{MakeNiceTypeFullName(dataTypeDescriptor)}DataProviderHelper"; } @@ -77,5 +77,10 @@ internal static string MakeNamespaceName(string providerName) { return "CompositeGenerated." + providerName; } + + private static string MakeNiceTypeFullName(DataTypeDescriptor dataTypeDescriptor) + { + return dataTypeDescriptor.GetFullInterfaceName().Replace('.', '_').Replace('+', '_'); + } } } 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/Elements/ElementProviders/BaseFunctionProviderElementProvider/BaseFunctionFolderElementEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/BaseFunctionProviderElementProvider/BaseFunctionFolderElementEntityToken.cs index bf88ee114a..fd5b0175c4 100644 --- a/Composite/Plugins/Elements/ElementProviders/BaseFunctionProviderElementProvider/BaseFunctionFolderElementEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/BaseFunctionProviderElementProvider/BaseFunctionFolderElementEntityToken.cs @@ -3,6 +3,7 @@ using System.Threading; using Composite.C1Console.Security; using Composite.Core.Extensions; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.BaseFunctionProviderElementProvider @@ -66,6 +67,7 @@ public override string Serialize() /// /// Gets the function namespace. /// + [JsonIgnore] public string FunctionNamespace { get @@ -81,6 +83,7 @@ public string FunctionNamespace /// /// The name of the function provider. /// + [JsonIgnore] public string ElementProviderName { get diff --git a/Composite/Plugins/Elements/ElementProviders/DeveloperApplicationProvider/DeveloperApplicationProviderEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/DeveloperApplicationProvider/DeveloperApplicationProviderEntityToken.cs index a342ff15be..d97a44b3c5 100644 --- a/Composite/Plugins/Elements/ElementProviders/DeveloperApplicationProvider/DeveloperApplicationProviderEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/DeveloperApplicationProvider/DeveloperApplicationProviderEntityToken.cs @@ -3,6 +3,7 @@ using Composite.C1Console.Security.SecurityAncestorProviders; using Composite.Core.Configuration; using Composite.Core.IO; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.DeveloperApplicationProvider @@ -65,6 +66,7 @@ public override string Id /// + [JsonIgnore] public string Filename { get { return this.Source; } @@ -72,6 +74,7 @@ public string Filename /// + [JsonIgnore] public string FullTreePath { get diff --git a/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProvider.cs index 704cf77875..83a27ebc7a 100644 --- a/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProvider.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Configuration; using System.Linq; @@ -70,13 +70,10 @@ public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowC [ActionExecutor(typeof(DataTypeDescriptorToXmlActionExecutor))] public sealed class DataTypeDescriptorToXmlActionToken : ActionToken { - private static PermissionType[] _permissionTypes = new PermissionType[] { PermissionType.Administrate }; + private static readonly PermissionType[] _permissionTypes = { PermissionType.Administrate }; /// - public override IEnumerable PermissionTypes - { - get { return _permissionTypes; } - } + public override IEnumerable PermissionTypes => _permissionTypes; /// public override string Serialize() @@ -105,52 +102,52 @@ internal sealed class GeneratedDataTypesElementProvider : IHooklessElementProvid /// - public static ResourceHandle RootOpen { get { return GetIconHandle("generated-root-open"); } } + public static ResourceHandle RootOpen => GetIconHandle("generated-root-open"); /// - public static ResourceHandle RootClosed { get { return GetIconHandle("generated-root-closed"); } } + public static ResourceHandle RootClosed => GetIconHandle("generated-root-closed"); /// - public static ResourceHandle DynamicDataTypeIconOpen { get { return GetIconHandle("generated-type-open"); } } + public static ResourceHandle DynamicDataTypeIconOpen => GetIconHandle("generated-type-open"); /// - public static ResourceHandle DynamicDataTypeIconClosed { get { return GetIconHandle("generated-type-closed"); } } + public static ResourceHandle DynamicDataTypeIconClosed => GetIconHandle("generated-type-closed"); /// - public static ResourceHandle InterfaceOpen { get { return GetIconHandle("data-interface-open"); } } + public static ResourceHandle InterfaceOpen => GetIconHandle("data-interface-open"); /// - public static ResourceHandle InterfaceClosed { get { return GetIconHandle("data-interface-closed"); } } + public static ResourceHandle InterfaceClosed => GetIconHandle("data-interface-closed"); /// - public static ResourceHandle AddDataTypeIcon { get { return GetIconHandle("generated-type-add"); } } + public static ResourceHandle AddDataTypeIcon => GetIconHandle("generated-type-add"); /// - public static ResourceHandle EditDataTypeIcon { get { return GetIconHandle("generated-type-edit"); } } + public static ResourceHandle EditDataTypeIcon => GetIconHandle("generated-type-edit"); /// - public static ResourceHandle DeleteDataTypeIcon { get { return GetIconHandle("generated-type-delete"); } } + public static ResourceHandle DeleteDataTypeIcon => GetIconHandle("generated-type-delete"); /// - public static ResourceHandle LocalizeDataTypeIcon { get { return GetIconHandle("generated-type-localize"); } } + public static ResourceHandle LocalizeDataTypeIcon => GetIconHandle("generated-type-localize"); /// - public static ResourceHandle DelocalizeDataTypeIcon { get { return GetIconHandle("generated-type-delocalize"); } } + public static ResourceHandle DelocalizeDataTypeIcon => GetIconHandle("generated-type-delocalize"); /// - public static ResourceHandle AddDataIcon { get { return GetIconHandle("generated-type-data-add"); } } + public static ResourceHandle AddDataIcon => GetIconHandle("generated-type-data-add"); /// - public static ResourceHandle DuplicateDataIcon { get { return GetIconHandle("copy"); } } + public static ResourceHandle DuplicateDataIcon => GetIconHandle("copy"); /// - public static ResourceHandle EditDataIcon { get { return GetIconHandle("generated-type-data-edit"); } } + public static ResourceHandle EditDataIcon => GetIconHandle("generated-type-data-edit"); /// - public static ResourceHandle DeleteDataIcon { get { return GetIconHandle("generated-type-data-delete"); } } + public static ResourceHandle DeleteDataIcon => GetIconHandle("generated-type-data-delete"); /// - public static ResourceHandle LocalizeDataIcon { get { return GetIconHandle("generated-type-data-localize"); } } + public static ResourceHandle LocalizeDataIcon => GetIconHandle("generated-type-data-localize"); /// public static ResourceHandle ListUnpublishedItemsIcon = GetIconHandle("generated-type-list-unpublished-items"); @@ -159,30 +156,30 @@ internal sealed class GeneratedDataTypesElementProvider : IHooklessElementProvid public static ResourceHandle ShowincontentareaIcon = GetIconHandle("generated-type-showincontentarea"); /// - public static ResourceHandle EditFormMarkupIcon { get { return GetIconHandle("generated-type-form-markup-edit"); } } + public static ResourceHandle EditFormMarkupIcon => GetIconHandle("generated-type-form-markup-edit"); /// - public static ResourceHandle ToXmlIcon { get { return GetIconHandle("generated-type-to-xml"); } } + public static ResourceHandle ToXmlIcon => GetIconHandle("generated-type-to-xml"); /// public static readonly Dictionary DataIconLookup; /// - public static ResourceHandle ErrorIcon { get { return GetIconHandle("error"); } } + public static ResourceHandle ErrorIcon => GetIconHandle("error"); private static readonly ActionGroup PrimaryActionGroup = new ActionGroup(ActionGroupPriority.PrimaryHigh); private static readonly ActionGroup ViewActionGroup = new ActionGroup("View", ActionGroupPriority.PrimaryLow); private static readonly ActionGroup AppendedActionGroup = new ActionGroup("Develop", ActionGroupPriority.GeneralAppendMedium); - private static readonly PermissionType[] _addNewInterfaceTypePermissionTypes = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _editInterfaceTypePermissionTypes = new PermissionType[] { PermissionType.Edit }; - private static readonly PermissionType[] _editFormMarkupPermissionTypes = new PermissionType[] { PermissionType.Edit }; - private static readonly PermissionType[] _deleteInterfaceTypePermissionTypes = new PermissionType[] { PermissionType.Delete }; + private static readonly PermissionType[] _addNewInterfaceTypePermissionTypes = { PermissionType.Add }; + private static readonly PermissionType[] _editInterfaceTypePermissionTypes = { PermissionType.Edit }; + private static readonly PermissionType[] _editFormMarkupPermissionTypes = { PermissionType.Edit }; + private static readonly PermissionType[] _deleteInterfaceTypePermissionTypes = { PermissionType.Delete }; - private static readonly PermissionType[] _addNewDataPermissionTypes = new PermissionType[] { PermissionType.Add }; - private static readonly PermissionType[] _editDataPermissionTypes = new PermissionType[] { PermissionType.Edit }; - private static readonly PermissionType[] _deleteDataPermissionTypes = new PermissionType[] { PermissionType.Delete }; - private static readonly PermissionType[] _localizeDataPermissionTypes = new PermissionType[] { PermissionType.Add }; + private static readonly PermissionType[] _addNewDataPermissionTypes = { PermissionType.Add }; + private static readonly PermissionType[] _editDataPermissionTypes = { PermissionType.Edit }; + private static readonly PermissionType[] _deleteDataPermissionTypes = { PermissionType.Delete }; + private static readonly PermissionType[] _localizeDataPermissionTypes = { PermissionType.Add }; @@ -229,12 +226,7 @@ public ElementProviderContext Context if (PageFolderFacade.GetAllFolderTypes().Contains(type)) return false; if (PageMetaDataFacade.GetAllMetaDataTypes().Contains(type)) return false; - if (_websiteItemsView) - { - return IsTypeWhiteListed(type); - } - - return true; + return !_websiteItemsView || IsTypeWhiteListed(type); }, OnGetPayload = token => null }; @@ -441,28 +433,26 @@ public IEnumerable GetForeignChildren(EntityToken entityToken, SearchTo private IEnumerable GetChildren(EntityToken entityToken, SearchToken searchToken, bool showForeignChildren) { - if (entityToken is GeneratedDataTypesElementProviderRootEntityToken) + if (entityToken is GeneratedDataTypesElementProviderRootEntityToken rootEntityToken) { - return GetRootChildren(searchToken, entityToken as GeneratedDataTypesElementProviderRootEntityToken); + return GetRootChildren(searchToken, rootEntityToken); } - if (entityToken is GeneratedDataTypesElementProviderTypeEntityToken) + if (entityToken is GeneratedDataTypesElementProviderTypeEntityToken castedEntityToken) { - var castedEntityToken = entityToken as GeneratedDataTypesElementProviderTypeEntityToken; - string typeManagerName = castedEntityToken.SerializedTypeName; Type type = TypeManager.TryGetType(typeManagerName); if (type == null) { - return null; + return Enumerable.Empty(); } // These are never shown in the tree if (typeof(IPageMetaData).IsAssignableFrom(type)) { - return new List(); + return Enumerable.Empty(); } IEnumerable elements = _dataGroupingProviderHelper.GetRootGroupFolders(type, entityToken, showForeignChildren); @@ -470,16 +460,17 @@ private IEnumerable GetChildren(EntityToken entityToken, SearchToken se return elements.ToList(); } - if (entityToken is DataGroupingProviderHelperEntityToken) + if (entityToken is DataGroupingProviderHelperEntityToken dataGroupingEntityToken) { - List elements = _dataGroupingProviderHelper.GetGroupChildren(entityToken as DataGroupingProviderHelperEntityToken, showForeignChildren).ToList(); + List elements = _dataGroupingProviderHelper + .GetGroupChildren(dataGroupingEntityToken, showForeignChildren).ToList(); return elements; } if (entityToken is DataEntityToken) { - return new List(); + return Enumerable.Empty(); } throw new InvalidOperationException("This code should not be reachable"); @@ -790,7 +781,7 @@ private void AddNonShowOnlyGlobalActions(Type type, string typeName, Element ele private static ElementAction GetChangeLocalizationElementAction(Type type, bool isEditable) { - if (DataLocalizationFacade.IsLocalized(type) == false) + if (!DataLocalizationFacade.IsLocalized(type)) { return new ElementAction(new ActionHandle(new WorkflowActionToken(WorkflowFacade.GetWorkflowType("Composite.Plugins.Elements.ElementProviders.GeneratedDataTypesElementProvider.EnableTypeLocalizationWorkflow"), _editInterfaceTypePermissionTypes))) @@ -800,7 +791,7 @@ private static ElementAction GetChangeLocalizationElementAction(Type type, bool Label = GetText("EnableLocalization"), ToolTip = GetText("EnableLocalizationToolTip"), Icon = GeneratedDataTypesElementProvider.LocalizeDataTypeIcon, - Disabled = DataLocalizationFacade.ActiveLocalizationCultures.Any() == false || !isEditable, + Disabled = !DataLocalizationFacade.ActiveLocalizationCultures.Any() || !isEditable, ActionLocation = new ActionLocation { ActionType = ActionType.Other, @@ -1290,7 +1281,7 @@ internal sealed class GeneratedDataTypesElementProviderAssembler : IAssembler + [JsonIgnore] public static string GlobalDataTypeFolderId { get { return "GlobalDataTypeFolder"; } } /// [Obsolete("Not used any more")] + [JsonIgnore] public static string StaticGlobalDataTypeFolderId { get { return "StaticGlobalDataTypeFolder"; } } /// + [JsonIgnore] public static string PageDataFolderTypeFolderId { get { return "PageDataFolderTypeFolder"; } } /// + [JsonIgnore] public static string PageMetaDataTypeFolderId { get { return "PageMetaDataTypeFolder"; } } } } diff --git a/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProviderTypeEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProviderTypeEntityToken.cs index 678c2419a1..60821e7f2c 100644 --- a/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProviderTypeEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/GeneratedDataTypesElementProvider/GeneratedDataTypesElementProviderTypeEntityToken.cs @@ -1,8 +1,9 @@ -using System; +using System; using System.Collections.Generic; -using System.Text; using Composite.C1Console.Security; +using Composite.Core; using Composite.Core.Serialization; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.GeneratedDataTypesElementProvider @@ -14,39 +15,29 @@ namespace Composite.Plugins.Elements.ElementProviders.GeneratedDataTypesElementP [SecurityAncestorProvider(typeof(GeneratedDataTypesElementProviderSecurityAncestorProvider))] public sealed class GeneratedDataTypesElementProviderTypeEntityToken : EntityToken { - private string _id; - private string _providerName; + private readonly string _providerName; /// - public GeneratedDataTypesElementProviderTypeEntityToken(string serializedTypeName, string providerName, string id) + [JsonConstructor] + public GeneratedDataTypesElementProviderTypeEntityToken(string serializedTypeName, string source, string id) { - _id = id; - _providerName = providerName; + Id = id; + _providerName = source; this.SerializedTypeName = serializedTypeName; } - + /// - public override string Type - { - get { return "GeneratedDataTypesElementProvider"; } - } + public override string Type => "GeneratedDataTypesElementProvider"; /// - public override string Source - { - get { return _providerName; } - - } + public override string Source => _providerName; /// - public override string Id - { - get { return _id; } - } + public override string Id { get; } /// @@ -60,25 +51,36 @@ public string SerializedTypeName /// public override string Serialize() { - StringBuilder sb = new StringBuilder(); - - DoSerialize(sb); - - StringConversionServices.SerializeKeyValuePair(sb, "_SerializedTypeName_", this.SerializedTypeName); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = CompositeJsonSerializer + .Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(GeneratedDataTypesElementProviderTypeEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; Dictionary dic; DoDeserialize(serializedEntityToken, out type, out source, out id, out dic); - if (dic.ContainsKey("_SerializedTypeName_") == false) + if (dic.ContainsKey("_SerializedTypeName_") == false) { throw new ArgumentException("The serializedEntityToken is not a serialized entity token", "serializedEntityToken"); } @@ -88,7 +90,6 @@ public static EntityToken Deserialize(string serializedEntityToken) return new GeneratedDataTypesElementProviderTypeEntityToken(serializedTypeName, source, id); } - /// public override bool Equals(object obj) { diff --git a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesGroupFolderEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesGroupFolderEntityToken.cs index 38b39180b0..15dac2ae06 100644 --- a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesGroupFolderEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesGroupFolderEntityToken.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Composite.C1Console.Security; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.PackageElementProvider @@ -25,6 +26,7 @@ public PackageElementProviderAvailablePackagesGroupFolderEntityToken(string grou this.GroupName = groupName; } + [JsonIgnore] public string GroupName { get; private set; } public override string Type diff --git a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesItemEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesItemEntityToken.cs index 4319088f6b..8e6a2973e2 100644 --- a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesItemEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderAvailablePackagesItemEntityToken.cs @@ -2,7 +2,9 @@ using System.Collections.Generic; using System.Text; using Composite.C1Console.Security; +using Composite.Core; using Composite.Core.Serialization; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.PackageElementProvider @@ -18,21 +20,16 @@ public IEnumerable GetParents(EntityToken entityToken) } - - - /// - /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityAncestorProvider(typeof(PackageElementProviderAvailablePackagesItemEntityTokenAncestorProvider))] public sealed class PackageElementProviderAvailablePackagesItemEntityToken : EntityToken { - private readonly string _id; - /// + [JsonConstructor] public PackageElementProviderAvailablePackagesItemEntityToken(string id, string groupName) { - _id = id; + Id = id; this.GroupName = groupName; } @@ -40,40 +37,41 @@ public PackageElementProviderAvailablePackagesItemEntityToken(string id, string public string GroupName { get; private set; } /// - public Guid PackageId { get { return new Guid(this.Id); } } + public Guid PackageId => new Guid(this.Id); /// - public override string Type - { - get { return ""; } - } + public override string Type => ""; /// - public override string Source - { - get { return ""; } - } + public override string Source => ""; /// - public override string Id - { - get { return _id; } - } + public override string Id { get; } /// public override string Serialize() { - var sb = new StringBuilder(); - - StringConversionServices.SerializeKeyValuePair(sb, "_GroupName_", this.GroupName); - - DoSerialize(sb); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = CompositeJsonSerializer.Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(PackageElementProviderAvailablePackagesItemEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; Dictionary dic; diff --git a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageGroupFolderEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageGroupFolderEntityToken.cs index ab771b6dd6..7183a4b9ef 100644 --- a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageGroupFolderEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageGroupFolderEntityToken.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Composite.C1Console.Security; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.PackageElementProvider @@ -25,6 +26,7 @@ public PackageElementProviderInstalledPackageGroupFolderEntityToken(string group this.GroupName = groupName; } + [JsonIgnore] public string GroupName { get; private set; } public override string Type diff --git a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageItemEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageItemEntityToken.cs index 4b5a1d45de..39b6eb61ed 100644 --- a/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageItemEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/PackageElementProvider/PackageElementProviderInstalledPackageItemEntityToken.cs @@ -11,7 +11,7 @@ internal sealed class PackageElementProviderInstalledPackageItemEntityTokenAnces { public IEnumerable GetParents(EntityToken entityToken) { - if (entityToken == null) throw new ArgumentNullException("entityToken"); + if (entityToken == null) throw new ArgumentNullException(nameof(entityToken)); var castedEntityToken = (PackageElementProviderInstalledPackageItemEntityToken)entityToken; @@ -27,10 +27,6 @@ public IEnumerable GetParents(EntityToken entityToken) } - - - /// - /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityAncestorProvider(typeof(PackageElementProviderInstalledPackageItemEntityTokenAncestorProvider))] @@ -60,61 +56,29 @@ public PackageElementProviderInstalledPackageItemEntityToken(Guid packageId, str /// - public override string Type - { - get { return ""; } - } + public override string Type => ""; /// - public override string Source - { - get { return ""; } - } + public override string Source => ""; /// - public override string Id - { - get { return this.PackageId.ToString(); } - } + public override string Id => this.PackageId.ToString(); /// public override string Serialize() { - StringBuilder sb = new StringBuilder(); - - StringConversionServices.SerializeKeyValuePair(sb, "_GroupName_", this.GroupName); - StringConversionServices.SerializeKeyValuePair(sb, "_IsLocalInstalled_", this.IsLocalInstalled); - StringConversionServices.SerializeKeyValuePair(sb, "_CanBeUninstalled_", this.CanBeUninstalled); - - DoSerialize(sb); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedEntityToken) { - string type, source, id; - Dictionary dic; - - DoDeserialize(serializedEntityToken, out type, out source, out id, out dic); - - if (!dic.ContainsKey("_GroupName_") - || !dic.ContainsKey("_IsLocalInstalled_") - || !dic.ContainsKey("_CanBeUninstalled_")) - { - throw new ArgumentException("serializedEntityToken is of wrong format"); - } - - string groupName = StringConversionServices.DeserializeValueString(dic["_GroupName_"]); - bool isLocalInstalled = StringConversionServices.DeserializeValueBool(dic["_IsLocalInstalled_"]); - bool canBeUninstalled = StringConversionServices.DeserializeValueBool(dic["_CanBeUninstalled_"]); - - return new PackageElementProviderInstalledPackageItemEntityToken(new Guid(id), groupName, isLocalInstalled, canBeUninstalled); + return CompositeJsonSerializer + .Deserialize(serializedEntityToken); } diff --git a/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs index dc44e7d46d..646be69878 100644 --- a/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs @@ -550,7 +550,7 @@ public bool OnElementDraggedAndDropped(EntityToken draggedEntityToken, EntityTok } else if (newParentEntityToken is DataEntityToken dataEntityToken) { - IPage newParentPage = dataEntityToken.Data as IPage; + IPage newParentPage = (IPage)dataEntityToken.Data; newParentPageId = newParentPage.Id; } else @@ -595,7 +595,7 @@ public bool OnElementDraggedAndDropped(EntityToken draggedEntityToken, EntityTok draggedPage.MoveTo(newParentPageId, realDropIndex, false); - DataFacade.Update(draggedPage); + DataFacade.Update(draggedPage, false, false, true); EntityTokenCacheFacade.ClearCache(draggedPage.GetDataEntityToken()); diff --git a/Composite/Plugins/Elements/ElementProviders/PageTemplateFeatureElementProvider/PageTemplateFeatureEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/PageTemplateFeatureElementProvider/PageTemplateFeatureEntityToken.cs index d859b911c7..f188ae8099 100644 --- a/Composite/Plugins/Elements/ElementProviders/PageTemplateFeatureElementProvider/PageTemplateFeatureEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/PageTemplateFeatureElementProvider/PageTemplateFeatureEntityToken.cs @@ -3,6 +3,7 @@ using Composite.C1Console.Security.SecurityAncestorProviders; using Composite.Core.Configuration; using Composite.Core.IO; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.PageTemplateFeatureElementProvider @@ -71,6 +72,7 @@ public override string Id /// + [JsonIgnore] public string FeatureName { get { return this.Source; } diff --git a/Composite/Plugins/Elements/ElementProviders/VirtualElementProvider/VirtualElementProviderEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/VirtualElementProvider/VirtualElementProviderEntityToken.cs index 9da0c4098b..cc9f23f533 100644 --- a/Composite/Plugins/Elements/ElementProviders/VirtualElementProvider/VirtualElementProviderEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/VirtualElementProvider/VirtualElementProviderEntityToken.cs @@ -1,6 +1,9 @@ -using System; +using System; using Composite.C1Console.Security; +using Composite.Core; +using Composite.Core.Serialization; using Composite.Core.Types; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.VirtualElementProvider @@ -13,57 +16,72 @@ namespace Composite.Plugins.Elements.ElementProviders.VirtualElementProvider [SecurityAncestorProvider(typeof(VirtualElementProviderSecurityAncestorProvider))] public sealed class VirtualElementProviderEntityToken : EntityToken { - private readonly string _source; - private readonly string _id; - private readonly string _type; - + private string _type; /// public VirtualElementProviderEntityToken() { - _type = TypeManager.SerializeType(this.GetType()); } /// + [JsonConstructor] public VirtualElementProviderEntityToken(string source, string id) - :this() { - _source = source; - _id = id; + Source = source; + Id = id; } /// + [JsonIgnore] public override string Type { - get { return _type; } + get + { + if (_type == null) + { + _type= TypeManager.SerializeType(this.GetType()); + } + + return _type; + } } /// - public override string Source - { - get { return _source; } - } + public override string Source { get; } /// - public override string Id - { - get { return _id; } - } + public override string Id { get; } /// public override string Serialize() { - return DoSerialize(); + return CompositeJsonSerializer.Serialize(this); } - /// public static EntityToken Deserialize(string serializedEntityToken) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedEntityToken)) + { + entityToken = + CompositeJsonSerializer.Deserialize(serializedEntityToken); + } + else + { + entityToken = DeserializeLegacy(serializedEntityToken); + Log.LogVerbose(nameof(VirtualElementProviderEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + + /// + public static EntityToken DeserializeLegacy(string serializedEntityToken) { string type, source, id; diff --git a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs index b02babab34..16e6d58e9e 100644 --- a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProvider.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; +using System.Globalization; using System.IO; using System.Linq; using Composite.C1Console.Actions; @@ -27,27 +28,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 +56,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 +71,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 +88,7 @@ public WebsiteFileElementProvider(WebsiteFileElementProviderData objectConfigura public ElementProviderContext Context { - set { _context = value; } + set => _context = value; } @@ -109,7 +110,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 +196,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 +214,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 +256,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 +289,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 +337,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 +354,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 +514,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"); @@ -627,24 +618,74 @@ private IEnumerable GetFileActions(WebsiteFile websiteFile) if (IsEditActionAllowed(websiteFile)) { fileActions.Add( - new ElementAction(new ActionHandle(new WorkflowActionToken(WorkflowFacade.GetWorkflowType("Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider.EditWebsiteFileTextContentWorkflow"), _editWebsiteFilePermissionTypes))) - { - VisualData = new ActionVisualizedData - { - Label = StringResourceSystemFacade.GetString("Composite.Plugins.WebsiteFileElementProvider", "EditWebsiteFileTitle"), - ToolTip = StringResourceSystemFacade.GetString("Composite.Plugins.WebsiteFileElementProvider", "EditWebsiteFileToolTip"), - Icon = EditWebsiteFile, - Disabled = websiteFile.IsReadOnly, - ActionLocation = new ActionLocation - { - ActionType = ActionType.Edit, - IsInFolder = false, - IsInToolbar = true, - ActionGroup = PrimaryFileActionGroup - } - } - }); + new ElementAction(new ActionHandle( + websiteFile.MimeType==MimeTypeInfo.Resx? + (ActionToken) new UrlActionToken(websiteFile.FileName, EditWebsiteFile, + UrlUtils.ResolvePublicUrl($"Composite/content/misc/editors/resxeditor/resxeditor.aspx?f={websiteFile.FullPath}"), _editWebsiteFilePermissionTypes): + new WorkflowActionToken( + WorkflowFacade.GetWorkflowType( + "Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider.EditWebsiteFileTextContentWorkflow"), + _editWebsiteFilePermissionTypes))) + { + VisualData = new ActionVisualizedData + { + Label = StringResourceSystemFacade.GetString( + "Composite.Plugins.WebsiteFileElementProvider", "EditWebsiteFileTitle"), + ToolTip = StringResourceSystemFacade.GetString( + "Composite.Plugins.WebsiteFileElementProvider", "EditWebsiteFileToolTip"), + Icon = EditWebsiteFile, + Disabled = websiteFile.IsReadOnly, + ActionLocation = new ActionLocation + { + ActionType = ActionType.Edit, + IsInFolder = false, + IsInToolbar = true, + ActionGroup = PrimaryFileActionGroup + } + } + }); + + if (websiteFile.MimeType == MimeTypeInfo.Resx && + !CultureInfo.GetCultures(CultureTypes.SpecificCultures). + Any(f => f.Name != "" && + websiteFile.FileName.EndsWith("." + f.Name + ".Resx", StringComparison.OrdinalIgnoreCase))) + { + var files = Directory.GetFiles(Path.GetDirectoryName(websiteFile.FullPath)); + foreach (var cultureInfo in DataLocalizationFacade.ActiveLocalizationCultures) + { + if (!files.Any(f => cultureInfo.Name!="" && f.EndsWith("." + cultureInfo.Name + ".Resx", StringComparison.OrdinalIgnoreCase))) + { + + fileActions.Add( + new ElementAction(new ActionHandle( + new UrlActionToken(websiteFile.FileName, EditWebsiteFile, + UrlUtils.ResolvePublicUrl( + $"Composite/content/misc/editors/resxeditor/resxeditor.aspx?f={websiteFile.FullPath}&t={cultureInfo.Name}"), + _editWebsiteFilePermissionTypes) + )) + { + VisualData = new ActionVisualizedData + { + Label = string.Format(StringResourceSystemFacade.GetString( + "Composite.Web.SourceEditor", "ResxEditor.TranslateTo.Label"), cultureInfo.DisplayName), + ToolTip = string.Format(StringResourceSystemFacade.GetString( + "Composite.Web.SourceEditor", "ResxEditor.TranslateTo.Tooltip"), cultureInfo.DisplayName), + Icon = EditWebsiteFile, + Disabled = websiteFile.IsReadOnly, + ActionLocation = new ActionLocation + { + ActionType = ActionType.Add, + IsInFolder = false, + IsInToolbar = true, + ActionGroup = PrimaryFileActionGroup + } + } + }); + } + } + } } + return fileActions; } @@ -653,7 +694,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 +703,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 +713,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 +750,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 +759,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 +779,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 }; + internal static readonly PermissionType[] RequiredPermissionTypes = { PermissionType.Administrate, PermissionType.Edit }; - public override IEnumerable PermissionTypes - { - get { return _permissionTypes; } - } + public override IEnumerable PermissionTypes => RequiredPermissionTypes; - - 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 +842,6 @@ public string ManageableKeyNameLabels get { return (string)base[_manageableKeyNameLabels]; } set { base[_manageableKeyNameLabels] = value; } } - - - } diff --git a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProviderEntityToken.cs b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProviderEntityToken.cs index 7f86912201..fffbfe03eb 100644 --- a/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProviderEntityToken.cs +++ b/Composite/Plugins/Elements/ElementProviders/WebsiteFileElementProvider/WebsiteFileElementProviderEntityToken.cs @@ -1,22 +1,20 @@ using System; using System.Collections.Generic; -using System.Text; using Composite.C1Console.Security; +using Composite.Core; using Composite.Core.IO; using Composite.Core.Serialization; +using Newtonsoft.Json; namespace Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider { - /// - /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] [SecurityAncestorProvider(typeof(WebsiteFileProviderEntityTokenSecurityAncestorProvider))] public sealed class WebsiteFileElementProviderEntityToken : EntityToken { - private static readonly string _baseDirectory; - private readonly string _providerName; + private static readonly string BaseDirectory; private readonly string _relativePath; static WebsiteFileElementProviderEntityToken() @@ -27,69 +25,68 @@ static WebsiteFileElementProviderEntityToken() baseDirectory = baseDirectory.Substring(0, baseDirectory.Length - 1); } - _baseDirectory = baseDirectory; + BaseDirectory = baseDirectory; } /// + [JsonConstructor] public WebsiteFileElementProviderEntityToken(string providerName, string path, string rootPath) { Verify.ArgumentNotNull(path, "path"); Verify.ArgumentNotNull(rootPath, "rootPath"); Verify.ArgumentCondition(path.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase), "path", "Path should start with root path"); - Verify.That(rootPath.StartsWith(_baseDirectory, StringComparison.OrdinalIgnoreCase), "Path does not belong to the website"); + Verify.That(rootPath.StartsWith(BaseDirectory, StringComparison.OrdinalIgnoreCase), "Path does not belong to the website"); - _relativePath = path.Substring(_baseDirectory.Length); + _relativePath = path.Substring(BaseDirectory.Length); this.Path = path; this.RootPath = rootPath; - _providerName = providerName; + ProviderName = providerName; } /// - public string ProviderName - { - get { return _providerName; } - } + public string ProviderName { get; } /// - public override string Type - { - get { return ""; } - } + public override string Type => ""; /// - public override string Source - { - get { return _providerName; } - } + public override string Source => ProviderName; /// - public override string Id - { - get { return _relativePath; } - } + public override string Id => _relativePath; /// public override string Serialize() { - StringBuilder sb = new StringBuilder(); - - DoSerialize(sb); - - StringConversionServices.SerializeKeyValuePair(sb, "root", RelativeRootPath); - - return sb.ToString(); + return CompositeJsonSerializer.Serialize(this); } /// public static EntityToken Deserialize(string serializedData) + { + EntityToken entityToken; + if (CompositeJsonSerializer.IsJsonSerialized(serializedData)) + { + entityToken = CompositeJsonSerializer.Deserialize(serializedData); + } + else + { + entityToken = DeserializeLegacy(serializedData); + Log.LogVerbose(nameof(WebsiteFileElementProviderEntityToken), entityToken.GetType().FullName); + } + return entityToken; + } + + /// + public static EntityToken DeserializeLegacy(string serializedData) { Dictionary dic; string type, source, id; @@ -97,19 +94,18 @@ public static EntityToken Deserialize(string serializedData) EntityToken.DoDeserialize(serializedData, out type, out source, out id, out dic); // Backward compatibility - if(dic.ContainsKey("RootPath")) + if (dic.ContainsKey("RootPath")) { string rootPath = StringConversionServices.DeserializeValueString(dic["RootPath"]); return new WebsiteFileElementProviderEntityToken(source, id, rootPath); } - + string relativeRootPath = StringConversionServices.DeserializeValueString(dic["root"]); - return new WebsiteFileElementProviderEntityToken(source, _baseDirectory + id, _baseDirectory + relativeRootPath); + return new WebsiteFileElementProviderEntityToken(source, BaseDirectory + id, BaseDirectory + relativeRootPath); } - /// public string Path { @@ -117,11 +113,9 @@ public string Path private set; } - private string RelativeRootPath - { - get { return RootPath.Substring(_baseDirectory.Length); } - } + private string RelativeRootPath => RootPath.Substring(BaseDirectory.Length); + [JsonProperty] internal string RootPath { get; @@ -129,10 +123,7 @@ internal string RootPath } - internal bool IsRoot - { - get { return this.Path == this.RootPath; } - } + internal bool IsRoot => this.Path == this.RootPath; /// 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; + } +} 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/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/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); - } + } } } diff --git a/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Web/Html/Template/CommonMetaTagsFunction.cs b/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Web/Html/Template/CommonMetaTagsFunction.cs index 6b74823d7e..73e8dfa96e 100644 --- a/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Web/Html/Template/CommonMetaTagsFunction.cs +++ b/Composite/Plugins/Functions/FunctionProviders/StandardFunctionProvider/Web/Html/Template/CommonMetaTagsFunction.cs @@ -73,7 +73,7 @@ public override object Execute(ParameterList parameters, FunctionContextContaine { metaTags.Add(new XElement(Namespaces.Xhtml + "meta", new XAttribute("name", "Generator"), - new XAttribute("content", "C1 CMS Foundation - Free Open Source from Orckestra and https://github.com/Orckestra/CMS-Foundation"))); + new XAttribute("content", "C1 CMS Foundation - Free Open Source from Orckestra and https://github.com/Orckestra/C1-CMS-Foundation"))); } return metaTags; 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/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'."); + } + } + } +} 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 diff --git a/Composite/Plugins/GlobalSettings/GlobalSettingsProviders/ConfigBasedGlobalSettingsProvider.cs b/Composite/Plugins/GlobalSettings/GlobalSettingsProviders/ConfigBasedGlobalSettingsProvider.cs index a62625cb06..8d5c9ac0f3 100644 --- a/Composite/Plugins/GlobalSettings/GlobalSettingsProviders/ConfigBasedGlobalSettingsProvider.cs +++ b/Composite/Plugins/GlobalSettings/GlobalSettingsProviders/ConfigBasedGlobalSettingsProvider.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Configuration; - +using System.Linq; using Composite.Core.Configuration; using Composite.Core.Configuration.Plugins.GlobalSettingsProvider; using Composite.Core.Extensions; @@ -29,215 +29,67 @@ public ConfigBasedGlobalSettingsProvider(ConfigBasedGlobalSettingsProviderData c } - public string AppCodeDirectory - { - get { return _configurationData.AppCodeDirectory; } - } + public string AppCodeDirectory => _configurationData.AppCodeDirectory; + public string ApplicationName => _configurationData.ApplicationName; - public string ApplicationName - { - get { return _configurationData.ApplicationName; } - } - + public string ApplicationShortName => _configurationData.ApplicationShortName; - public string ApplicationShortName - { - get { return _configurationData.ApplicationShortName; } - } + public string BrandedVersionAssemblySource => _configurationData.BrandedVersionAssemblySource; + public string DefaultCultureName => _configurationData.DefaultCultureName; - public string BrandedVersionAssemblySource - { - get { return _configurationData.BrandedVersionAssemblySource; } - } + public string ConfigurationDirectory => _configurationData.ConfigurationDirectory; + public string GeneratedAssembliesDirectory => _configurationData.GeneratedAssembliesDirectory; - public string DefaultCultureName - { - get { return _configurationData.DefaultCultureName; } - } + public string SerializedWorkflowsDirectory => _configurationData.SerializedWorkflowsDirectory; + public string SerializedConsoleMessagesDirectory => _configurationData.SerializedConsoleMessagesDirectory; + public string BinDirectory => _configurationData.BinDirectory; - public string ConfigurationDirectory - { - get { return _configurationData.ConfigurationDirectory; } - } + public string TempDirectory => _configurationData.TempDirectory; + public string CacheDirectory => _configurationData.CacheDirectory; + public string PackageDirectory => _configurationData.PackageDirectory; - public string GeneratedAssembliesDirectory - { - get { return _configurationData.GeneratedAssembliesDirectory; } - } + public string AutoPackageInstallDirectory => _configurationData.AutoPackageInstallDirectory; + public string TreeDefinitionsDirectory => _configurationData.TreeDefinitionsDirectory; + public string PageTemplateFeaturesDirectory => _configurationData.PageTemplateFeaturesDirectory; - public string SerializedWorkflowsDirectory - { - get { return _configurationData.SerializedWorkflowsDirectory; } - } + public string DataMetaDataDirectory => _configurationData.DataMetaDataDirectory; + public string InlineCSharpFunctionDirectory => _configurationData.InlineCSharpFunctionDirectory; + public string PackageLicenseDirectory => _configurationData.PackageLicenseDirectory; - public string SerializedConsoleMessagesDirectory - { - get { return _configurationData.SerializedConsoleMessagesDirectory; } - } + public IEnumerable NonProbableAssemblyNames => _nonProbeableAssemblyNames; + public int ConsoleMessageQueueItemSecondToLive => _configurationData.ConsoleMessageQueueItemSecondToLive; + public bool EnableDataTypesAutoUpdate => _configurationData.EnableDataTypesAutoUpdate; - public string BinDirectory - { - get { return _configurationData.BinDirectory; } - } + public bool BroadcastConsoleElementChanges => _configurationData.BroadcastConsoleElementChanges; + public string AutoCreatedAdministratorUserName => _configurationData.AutoCreatedAdministratorUserName; + public bool OnlyTranslateWhenApproved => _configurationData.OnlyTranslateWhenApproved; - public string TempDirectory - { - get { return _configurationData.TempDirectory; } - } + public string WorkflowTimeout => _configurationData.WorkflowTimeout; + public string ConsoleTimeout => _configurationData.ConsoleTimeout; - public string CacheDirectory - { - get { return _configurationData.CacheDirectory; } - } - + public ICachingSettings Caching => _cachingSettings; - public string PackageDirectory - { - get { return _configurationData.PackageDirectory; } - } + public int ImageQuality => _configurationData.ImageQuality; + public bool PrettifyPublicMarkup => _configurationData.PrettifyPublicMarkup; - - public string AutoPackageInstallDirectory - { - get { return _configurationData.AutoPackageInstallDirectory; } - } - - - - public string TreeDefinitionsDirectory - { - get { return _configurationData.TreeDefinitionsDirectory; } - } - - - - public string PageTemplateFeaturesDirectory - { - get { return _configurationData.PageTemplateFeaturesDirectory; } - } - - - - public string DataMetaDataDirectory - { - get { return _configurationData.DataMetaDataDirectory; } - } - - - public string InlineCSharpFunctionDirectory - { - get { return _configurationData.InlineCSharpFunctionDirectory; } - } - - - public string PackageLicenseDirectory - { - get { return _configurationData.PackageLicenseDirectory; } - } - - - public IResourceCacheSettings ResourceCacheSettings - { - get - { - return new ConfigResourceCacheSettings(_configurationData.ResourceCacheDirectory, _configurationData.ServerCacheMinutes, _configurationData.ClientCacheMinutes); - } - } - - public IEnumerable NonProbableAssemblyNames - { - get { return _nonProbeableAssemblyNames; } - } - - - - public int ConsoleMessageQueueItemSecondToLive - { - get { return _configurationData.ConsoleMessageQueueItemSecondToLive; } - } - - - public bool EnableDataTypesAutoUpdate - { - get { return _configurationData.EnableDataTypesAutoUpdate; } - } - - - public bool BroadcastConsoleElementChanges - { - get { return _configurationData.BroadcastConsoleElementChanges; } - } - - - public string AutoCreatedAdministratorUserName - { - get { return _configurationData.AutoCreatedAdministratorUserName; } - } - - - public bool OnlyTranslateWhenApproved - { - get { return _configurationData.OnlyTranslateWhenApproved; } - } - - - public string WorkflowTimeout - { - get { return _configurationData.WorkflowTimeout; } - } - - - - public string ConsoleTimeout - { - get { return _configurationData.ConsoleTimeout; } - } - - public ICachingSettings Caching - { - get { return _cachingSettings; } - } - - public int ImageQuality - { - get - { - return _configurationData.ImageQuality; - } - } - - public bool PrettifyPublicMarkup - { - get - { - return _configurationData.PrettifyPublicMarkup; - } - } - - public bool PrettifyRenderFunctionExceptions - { - get - { - return _configurationData.PrettifyRenderFunctionExceptions; - } - } + public bool PrettifyRenderFunctionExceptions => _configurationData.PrettifyRenderFunctionExceptions; public bool FunctionPreviewEnabled => _configurationData.FunctionPreviewEnabled; @@ -260,19 +112,14 @@ public ConfigCachingSettings(CachingConfigurationElement data) _data = data; } - public bool Enabled - { - get { return _data.Enabled; } - } + public bool Enabled => _data.Enabled; public IEnumerable Caches { get { - foreach(var element in _data) - { - yield return new ConfigCacheSettings(element as CacheSettingsElement); - } + return _data.Cast() + .Select(element => new ConfigCacheSettings(element)); } } } @@ -284,53 +131,17 @@ internal class ConfigCacheSettings: ICacheSettings public ConfigCacheSettings(CacheSettingsElement data) { Name = data.Name; - Enabled = data.Enabled; + Enabled = data.Enabled; Size = data.Size; } - public string Name { get; private set; } - public bool Enabled { get; private set; } - public int Size { get; private set; } + public string Name { get; } + public bool Enabled { get; } + public int Size { get; } } - - internal class ConfigResourceCacheSettings : IResourceCacheSettings - { - private readonly string _resourceCacheDirectory; - private readonly int _serverChacheMinutes; - private readonly int _clientCacheMinutes; - - internal ConfigResourceCacheSettings(string resourceCacheDirectory, int serverCacheMinutes, int clientCacheMinutes) - { - _resourceCacheDirectory = resourceCacheDirectory; - _serverChacheMinutes = serverCacheMinutes; - _clientCacheMinutes = clientCacheMinutes; - } - - public string ResourceCacheDirectory - { - get { return _resourceCacheDirectory; } - set { throw new NotSupportedException(GetType().ToString()); } - } - - public int ServerCacheMinutes - { - get { return _serverChacheMinutes; } - set { throw new NotSupportedException(GetType().ToString()); } - } - - public int ClientCacheMinutes - { - get { return _clientCacheMinutes; } - set { throw new NotSupportedException(GetType().ToString()); } - } - } - - - - internal sealed class ConfigBasedGlobalSettingsProviderAssembler : IAssembler { public IGlobalSettingsProvider Assemble(IBuilderContext context, GlobalSettingsProviderData objectConfiguration, IConfigurationSource configurationSource, ConfigurationReflectionCache reflectionCache) @@ -529,7 +340,8 @@ public string PackageLicenseDirectory private const string _resourceCacheDirectory = "resourceCacheDirectory"; - [ConfigurationProperty(_resourceCacheDirectory, IsRequired = true)] + [Obsolete] + [ConfigurationProperty(_resourceCacheDirectory, IsRequired = false)] public string ResourceCacheDirectory { get { return (string)base[_resourceCacheDirectory]; } @@ -540,6 +352,7 @@ public string ResourceCacheDirectory private const string _clientCacheMinutesProperty = "clientCacheMinutes"; public static readonly int DefaultClientCacheMinutes = A_WEEK_IN_MINUTES; + [Obsolete] [ConfigurationProperty(_clientCacheMinutesProperty, IsRequired = false, DefaultValue = A_WEEK_IN_MINUTES)] public int ClientCacheMinutes { @@ -551,6 +364,7 @@ public int ClientCacheMinutes private const string _serverCacheMinutesProperty = "serverCacheMinutes"; public static readonly int DefaultServerCacheMinutes = A_DAY_IN_MINUTES; + [Obsolete] [ConfigurationProperty(_serverCacheMinutesProperty, IsRequired = false, DefaultValue = A_DAY_IN_MINUTES)] public int ServerCacheMinutes { @@ -758,7 +572,4 @@ public bool Enabled set { base[_enabledPropertyName] = value; } } } -} - - - +} \ No newline at end of file diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1FileStream.cs index 9833afdbcd..97e46830ed 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,10 +123,20 @@ public void Close() [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass")] public void Dispose() { - this.Close(); - - _fileStream.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 ad32e71d75..9bcb40fa61 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/FileLogger.cs @@ -1,4 +1,6 @@ -using System; +//#define UseLockFiles + +using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; @@ -6,7 +8,6 @@ using System.IO; using System.Text; using System.Threading; -using Composite.Core.Extensions; using Composite.Core.IO; using Composite.Core.Logging; @@ -18,14 +19,15 @@ namespace Composite.Plugins.Logging.LogTraceListeners.FileLogTraceListener /// internal class FileLogger : IDisposable { +#if UseLockFiles private static readonly TimeSpan LockFileUpdateFrequency = TimeSpan.FromSeconds(20); private static readonly TimeSpan OldLockFilesPreservationTime = TimeSpan.FromSeconds(60); + private DateTime _lockFileUpdatedLast = DateTime.MinValue; +#endif private readonly string _logDirectoryPath; private readonly bool _flushImmediately; - private DateTime _lockFileUpdatedLast = DateTime.MinValue; - public static event ThreadStart OnReset; @@ -45,7 +47,9 @@ public FileLogger(string logDirectoryPath, bool flushImmediately) } _flushImmediately = flushImmediately; +#if UseLockFiles TouchLockFile(); +#endif } @@ -121,7 +125,9 @@ public LogFileReader[] GetLogFiles() { EnsureInitialize(); - if (MoreThanOneAppDomainRunning()) return new LogFileReader[] { }; +#if UseLockFiles + if (MoreThanOneAppDomainRunning()) return Array.Empty(); +#endif string[] filePathes = Directory.GetFiles(_logDirectoryPath); @@ -257,9 +263,10 @@ private static bool TryReadAndOpen(string filePath, out string[] content, out Fi [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseFileStreamClass:DoNotUseFileStreamClass", Justification = "This is what we want, touch is used later on")] private void EnsureInitialize() { +#if UseLockFiles TouchLockFile(); - - RemoveOldLockFiles(); + RemoveOldLockFiles(); +#endif if (FileConnection != null) return; @@ -270,15 +277,14 @@ private void EnsureInitialize() DateTime creationDate = DateTime.Now; string fileNamePrefix = creationDate.ToString("yyyyMMdd"); - string fileName; - FileStream stream; - Exception ex; for (int i = 0; i < 10; i++) { - fileName = fileNamePrefix + (i > 0 ? "_" + i : string.Empty) + ".txt"; + var fileName = fileNamePrefix + (i > 0 ? "_" + i : string.Empty) + ".txt"; string filePath = Path.Combine(_logDirectoryPath, fileName); + FileStream stream; + Exception ex; if (!File.Exists(filePath)) { stream = TryOpenFile(filePath, out ex); @@ -288,7 +294,7 @@ private void EnsureInitialize() // Ignoring this exception if the file has already created if (File.Exists(filePath)) continue; - throw new Exception("Failed to create file '{0}'".FormatWith(filePath), ex); + throw new Exception($"Failed to create file '{filePath}'", ex); } FileConnection = new LogFileInfo @@ -329,7 +335,7 @@ private void EnsureInitialize() } - +#if UseLockFiles [SuppressMessage("Composite.IO", "Composite.DoNotUseDirectoryClass:DoNotUseDirectoryClass")] [SuppressMessage("Composite.IO", "Composite.DoNotUseFileClass:DoNotUseFileClass")] private void RemoveOldLockFiles() @@ -361,14 +367,12 @@ private void RemoveOldLockFiles() } } - - [SuppressMessage("Composite.IO", "Composite.DoNotUseDirectoryClass:DoNotUseDirectoryClass")] private bool MoreThanOneAppDomainRunning() { return Directory.GetFiles(_logDirectoryPath, "*.lock").Length > 1; } - +#endif private void ResetInitialization() @@ -381,10 +385,7 @@ private void ResetInitialization() FileConnection = null; } - if (OnReset != null) - { - OnReset(); - } + OnReset?.Invoke(); EnsureInitialize(); } @@ -398,7 +399,7 @@ private static void WriteUTF8EncodingHeader(Stream stream) } - +#if UseLockFiles [SuppressMessage("Composite.IO", "Composite.DoNotUseFileClass:DoNotUseFileClass")] private void TouchLockFile() { @@ -419,20 +420,14 @@ private void TouchLockFile() // Ignore } } +#endif +#if UseLockFiles + private string LockFileName => Path.Combine(_logDirectoryPath, AppDomain.CurrentDomain.Id + ".lock"); +#endif - private string LockFileName - { - get - { - return Path.Combine(_logDirectoryPath, AppDomain.CurrentDomain.Id + ".lock"); - } - } - - - - bool _disposed = false; + bool _disposed; [SuppressMessage("Composite.IO", "Composite.DoNotUseFileClass:DoNotUseFileClass")] protected virtual void Dispose(bool disposing) { @@ -449,7 +444,7 @@ protected virtual void Dispose(bool disposing) _disposed = true; } - +#if UseLockFiles // Delete the file in any case try { @@ -459,18 +454,27 @@ protected virtual void Dispose(bool disposing) { // Ignore } +#endif } 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 36ee7aa274..ffec4cce92 100644 --- a/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs +++ b/Composite/Plugins/Logging/LogTraceListeners/FileLogTraceListener/LogFileInfo.cs @@ -20,14 +20,22 @@ public void Dispose() { if (!_disposed) { - FileStream.Close(); + FileStream.Dispose(); _disposed = true; } + +#if LeakCheck + GC.SuppressFinalize(this); +#endif } +#if LeakCheck + private string stack = Environment.StackTrace; + /// ~LogFileInfo() { - Dispose(); + 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/Plugins/PageTemplates/Razor/RazorPageRenderer.cs b/Composite/Plugins/PageTemplates/Razor/RazorPageRenderer.cs index 2158f759a3..8e77fe2e9b 100644 --- a/Composite/Plugins/PageTemplates/Razor/RazorPageRenderer.cs +++ b/Composite/Plugins/PageTemplates/Razor/RazorPageRenderer.cs @@ -7,7 +7,6 @@ using System.Xml.Linq; using Composite.AspNet.Razor; using Composite.Core.Collections.Generic; -using Composite.Core.Extensions; using Composite.Core.Instrumentation; using Composite.Core.PageTemplates; using Composite.Core.WebClient.Renderings.Page; @@ -39,9 +38,9 @@ public void AttachToPage(Page renderTaget, PageContentToRender contentToRender) _aspnetPage.Init += RendererPage; } - private void RendererPage(object sender, EventArgs e) + public XDocument Render(PageContentToRender contentToRender, FunctionContextContainer functionContextContainer) { - Guid templateId = _job.Page.TemplateId; + Guid templateId = contentToRender.Page.TemplateId; var renderingInfo = _renderingInfo[templateId]; if (renderingInfo == null) @@ -52,32 +51,29 @@ private void RendererPage(object sender, EventArgs e) throw loadingException; } - Verify.ThrowInvalidOperationException("Missing template '{0}'".FormatWith(templateId)); + Verify.ThrowInvalidOperationException($"Missing template '{templateId}'"); } string output; - FunctionContextContainer functionContextContainer; RazorPageTemplate webPage = null; try { - webPage = WebPageBase.CreateInstanceFromVirtualPath(renderingInfo.ControlVirtualPath) as AspNet.Razor.RazorPageTemplate; + webPage = WebPageBase.CreateInstanceFromVirtualPath(renderingInfo.ControlVirtualPath) as RazorPageTemplate; Verify.IsNotNull(webPage, "Razor compilation failed or base type does not inherit '{0}'", - typeof (AspNet.Razor.RazorPageTemplate).FullName); + typeof(RazorPageTemplate).FullName); webPage.Configure(); - functionContextContainer = PageRenderer.GetPageRenderFunctionContextContainer(); - using (Profiler.Measure("Evaluating placeholders")) { - TemplateDefinitionHelper.BindPlaceholders(webPage, _job, renderingInfo.PlaceholderProperties, - functionContextContainer); + TemplateDefinitionHelper.BindPlaceholders(webPage, contentToRender, renderingInfo.PlaceholderProperties, + functionContextContainer); } // Executing razor code var httpContext = new HttpContextWrapper(HttpContext.Current); - var startPage = StartPage.GetStartPage(webPage, "_PageStart", new[] {"cshtml"}); + var startPage = StartPage.GetStartPage(webPage, "_PageStart", new[] { "cshtml" }); var pageContext = new WebPageContext(httpContext, webPage, startPage); pageContext.PageData.Add(RazorHelper.PageContext_FunctionContextContainer, functionContextContainer); @@ -94,16 +90,25 @@ private void RendererPage(object sender, EventArgs e) } finally { - if (webPage != null) - { - webPage.Dispose(); - } + webPage?.Dispose(); } - XDocument resultDocument = XDocument.Parse(output); - + return XDocument.Parse(output); + } + + private void RendererPage(object sender, EventArgs e) + { + var functionContextContainer = PageRenderer.GetPageRenderFunctionContextContainer(); + + var resultDocument = Render(_job, functionContextContainer); + var controlMapper = (IXElementToControlMapper)functionContextContainer.XEmbedableMapper; - Control control = PageRenderer.Render(resultDocument, functionContextContainer, controlMapper, _job.Page); + Control control; + + using (Profiler.Measure("Rendering the page")) + { + control = PageRenderer.Render(resultDocument, functionContextContainer, controlMapper, _job.Page); + } using (Profiler.Measure("ASP.NET controls: PagePreInit")) { diff --git a/Composite/Plugins/PageTemplates/Razor/RazorPageTemplateProvider.cs b/Composite/Plugins/PageTemplates/Razor/RazorPageTemplateProvider.cs index 56e3d67540..fe225e145a 100644 --- a/Composite/Plugins/PageTemplates/Razor/RazorPageTemplateProvider.cs +++ b/Composite/Plugins/PageTemplates/Razor/RazorPageTemplateProvider.cs @@ -240,7 +240,7 @@ internal bool LoadRazorTemplate( return false; } - if (webPage == null || !(webPage is RazorPageTemplate)) + if (!(webPage is RazorPageTemplate)) { parsedTemplate = null; placeholderProperties = null; 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; } diff --git a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs index 49a4e09f73..14dbc8315c 100644 --- a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs +++ b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs @@ -780,7 +780,8 @@ private bool BuildRootPageUrl(IPage rootPage, CultureInfo cultureInfo, UrlSpace { if (hostnameBinding.Hostname != urlSpace.Hostname) { - result.Append("http://").Append(hostnameBinding.Hostname); + result.AppendFormat("http{0}://", hostnameBinding.EnforceHttps ? "s" : "") + .Append(hostnameBinding.Hostname); } } else 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/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.*")] 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/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/SearchDocumentBuilder.cs b/Composite/Search/Crawling/SearchDocumentBuilder.cs index a360413ac5..6b82bffb0c 100644 --- a/Composite/Search/Crawling/SearchDocumentBuilder.cs +++ b/Composite/Search/Crawling/SearchDocumentBuilder.cs @@ -359,9 +359,14 @@ private IEnumerable ProcessXhtml(string textFragment) { if (textFragment.StartsWith(" /// Crawls xhtml content and extracts text parts @@ -41,24 +46,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 +115,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) @@ -111,20 +158,24 @@ private void ProcessFunctionCall(XElement functionNode) return; } - foreach (var paramElement in functionNode.Elements()) + if (CrawlFunctionParameters) { - var parameterName = paramElement.GetAttributeValue("name"); - if(parameterName == null) continue; - - var profile = function.ParameterProfiles.FirstOrDefault(p => p.Name == parameterName); - if (profile != null) + foreach (var paramElement in functionNode.Elements()) { - if (profile.Type == typeof (XhtmlDocument)) + var parameterName = paramElement.GetAttributeValue("name"); + if (parameterName == null) continue; + + var profile = function.ParameterProfiles.FirstOrDefault(p => p.Name == parameterName); + if (profile != null) { - ProcessElement(paramElement); - } + if (profile.Type == typeof(XhtmlDocument) + || profile.Type == typeof(Lazy)) + { + ProcessElement(paramElement); + } - // TODO: handle the other parameter types + // TODO: handle the other parameter types + } } } @@ -146,7 +197,7 @@ private void ProcessFunctionCall(XElement functionNode) } else { - _textParts.Add(str); + AppendToCurrentTextFragment(str); } } } @@ -214,7 +265,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/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; } } 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 } } 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 + }); + } } } } 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. 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. diff --git a/Website/App_Data/Composite/DebugBuild.Composite.config b/Website/App_Data/Composite/DebugBuild.Composite.config index 304f815f68..f4ffdfa35e 100644 --- a/Website/App_Data/Composite/DebugBuild.Composite.config +++ b/Website/App_Data/Composite/DebugBuild.Composite.config @@ -80,7 +80,6 @@ generatedAssembliesDirectory="~/App_Data/Composite/Cache/Assemblies" tempDirectory="~/App_Data/Composite/Cache/Temp" cacheDirectory="~/App_Data/Composite/Cache" - resourceCacheDirectory="~/App_Data/Composite/Cache/ResourceCache" packageDirectory="~/App_Data/Composite/Packages" autoPackageInstallDirectory="~/App_Data/Composite/AutoInstallPackages" broadcastConsoleElementChanges="true" @@ -89,8 +88,6 @@ inlineCSharpFunctionDirectory="~/App_Data/Composite/InlineCSharpFunctions" packageLicenseDirectory="~/App_Data/Composite/PackageLicenses" binDirectory="~/Bin" - clientCacheMinutes="10080" - serverCacheMinutes="1440" workflowTimeout="7.00:00:00" consoleTimeout="00:05:00" nonProbeableAssemblyNames="System*,VJSharpCodeProvider,WebDev.*,Microsoft.Practices.EnterpriseLibrary.Common,CppCodeProvider,Microsoft.JScript,Microsoft.Practices.ObjectBuilder" @@ -99,7 +96,7 @@ imageQuality="80" prettifyPublicMarkup="true" prettifyRenderFunctionExceptions="true" - functionPreviewEnabled="true" + functionPreviewEnabled="true" inheritGlobalReadPermissionOnHiddenPerspectives="false" > diff --git a/Website/App_Data/Composite/ReleaseBuild.Composite.config b/Website/App_Data/Composite/ReleaseBuild.Composite.config index 160d52cc9a..843a57af4a 100644 --- a/Website/App_Data/Composite/ReleaseBuild.Composite.config +++ b/Website/App_Data/Composite/ReleaseBuild.Composite.config @@ -76,7 +76,6 @@ generatedAssembliesDirectory="~/App_Data/Composite/Cache/Assemblies" tempDirectory="~/App_Data/Composite/Cache/Temp" cacheDirectory="~/App_Data/Composite/Cache" - resourceCacheDirectory="~/App_Data/Composite/Cache/ResourceCache" packageDirectory="~/App_Data/Composite/Packages" autoPackageInstallDirectory="~/App_Data/Composite/AutoInstallPackages" treeDefinitionsDirectory="~/App_Data/Composite/TreeDefinitions" @@ -85,8 +84,6 @@ inlineCSharpFunctionDirectory="~/App_Data/Composite/InlineCSharpFunctions" packageLicenseDirectory="~/App_Data/Composite/PackageLicenses" binDirectory="~/Bin" - clientCacheMinutes="10080" - serverCacheMinutes="1440" workflowTimeout="7.00:00:00" consoleTimeout="00:05:00" nonProbeableAssemblyNames="System*,VJSharpCodeProvider,WebDev.*,Microsoft.Practices.EnterpriseLibrary.Common,CppCodeProvider,Microsoft.JScript,Microsoft.Practices.ObjectBuilder" diff --git a/Website/App_Data/Composite/TreeDefinitions/UrlConfiguration.xml b/Website/App_Data/Composite/TreeDefinitions/UrlConfiguration.xml new file mode 100644 index 0000000000..05408cd749 --- /dev/null +++ b/Website/App_Data/Composite/TreeDefinitions/UrlConfiguration.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Website/App_Data/Composite/reset-installation.bat b/Website/App_Data/Composite/reset-installation.bat index 4c58a9f662..82d59e9ad2 100644 --- a/Website/App_Data/Composite/reset-installation.bat +++ b/Website/App_Data/Composite/reset-installation.bat @@ -1,3 +1,4 @@ + del DataMetaData\*.xml del DataStores\*.xml del Configuration\DynamicSqlDataProvider.config @@ -31,7 +32,6 @@ md ApplicationState\SerializedWorkflows rd ..\..\Composite\InstalledPackages /S /Q rd ..\..\Views /S /Q rd Azure /S /Q -md Azure rd InlineCSharpFunctions /S /Q md InlineCSharpFunctions @@ -46,26 +46,32 @@ 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 copy ..\..\App_Data\Razor\web.config ..\..\App_Data\Razor.web.config -rd ..\..\App_Data\Razor /S /Q -md ..\..\App_Data\Razor +del /q ..\..\App_Data\Razor +FOR /D %%p IN (..\..\App_Data\Razor\*.*) DO rmdir "%%p" /s /q 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 +del /q ..\..\App_Data\UserControls +FOR /D %%p IN (..\..\App_Data\UserControls\*.*) DO rmdir "%%p" /s /q +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 -rd ..\..\App_Data\PageTemplates /S /Q -md ..\..\App_Data\PageTemplates +del /q ..\..\App_Data\PageTemplates +FOR /D %%p IN (..\..\App_Data\PageTemplates\*.*) DO rmdir "%%p" /s /q copy ..\..\App_Data\PageTemplates.web.config ..\..\App_Data\PageTemplates\web.config del ..\..\App_Data\PageTemplates.web.config /f :: License file cleanup -del PackageLicenses\*.* /y +del PackageLicenses\*.* /q :: Assembly cleanup rmdir ..\..\bin /S /Q diff --git a/Website/Composite/CompileScripts.xml b/Website/Composite/CompileScripts.xml index 42d26a36d8..6cca8a60ce 100644 --- a/Website/Composite/CompileScripts.xml +++ b/Website/Composite/CompileScripts.xml @@ -27,6 +27,7 @@ + + +
+ + + + + + + + + + + + + + + + + + + <% if (OtherCultureExist) + { %> + + <% } %> + + + + + + + + + <% if (!OtherCultureExist) + { %> + + <% } %> + <% if (OtherCultureExist) + { %> + + + <% } %> + + + + + + + +
+ <%= LocalizationFiles.Composite_Web_SourceEditor.ResxEditor_Label %> + + <%= LocalizationFiles.Composite_Web_SourceEditor.ResxEditor_OriginalText %> + + <%= LocalizationFiles.Composite_Web_SourceEditor.ResxEditor_TranslatedText %> +
+ + + + + + + + + + + + + + + +
+
+
+
+ + diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs new file mode 100644 index 0000000000..0c2fe53ba0 --- /dev/null +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.cs @@ -0,0 +1,247 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using System.Web.UI.WebControls; +using System.Xml.Linq; +using Castle.Core.Internal; +using Composite.C1Console.Events; +using Composite.Core.IO; +using Composite.Plugins.Elements.ElementProviders.WebsiteFileElementProvider; + +public partial class ResxEditor : System.Web.UI.Page +{ + public string FileName + { + get { return (string)ViewState["FileName"]; } + set { ViewState["FileName"] = value; } + } + + public string CultureName + { + get { return (string)ViewState["CultureName"]; } + set { ViewState["CultureName"] = value; } + } + + public string PageTitle + { + get { return Path.GetFileName(GetCurrentAlternateFileName(FileName)); } + } + + public bool OtherCultureExist + { + get { return bool.Parse((string)ViewState["OtherCultureExist"]); } + set { ViewState["OtherCultureExist"] = value.ToString(); } + } + public List OtherCulturesCultureNames { get; set; } + + + protected void Page_Load(object sender, EventArgs e) + { + + if (!this.IsPostBack) + { + OtherCultureExist = false; + + FileName = Request.QueryString["f"]; + + CultureName = Request.QueryString["t"]; + + if (CultureName != null) + { + OtherCultureExist = true; + Save(null, null); + var entityToken = new WebsiteFileElementProviderEntityToken("WebsiteFileElementProvider", + Path.GetDirectoryName(FileName), Path.GetDirectoryName(PathUtil.BaseDirectory)); + ConsoleMessageQueueFacade.Enqueue(new RefreshTreeMessageQueueItem { EntityToken = entityToken }, null); + } + + if (!FileName.EndsWith(".resx", StringComparison.OrdinalIgnoreCase)) + { + FileName = null; + return; + } + + var loc = CultureInfo.GetCultures(CultureTypes.AllCultures) + .LastOrDefault(f => f.Name != "" && FileName.EndsWith(f.Name + ".Resx", StringComparison.OrdinalIgnoreCase)); + + if (loc != null) + { + FileName = FileName.Replace(loc.Name + ".", ""); + OtherCultureExist = true; + } + + if (loc != null) + { + CultureName = loc.Name; + } + + this.BindGridView(); + } + } + + + + private static Dictionary GetResDic(string file) + { + Dictionary res = new Dictionary(); + + try + { + var xdoc = XDocument.Load(file); + var topicNodes = xdoc.Descendants("data"); + foreach (var node in topicNodes) + { + if (node.Attributes().Any(f => f.Name == XNamespace.Xml + "space" && f.Value == "preserve")) + { + var xAttribute = node.Attribute("name"); + if (xAttribute != null) res.Add(xAttribute.Value, node.Descendants("value").First().Value); + } + + } + + return res; + } + catch + { + return null; + } + } + + public async void Save(object sender, CommandEventArgs e) + { + if (FileName == null) + return; + + if (!OtherCultureExist) + { + var xdoc = await Task.Run(() => XDocument.Load(FileName)); + foreach (RepeaterItem item in DataRepeater.Items) + { + var labelcontrol = item.FindControl("Label") as Label; + if (labelcontrol != null) + { + var label = labelcontrol.Text; + var textBox = item.FindControl("Original") as TextBox; + if (textBox != null) + { + var original = textBox.Text; + var target = xdoc.Descendants("data").SingleOrDefault(f => + { + var xAttribute = f.Attribute("name"); + return xAttribute != null && xAttribute.Value == label; + }); + if (target != null) + { + target.Descendants("value").Single().Value = original ?? ""; + } + } + } + } + + await Task.Run(() => xdoc.Save(FileName)); + } + else + { + var xdoc = await Task.Run(() => XDocument.Load(FileName)); + foreach (RepeaterItem item in DataRepeater.Items) + { + var labelcontrol = item.FindControl("Label") as Label; + if (labelcontrol != null) + { + var label = labelcontrol.Text; + var textBox = item.FindControl("Translated") as TextBox; + if (textBox != null) + { + var translated = textBox.Text; + var target = xdoc.Descendants("data").SingleOrDefault(f => + { + var xAttribute = f.Attribute("name"); + return xAttribute != null && xAttribute.Value == label; + }); + if (target != null) + { + target.Descendants("value").Single().Value = translated ?? ""; + } + } + } + } + + await Task.Run(() => xdoc.Save(GetCurrentAlternateFileName(FileName))); + } + SaveStatus(true); + } + + public void OnMessage() + { + string message = ctlFeedback.GetPostedMessage(); + + if (message == "save") + { + Save(null, null); + ctlFeedback.SetStatus(true); + SaveStatus(true); + } + } + + protected void SaveStatus(bool succeeded) + { + var viewId = Request["__VIEWID"]; + var consoleId = Request["__CONSOLEID"]; + ConsoleMessageQueueFacade.Enqueue(new SaveStatusConsoleMessageQueueItem { ViewId = viewId, Succeeded = succeeded }, consoleId); + } + + private string GetCurrentAlternateFileName(string file) + { + return Path.GetDirectoryName(file) + + Path.DirectorySeparatorChar + + Path.GetFileNameWithoutExtension(file) + ((!CultureName.IsNullOrEmpty()) ? "." : "") + + CultureName + Path.GetExtension(file); + } + + private void BindGridView() + { + Dictionary otherculturedic = null; + + if (FileName == null) + return; + + var dic = GetResDic(FileName); + + if (dic == null) + return; + + if (OtherCultureExist) + { + otherculturedic = GetResDic(GetCurrentAlternateFileName(FileName)); + } + + List li = new List(); + + foreach (var n in dic) + { + var p = new Phrase() + { + Label = n.Key, + Original = n.Value, + Translated = (otherculturedic != null) ? otherculturedic[n.Key] : "" + }; + li.Add(p); + } + + DataRepeater.DataSource = li; + DataRepeater.DataBind(); + } + + + public class Phrase + { + public string Label { get; set; } + public string Original { get; set; } + public string Translated { get; set; } + } +} + + diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.designer.cs b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.designer.cs new file mode 100644 index 0000000000..815d619123 --- /dev/null +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.aspx.designer.cs @@ -0,0 +1,6 @@ +public partial class ResxEditor +{ + protected global::Composite.Core.WebClient.UiControlLib.Feedback ctlFeedback; + + protected global::System.Web.UI.WebControls.Repeater DataRepeater; +} \ No newline at end of file diff --git a/Website/Composite/content/misc/editors/resxeditor/resxeditor.css b/Website/Composite/content/misc/editors/resxeditor/resxeditor.css new file mode 100644 index 0000000000..b61ff86294 --- /dev/null +++ b/Website/Composite/content/misc/editors/resxeditor/resxeditor.css @@ -0,0 +1,48 @@ +@namespace url("http://www.w3.org/1999/xhtml"); +@namespace ui url("http://www.w3.org/1999/xhtml"); + +html, input, textarea, button, select, td, th { + font-family: "Open Sans", sans-serif; + font-size: 13px; +} + +th { + font-weight: 600; + line-height: 18px; +} + +td { + font-weight: 400; +} + +.title { + font-size: 18px; +} + +.label { + width: 400px; + word-wrap: normal; + white-space: normal !important; +} + +.inputbox input { + width: 400px; +} + +.table th:last-child, +.table td:last-child { + width: 100%; +} + +.table td { + overflow: visible; + color: inherit; +} + +.table .hidden { + visibility: hidden; +} + +.table .hilite .hidden{ + visibility: visible; +} diff --git a/Website/Composite/content/views/browser/BrowserTabBoxBinding.js b/Website/Composite/content/views/browser/BrowserTabBoxBinding.js index e70a363210..516e6fe5b4 100644 --- a/Website/Composite/content/views/browser/BrowserTabBoxBinding.js +++ b/Website/Composite/content/views/browser/BrowserTabBoxBinding.js @@ -1,4 +1,4 @@ -BrowserTabBoxBinding.prototype = new TabBoxBinding; +BrowserTabBoxBinding.prototype = new TabBoxBinding; BrowserTabBoxBinding.prototype.constructor = BrowserTabBoxBinding; BrowserTabBoxBinding.superclass = TabBoxBinding.prototype; @@ -186,7 +186,12 @@ BrowserTabBoxBinding.prototype.getLocation = function () { var tab = this.getBrowserTabBinding(); var win = tab.browserwindow; - return new String ( win.getContentDocument ().location ); + var doc = win.getContentDocument(); + if (doc == null) { + return undefined; + } else { + return new String(win.getContentDocument().location); + } } diff --git a/Website/Composite/content/views/relationshipgraph/Default.aspx.cs b/Website/Composite/content/views/relationshipgraph/Default.aspx.cs index 3628568dde..7bfe292384 100644 --- a/Website/Composite/content/views/relationshipgraph/Default.aspx.cs +++ b/Website/Composite/content/views/relationshipgraph/Default.aspx.cs @@ -1,24 +1,12 @@ -using System; -using System.Data; -using System.Configuration; -using System.Collections; +using System; using System.Linq; -using System.Web; -using System.Web.Security; using System.Web.UI; -using System.Web.UI.WebControls; -using System.Web.UI.WebControls.WebParts; -using System.Web.UI.HtmlControls; using Composite.Core.Serialization; using System.Collections.Generic; -using Composite.Core.Types; -using System.Reflection; using Composite.C1Console.Security; using System.Xml.Linq; -using System.Text; using Composite.Data.Types; using Composite.Data; -using Composite.Core.WebClient; public partial class Spikes_RelationshipGraph_Default : System.Web.UI.Page @@ -59,12 +47,24 @@ protected void Page_Load(object sender, EventArgs e) } } + private void AddPersissionsLine(List elements, string entity, IEnumerable permissions) + { + var permissionLabels = permissions.Select(p => new PermissionDescriptor(p).Label).OrderBy(l => l).ToList(); + + if (permissionLabels.Count == 0) return; + + elements.Add(new XElement("span", + new XAttribute("style", "padding-left: 15px;"), + entity + " = " + string.Join(", ", permissionLabels))); + elements.Add(new XElement("br")); + } + private void PrettyPrintEntityToken(EntityToken entityToken, string color) { - List idList = new List(); + var idList = new List(); - if (entityToken.Id.Contains("=") == true) + if (entityToken.Id.Contains("=")) { Dictionary dic = StringConversionServices.ParseKeyValueCollection(entityToken.Id); @@ -72,11 +72,10 @@ private void PrettyPrintEntityToken(EntityToken entityToken, string color) foreach (KeyValuePair kvp in dic) { - idList.Add(new XElement("span", new XAttribute("style", "padding-left: 15px;"), - string.Format("{0} = {1}", kvp.Key, kvp.Value))); + idList.Add(new XElement("span", new XAttribute("style", "padding-left: 15px; color: " + color), + string.Format("{0} = {1}", kvp.Key, kvp.Value))); idList.Add(new XElement("br")); } - } else { @@ -84,103 +83,52 @@ private void PrettyPrintEntityToken(EntityToken entityToken, string color) } - - - - - - - - - List usersermisstionsDefinedHere = new List(); - List currentUsersPermisstionTypes = new List(); + var userPermissionsDefinedHere = new List(); + var currentUsersPermissionTypes = new List(); string serializedEntityToken = EntityTokenSerializer.Serialize(entityToken); - IEnumerable usernames = UserValidationFacade.AllUsernames; + var usernames = UserValidationFacade.AllUsernames.OrderBy(u => u).ToList(); foreach (string username in usernames) { - IEnumerable userPermissionTypes = PermissionTypeFacade.GetLocallyDefinedUserPermissionTypes(new UserToken(username), entityToken); - - StringBuilder sb = new StringBuilder(); - foreach (PermissionType permissionType in userPermissionTypes) - { - if (sb.ToString() != "") - { - sb.Append(", "); - } - - sb.Append(new PermissionDescriptor(permissionType).Label); - } - - usersermisstionsDefinedHere.Add(new XElement("span", new XAttribute("style", "padding-left: 15px;"), string.Format("{0} = {1}", username, sb.ToString()))); - usersermisstionsDefinedHere.Add(new XElement("br")); + IEnumerable userPermissionTypes = PermissionTypeFacade.GetLocallyDefinedUserPermissionTypes( + new UserToken(username), entityToken); + AddPersissionsLine(userPermissionsDefinedHere, username, userPermissionTypes); + var currentPermissionTypes = PermissionTypeFacade.GetCurrentPermissionTypes( + new UserToken(username), entityToken, + PermissionTypeFacade.GetUserPermissionDefinitions(username), + PermissionTypeFacade.GetUserGroupPermissionDefinitions(username)); - IEnumerable currentPermissionTypes = PermissionTypeFacade.GetCurrentPermissionTypes(new UserToken(username), entityToken, PermissionTypeFacade.GetUserPermissionDefinitions(username), PermissionTypeFacade.GetUserGroupPermissionDefinitions(username)); - - - StringBuilder currentSb = new StringBuilder(); - foreach (PermissionType permissionType in currentPermissionTypes) - { - if (currentSb.ToString() != "") - { - currentSb.Append(", "); - } - - currentSb.Append(new PermissionDescriptor(permissionType).Label); - } - - currentUsersPermisstionTypes.Add(new XElement("span", new XAttribute("style", "padding-left: 15px;"), string.Format("{0} = {1}", username, currentSb.ToString()))); - currentUsersPermisstionTypes.Add(new XElement("br")); + AddPersissionsLine(currentUsersPermissionTypes, username, currentPermissionTypes); } - List userGroupPermissionsDefinedHere = new List(); - List inheritedGroupPermissions = new List(); + var userGroupPermissionsDefinedHere = new List(); + var inheritedGroupPermissions = new List(); - List userGroups = DataFacade.GetData().ToList(); + var userGroups = DataFacade.GetData().OrderBy(ug => ug.Name).ToList(); foreach (IUserGroup userGroup in userGroups) { - IEnumerable userGroupPermissionTypes = PermissionTypeFacade.GetLocallyDefinedUserGroupPermissionTypes(userGroup.Id, entityToken); - - StringBuilder sb = new StringBuilder(); - foreach (PermissionType permissionType in userGroupPermissionTypes) - { - if (sb.ToString() != "") - { - sb.Append(", "); - } - - sb.Append(new PermissionDescriptor(permissionType).Label); - } - - userGroupPermissionsDefinedHere.Add(new XElement("span", new XAttribute("style", "padding-left: 15px;"), string.Format("{0} = {1}", userGroup.Name, sb.ToString()))); - userGroupPermissionsDefinedHere.Add(new XElement("br")); + var userGroupPermissionTypes = PermissionTypeFacade.GetLocallyDefinedUserGroupPermissionTypes(userGroup.Id, entityToken); + AddPersissionsLine(userGroupPermissionsDefinedHere, userGroup.Name, userGroupPermissionTypes); IEnumerable inheritedUserGroupPermissionTypes = PermissionTypeFacade.GetInheritedGroupPermissionsTypes(userGroup.Id, entityToken); - sb = new StringBuilder(); - foreach (PermissionType permissionType in inheritedUserGroupPermissionTypes) - { - if (sb.ToString() != "") - { - sb.Append(", "); - } - - sb.Append(new PermissionDescriptor(permissionType).Label); - } - inheritedGroupPermissions.Add(new XElement("span", new XAttribute("style", "padding-left: 15px;"), string.Format("{0} = {1}", userGroup.Name, sb.ToString()))); - inheritedGroupPermissions.Add(new XElement("br")); + AddPersissionsLine(inheritedGroupPermissions, userGroup.Name, inheritedUserGroupPermissionTypes); } - XElement element = - new XElement("div", new XAttribute("style", string.Format("border:2px; border-style: solid; border-color: {0}; margin-bottom: 2px; margin-left:5px; margin-right:5px; padding: 3px;", color)), + var element = + new XElement("div", + new XAttribute("style", + string.Format( + "border:2px; border-style: solid; border-color: {0}; margin-bottom: 2px; margin-left:5px; margin-right:5px; padding: 3px;", + color)), new XElement("b", "Runtime type: "), entityToken.GetType().ToString(), new XElement("br"), @@ -196,30 +144,49 @@ private void PrettyPrintEntityToken(EntityToken entityToken, string color) new XElement("b", "Id: "), idList, new XElement("br"), - new XElement("b", "Serialized entity token: "), + new XElement("b", "Serialized entity token: "), serializedEntityToken, new XElement("br"), + new XElement("br")); - new XElement("b", "Users permissions defined here: "), - new XElement("br"), - usersermisstionsDefinedHere, + + if (currentUsersPermissionTypes.Any()) + { + element.Add( + new XElement("b", "Resolved users permissions here: "), new XElement("br"), + currentUsersPermissionTypes, + new XElement("br")); + } + - new XElement("b", "Current users permissions here: "), + if (userPermissionsDefinedHere.Any()) + { + element.Add( + new XElement("b", "Users permissions defined here: "), new XElement("br"), - currentUsersPermisstionTypes, + userPermissionsDefinedHere, + new XElement("br")); + } + + if (inheritedGroupPermissions.Any()) + { + element.Add( + new XElement("b", "Inherted user group permissions: "), new XElement("br"), + inheritedGroupPermissions, + new XElement("br")); + } - new XElement("b", "User group permissions defined here: "), + if (userGroupPermissionsDefinedHere.Any()) + { + element.Add( + new XElement("b", "User group permissions defined here: "), new XElement("br"), userGroupPermissionsDefinedHere, - new XElement("br"), - - new XElement("b", "Inherted user group permissions: "), - new XElement("br"), - inheritedGroupPermissions, - new XElement("br") - ); + new XElement("br")); + } + RelationshipGraphHolder.Controls.Add(new LiteralControl(element.ToString())); } 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; 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/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 @@ - + diff --git a/Website/Composite/localization/Composite.Web.SourceEditor.en-us.xml b/Website/Composite/localization/Composite.Web.SourceEditor.en-us.xml index 9a11cdb19f..d6976323f3 100644 --- a/Website/Composite/localization/Composite.Web.SourceEditor.en-us.xml +++ b/Website/Composite/localization/Composite.Web.SourceEditor.en-us.xml @@ -36,4 +36,12 @@ + + + + + + + + \ No newline at end of file diff --git a/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml b/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml new file mode 100644 index 0000000000..39e1211642 --- /dev/null +++ b/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file 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. diff --git a/Website/Composite/scripts/source/top/core/MessageQueue.js b/Website/Composite/scripts/source/top/core/MessageQueue.js index dc6b78849a..f9b63793b8 100644 --- a/Website/Composite/scripts/source/top/core/MessageQueue.js +++ b/Website/Composite/scripts/source/top/core/MessageQueue.js @@ -1,4 +1,4 @@ -/** +/** * MessageQueue! */ window.MessageQueue = new function () { @@ -327,32 +327,18 @@ window.MessageQueue = new function () { case "SelectElement": var perspectiveElementKey = action.SelectElementParams.PerspectiveElementKey; - if (perspectiveElementKey && perspectiveElementKey != StageBinding.getSelectionHandle()) { - var handler = { - handleBroadcast: function(broadcast, arg) { - switch (broadcast) { - case BroadcastMessages.STAGEDECK_CHANGED: - if (arg == perspectiveElementKey) { - StageBinding.selectBrowserTab(); - EventBroadcaster.broadcast( - BroadcastMessages.SYSTEMTREEBINDING_FOCUS, - action.SelectElementParams.EntityToken - ); - EventBroadcaster.unsubscribe(BroadcastMessages.STAGEDECK_CHANGED, this); - } - break; - } - } + var entityToken = action.SelectElementParams.EntityToken; + + StageBinding.select(perspectiveElementKey) + .then( + function () { + StageBinding.selectBrowserTab(); + EventBroadcaster.broadcast( + BroadcastMessages.SYSTEMTREEBINDING_FOCUS, + entityToken + ); } - EventBroadcaster.subscribe(BroadcastMessages.STAGEDECK_CHANGED, handler); - StageBinding.setSelectionByHandle(perspectiveElementKey); - } else { - EventBroadcaster.broadcast( - BroadcastMessages.SYSTEMTREEBINDING_FOCUS, - action.SelectElementParams.EntityToken - ); - } - + ) this._nextAction(); break; diff --git a/Website/Composite/scripts/source/top/ui/bindings/data/keyboard/DataInputBinding.js b/Website/Composite/scripts/source/top/ui/bindings/data/keyboard/DataInputBinding.js index 0730e87d4c..4d1cfb54b8 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/data/keyboard/DataInputBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/data/keyboard/DataInputBinding.js @@ -1,4 +1,4 @@ -DataInputBinding.prototype = new DataBinding; +DataInputBinding.prototype = new DataBinding; DataInputBinding.prototype.constructor = DataInputBinding; DataInputBinding.superclass = DataBinding.prototype; @@ -298,8 +298,17 @@ DataInputBinding.prototype._buildDOMContent = function () { } if (this.spellcheck && Client.hasSpellcheck) { - var currentLang = Localization.currentLang(); - if (currentLang != null) { + var currentLang = Localization.currentLang(); + var parentLangAttribute = null; + if (this.shadowTree.input.parentNode != null) { + if (this.shadowTree.input.parentNode.parentElement!=null) { + parentLangAttribute = this.shadowTree.input.parentNode.parentElement.getAttribute("lang"); + } + } + if (parentLangAttribute != null) { + this.shadowTree.input.setAttribute("spellcheck", "true"); + this.shadowTree.input.setAttribute("lang", parentLangAttribute); + } else if (currentLang != null) { this.shadowTree.input.setAttribute("spellcheck", "true"); this.shadowTree.input.setAttribute("lang", Localization.currentLang()); } else { 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; } diff --git a/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerBinding.js b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerBinding.js index 2ded95c84e..52a789f241 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerBinding.js @@ -1,4 +1,4 @@ -ExplorerBinding.prototype = new FlexBoxBinding; +ExplorerBinding.prototype = new FlexBoxBinding; ExplorerBinding.prototype.constructor = ExplorerBinding; ExplorerBinding.superclass = FlexBoxBinding.prototype; ExplorerBinding.ACTION_INITIALIZED = "explorer initialized"; @@ -119,6 +119,18 @@ ExplorerBinding.prototype.onBindingAttach = function () { ); } + +/** + * @overloads {TreeBinding#onBindingRegister} + */ +ExplorerBinding.prototype.onBindingRegister = function () { + + ExplorerBinding.superclass.onBindingRegister.call(this); + + this.setContextMenu(top.app.bindingMap.explorerpopup); +} + + /** * @overloads {Binding#onBindingInitialize} */ diff --git a/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerPopupBinding.js b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerPopupBinding.js new file mode 100644 index 0000000000..76cd6ab5f2 --- /dev/null +++ b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerPopupBinding.js @@ -0,0 +1,119 @@ +ExplorerPopupBinding.prototype = new SystemTreePopupBinding; +ExplorerPopupBinding.prototype.constructor = ExplorerPopupBinding; +ExplorerPopupBinding.superclass = SystemTreePopupBinding.prototype; + +/** + * @class + */ +function ExplorerPopupBinding () { + + /** + * @type {SystemLogger} + */ + this.logger = SystemLogger.getLogger ( "ExplorerPopupBinding" ); +} + +/** + * @overloads {Binding#onBindingRegister} + */ +ExplorerPopupBinding.prototype.onBindingRegister = function () { + + SystemTreePopupBinding.superclass.onBindingRegister.call ( this ); + this.addActionListener ( MenuItemBinding.ACTION_COMMAND, this ); +} + + +/** + * Setup clipboard operation menuitems. + */ +ExplorerPopupBinding.prototype._setupClipboardItems = function () { + +} + +/** + * @implements {IActionListener} + * @overloads {PopupBinding#handleAction} + * @param {Action} action + */ +ExplorerPopupBinding.prototype.handleAction = function ( action ) { + + switch ( action.type ) { + + case MenuItemBinding.ACTION_COMMAND : + + var self = this; + StageBinding.select(this._pespectiveKey) + .then( + function () { + ExplorerPopupBinding.superclass.handleAction.call(self, action); + } + ) + break; + } +} + +/** + * @overwrites {PopupBinding#snapToMouse} + * @param {MouseEvent} e + */ +ExplorerPopupBinding.prototype.snapToMouse = function ( e ) { + + var node = e.target ? e.target : e.srcElement; + var name = DOMUtil.getLocalName ( node ); + var binding = null; + + if ( name != "tree" ) { + switch ( name ) { + default : + + var target = DOMUtil.getAncestorByLocalName("explorertoolbarbutton", node); + + if (target != null) { + binding = UserInterface.getBinding(target); + if (binding.isDisabled) { // no contextmenu for disabled treenodes + binding = null; + } + } + break; + } + if ( binding != null && binding.node != null && binding.node.getActionProfile () != null ) { + + this._node = binding.node; + this._actionProfile = this.getCompiledActionProfile(binding.node); + this._pespectiveKey = binding.handle; + + SystemTreePopupBinding.superclass.snapToMouse.call(this, e); + } + } +} + + +/** + * @return {Map>} + */ +ExplorerPopupBinding.prototype.getCompiledActionProfile = function (node) { + var result = new Map(); + + var actionProfile = node.getActionProfile(); + + if (actionProfile != null) { + var self = this; + actionProfile.each( + function (groupid, list) { + var newList = new List(); + list.each(function (systemAction) { + if (systemAction.getActivePositions() & 1) { + newList.add(systemAction); + } + }); + if (newList.hasEntries()) { + result.set(groupid, newList); + } + } + ); + } + + result.Node = node; + + return result; +} diff --git a/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerToolBarButtonBinding.js b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerToolBarButtonBinding.js index 7928ff44ea..b3660cec22 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerToolBarButtonBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/explorer/ExplorerToolBarButtonBinding.js @@ -1,4 +1,4 @@ -ExplorerToolBarButtonBinding.prototype = new ToolBarButtonBinding; +ExplorerToolBarButtonBinding.prototype = new ToolBarButtonBinding; ExplorerToolBarButtonBinding.prototype.constructor = ExplorerToolBarButtonBinding; ExplorerToolBarButtonBinding.superclass = ToolBarButtonBinding.prototype; ExplorerToolBarButtonBinding.TYPE_NORMAL = "normal"; @@ -44,13 +44,40 @@ ExplorerToolBarButtonBinding.prototype.toString = function () { * @overloads {Binding#onBindingAttach} */ ExplorerToolBarButtonBinding.prototype.onBindingAttach = function () { - + + this.addEventListener(DOMEvents.MOUSEUP); var isLargeButton = this.explorerToolBarButtonType == ExplorerToolBarButtonBinding.TYPE_LARGE; var imageSizeParameter = isLargeButton ? ToolBarBinding.IMAGESIZE_LARGE : ToolBarBinding.IMAGESIZE_NORMAL; - this.imageProfile = this.node.getImageProfile ( imageSizeParameter ); + this.imageProfile = this.node.getImageProfile(imageSizeParameter); + ExplorerToolBarButtonBinding.superclass.onBindingAttach.call ( this ); } + +/** + * @implements {IEventListener} + * @overloads {Binding#handleEvent} + * @param {MouseEvent} e + */ +ExplorerToolBarButtonBinding.prototype.handleEvent = function (e) { + + ExplorerToolBarButtonBinding.superclass.handleEvent.call(this, e); + + if (!this.isDisabled && !BindingDragger.isDragging) { + + switch (e.type) { + case DOMEvents.MOUSEUP: + if (e.button != ButtonStateManager.RIGHT_BUTTON) { + if (this.isChecked) { + StageBinding.bindingInstance.selectBrowserTab(); + } + } + break; + } + } +} + + /** * ExplorerToolBarButtonBinding factory. * @param {DOMDocument} ownerDocument @@ -60,7 +87,6 @@ ExplorerToolBarButtonBinding.prototype.onBindingAttach = function () { ExplorerToolBarButtonBinding.newInstance = function ( ownerDocument, explorerToolBarButtonType ) { var nodename = "ui:explorertoolbarbutton"; - var element = DOMUtil.createElementNS ( Constants.NS_UI, nodename, ownerDocument ); var binding = UserInterface.registerBinding ( element, ExplorerToolBarButtonBinding ); binding.explorerToolBarButtonType = explorerToolBarButtonType; diff --git a/Website/Composite/scripts/source/top/ui/bindings/request/RequestBinding.js b/Website/Composite/scripts/source/top/ui/bindings/request/RequestBinding.js index c54f9e3da2..2aed89b5a1 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/request/RequestBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/request/RequestBinding.js @@ -1,4 +1,4 @@ -RequestBinding.prototype = new Binding; +RequestBinding.prototype = new Binding; RequestBinding.prototype.constructor = RequestBinding; RequestBinding.superclass = Binding.prototype; @@ -13,7 +13,9 @@ RequestBinding.CALLBACK_ID = "__REQUEST"; * poluate in order to let the server know who we are... * @type {String} */ -RequestBinding.INPUT_ID = "__CONSOLEID" +RequestBinding.INPUT_ID = "__CONSOLEID"; + +RequestBinding.VIEW_ID = "__VIEWID"; /** * @class @@ -53,6 +55,17 @@ RequestBinding.prototype.onBindingAttach = function () { if ( input != null ) { input.value = Application.CONSOLE_ID; } + + input = this.bindingDocument.getElementById(RequestBinding.VIEW_ID); + if (input != null) { + + var viewBinding = this.getAncestorBindingByType(ViewBinding, true); + if (viewBinding != null && viewBinding.getHandle) + { + input.value = viewBinding.getHandle(); + } + + } } /** diff --git a/Website/Composite/scripts/source/top/ui/bindings/stage/StageBinding.js b/Website/Composite/scripts/source/top/ui/bindings/stage/StageBinding.js index 94b85ebffc..ac1a8969f2 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/stage/StageBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/stage/StageBinding.js @@ -1,4 +1,4 @@ -StageBinding.prototype = new FocusBinding; +StageBinding.prototype = new FocusBinding; StageBinding.prototype.constructor = StageBinding; StageBinding.superclass = FocusBinding.prototype; @@ -103,6 +103,38 @@ StageBinding.presentViewDefinition = function (definition, contextSource) { StageBinding.bindingInstance._presentViewDefinition(definition, contextSource); } + +/** + * @param {string} handle + * @return {Promise} + */ +StageBinding.select = function (perspectiveElementKey) { + + var promise = new Promise(function (resolve, reject) { + if (perspectiveElementKey && perspectiveElementKey != StageBinding.getSelectionHandle()) { + var handler = { + handleBroadcast: function (broadcast, arg) { + switch (broadcast) { + case BroadcastMessages.STAGEDECK_CHANGED: + if (arg == perspectiveElementKey) { + EventBroadcaster.unsubscribe(BroadcastMessages.STAGEDECK_CHANGED, this); + resolve(); + } + break; + } + } + } + EventBroadcaster.subscribe(BroadcastMessages.STAGEDECK_CHANGED, handler); + StageBinding.setSelectionByHandle(perspectiveElementKey); + } else { + resolve(); + } + }); + + return promise; +} + + /** * @class */ diff --git a/Website/Composite/scripts/source/top/ui/bindings/stage/decks/StageDeckBinding.js b/Website/Composite/scripts/source/top/ui/bindings/stage/decks/StageDeckBinding.js index f26a2f124c..ef86a120a9 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/stage/decks/StageDeckBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/stage/decks/StageDeckBinding.js @@ -1,4 +1,4 @@ -StageDeckBinding.prototype = new DeckBinding; +StageDeckBinding.prototype = new DeckBinding; StageDeckBinding.prototype.constructor = StageDeckBinding; StageDeckBinding.superclass = DeckBinding.prototype; @@ -275,7 +275,14 @@ StageDeckBinding.prototype.getBrowserTab = function () { StageDeckBinding.prototype.getBrowserPage = function () { - return this.getBrowserTab().getAssociatedView().getContentWindow().bindingMap.browserpage; + var browserTab = this.getBrowserTab(); + if (browserTab == null) return null; + var associatedView = browserTab.getAssociatedView(); + if (associatedView == null) return null; + var contentWindow = associatedView.getContentWindow(); + if (contentWindow == null) return null; + if (contentWindow.bindingMap == null) return null; + return contentWindow.bindingMap.browserpage; } StageDeckBinding.prototype.getSystemTree = function () { diff --git a/Website/Composite/scripts/source/top/ui/bindings/system/SystemTreePopupBinding.js b/Website/Composite/scripts/source/top/ui/bindings/system/SystemTreePopupBinding.js index 4b24470baa..604a785280 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/system/SystemTreePopupBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/system/SystemTreePopupBinding.js @@ -1,4 +1,4 @@ -SystemTreePopupBinding.prototype = new PopupBinding; +SystemTreePopupBinding.prototype = new PopupBinding; SystemTreePopupBinding.prototype.constructor = SystemTreePopupBinding; SystemTreePopupBinding.superclass = PopupBinding.prototype; @@ -17,12 +17,12 @@ SystemTreePopupBinding.isRefreshAllowed = true; /** * @class */ -function SystemTreePopupBinding () { +function SystemTreePopupBinding() { /** * @type {SystemLogger} */ - this.logger = SystemLogger.getLogger ( "SystemTreePopupBinding" ); + this.logger = SystemLogger.getLogger("SystemTreePopupBinding"); /** * @type {string} @@ -55,9 +55,9 @@ function SystemTreePopupBinding () { */ SystemTreePopupBinding.prototype.onBindingRegister = function () { - SystemTreePopupBinding.superclass.onBindingRegister.call ( this ); - this.subscribe ( BroadcastMessages.SYSTEM_ACTIONPROFILE_PUBLISHED ); - this.addActionListener ( MenuItemBinding.ACTION_COMMAND, this ); + SystemTreePopupBinding.superclass.onBindingRegister.call(this); + this.subscribe(BroadcastMessages.SYSTEM_ACTIONPROFILE_PUBLISHED); + this.addActionListener(MenuItemBinding.ACTION_COMMAND, this); } /** @@ -65,8 +65,8 @@ SystemTreePopupBinding.prototype.onBindingRegister = function () { */ SystemTreePopupBinding.prototype.onBindingAttach = function () { - SystemTreePopupBinding.superclass.onBindingAttach.call ( this ); - this._indexMenuContent (); + SystemTreePopupBinding.superclass.onBindingAttach.call(this); + this._indexMenuContent(); } /** @@ -74,12 +74,12 @@ SystemTreePopupBinding.prototype.onBindingAttach = function () { * @param {string} broadcast * @param {object} arg */ -SystemTreePopupBinding.prototype.handleBroadcast = function ( broadcast, arg ) { +SystemTreePopupBinding.prototype.handleBroadcast = function (broadcast, arg) { - SystemTreePopupBinding.superclass.handleBroadcast.call ( this, broadcast, arg ); + SystemTreePopupBinding.superclass.handleBroadcast.call(this, broadcast, arg); - switch ( broadcast ) { - case BroadcastMessages.SYSTEM_ACTIONPROFILE_PUBLISHED : + switch (broadcast) { + case BroadcastMessages.SYSTEM_ACTIONPROFILE_PUBLISHED: if (arg != null && arg.actionProfile != null) { //TODO: refactor with updating API this._node = arg.actionProfile.Node; @@ -105,21 +105,21 @@ SystemTreePopupBinding.prototype.show = function () { /* * Build content */ - var key = this._getProfileKey (); + var key = this._getProfileKey(); - if ( key != this._currentProfileKey ) { - this.disposeContent (); - this.constructContent (); + if (key != this._currentProfileKey) { + this.disposeContent(); + this.constructContent(); this._currentProfileKey = key; } - this._setupClipboardItems (); - this._setupRefreshItem (); + this._setupClipboardItems(); + this._setupRefreshItem(); /* * Show. */ - SystemTreePopupBinding.superclass.show.call ( this ); + SystemTreePopupBinding.superclass.show.call(this); } /** @@ -127,13 +127,13 @@ SystemTreePopupBinding.prototype.show = function () { */ SystemTreePopupBinding.prototype._setupClipboardItems = function () { - var cut = this.getMenuItemForCommand ( SystemTreePopupBinding.CMD_CUT ); - var copy = this.getMenuItemForCommand ( SystemTreePopupBinding.CMD_COPY ); - var paste = this.getMenuItemForCommand ( SystemTreePopupBinding.CMD_PASTE ); + var cut = this.getMenuItemForCommand(SystemTreePopupBinding.CMD_CUT); + var copy = this.getMenuItemForCommand(SystemTreePopupBinding.CMD_COPY); + var paste = this.getMenuItemForCommand(SystemTreePopupBinding.CMD_PASTE); - cut.setDisabled ( !SystemTreePopupBinding.isCutAllowed ); - copy.setDisabled ( !SystemTreePopupBinding.isCutAllowed ); - paste.setDisabled ( SystemTreeBinding.clipboard == null ); + cut.setDisabled(!SystemTreePopupBinding.isCutAllowed); + copy.setDisabled(!SystemTreePopupBinding.isCutAllowed); + paste.setDisabled(SystemTreeBinding.clipboard == null); } /** @@ -141,8 +141,8 @@ SystemTreePopupBinding.prototype._setupClipboardItems = function () { */ SystemTreePopupBinding.prototype._setupRefreshItem = function () { - var refresh = this.getMenuItemForCommand ( SystemTreePopupBinding.CMD_REFRESH ); - refresh.setDisabled ( !SystemTreePopupBinding.isRefreshAllowed ); + var refresh = this.getMenuItemForCommand(SystemTreePopupBinding.CMD_REFRESH); + refresh.setDisabled(!SystemTreePopupBinding.isRefreshAllowed); } /** @@ -150,32 +150,32 @@ SystemTreePopupBinding.prototype._setupRefreshItem = function () { * @overloads {PopupBinding#handleAction} * @param {Action} action */ -SystemTreePopupBinding.prototype.handleAction = function ( action ) { +SystemTreePopupBinding.prototype.handleAction = function (action) { - SystemTreePopupBinding.superclass.handleAction.call ( this, action ) + SystemTreePopupBinding.superclass.handleAction.call(this, action) - switch ( action.type ) { + switch (action.type) { /* * Note to self: The first part is duplicated by SystemToolBarBinding! */ - case MenuItemBinding.ACTION_COMMAND : + case MenuItemBinding.ACTION_COMMAND: var menuitemBinding = action.target; var systemAction = menuitemBinding.associatedSystemAction; - if ( systemAction ) { + if (systemAction) { SystemAction.invoke(systemAction, this._node); - if(this._keepBundleState) { + if (this._keepBundleState) { var bundleName = systemAction.getBundleName(); if (bundleName != null) { LocalStorage.set(ToolBarComboButtonBinding.STORAGE_PREFFIX + bundleName, systemAction.getHandle()); } } } else { - var cmd = menuitemBinding.getProperty ( "cmd" ); - if ( cmd ) { - this._handleCommand ( cmd ); + var cmd = menuitemBinding.getProperty("cmd"); + if (cmd) { + this._handleCommand(cmd); } } @@ -189,29 +189,29 @@ SystemTreePopupBinding.prototype.handleAction = function ( action ) { * Handle command. * @param {string} cmd */ -SystemTreePopupBinding.prototype._handleCommand = function ( cmd ) { +SystemTreePopupBinding.prototype._handleCommand = function (cmd) { var broadcast = null; - switch ( cmd ) { - case SystemTreePopupBinding.CMD_CUT : + switch (cmd) { + case SystemTreePopupBinding.CMD_CUT: broadcast = BroadcastMessages.SYSTEMTREEBINDING_CUT; break; - case SystemTreePopupBinding.CMD_COPY : + case SystemTreePopupBinding.CMD_COPY: broadcast = BroadcastMessages.SYSTEMTREEBINDING_COPY; break; - case SystemTreePopupBinding.CMD_PASTE : + case SystemTreePopupBinding.CMD_PASTE: broadcast = BroadcastMessages.SYSTEMTREEBINDING_PASTE; break; - case SystemTreePopupBinding.CMD_REFRESH : + case SystemTreePopupBinding.CMD_REFRESH: broadcast = BroadcastMessages.SYSTEMTREEBINDING_REFRESH; break; } - if ( broadcast ) { // allows the popup to close - setTimeout ( function () { - EventBroadcaster.broadcast ( broadcast ); - }, 0 ); + if (broadcast) { // allows the popup to close + setTimeout(function () { + EventBroadcaster.broadcast(broadcast); + }, 0); } } @@ -220,13 +220,13 @@ SystemTreePopupBinding.prototype._handleCommand = function ( cmd ) { */ SystemTreePopupBinding.prototype.disposeContent = function () { - var members = new List ( - DOMUtil.getElementsByTagName ( this.bindingElement, "menugroup" ) + var members = new List( + DOMUtil.getElementsByTagName(this.bindingElement, "menugroup") ).reverse(); - while ( members.hasNext ()) { - var binding = UserInterface.getBinding ( members.getNext ()); - if ( !binding.getProperty ( "rel" )) { - binding.dispose (); + while (members.hasNext()) { + var binding = UserInterface.getBinding(members.getNext()); + if (!binding.getProperty("rel")) { + binding.dispose(); } } } @@ -236,17 +236,17 @@ SystemTreePopupBinding.prototype.disposeContent = function () { */ SystemTreePopupBinding.prototype.constructContent = function () { - if ( this._actionProfile != null ) { + if (this._actionProfile != null) { var doc = this.bindingDocument; - var groups = new List (); + var groups = new List(); var self = this; var bundles = new Map(); - this._actionProfile.each ( function ( group, list ) { - var groupBinding = MenuGroupBinding.newInstance ( doc ); - list.each ( function ( action ) { + this._actionProfile.each(function (group, list) { + var groupBinding = MenuGroupBinding.newInstance(doc); + list.each(function (action) { var menuItemBinding = self.getMenuItemBinding(action); @@ -281,20 +281,20 @@ SystemTreePopupBinding.prototype.constructContent = function () { }, this); - groups.add ( groupBinding ); - },this); + groups.add(groupBinding); + }, this); /* * Build in reverse order, so that clipboardoperations appear last. * Remember that clipboard menuitems has been hardcoded into menu. */ - groups.reverse (); - while ( groups.hasNext ()) { - this._bodyBinding.addFirst ( groups.getNext ()); + groups.reverse(); + while (groups.hasNext()) { + this._bodyBinding.addFirst(groups.getNext()); } this._bodyBinding.attachRecursive(); - bundles.each(function(bundleName, bundleMenuItemBinding) { + bundles.each(function (bundleName, bundleMenuItemBinding) { if (bundleMenuItemBinding.isMenuContainer) { if (this._keepBundleState || bundleMenuItemBinding.isDisabled) { @@ -303,7 +303,7 @@ SystemTreePopupBinding.prototype.constructContent = function () { if (this._keepBundleState) { var latestBundleHandle = LocalStorage.get(ToolBarComboButtonBinding.STORAGE_PREFFIX + bundleName); - bundleMenuItemsBinding.each(function(menuItemBinding) { + bundleMenuItemsBinding.each(function (menuItemBinding) { if (menuItemBinding.associatedSystemAction && menuItemBinding.associatedSystemAction.getHandle() === latestBundleHandle && !menuItemBinding.isDisabled) { latestBundleMenuItem = menuItemBinding; return false; @@ -313,7 +313,7 @@ SystemTreePopupBinding.prototype.constructContent = function () { } if (latestBundleMenuItem == null) { - bundleMenuItemsBinding.each(function(menuItemBinding) { + bundleMenuItemsBinding.each(function (menuItemBinding) { if (!menuItemBinding.isDisabled) { latestBundleMenuItem = menuItemBinding; return false; @@ -340,9 +340,9 @@ SystemTreePopupBinding.prototype.constructContent = function () { * @param {SystemAction} action * @return {MenuItemBinding} */ -SystemTreePopupBinding.prototype.getMenuItemBinding = function ( action ) { +SystemTreePopupBinding.prototype.getMenuItemBinding = function (action) { - var binding = MenuItemBinding.newInstance ( this.bindingDocument ); + var binding = MenuItemBinding.newInstance(this.bindingDocument); SystemTreePopupBinding.prototype.setSystemAction.call(this, binding, action); return binding; @@ -377,7 +377,7 @@ SystemTreePopupBinding.prototype.setSystemAction = function (binding, action) { } if (action.isDisabled()) { binding.disable(); - } else if(binding.isDisabled) { + } else if (binding.isDisabled) { binding.enable(); } @@ -392,28 +392,28 @@ SystemTreePopupBinding.prototype.setSystemAction = function (binding, action) { * @overwrites {PopupBinding#snapToMouse} * @param {MouseEvent} e */ -SystemTreePopupBinding.prototype.snapToMouse = function ( e ) { +SystemTreePopupBinding.prototype.snapToMouse = function (e) { var node = e.target ? e.target : e.srcElement; - var name = DOMUtil.getLocalName ( node ); + var name = DOMUtil.getLocalName(node); var binding = null; - if ( name != "tree" ) { - switch ( name ) { - case "treenode" : + if (name != "tree") { + switch (name) { + case "treenode": // visually empty space - this should not open the popup! break; - default : - node = DOMUtil.getAncestorByLocalName ( "treenode", node ); - if ( node != null ) { - binding = UserInterface.getBinding ( node ); - if ( binding.isDisabled ) { // no contextmenu for disabled treenodes + default: + node = DOMUtil.getAncestorByLocalName("treenode", node); + if (node != null) { + binding = UserInterface.getBinding(node); + if (binding.isDisabled) { // no contextmenu for disabled treenodes binding = null; } } break; } - if ( binding != null && binding.node != null && binding.node.getActionProfile () != null ) { + if (binding != null && binding.node != null && binding.node.getActionProfile() != null) { /* * This timeout will allow a right-click to focus the treenode, @@ -426,7 +426,7 @@ SystemTreePopupBinding.prototype.snapToMouse = function ( e ) { }, 0 ); */ - SystemTreePopupBinding.superclass.snapToMouse.call ( this, e ); + SystemTreePopupBinding.superclass.snapToMouse.call(this, e); } } } diff --git a/Website/Composite/scripts/source/top/ui/bindings/windows/PreviewWindowBinding.js b/Website/Composite/scripts/source/top/ui/bindings/windows/PreviewWindowBinding.js index b94bde6fbd..5d4aa99b08 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/windows/PreviewWindowBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/windows/PreviewWindowBinding.js @@ -1,4 +1,4 @@ -PreviewWindowBinding.prototype = new WindowBinding; +PreviewWindowBinding.prototype = new WindowBinding; PreviewWindowBinding.prototype.constructor = PreviewWindowBinding; PreviewWindowBinding.superclass = WindowBinding.prototype; @@ -96,35 +96,40 @@ PreviewWindowBinding.prototype.onBindingAttach = function () { */ PreviewWindowBinding.prototype.onWindowLoaded = function ( win ) { - if ( this.getURL () != WindowBinding.DEFAULT_URL ) { - if ( !this._hasFullStop ) { - if ( win.isPostBackDocument ) { - if ( this._isReturning ) { - win.submit ( this._postBackList, this._postBackURL ); - this._isReturning = false; + if (this.getURL() != WindowBinding.DEFAULT_URL) { + + if (this.hasAccess(win)) { + if (!this._hasFullStop) { + if (win.isPostBackDocument) { + if (this._isReturning) { + win.submit(this._postBackList, this._postBackURL); + this._isReturning = false; + } + } else { + this._coverBinding.hide(); } - } else { - this._coverBinding.hide (); - } - if ( !win.isDefaultDocument ) { - var self = this; - this._loadhandler = { - handleEvent : function ( e ) { - //self._coverBinding.show (); - if ( win.isPostBackDocument ) { - self._postBackList = win.postBackList; - self._postBackURL = win.postBackURL; - } else if ( !win.isDefaultDocument ) { - self._fullStop (); + if (!win.isDefaultDocument) { + var self = this; + this._loadhandler = { + handleEvent: function (e) { + //self._coverBinding.show (); + if (win.isPostBackDocument) { + self._postBackList = win.postBackList; + self._postBackURL = win.postBackURL; + } else if (!win.isDefaultDocument) { + self._fullStop(); + } } - } - }; - DOMEvents.addEventListener ( - win, - DOMEvents.BEFOREUNLOAD, - this._loadhandler - ); + }; + DOMEvents.addEventListener( + win, + DOMEvents.BEFOREUNLOAD, + this._loadhandler + ); + } } + } else { + this._coverBinding.hide(); } } @@ -277,12 +282,14 @@ PreviewWindowBinding.prototype.reset = function () { } if ( this._loadhandler != null ) { - if ( this.getURL () != WindowBinding.DEFAULT_URL ) { - DOMEvents.removeEventListener ( - this.getContentWindow (), - DOMEvents.BEFOREUNLOAD, - this._loadhandler - ); + if (this.getURL() != WindowBinding.DEFAULT_URL) { + if (this.hasAccess(this._windowBinding)) { + DOMEvents.removeEventListener( + this.getContentWindow(), + DOMEvents.BEFOREUNLOAD, + this._loadhandler + ); + } this._loadhandler = null; } } diff --git a/Website/Composite/scripts/source/top/ui/bindings/windows/WindowBinding.js b/Website/Composite/scripts/source/top/ui/bindings/windows/WindowBinding.js index 94d6e68a12..c470d5b267 100644 --- a/Website/Composite/scripts/source/top/ui/bindings/windows/WindowBinding.js +++ b/Website/Composite/scripts/source/top/ui/bindings/windows/WindowBinding.js @@ -1,4 +1,4 @@ -WindowBinding.prototype = new FlexBoxBinding; +WindowBinding.prototype = new FlexBoxBinding; WindowBinding.prototype.constructor = WindowBinding; WindowBinding.superclass = FlexBoxBinding.prototype; @@ -405,7 +405,7 @@ WindowBinding.prototype.onWindowLoaded = function ( win ) { if ( win == null ) { this.logger.error ( "WindowBinding#onWindowLoaded: Bad argument: " + this.getURL ()); } else if ( this.getURL () != WindowBinding.DEFAULT_URL ) { - if ( !this._hasLoadActionFired ) { + if (!this._hasLoadActionFired && this.hasAccess(win)) { if ( win != null && win.document != null && win.document.body != null ) { win.document.body.style.border = "none"; if ( win.WindowManager == undefined && !this._native) { @@ -555,6 +555,22 @@ WindowBinding.prototype.getFrameElement = function () { return result; }; +/** + * @return {Boolean} + */ +WindowBinding.prototype.hasAccess = function (win) { + + var result = false; + if (win) { + try { + result = win.document != null; + } catch (e) { + } + } + return result; +}; + + /** * Get the window object hosted by this WindowBinding. * During startup, this may be undefined. @@ -580,7 +596,7 @@ WindowBinding.prototype.getContentWindow = function () { WindowBinding.prototype.getContentDocument = function () { var result = null, win = this.getContentWindow (); - if ( win ) { + if (this.hasAccess(win) ) { result = win.document; } return result; 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 diff --git a/Website/Composite/services/Tree/TreeServices.asmx b/Website/Composite/services/Tree/TreeServices.asmx index 73169c5b6a..7802c2b50b 100644 --- a/Website/Composite/services/Tree/TreeServices.asmx +++ b/Website/Composite/services/Tree/TreeServices.asmx @@ -60,20 +60,15 @@ namespace Composite.Services string username = UserValidationFacade.GetUsername(); List allPerspectives = ElementFacade.GetPerspectiveElementsWithNoSecurity().ToList(); - List activePerspectiveEntityTokens = - UserPerspectiveFacade.GetSerializedEntityTokens(username) - .Concat(UserGroupPerspectiveFacade.GetSerializedEntityTokens(username)) - .Distinct().ToList(); + List activePerspectiveEntityTokens = + UserPerspectiveFacade.GetSerializedEntityTokens(username) + .Concat(UserGroupPerspectiveFacade.GetSerializedEntityTokens(username)) + .Distinct().ToList(); List activePerspectives = allPerspectives - .Where(f => activePerspectiveEntityTokens.Contains(EntityTokenSerializer.Serialize(f.ElementHandle.EntityToken))) - .ToList().ToClientElementList(); - - foreach (ClientElement clientElement in activePerspectives) - { - clientElement.Actions.Clear(); - clientElement.ActionKeys.Clear(); - } + .Where(f => activePerspectiveEntityTokens.Contains(EntityTokenSerializer.Serialize(f.ElementHandle.EntityToken))) + .FilterElementsAndActions() + .ToList().ToClientElementList(); return activePerspectives; } @@ -111,7 +106,7 @@ namespace Composite.Services var propertyBag = actionRequiredPage.Item1.PropertyBag; var data = actionRequiredPage.Item2; - + propertyBag[Texts.ViewUnpublishedItems_PageTitleLabel] = data.GetLabel(); var publicationStatus = data.PublicationStatus; 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.