diff --git a/Composite.Workflows/C1Console/Scheduling/PublishControlledHelper.cs b/Composite.Workflows/C1Console/Scheduling/PublishControlledHelper.cs index af424ddb08..ef921b4b9a 100644 --- a/Composite.Workflows/C1Console/Scheduling/PublishControlledHelper.cs +++ b/Composite.Workflows/C1Console/Scheduling/PublishControlledHelper.cs @@ -87,11 +87,16 @@ public static void ReloadPageElementInConsole(IPage page) public static void ReloadDataElementInConsole(DataEntityToken dataEntityToken) { + if (dataEntityToken == null) throw new ArgumentNullException(nameof(dataEntityToken)); + var parentEntityTokens = AuxiliarySecurityAncestorFacade.GetParents(dataEntityToken); - foreach (var parentEntityToken in parentEntityTokens) + if (parentEntityTokens != null) { - ConsoleMessageQueueFacade.Enqueue(new RefreshTreeMessageQueueItem { EntityToken = parentEntityToken }, null); + foreach (var parentEntityToken in parentEntityTokens) + { + ConsoleMessageQueueFacade.Enqueue(new RefreshTreeMessageQueueItem { EntityToken = parentEntityToken }, null); + } } } diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs index a553f6003a..b5b6f6bf73 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserElementProvider/EditUserWorkflow.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; @@ -48,6 +48,7 @@ private static class BindingNames public const string User = "User"; public const string UserFormLogin = "UserFormLogin"; public const string NewPassword = "NewPassword"; + public const string ActiveContentLanguage = "ActiveLocaleName"; } private void CheckActiveLanguagesExists(object sender, System.Workflow.Activities.ConditionalEventArgs e) @@ -80,11 +81,9 @@ private void initializeCodeActivity_ExecuteCode(object sender, EventArgs e) this.Bindings.Add("C1ConsoleUiCultures", regionLanguageList); this.Bindings.Add("C1ConsoleUiLanguageName", c1ConsoleUiLanguage.Name); - if (UserSettings.GetActiveLocaleCultureInfos(user.Username).Any() && (user.Username != UserSettings.Username)) - { - this.Bindings.Add("ActiveLocaleName", UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username).Name); - this.Bindings.Add("ActiveLocaleList", DataLocalizationFacade.ActiveLocalizationCultures.ToDictionary(f => f.Name, DataLocalizationFacade.GetCultureTitle)); - } + var currentActiveCulture = UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username); + this.Bindings.Add("ActiveLocaleName", currentActiveCulture != null ? currentActiveCulture.Name : null); + this.Bindings.Add("ActiveLocaleList", DataLocalizationFacade.ActiveLocalizationCultures.ToDictionary(f => f.Name, DataLocalizationFacade.GetCultureTitle)); var clientValidationRules = new Dictionary> { @@ -157,7 +156,7 @@ private void UpdateFormDefinitionWithActiveLocales(IUser user, XElement bindings bindingsElement.Add(helper.GetBindingsMarkup()); placeHolderElement.Add(helper.GetFormMarkup()); - helper.UpdateWithNewBindings(this.Bindings, UserSettings.GetActiveLocaleCultureInfos(user.Username)); + helper.UpdateWithNewBindings(this.Bindings, UserSettings.GetActiveLocaleCultureInfos(user.Username, false)); } @@ -198,41 +197,11 @@ private void saveCodeActivity_ExecuteCode(object sender, EventArgs e) List newActiveLocales = ActiveLocalesFormsHelper.GetSelectedLocalesTypes(this.Bindings).ToList(); - List currentActiveLocales = null; - CultureInfo selectedActiveLocal = null; - - if (newActiveLocales.Count > 0) - { - currentActiveLocales = UserSettings.GetActiveLocaleCultureInfos(user.Username).ToList(); + List currentActiveLocales = UserSettings.GetActiveLocaleCultureInfos(user.Username, false).ToList(); + string selectedActiveLocaleName = this.GetBinding("ActiveLocaleName"); - string selectedActiveLocaleName = (user.Username != UserSettings.Username ? - this.GetBinding("ActiveLocaleName") : - UserSettings.ActiveLocaleCultureInfo.ToString()); - - if (selectedActiveLocaleName != null) - { - selectedActiveLocal = CultureInfo.CreateSpecificCulture(selectedActiveLocaleName); - if (!newActiveLocales.Contains(selectedActiveLocal)) - { - if (user.Username != UserSettings.Username) - { - this.ShowFieldMessage("ActiveLocaleName", GetText("Website.Forms.Administrative.EditUserStep1.ActiveLocaleNotChecked")); - } - else - { - this.ShowFieldMessage("ActiveLocalesFormsHelper_Selected", GetText("Website.Forms.Administrative.EditUserStep1.NoActiveLocaleSelected")); - } - userValidated = false; - } - } - } - else - { - this.ShowFieldMessage("ActiveLocalesFormsHelper_Selected", GetText("Website.Forms.Administrative.EditUserStep1.NoActiveLocaleSelected")); - userValidated = false; - } - + CultureInfo selectedActiveLocale = CultureInfo.CreateSpecificCulture(selectedActiveLocaleName); string systemPerspectiveEntityToken = EntityTokenSerializer.Serialize(AttachingPoint.SystemPerspective.EntityToken); @@ -359,14 +328,14 @@ private void saveCodeActivity_ExecuteCode(object sender, EventArgs e) } } - if (selectedActiveLocal != null) + if (selectedActiveLocale != null) { - if (!UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username).Equals(selectedActiveLocal)) + if (!selectedActiveLocale.Equals(UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username))) { reloadUsersConsoles = true; } - UserSettings.SetCurrentActiveLocaleCultureInfo(user.Username, selectedActiveLocal); + UserSettings.SetCurrentActiveLocaleCultureInfo(user.Username, selectedActiveLocale); } else if (UserSettings.GetActiveLocaleCultureInfos(user.Username).Any()) { @@ -398,18 +367,26 @@ from r in oldRelations } LoggingService.LogEntry("UserManagement", - $"C1 Console user '{user.Username}' updated by '{UserValidationFacade.GetUsername()}'.", + $"C1 Console user '{user.Username}' updated by '{UserValidationFacade.GetUsername()}'.", LoggingService.Category.Audit, TraceEventType.Information); transactionScope.Complete(); } - if (reloadUsersConsoles) + if (UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username) == null) + { + this.ShowFieldMessage(BindingNames.ActiveContentLanguage, "The user doesn't have permissions to access the language you selected here. Assign permissions so the user may access this."); + this.ShowMessage(DialogType.Warning, "User missing permissions for language", "The user doesn't have permissions to access the language you selected as 'Active content language'."); + } + else { - foreach (string consoleId in GetConsoleIdsOpenedByCurrentUser()) + if (reloadUsersConsoles) { - ConsoleMessageQueueFacade.Enqueue(new RebootConsoleMessageQueueItem(), consoleId); + foreach (string consoleId in GetConsoleIdsOpenedByUser(user.Username)) + { + ConsoleMessageQueueFacade.Enqueue(new RebootConsoleMessageQueueItem(), consoleId); + } } } @@ -438,7 +415,7 @@ private void IsUserLoggedOn(object sender, System.Workflow.Activities.Conditiona { CultureInfo selectedActiveLocale = CultureInfo.CreateSpecificCulture(selectedActiveLocaleName); - if (!UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username).Equals(selectedActiveLocale)) + if (!selectedActiveLocale.Equals(UserSettings.GetCurrentActiveLocaleCultureInfo(user.Username))) { e.Result = ConsoleFacade.GetConsoleIdsByUsername(user.Username).Any(); return; diff --git a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs index 0eea5156fb..9d0f9c7162 100644 --- a/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs +++ b/Composite.Workflows/Plugins/Elements/ElementProviders/UserGroupElementProvider/EditUserGroupWorkflow.cs @@ -1,12 +1,14 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Linq; using System.Workflow.Activities; using System.Xml.Linq; using Composite.C1Console.Actions; using Composite.C1Console.Events; using Composite.C1Console.Users; +using Composite.Core.Logging; using Composite.Data; using Composite.Data.DynamicTypes; using Composite.Data.Types; @@ -22,7 +24,6 @@ using Microsoft.Practices.EnterpriseLibrary.Validation; using SR = Composite.Core.ResourceSystem.StringResourceSystemFacade; -using Composite.Core.Logging; namespace Composite.Plugins.Elements.ElementProviders.UserGroupElementProvider { @@ -66,6 +67,7 @@ private void step1CodeActivity_ShowDocument_ExecuteCode(object sender, EventArgs UpdateFormDefinitionWithActivePerspectives(userGroup, bindingsElement, placeHolderElement); UpdateFormDefinitionWithGlobalPermissions(userGroup, bindingsElement, placeHolderElement); + UpdateFormDefinitionWithActiveLocalePermissions(userGroup, bindingsElement, placeHolderElement); var clientValidationRules = new Dictionary>(); clientValidationRules.Add("Name", ClientValidationRuleFacade.GetClientValidationRules(userGroup, "Name")); @@ -139,6 +141,24 @@ private void saveCodeActivity_Save_ExecuteCode(object sender, EventArgs e) UserGroupPerspectiveFacade.SetSerializedEntityTokens(userGroup.Id, newUserGroupEntityTokens); + List selectedUserGroupActiveLocales = ActiveLocalesFormsHelper.GetSelectedLocalesTypes(this.Bindings).ToList(); + + using (var connection = new DataConnection()) + { + var existingLocales = connection.Get().Where(f=> f.UserGroupId == userGroup.Id).ToList(); + var toDelete = existingLocales.Where(f => !selectedUserGroupActiveLocales.Contains(new CultureInfo(f.CultureName))); + connection.Delete(toDelete); + + foreach (var localeToAdd in selectedUserGroupActiveLocales.Where(f => !existingLocales.Any(g => g.CultureName == f.Name))) + { + var toAdd = connection.CreateNew(); + toAdd.Id = Guid.NewGuid(); + toAdd.UserGroupId = userGroup.Id; + toAdd.CultureName = localeToAdd.Name; + connection.Add(toAdd); + } + } + SetSaveStatus(true); LoggingService.LogEntry("UserManagement", @@ -206,6 +226,28 @@ private void UpdateFormDefinitionWithGlobalPermissions(IUserGroup userGroup, XEl helper.UpdateWithNewBindings(this.Bindings, permissionTypes); } + private void UpdateFormDefinitionWithActiveLocalePermissions(IUserGroup userGroup, XElement bindingsElement, XElement placeHolderElement) + { + var helper = new ActiveLocalesFormsHelper( + SR.GetString("Composite.Plugins.UserGroupElementProvider", "EditUserGroup.EditUserGroupStep1.ActiveLocalesFieldLabel"), + SR.GetString("Composite.Plugins.UserGroupElementProvider", "EditUserGroup.EditUserGroupStep1.ActiveLocalesMultiSelectLabel"), + SR.GetString("Composite.Plugins.UserGroupElementProvider", "EditUserGroup.EditUserGroupStep1.ActiveLocalesMultiSelectHelp") + ); + + bindingsElement.Add(helper.GetBindingsMarkup()); + placeHolderElement.Add(helper.GetFormMarkup()); + + EntityToken rootEntityToken = ElementFacade.GetRootsWithNoSecurity().Select(f => f.ElementHandle.EntityToken).Single(); + + using (var connection = new DataConnection()) + { + IEnumerable activeCultures = null; + activeCultures = connection.Get().Where(f => f.UserGroupId == userGroup.Id).Select(f => new CultureInfo(f.CultureName)); + helper.UpdateWithNewBindings(this.Bindings, activeCultures); + } + + } + private void UpdateFormDefinitionWithActivePerspectives(IUserGroup userGroup, XElement bindingsElement, XElement placeHolderElement) diff --git a/Composite/C1Console/Actions/ActionLockingFacade.cs b/Composite/C1Console/Actions/ActionLockingFacade.cs index 40beeda0a2..f600799fae 100644 --- a/Composite/C1Console/Actions/ActionLockingFacade.cs +++ b/Composite/C1Console/Actions/ActionLockingFacade.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; @@ -21,7 +21,7 @@ namespace Composite.C1Console.Actions public static class ActionLockingFacade { private static readonly string LogTitle = typeof(ActionLockingFacade).Name; - private static Dictionary _lockingInformations = null; + private static Dictionary _lockingInformations = null; private static readonly object _lock = new object(); private static readonly IFormatter _ownerIdFormatter = new BinaryFormatter(); @@ -90,7 +90,8 @@ public static void ReleaseLock(EntityToken entityToken, object ownerId) { EnsureInitialization(); - RemoveLockingInformation(entityToken, ownerId); + string lockKey = GetLockKey(entityToken); + RemoveLockingInformation(lockKey, ownerId); } } @@ -108,14 +109,14 @@ public static void ReleaseAllLocks(object ownerId) { EnsureInitialization(); - List entityTokens = + List lockKeys = (from li in _lockingInformations where object.Equals(li.Value.OwnerId, ownerId) select li.Key).ToList(); - foreach (EntityToken entityToken in entityTokens) + foreach (string lockKey in lockKeys) { - RemoveLockingInformation(entityToken, ownerId); + RemoveLockingInformation(lockKey, ownerId); } } } @@ -132,8 +133,8 @@ public static bool IsLocked(EntityToken entityToken) using (GlobalInitializerFacade.CoreIsInitializedScope) { EnsureInitialization(); - - return _lockingInformations.ContainsKey(entityToken); + string lockKey = GetLockKey(entityToken); + return _lockingInformations.ContainsKey(lockKey); } } @@ -150,8 +151,10 @@ public static string LockedBy(EntityToken entityToken) { EnsureInitialization(); + string lockKey = GetLockKey(entityToken); + LockingInformation lockingInformation; - if (!_lockingInformations.TryGetValue(entityToken, out lockingInformation)) + if (!_lockingInformations.TryGetValue(lockKey, out lockingInformation)) { return null; } @@ -181,10 +184,10 @@ internal static void ReleaseAll(string username) { EnsureInitialization(); - List> itemsToRemove = + List> itemsToRemove = (from info in _lockingInformations where info.Value.Username == username - select new Tuple(info.Key, info.Value.OwnerId)).ToList(); + select new Tuple(info.Key, info.Value.OwnerId)).ToList(); foreach (var item in itemsToRemove) { @@ -205,9 +208,11 @@ public static void RemoveLock(EntityToken entityToken) { EnsureInitialization(); - if (_lockingInformations.ContainsKey(entityToken)) + string lockKey = GetLockKey(entityToken); + + if (_lockingInformations.ContainsKey(lockKey)) { - RemoveLockingInformation(entityToken, _lockingInformations[entityToken].OwnerId); + RemoveLockingInformation(lockKey, _lockingInformations[lockKey].OwnerId); } } } @@ -223,7 +228,7 @@ private static void DoInitialize() if (_lockingInformations == null) { - _lockingInformations = new Dictionary(); + _lockingInformations = new Dictionary(); LoadLockingInformation(); } @@ -251,9 +256,7 @@ private static void LoadLockingInformation() try { - EntityToken entityToken = EntityTokenSerializer.Deserialize(lockingInformation.SerializedEntityToken); - - _lockingInformations.Add(entityToken, li); + _lockingInformations.Add(lockingInformation.LockKey, li); } catch (Exception) { @@ -272,8 +275,10 @@ private static void LoadLockingInformation() private static void AddLockingInformation(EntityToken entityToken, object ownerId) { + string lockKey = GetLockKey(entityToken); + LockingInformation lockingInformation; - if (_lockingInformations.TryGetValue(entityToken, out lockingInformation)) + if (_lockingInformations.TryGetValue(lockKey, out lockingInformation)) { if (object.Equals(lockingInformation.OwnerId, ownerId)) { @@ -295,26 +300,26 @@ private static void AddLockingInformation(EntityToken entityToken, object ownerI ILockingInformation li = DataFacade.BuildNew(); li.Id = Guid.NewGuid(); - li.SerializedEntityToken = EntityTokenSerializer.Serialize(entityToken); + li.LockKey = lockKey; li.SerializedOwnerId = serializedOwnerId; li.Username = lockingInformation.Username; DataFacade.AddNew(li); - _lockingInformations.Add(entityToken, lockingInformation); + _lockingInformations.Add(lockKey, lockingInformation); } private static void UpdateLockingInformation(EntityToken entityToken, object newOwnerId) { - LockingInformation lockingInformation; - if (!_lockingInformations.TryGetValue(entityToken, out lockingInformation)) throw new InvalidOperationException("LockingInformation record missing"); + string lockKey = GetLockKey(entityToken); - string serializedEntityToken = EntityTokenSerializer.Serialize(entityToken); + LockingInformation lockingInformation; + if (!_lockingInformations.TryGetValue(lockKey, out lockingInformation)) throw new InvalidOperationException("LockingInformation record missing"); ILockingInformation lockingInformationDataItem = DataFacade.GetData(). - Single(f => f.SerializedEntityToken == serializedEntityToken); + Single(f => f.LockKey == lockKey); lockingInformationDataItem.SerializedOwnerId = SerializeOwnerId(newOwnerId); DataFacade.Update(lockingInformationDataItem); @@ -324,27 +329,26 @@ private static void UpdateLockingInformation(EntityToken entityToken, object new - private static void RemoveLockingInformation(EntityToken entityToken, object ownerId) + private static void RemoveLockingInformation(string lockKey, object ownerId) { LockingInformation lockingInformation; - if (!_lockingInformations.TryGetValue(entityToken, out lockingInformation)) return; + if (!_lockingInformations.TryGetValue(lockKey, out lockingInformation)) return; if (Equals(lockingInformation.OwnerId, ownerId)) { - _lockingInformations.Remove(entityToken); + _lockingInformations.Remove(lockKey); } string serializedOwnerId = SerializeOwnerId(ownerId); - string serializedEntityToken = EntityTokenSerializer.Serialize(entityToken); ILockingInformation lockingInformationDataItem = DataFacade.GetData() - .SingleOrDefault(f => f.SerializedEntityToken == serializedEntityToken + .SingleOrDefault(f => f.LockKey == lockKey && f.SerializedOwnerId == serializedOwnerId); if (lockingInformationDataItem == null) { - Log.LogWarning(LogTitle, "Failed to find entity token lock. EntityToken: " + serializedEntityToken); + Log.LogWarning(LogTitle, "Failed to find entity token lock. EntityToken: " + lockKey); return; } @@ -405,6 +409,23 @@ private static void Exit() } + private static string GetLockKey(EntityToken entityToken) + { + string lockKey = entityToken.Serialize(); + + if (entityToken is DataEntityToken) + { + var dataEntityToken = entityToken as DataEntityToken; + if (dataEntityToken.DataSourceId != null && dataEntityToken.DataSourceId.LocaleScope != null) + { + lockKey = lockKey + dataEntityToken.DataSourceId.LocaleScope.ToString(); + } + } + + return lockKey; + } + + private sealed class LockerToken : IDisposable diff --git a/Composite/C1Console/Actions/FlowHandle.cs b/Composite/C1Console/Actions/FlowHandle.cs index 5cb7d76d3c..dd121ca26c 100644 --- a/Composite/C1Console/Actions/FlowHandle.cs +++ b/Composite/C1Console/Actions/FlowHandle.cs @@ -50,7 +50,7 @@ public static FlowHandle Deserialize(string serializedFlowHandle) Type flowTokenType = TypeManager.GetType(flowTokenTypeString); MethodInfo methodInfo = flowTokenType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); - if (methodInfo == null) + if (methodInfo == null || !(typeof(FlowToken).IsAssignableFrom(methodInfo.ReturnType))) { throw new InvalidOperationException(string.Format("The flow token '{0}' is missing a public static Deserialize method taking a string as parameter and returning an '{1}'", flowTokenType, typeof(FlowToken))); } diff --git a/Composite/C1Console/Actions/FlowTokenSerializer.cs b/Composite/C1Console/Actions/FlowTokenSerializer.cs index 13b35d6705..5903281fee 100644 --- a/Composite/C1Console/Actions/FlowTokenSerializer.cs +++ b/Composite/C1Console/Actions/FlowTokenSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Security; @@ -76,7 +76,7 @@ public static FlowToken Deserialize(string serialziedFlowToken, bool includeHash Type flowType = TypeManager.GetType(flowTokenTypeString); MethodInfo methodInfo = flowType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); - if (methodInfo == null) + if (methodInfo == null || !(typeof(FlowToken).IsAssignableFrom(methodInfo.ReturnType))) { throw new InvalidOperationException(string.Format("The flow token {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", flowType, typeof(FlowToken))); } diff --git a/Composite/C1Console/Actions/UrlActionToken.cs b/Composite/C1Console/Actions/UrlActionToken.cs index 51a2dbbfdf..4c0788d25a 100644 --- a/Composite/C1Console/Actions/UrlActionToken.cs +++ b/Composite/C1Console/Actions/UrlActionToken.cs @@ -91,6 +91,11 @@ public FlowToken Execute(EntityToken entityToken, ActionToken actionToken, FlowC string extendedUrl = $"{url}{(url.Contains("?") ? "&" : "?")}EntityToken={HttpUtility.UrlEncode(serializedEntityToken)}"; + if (extendedUrl.IndexOf("consoleId", StringComparison.OrdinalIgnoreCase) == -1) + { + extendedUrl = $"{extendedUrl}&consoleId={currentConsoleId}"; + } + ConsoleMessageQueueFacade.Enqueue( new OpenViewMessageQueueItem { diff --git a/Composite/C1Console/Security/ActionTokenSerializer.cs b/Composite/C1Console/Security/ActionTokenSerializer.cs index 8fb04182eb..cd52bba4e2 100644 --- a/Composite/C1Console/Security/ActionTokenSerializer.cs +++ b/Composite/C1Console/Security/ActionTokenSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Security; @@ -83,7 +83,7 @@ public static ActionToken Deserialize(string serialziedActionToken, bool include Type actionType = TypeManager.GetType(actionTokenTypeString); MethodInfo methodInfo = actionType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); - if (methodInfo == null) + if (methodInfo == null || !(typeof(ActionToken).IsAssignableFrom(methodInfo.ReturnType))) { Log.LogWarning("ActionTokenSerializer", string.Format("The action token {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", actionType, typeof(ActionToken))); throw new InvalidOperationException(string.Format("The action token {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", actionType, typeof(ActionToken))); diff --git a/Composite/C1Console/Security/EntityTokenSerializer.cs b/Composite/C1Console/Security/EntityTokenSerializer.cs index a9de2f7666..86be9498a2 100644 --- a/Composite/C1Console/Security/EntityTokenSerializer.cs +++ b/Composite/C1Console/Security/EntityTokenSerializer.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Reflection; using System.Security; using Composite.Core.Serialization; @@ -95,7 +95,7 @@ private static EntityToken DeserializeLegacy(string serializedEntityToken, bool Type entityType = TypeManager.GetType(entityTokenTypeString); MethodInfo methodInfo = entityType.GetMethod("Deserialize", BindingFlags.Public | BindingFlags.Static); - if (methodInfo == null) + if (methodInfo == null || !(typeof(EntityToken).IsAssignableFrom(methodInfo.ReturnType))) { throw new InvalidOperationException($"The entity token {entityType} is missing a public static Deserialize method taking a string as parameter and returning an {typeof(EntityToken)}"); } diff --git a/Composite/C1Console/Security/UserGroupFacade.cs b/Composite/C1Console/Security/UserGroupFacade.cs index 1ca4718c57..c5678c9eda 100644 --- a/Composite/C1Console/Security/UserGroupFacade.cs +++ b/Composite/C1Console/Security/UserGroupFacade.cs @@ -1,6 +1,7 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.Linq; using Composite.Data; using Composite.Data.Types; @@ -46,6 +47,19 @@ public static IReadOnlyCollection GetUserGroupIds(string username) }); } + /// + + public static IEnumerable GetUserGroupActiveCultures(string username) + { + Verify.ArgumentNotNullOrEmpty(username, nameof(username)); + + return + from ugal in DataFacade.GetData() + join uugr in DataFacade.GetData() on ugal.UserGroupId equals uugr.UserGroupId + join user in DataFacade.GetData() on uugr.UserId equals user.Id + where user.Username == username + select CultureInfo.CreateSpecificCulture(ugal.CultureName); + } private static void OnDataChanged(object sender, StoreEventArgs storeEventArgs) diff --git a/Composite/C1Console/Trees/RootTreeNode.cs b/Composite/C1Console/Trees/RootTreeNode.cs index c8ca95d4e5..06f0deedf6 100644 --- a/Composite/C1Console/Trees/RootTreeNode.cs +++ b/Composite/C1Console/Trees/RootTreeNode.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Composite.C1Console.Elements; @@ -67,16 +67,30 @@ public override IEnumerable GetEntityTokens(EntityToken childEntity private AncestorMatch GetAncestorFromParentFilter(TreeNodeDynamicContext dynamicContext) { - var dataNode = dynamicContext.CurrentTreeNode as DataElementsTreeNode; - if(dataNode == null) return null; - - var parentIdFilter = dynamicContext.CurrentTreeNode.FilterNodes.OfType().FirstOrDefault(); - if (parentIdFilter == null) return null; + var treeNode = dynamicContext.CurrentTreeNode; + if (treeNode is DataElementsTreeNode) + { + var parentIdFilter = treeNode.FilterNodes.OfType().FirstOrDefault(); + if (parentIdFilter == null) return null; + + Type ancestorType = parentIdFilter.ParentFilterType; + object key = parentIdFilter.FindParentKeyValue(dynamicContext); + + return key == null ? null : new AncestorMatch { InterfaceType = ancestorType, KeyValue = key}; + } - Type ancestorType = parentIdFilter.ParentFilterType; - object key = parentIdFilter.FindParentKeyValue(dynamicContext); + if (treeNode is DataFolderElementsTreeNode + && dynamicContext.CurrentEntityToken is TreeDataFieldGroupingElementEntityToken groupEntityToken + && groupEntityToken.ChildGeneratingDataElementsReferenceValue != null) + { + return new AncestorMatch + { + InterfaceType = groupEntityToken.ChildGeneratingDataElementsReferenceType, + KeyValue = groupEntityToken.ChildGeneratingDataElementsReferenceValue + }; + } - return key == null ? null : new AncestorMatch { InterfaceType = ancestorType, KeyValue = key}; + return null; } diff --git a/Composite/C1Console/Users/UserSettings.cs b/Composite/C1Console/Users/UserSettings.cs index 927777e98b..4c6120646d 100644 --- a/Composite/C1Console/Users/UserSettings.cs +++ b/Composite/C1Console/Users/UserSettings.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net; using Composite.C1Console.Security; -using System.Threading; +using Composite.Core.Caching; +using Composite.Data; namespace Composite.C1Console.Users @@ -109,7 +111,17 @@ public static CultureInfo ActiveLocaleCultureInfo /// public static CultureInfo GetCurrentActiveLocaleCultureInfo(string username) { - return _implementation.GetCurrentActiveLocaleCultureInfo(username); + var key = "CurrentActiveCulture" + username; + if (RequestLifetimeCache.HasKey(key)) return RequestLifetimeCache.TryGet(key); + + var cultureInfo = _implementation.GetCurrentActiveLocaleCultureInfo(username); + var allowedCultures = GetActiveLocaleCultureInfos(username, true); + + if( !allowedCultures.Contains(cultureInfo)) cultureInfo = allowedCultures.FirstOrDefault(); + + if (cultureInfo != null && !RequestLifetimeCache.HasKey(key)) RequestLifetimeCache.Add(key, cultureInfo); + + return cultureInfo ?? CultureInfo.InvariantCulture; } @@ -170,22 +182,24 @@ public static void RemoveActiveLocaleCultureInfo(string username, CultureInfo cu // Overload /// - /// This is an overload for GetActiveLocaleCultureInfos(string username) + /// This is an overload for GetActiveLocaleCultureInfos(string username, includeGroupAssignedCultures = true) /// using the current username. /// public static IEnumerable ActiveLocaleCultureInfos { get { - return GetActiveLocaleCultureInfos(UserSettings.Username); + return GetActiveLocaleCultureInfos(UserSettings.Username, true); } } /// - public static IEnumerable GetActiveLocaleCultureInfos(string username) + public static IEnumerable GetActiveLocaleCultureInfos(string username, bool includeGroupAssignedCultures = true) { - return _implementation.GetActiveLocaleCultureInfos(username); + return includeGroupAssignedCultures ? + _implementation.GetActiveLocaleCultureInfos(username).Union(UserGroupFacade.GetUserGroupActiveCultures(username)) : + _implementation.GetActiveLocaleCultureInfos(username); } diff --git a/Composite/C1Console/Workflow/Activities/FormsWorkflow.cs b/Composite/C1Console/Workflow/Activities/FormsWorkflow.cs index 0a18a35c3f..03e079504e 100644 --- a/Composite/C1Console/Workflow/Activities/FormsWorkflow.cs +++ b/Composite/C1Console/Workflow/Activities/FormsWorkflow.cs @@ -572,13 +572,24 @@ protected string GetCurrentConsoleId() return managementConsoleMessageService.CurrentConsoleId; } - /// protected IEnumerable GetConsoleIdsOpenedByCurrentUser() { - string currentConsoleId = GetCurrentConsoleId(); + return GetConsoleIdsOpenedByUser(UserSettings.Username); + } - return ConsoleFacade.GetConsoleIdsByUsername(UserSettings.Username).Union(new[] { currentConsoleId }); + /// + protected IEnumerable GetConsoleIdsOpenedByUser(string username) + { + if (UserSettings.Username == username) + { + string currentConsoleId = GetCurrentConsoleId(); + return ConsoleFacade.GetConsoleIdsByUsername(username).Union(new[] { currentConsoleId }); + } + else + { + return ConsoleFacade.GetConsoleIdsByUsername(username); + } } diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj index 794277cddf..58956dd4f5 100644 --- a/Composite/Composite.csproj +++ b/Composite/Composite.csproj @@ -256,6 +256,7 @@ + @@ -264,6 +265,7 @@ + diff --git a/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs b/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs index e5db0d6a8f..107358c21b 100644 --- a/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs +++ b/Composite/Core/Configuration/FileConfigurationSourceImplementation.cs @@ -1,4 +1,4 @@ -//=============================================================================== +//=============================================================================== // Microsoft patterns & practices Enterprise Library // Core //=============================================================================== @@ -9,6 +9,7 @@ // FITNESS FOR A PARTICULAR PURPOSE. //=============================================================================== +using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; using System.Collections.Generic; @@ -47,8 +48,19 @@ public FileConfigurationSourceImplementation(string configurationFilepath, bool public override System.Configuration.ConfigurationSection GetSection(string sectionName) { System.Configuration.Configuration configuration = GetConfiguration(); + System.Configuration.ConfigurationSection configurationSection; - System.Configuration.ConfigurationSection configurationSection = configuration.GetSection(sectionName) as System.Configuration.ConfigurationSection; + try + { + configurationSection = configuration.GetSection(sectionName) as System.Configuration.ConfigurationSection; + } + catch (System.Configuration.ConfigurationException ex) + { + // retry once + UpdateCache(); + configuration = GetConfiguration(); + configurationSection = configuration.GetSection(sectionName) as System.Configuration.ConfigurationSection; + } SetConfigurationWatchers(sectionName, configurationSection); diff --git a/Composite/Core/Localization/LocalizationFacade.cs b/Composite/Core/Localization/LocalizationFacade.cs index f92ad74eb5..f06c60ee2b 100644 --- a/Composite/Core/Localization/LocalizationFacade.cs +++ b/Composite/Core/Localization/LocalizationFacade.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -109,7 +109,7 @@ public static void RenameUrlMappingNameForLocale(string cultureName, string newU Verify.That(IsLocaleInstalled(cultureName), "The locale '{0}' is not installed and the url mapping name can not be renamed", cultureName); Verify.That(!IsUrlMappingNameInUse(cultureName, newUrlMappingName), "The url mapping '{0}' is already used", newUrlMappingName); - ISystemActiveLocale systemActiveLocale = DataFacade.GetData().Single(f => f.CultureName != cultureName); + ISystemActiveLocale systemActiveLocale = DataFacade.GetData().Single(f => f.CultureName == cultureName); systemActiveLocale.UrlMappingName = newUrlMappingName; DataFacade.Update(systemActiveLocale); } @@ -189,6 +189,11 @@ internal static void AddLocale(CultureInfo cultureInfo, string urlMappingName, b } DynamicTypeManager.AddLocale(cultureInfo); + + if (makeFlush) + { + C1Console.Events.GlobalEventSystemFacade.FlushTheSystem(false); + } } diff --git a/Composite/Core/Logging/LoggingService.cs b/Composite/Core/Logging/LoggingService.cs index e5650d271e..40e20244da 100644 --- a/Composite/Core/Logging/LoggingService.cs +++ b/Composite/Core/Logging/LoggingService.cs @@ -63,23 +63,23 @@ static private void LogEntry(string title, string message, Exception exc, Catego static private void LogEntry(string title, string message, Exception exc, Category category, System.Diagnostics.TraceEventType severity, int priority, int eventid) { - var entry = new Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry(); - - // TODO: refactor this code - entry.Title = string.Format("({0} - {1}) {2}", AppDomain.CurrentDomain.Id, Thread.CurrentThread.ManagedThreadId, title); - entry.Message = message; - entry.Severity = severity; - entry.Priority = priority; - entry.EventId = eventid; + var entry = new Microsoft.Practices.EnterpriseLibrary.Logging.LogEntry + { + Title = $"({AppDomain.CurrentDomain.Id} - {Thread.CurrentThread.ManagedThreadId}) {title}", + Message = message, + Severity = severity, + Priority = priority, + EventId = eventid + }; if ((category & Category.General) != 0) { - entry.Categories.Add("General"); + entry.Categories.Add(nameof(Category.General)); } if ((category & Category.Audit) != 0) { - entry.Categories.Add("Audit"); + entry.Categories.Add(nameof(Category.Audit)); } if (exc != null) @@ -259,11 +259,10 @@ private static void OnFlush(FlushEventArgs args) static string PrettyExceptionCallStack(Exception ex) { - string serializedException = ex.ToString(); + if (ex == null) return "[exception is null]"; - string cleanException; - - if (ex.InnerException != null && ExcludeInnerExceptionInformation(ex, serializedException, out cleanException)) + string serializedException = ex.ToString(); + if (ex.InnerException != null && ExcludeInnerExceptionInformation(ex, serializedException, out var cleanException)) { return PrettyExceptionCallStack(ex.InnerException) + Environment.NewLine + cleanException; } @@ -323,7 +322,7 @@ public static void Initialize(Resources resources) } else { - string path = Path.Combine(PathUtil.BaseDirectory, string.Format("logging{0}.config", Guid.NewGuid())); + string path = Path.Combine(PathUtil.BaseDirectory, $"logging{Guid.NewGuid()}.config"); using (C1StreamWriter writer = new C1StreamWriter(path)) { diff --git a/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs b/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs index 250a49f9a1..afa2739080 100644 --- a/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs +++ b/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs @@ -120,9 +120,12 @@ public override IEnumerable Install() } else if (dataType.AddToCurrentLocale) { - Verify.That(UserValidationFacade.IsLoggedIn(), "Cannot add to the current locale as there's no logged in user."); + var currentLocale = DataLocalizationFacade.DefaultLocalizationCulture; + if (UserValidationFacade.IsLoggedIn()) + { + currentLocale = UserSettings.ActiveLocaleCultureInfo; + } - var currentLocale = UserSettings.ActiveLocaleCultureInfo; using (new DataScope(currentLocale)) { XElement element = AddData(dataType, currentLocale); diff --git a/Composite/Core/PackageSystem/PackageFragmentInstallers/UserGroupUserAdderFragmentInstaller.cs b/Composite/Core/PackageSystem/PackageFragmentInstallers/UserGroupUserAdderFragmentInstaller.cs index d92644532a..fb09a8820a 100644 --- a/Composite/Core/PackageSystem/PackageFragmentInstallers/UserGroupUserAdderFragmentInstaller.cs +++ b/Composite/Core/PackageSystem/PackageFragmentInstallers/UserGroupUserAdderFragmentInstaller.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using Composite.Core.Linq; @@ -9,7 +9,7 @@ namespace Composite.Core.PackageSystem.PackageFragmentInstallers { /// - /// Adds all the users to the specified user group. Used in starter site packages. + /// Adds all the users to the specified user group. Assign language permissions to those groups. Used in starter site packages. /// /// [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] @@ -52,6 +52,14 @@ public override IEnumerable Install() userUserGroupRelation.UserGroupId = userGroup.Id; DataFacade.AddNew(userUserGroupRelation); } + + foreach (var cultureInfo in DataLocalizationFacade.ActiveLocalizationCultures) + { + var userGroupActiveLocale = DataFacade.BuildNew(); + userGroupActiveLocale.UserGroupId = userGroup.Id; + userGroupActiveLocale.CultureName = cultureInfo.Name; + DataFacade.AddNew(userGroupActiveLocale); + } } yield break; diff --git a/Composite/Core/Serialization/CompositeJsonSerializer.cs b/Composite/Core/Serialization/CompositeJsonSerializer.cs index cd37ee0abc..3a224715e8 100644 --- a/Composite/Core/Serialization/CompositeJsonSerializer.cs +++ b/Composite/Core/Serialization/CompositeJsonSerializer.cs @@ -228,6 +228,14 @@ public static T Deserialize(string str, bool isSigned) { return Deserialize(obj); } + + if (!(typeof(T).IsAssignableFrom(methodInfo.ReturnType))) + { + string typeName = str.GetValue(TypeKeyString); + Log.LogWarning("CompositeJsonSerializer", string.Format("The action {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", typeName, typeof(T))); + throw new InvalidOperationException(string.Format("The token {0} is missing a public static Deserialize method taking a string as parameter and returning an {1}", typeName, typeof(T))); + } + return (T)methodInfo.Invoke(null, new object[] { obj }); } diff --git a/Composite/Core/WebClient/BuildManagerHelper.cs b/Composite/Core/WebClient/BuildManagerHelper.cs index 687c6669a0..a3344aa5f6 100644 --- a/Composite/Core/WebClient/BuildManagerHelper.cs +++ b/Composite/Core/WebClient/BuildManagerHelper.cs @@ -114,7 +114,8 @@ private static void LoadAllControls() { using (new DisableUrlMedataScope()) { - BuildManager.GetCompiledType(virtualPath); + string compilePath = (virtualPath.StartsWith("~") ? "" : "~") + virtualPath; + BuildManager.GetCompiledType(compilePath); } } catch (ThreadAbortException) diff --git a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs index 621490dcdb..a9f73003a3 100644 --- a/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs +++ b/Composite/Core/WebClient/Renderings/Page/PageRenderer.cs @@ -642,11 +642,8 @@ public static void NormalizeXhtmlDocument(XhtmlDocument rootDocument) rootDocument.Root.Add(nestedDocument.Attributes().Except(rootDocument.Root.Attributes(), _nameBasedAttributeComparer)); - // making from nested documents appear first. We will not filter them later and this ensure desired precedence - bool IsMetaProperty(XElement e) => e.Name.LocalName == "meta" && e.Attribute("property") != null; + rootDocument.Head.Add(nestedHead.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)); diff --git a/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs b/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs index 4a2cb99ec8..22a0ee05f0 100644 --- a/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs +++ b/Composite/Core/WebClient/Renderings/RequestInterceptorHttpModule.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Web; using Composite.Core.Extensions; @@ -220,7 +220,14 @@ static bool CheckForHostnameAliasRedirect(HttpContext httpContext) string newUrl = request.Url.AbsoluteUri.Replace("://" + hostname, "://" + hostnameBinding.Hostname); - httpContext.Response.Redirect(newUrl, false); + if (hostnameBinding.UsePermanentRedirect) + { + httpContext.Response.RedirectPermanent(newUrl, false); + } + else + { + httpContext.Response.Redirect(newUrl, false); + } httpContext.ApplicationInstance.CompleteRequest(); return true; } diff --git a/Composite/Core/WebClient/Setup/SetupServiceFacade.cs b/Composite/Core/WebClient/Setup/SetupServiceFacade.cs index b3fa9de824..53ab66410c 100644 --- a/Composite/Core/WebClient/Setup/SetupServiceFacade.cs +++ b/Composite/Core/WebClient/Setup/SetupServiceFacade.cs @@ -114,7 +114,7 @@ public static bool SetUp(string setupDescriptionXml, string username, string pas ApplicationLevelEventHandlers.ApplicationStartInitialize(); Log.LogInformation(VerboseLogTitle, "Creating first locale: " + language); - LocalizationFacade.AddLocale(locale, "", true, false, true); + LocalizationFacade.AddLocale(locale, "", true, true, true); Log.LogInformation(VerboseLogTitle, "Creating first user: " + username); diff --git a/Composite/Data/DataFacadeImpl.cs b/Composite/Data/DataFacadeImpl.cs index 571819ad79..6747898b58 100644 --- a/Composite/Data/DataFacadeImpl.cs +++ b/Composite/Data/DataFacadeImpl.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -720,11 +720,14 @@ private static void SetCreationHistoryInformation(object sender, DataEventArgs d ICreationHistory data = dataEventArgs.Data as ICreationHistory; if (data != null) { - data.CreationDate = DateTime.Now; + if (data.CreationDate == DateTime.MinValue) + { + data.CreationDate = DateTime.Now; + } try { - if (UserValidationFacade.IsLoggedIn()) + if (string.IsNullOrEmpty(data.CreatedBy) && UserValidationFacade.IsLoggedIn()) { data.CreatedBy = UserValidationFacade.GetUsername(); } diff --git a/Composite/Data/DataSourceId.cs b/Composite/Data/DataSourceId.cs index e341019e36..39512cbd0c 100644 --- a/Composite/Data/DataSourceId.cs +++ b/Composite/Data/DataSourceId.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Globalization; using Composite.Core.Serialization; @@ -117,7 +117,7 @@ public bool ShouldSerializeProviderName() public CultureInfo LocaleScope { get; internal set; } [JsonProperty(PropertyName = "localeScope")] - private string LocalScopeName => LocaleScope.Name; + private string LocalScopeName => (LocaleScope !=null ? LocaleScope.Name : null); /// /// True when the data element represents a physically stored element diff --git a/Composite/Data/DynamicTypes/DataTypeDescriptorFormsHelper.cs b/Composite/Data/DynamicTypes/DataTypeDescriptorFormsHelper.cs index b528cb6284..f5dac54791 100644 --- a/Composite/Data/DynamicTypes/DataTypeDescriptorFormsHelper.cs +++ b/Composite/Data/DynamicTypes/DataTypeDescriptorFormsHelper.cs @@ -265,27 +265,34 @@ public Dictionary GetNewBindings() private static Dictionary GetAvailablePublishingFlowTransitions(EntityToken entityToken) { - var transitionNames = new Dictionary + if(UserValidationFacade.IsLoggedIn()) + { + var transitionNames = new Dictionary { {GenericPublishProcessController.Draft, LocalizationFiles.Composite_Management.PublishingStatus_draft}, {GenericPublishProcessController.AwaitingApproval, LocalizationFiles.Composite_Management.PublishingStatus_awaitingApproval} }; - var username = UserValidationFacade.GetUsername(); - var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(username); - var userGroupPermissionDefinition = PermissionTypeFacade.GetUserGroupPermissionDefinitions(username); - var currentPermissionTypes = PermissionTypeFacade.GetCurrentPermissionTypes(UserValidationFacade.GetUserToken(), entityToken, userPermissionDefinitions, userGroupPermissionDefinition); - foreach (var permissionType in currentPermissionTypes) - { - if (GenericPublishProcessController.AwaitingPublicationActionPermissionType.Contains(permissionType)) + var username = UserValidationFacade.GetUsername(); + var userPermissionDefinitions = PermissionTypeFacade.GetUserPermissionDefinitions(username); + var userGroupPermissionDefinition = PermissionTypeFacade.GetUserGroupPermissionDefinitions(username); + var currentPermissionTypes = PermissionTypeFacade.GetCurrentPermissionTypes(UserValidationFacade.GetUserToken(), entityToken, userPermissionDefinitions, userGroupPermissionDefinition); + foreach (var permissionType in currentPermissionTypes) { - transitionNames.Add(GenericPublishProcessController.AwaitingPublication, - LocalizationFiles.Composite_Management.PublishingStatus_awaitingPublication); - break; + if (GenericPublishProcessController.AwaitingPublicationActionPermissionType.Contains(permissionType)) + { + transitionNames.Add(GenericPublishProcessController.AwaitingPublication, + LocalizationFiles.Composite_Management.PublishingStatus_awaitingPublication); + break; + } } - } - return transitionNames; + return transitionNames; + } + else + { + return new Dictionary(); + } } diff --git a/Composite/Data/Foundation/DataStoreExistenceVerifierImpl.cs b/Composite/Data/Foundation/DataStoreExistenceVerifierImpl.cs index d7a81ef970..7482fed427 100644 --- a/Composite/Data/Foundation/DataStoreExistenceVerifierImpl.cs +++ b/Composite/Data/Foundation/DataStoreExistenceVerifierImpl.cs @@ -66,6 +66,7 @@ internal sealed class DataStoreExistenceVerifierImpl : IDataStoreExistenceVerifi typeof(IUserDeveloperSettings), typeof(IUserFormLogin), typeof(IUserGroup), + typeof(IUserGroupActiveLocale), typeof(IUserGroupActivePerspective), typeof(IUserGroupPermissionDefinition), typeof(IUserGroupPermissionDefinitionPermissionType), diff --git a/Composite/Data/ProcessControlled/ProcessControllerFacade.cs b/Composite/Data/ProcessControlled/ProcessControllerFacade.cs index bd5849e97e..cad1b2a006 100644 --- a/Composite/Data/ProcessControlled/ProcessControllerFacade.cs +++ b/Composite/Data/ProcessControlled/ProcessControllerFacade.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -249,7 +249,7 @@ private static void OnDataBuildNew(object sender, DataEventArgs dataEventArgs) foreach (IPublishControlledAuxiliary publishControlledAuxiliary in publishControlledAuxiliaries) { - publishControlledAuxiliary.OnAfterDataUpdated(dataEventArgs.Data); + publishControlledAuxiliary.OnAfterBuildNew(dataEventArgs.Data); } } } diff --git a/Composite/Data/ProcessControlled/ProcessControllers/GenericPublishProcessController/GenericPublishProcessController.cs b/Composite/Data/ProcessControlled/ProcessControllers/GenericPublishProcessController/GenericPublishProcessController.cs index 52fb6ccdcf..ff2d64def2 100644 --- a/Composite/Data/ProcessControlled/ProcessControllers/GenericPublishProcessController/GenericPublishProcessController.cs +++ b/Composite/Data/ProcessControlled/ProcessControllers/GenericPublishProcessController/GenericPublishProcessController.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -354,7 +354,7 @@ public List GetActions(IData data, Type elementProviderType) return new List(); } - if (data is ILocalizedControlled && !UserSettings.ActiveLocaleCultureInfo.Equals(data.DataSourceId.LocaleScope)) + if (UserSettings.ActiveLocaleCultureInfo == null || data is ILocalizedControlled && !UserSettings.ActiveLocaleCultureInfo.Equals(data.DataSourceId.LocaleScope)) { return new List(); } diff --git a/Composite/Data/Types/IHostnameBinding.cs b/Composite/Data/Types/IHostnameBinding.cs index 2b1723ee14..79b8abf961 100644 --- a/Composite/Data/Types/IHostnameBinding.cs +++ b/Composite/Data/Types/IHostnameBinding.cs @@ -1,4 +1,4 @@ -using System; +using System; using Composite.Data.Hierarchy; using Composite.Data.Hierarchy.DataAncestorProviders; using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; @@ -48,7 +48,7 @@ public interface IHostnameBinding : IData string PageNotFoundUrl { get; set; } /// - [StoreFieldType(PhysicalStoreFieldType.String, 512, IsNullable = true)] + [StoreFieldType(PhysicalStoreFieldType.LargeString, IsNullable = true)] [ImmutableFieldId("{80C298F2-F493-465D-9F28-4F50DD0C03D5}")] string Aliases { get; set; } @@ -70,5 +70,14 @@ public interface IHostnameBinding : IData [DefaultFieldBoolValue(false)] [ImmutableFieldId("{1F45F2E7-2FAF-4F33-ACCA-BFF32EF7818A}")] bool EnforceHttps { get; set; } + + /// + /// When set to true, the system will use "HTTP 301 Permanent Redirect" when + /// redirecting from an alias to the hostname. + /// + [StoreFieldType(PhysicalStoreFieldType.Boolean)] + [DefaultFieldBoolValue(true)] + [ImmutableFieldId("{d933184a-19c1-4292-8e2e-f4a1a163f087}")] + bool UsePermanentRedirect { get; set; } } } diff --git a/Composite/Data/Types/ILockingInformation.cs b/Composite/Data/Types/ILockingInformation.cs index fcfa3d537f..ecb33c3c36 100644 --- a/Composite/Data/Types/ILockingInformation.cs +++ b/Composite/Data/Types/ILockingInformation.cs @@ -28,7 +28,7 @@ public interface ILockingInformation : IData [StoreFieldType(PhysicalStoreFieldType.LargeString)] [ImmutableFieldId("{34BD5C80-C5FD-4932-A1E6-3459E2D7802D}")] [NotNullValidator()] - string SerializedEntityToken { get; set; } + string LockKey { get; set; } /// diff --git a/Composite/Data/Types/IMediaFile.cs b/Composite/Data/Types/IMediaFile.cs index 7615e9185f..0bb14bc5e0 100644 --- a/Composite/Data/Types/IMediaFile.cs +++ b/Composite/Data/Types/IMediaFile.cs @@ -1,4 +1,4 @@ -using System; +using System; using Composite.Data.Hierarchy; using Composite.Core.WebClient.Renderings.Data; @@ -58,7 +58,7 @@ public interface IMediaFile : IFile /// [ImmutableFieldId("{016372B5-9692-4C2D-B64D-8FC6594BBCFF}")] [StoreFieldType(PhysicalStoreFieldType.LargeString)] - [SearchableField(true, true, false)] + [SearchableField(true, true, true)] string Tags { get; set; } diff --git a/Composite/Data/Types/IUserGroupActiveLocale.cs b/Composite/Data/Types/IUserGroupActiveLocale.cs new file mode 100644 index 0000000000..74bc7923e6 --- /dev/null +++ b/Composite/Data/Types/IUserGroupActiveLocale.cs @@ -0,0 +1,41 @@ +using System; +using Composite.Data.Hierarchy; +using Composite.Data.Hierarchy.DataAncestorProviders; +using Composite.Data.Validation.Validators; +using Microsoft.Practices.EnterpriseLibrary.Validation.Validators; + + +namespace Composite.Data.Types +{ + /// + /// + /// + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + [AutoUpdateble] + [Caching(CachingType.Full)] + [KeyPropertyName("Id")] + [DataScope(DataScopeIdentifier.PublicName)] + [ImmutableTypeId("{f60f4da8-59df-460f-84bf-5bf20700036b}")] + [DataAncestorProvider(typeof(NoAncestorDataAncestorProvider))] + public interface IUserGroupActiveLocale : IData + { + /// + [StoreFieldType(PhysicalStoreFieldType.Guid)] + [ImmutableFieldId("{80c5cc7c-bf23-4c7d-bfe0-d168eda41d50}")] + Guid Id { get; set; } + + + /// + [StoreFieldType(PhysicalStoreFieldType.Guid)] + [ImmutableFieldId("{f28ada6a-12bd-43bf-aef8-1b693ee3d390}")] + Guid UserGroupId { get; set; } + + + /// + [NotNullValidator()] + [StringSizeValidator(2, 16)] + [StoreFieldType(PhysicalStoreFieldType.String, 16)] + [ImmutableFieldId("{ddc38768-16e8-444c-a982-80f2a55b0b75}")] + string CultureName { get; set; } + } +} diff --git a/Composite/Data/Types/PageInsertPosition.cs b/Composite/Data/Types/PageInsertPosition.cs index 4f908c1d74..ff83f8b291 100644 --- a/Composite/Data/Types/PageInsertPosition.cs +++ b/Composite/Data/Types/PageInsertPosition.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using Composite.C1Console.Users; @@ -89,7 +89,7 @@ orderby ps.LocalOrdering var cultureInfo = UserSettings.CultureInfo; - int targetLocalOrdering = -1; + int targetLocalOrdering = pageStructures.Any() ? -1 : 0; foreach (IPageStructure pageStructure in pageStructures) { diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/Foundation/XmlDataProviderDocumentCache.cs index 9873e2b6a5..d0a99ea631 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; @@ -269,24 +269,28 @@ private static FileRecord LoadFileRecordFromDisk(string filePath, string element IList candidateFiles = GetCandidateFiles(filePath); + string errorCause = ""; + foreach (C1FileInfo candidateFile in candidateFiles) { bool tryLoad = true; + bool retriedLoad = false; while (tryLoad && dataDocument == null) { - dataDocument = TryLoad(candidateFile); + dataDocument = TryLoad(candidateFile, ref errorCause); if (dataDocument == null) { - if ((DateTime.Now - candidateFile.LastWriteTime).TotalSeconds > 30) + if (retriedLoad && (DateTime.Now - candidateFile.LastWriteTime).TotalSeconds > 30) { - tryLoad = false; + tryLoad = false; } else { Thread.Sleep(250); // other processes/servers may be writing to this file at the moment. Patience young padawan! } + retriedLoad = true; } } @@ -300,7 +304,7 @@ private static FileRecord LoadFileRecordFromDisk(string filePath, string element if (dataDocument == null) { dataDocument = new XDocument(new XElement("fallback")); - Log.LogWarning(LogTitle, "Did not find a healthy XML document for '{0}' - creating an empty store.", filePath); + Log.LogWarning(LogTitle, "Did not find a healthy XML document for '{0}' - creating an empty store. {1}", filePath, errorCause); } List elements = ExtractElements(dataDocument); @@ -350,7 +354,7 @@ private static FileRecord LoadFileRecordFromDisk(string filePath, string element - private static XDocument TryLoad(C1FileInfo candidateFile) + private static XDocument TryLoad(C1FileInfo candidateFile, ref string errorCause) { XDocument dataDocument = null; try @@ -362,8 +366,9 @@ private static XDocument TryLoad(C1FileInfo candidateFile) dataDocument = XDocument.Load(xmlReader); } } - catch (Exception) + catch (Exception ex) { + errorCause = ex.Message; // broken file - should not stop us... } diff --git a/Composite/Plugins/Data/DataProviders/XmlDataProvider/XmlDataProvider_Stores.cs b/Composite/Plugins/Data/DataProviders/XmlDataProvider/XmlDataProvider_Stores.cs index f75d049159..ef10df495c 100644 --- a/Composite/Plugins/Data/DataProviders/XmlDataProvider/XmlDataProvider_Stores.cs +++ b/Composite/Plugins/Data/DataProviders/XmlDataProvider/XmlDataProvider_Stores.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Configuration; using System.Globalization; @@ -115,7 +115,6 @@ public void AddLocale(CultureInfo cultureInfo) } - public void RemoveLocale(CultureInfo cultureInfo) { XmlDataProviderDocumentCache.ClearCache(); @@ -148,7 +147,7 @@ private void InitializeExistingStores() { if (!dataTypes.TryGetValue(dataTypeDescriptor.DataTypeId, out interfaceType) || interfaceType == null) { - Log.LogWarning(LogTitle, "The data interface type '{0}' does not exists and is not code generated. It will not be usable", dataTypeDescriptor.TypeManagerTypeName); + Log.LogWarning(LogTitle, "The data interface type '{0}' does not exist and is not code generated. It will not be usable", dataTypeDescriptor.TypeManagerTypeName); continue; } diff --git a/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs b/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs index 1fc57b4d6c..ae684b94a0 100644 --- a/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs +++ b/Composite/Plugins/Elements/ElementProviders/PageElementProvider/PageElementProvider.cs @@ -106,14 +106,28 @@ public ElementProviderContext Context public IEnumerable GetRoots(SearchToken searchToken) { + EntityToken entityToken = new PageElementProviderEntityToken(_context.ProviderName); + + if (UserValidationFacade.IsLoggedIn() + && !DataLocalizationFacade.ActiveLocalizationCultures.Contains(UserSettings.ActiveLocaleCultureInfo)) + { + yield return new Element(_context.CreateElementHandle(entityToken)) + { + VisualData = new ElementVisualizedData + { + Label = "No website access: Missing permissions to any Data Language", + Icon = PageElementProvider.DeactivateLocalization + } + }; + yield break; + } + int pages; using (new DataScope(DataScopeIdentifier.Administrated)) { pages = PageServices.GetChildrenCount(Guid.Empty); } - EntityToken entityToken = new PageElementProviderEntityToken(_context.ProviderName); - var dragAndDropInfo = new ElementDragAndDropInfo(); dragAndDropInfo.AddDropType(typeof(IPage)); dragAndDropInfo.SupportsIndexedPosition = true; @@ -268,6 +282,12 @@ var pageType in public IEnumerable GetChildren(EntityToken entityToken, SearchToken searchToken) { + if (UserValidationFacade.IsLoggedIn() + && !DataLocalizationFacade.ActiveLocalizationCultures.Contains(UserSettings.ActiveLocaleCultureInfo)) + { + return Enumerable.Empty(); + } + if (entityToken is AssociatedDataElementProviderHelperEntityToken associatedData) { return _pageAssociatedHelper.GetChildren(associatedData, false); @@ -301,6 +321,11 @@ public IEnumerable GetForeignRoots(SearchToken searchToken) public IEnumerable GetForeignChildren(EntityToken entityToken, SearchToken searchToken) { if (entityToken is DataEntityToken dataEntityToken && dataEntityToken.Data == null) return Array.Empty(); + if (UserValidationFacade.IsLoggedIn() + && !DataLocalizationFacade.ActiveLocalizationCultures.Contains(UserSettings.ActiveLocaleCultureInfo)) + { + return Enumerable.Empty(); + } if (entityToken is AssociatedDataElementProviderHelperEntityToken associatedData) { diff --git a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1Directory.cs b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1Directory.cs index 72809d0b62..5794007665 100644 --- a/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1Directory.cs +++ b/Composite/Plugins/IO/IOProviders/LocalIOProvider/LocalC1Directory.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.IO; using Composite.Core.IO; using Composite.Core.IO.Plugins.IOProvider; @@ -127,7 +127,7 @@ public string[] GetFiles(string path, string searchPattern) [System.Diagnostics.CodeAnalysis.SuppressMessage("Composite.IO", "Composite.DoNotUseDirectoryClass:DoNotUseDirectoryClass")] public string[] GetFiles(string path, string searchPattern, SearchOption searchOption) { - return Directory.GetFiles(path, searchPattern, searchOption); + return Directory.Exists(path) ? Directory.GetFiles(path, searchPattern, searchOption) : new string[] { }; } diff --git a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs index 654ef4dfc4..915284973b 100644 --- a/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs +++ b/Composite/Plugins/Routing/Pages/DefaultPageUrlProvider.cs @@ -39,6 +39,13 @@ static DefaultPageUrlProvider() { DataEvents.OnAfterAdd += (a, b) => UpdateFriendlyUrl((IPage) b.Data); DataEvents.OnAfterUpdate += (a, b) => UpdateFriendlyUrl((IPage) b.Data); + DataEvents.OnStoreChanged += (sender, args) => + { + if (!args.DataEventsFired) + { + lock (_friendlyUrls) _friendlyUrls.Clear(); + } + }; DataEvents.OnAfterAdd += (a, b) => _hostnameBindings = null; DataEvents.OnAfterUpdate += (a, b) => _hostnameBindings = null; @@ -425,7 +432,7 @@ private static void UpdateFriendlyUrl(IPage page) private PageUrlData ParsePagePath(string pagePath, PublicationScope publicationScope, CultureInfo locale, IHostnameBinding hostnameBinding) { - // Parshing what's left: + // Parsing what's left: // [/Path to a page][UrlSuffix]{/PathInfo} string pathInfo = null; diff --git a/Composite/Properties/SharedAssemblyInfo.cs b/Composite/Properties/SharedAssemblyInfo.cs index 9d8c21ed56..c83dfe9766 100644 --- a/Composite/Properties/SharedAssemblyInfo.cs +++ b/Composite/Properties/SharedAssemblyInfo.cs @@ -2,15 +2,15 @@ // General Information about the assemblies Composite and Composite.Workflows #if !InternalBuild -[assembly: AssemblyTitle("C1 CMS 6.6")] +[assembly: AssemblyTitle("C1 CMS 6.7")] #else -[assembly: AssemblyTitle("C1 CMS 6.6 (Internal Build)")] +[assembly: AssemblyTitle("C1 CMS 6.7 (Internal Build)")] #endif [assembly: AssemblyCompany("Orckestra Inc")] [assembly: AssemblyProduct("C1 CMS")] -[assembly: AssemblyCopyright("Copyright © Orckestra Inc 2018")] +[assembly: AssemblyCopyright("Copyright © Orckestra Inc 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("6.6.*")] +[assembly: AssemblyVersion("6.7.*")] diff --git a/Composite/Search/Crawling/DataFieldProcessors/DateTimeDataFieldProcessor.cs b/Composite/Search/Crawling/DataFieldProcessors/DateTimeDataFieldProcessor.cs index 29b40e2735..c0496e9a78 100644 --- a/Composite/Search/Crawling/DataFieldProcessors/DateTimeDataFieldProcessor.cs +++ b/Composite/Search/Crawling/DataFieldProcessors/DateTimeDataFieldProcessor.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Globalization; using System.Reflection; @@ -21,10 +21,12 @@ protected override DocumentFieldPreview.ValuePreviewDelegate GetPreviewFunction( return value => { if (value == null) return null; - var date = DateTime.ParseExact((string)value, "s", CultureInfo.InvariantCulture); - - return date.ToString("yyyy MMM d"); - }; + if (DateTime.TryParseExact((string)value, "s", CultureInfo.InvariantCulture, DateTimeStyles.None, out var date)) + { + return date.ToString("yyyy MMM d"); + } + return (string)value; + }; } /// diff --git a/Composite/Search/Crawling/DataFieldProcessors/MediaTagsDataFieldProcessor.cs b/Composite/Search/Crawling/DataFieldProcessors/MediaTagsDataFieldProcessor.cs new file mode 100644 index 0000000000..8527074194 --- /dev/null +++ b/Composite/Search/Crawling/DataFieldProcessors/MediaTagsDataFieldProcessor.cs @@ -0,0 +1,32 @@ +using Composite.Core.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Composite.Search.Crawling.DataFieldProcessors +{ + class MediaTagsDataFieldProcessor : DefaultDataFieldProcessor + { + public override string[] GetFacetValues(object value) + { + var str = (string)value; + var strList = str.Split(',').Select(s => s.Trim()).Where(s => !s.IsNullOrEmpty()).ToArray(); + return strList; + } + + public override DocumentFieldFacet GetDocumentFieldFacet(PropertyInfo propertyInfo) + { + var result = base.GetDocumentFieldFacet(propertyInfo); + + result.FacetType = FacetType.MultipleValues; + + return result; + } + + } + +} + diff --git a/Composite/Search/Crawling/DataTypeSearchReflectionHelper.cs b/Composite/Search/Crawling/DataTypeSearchReflectionHelper.cs index c876057c11..839f61e3c4 100644 --- a/Composite/Search/Crawling/DataTypeSearchReflectionHelper.cs +++ b/Composite/Search/Crawling/DataTypeSearchReflectionHelper.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -81,6 +81,12 @@ internal static IDataFieldProcessor GetDataFieldProcessor(PropertyInfo propertyI return new MimeTypeDataFieldProcessor(); } + if (propertyInfo.DeclaringType == typeof(IMediaFile) + && propertyInfo.Name == nameof(IMediaFile.Tags)) + { + return new MediaTagsDataFieldProcessor(); + } + return new DefaultDataFieldProcessor(); }); } diff --git a/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt b/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt index cce2ab8481..9b5c5997ea 100644 --- a/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt +++ b/Website/App_Data/Composite/ReleaseBuild.Composite.config.changeHistory.txt @@ -170,4 +170,7 @@ Changes in 6.0 or later Changes in 6.6 or later: -Added to loggingConfiguration/listeners - -Added to loggingConfiguration/specialSources/allEvents/listeners \ No newline at end of file + -Added to loggingConfiguration/specialSources/allEvents/listeners + + Changes in 6.7 or later: + - n/a \ No newline at end of file diff --git a/Website/Composite/content/forms/Administrative/Hostnames.xml b/Website/Composite/content/forms/Administrative/Hostnames.xml index e4c06b646f..a74aa10fb4 100644 --- a/Website/Composite/content/forms/Administrative/Hostnames.xml +++ b/Website/Composite/content/forms/Administrative/Hostnames.xml @@ -1,4 +1,4 @@ - + @@ -9,6 +9,7 @@ + @@ -67,6 +68,11 @@ + + + + + \ No newline at end of file diff --git a/Website/Composite/content/misc/editors/functioncalleditor/bindings/FunctionEditorPageBinding.js b/Website/Composite/content/misc/editors/functioncalleditor/bindings/FunctionEditorPageBinding.js index 2a4df5c486..38128ae299 100644 --- a/Website/Composite/content/misc/editors/functioncalleditor/bindings/FunctionEditorPageBinding.js +++ b/Website/Composite/content/misc/editors/functioncalleditor/bindings/FunctionEditorPageBinding.js @@ -61,17 +61,11 @@ FunctionEditorPageBinding.prototype.handleAction = function ( action ) { break; case PageBinding.ACTION_DOPOSTBACK : - if (action.target.getID() == "switchbutton") { - - //fix crashing Chrome if svg icon set, workarround - action.target.setImage(); - - if ( !this._isSourceMode ) { - this._cover ( false ); - var decks = this.bindingWindow.bindingMap.decks; - decks.select ( "sourcedeck" ); - this._isSourceMode = true; - } + if (action.target.getID() == "switchbutton" && !this._isSourceMode) { + this._cover ( false ); + var decks = this.bindingWindow.bindingMap.decks; + decks.select ( "sourcedeck" ); + this._isSourceMode = true; } break; diff --git a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Text/HtmlBlob.ascx b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Text/HtmlBlob.ascx index c7f5800ede..6019071249 100644 --- a/Website/Composite/controls/FormsControls/FormUiControlTemplates/Text/HtmlBlob.ascx +++ b/Website/Composite/controls/FormsControls/FormUiControlTemplates/Text/HtmlBlob.ascx @@ -16,7 +16,7 @@ } - + <%= ConvertRenderingUrls(this.Html) %> diff --git a/Website/Composite/localization/Composite.Plugins.UserGroupElementProvider.en-us.xml b/Website/Composite/localization/Composite.Plugins.UserGroupElementProvider.en-us.xml index d7130e8629..aab25115cb 100644 --- a/Website/Composite/localization/Composite.Plugins.UserGroupElementProvider.en-us.xml +++ b/Website/Composite/localization/Composite.Plugins.UserGroupElementProvider.en-us.xml @@ -1,4 +1,4 @@ - + @@ -20,14 +20,17 @@ - + - + + + + - - + + diff --git a/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml b/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml index 39e1211642..616d809f9a 100644 --- a/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml +++ b/Website/Composite/localization/Orckestra.Tools.UrlConfiguration.en-us.xml @@ -1,4 +1,4 @@ - + @@ -37,4 +37,7 @@ + + + \ No newline at end of file diff --git a/Website/Composite/scripts/source/top/core/KickStart.js b/Website/Composite/scripts/source/top/core/KickStart.js index 981e6725c9..2498d3f6d6 100644 --- a/Website/Composite/scripts/source/top/core/KickStart.js +++ b/Website/Composite/scripts/source/top/core/KickStart.js @@ -2,15 +2,15 @@ * Kickstarting the entire shebang. */ var KickStart = new function () { - + var isLocalStoreReady = false; - var isQualified = Client.qualifies (); - + var isQualified = Client.qualifies(); + var DEFAULT_USERNAME = "admin"; var DEFAULT_PASSWORD = "123456"; - - if ( !isQualified ) { + + if (!isQualified) { document.location = "unsupported.aspx"; return; } @@ -19,49 +19,49 @@ var KickStart = new function () { * Fire on load! */ this.fireOnLoad = function () { - - // iPad IOS7 hack - if (Client.isPad && Client.isOS7 && window.innerHeight != document.documentElement.clientHeight) { - document.documentElement.style.height = window.innerHeight + "px"; - } - - Application.lock ( this ); - fileEventBroadcasterSubscriptions ( true ); - EventBroadcaster.subscribe ( BroadcastMessages.APPLICATION_SHUTDOWN, this ); - - SetupService = WebServiceProxy.createProxy ( Constants.URL_WSDL_SETUPSERVICE ); - ReadyService = WebServiceProxy.createProxy ( Constants.URL_WSDL_READYSERVICE ); - LoginService = WebServiceProxy.createProxy ( Constants.URL_WSDL_LOGINSERVICE ); - InstallationService = WebServiceProxy.createProxy(Constants.URL_WSDL_INSTALLSERVICE); - StringService = WebServiceProxy.createProxy(Constants.URL_WSDL_STRINGSERVICE); - - EventBroadcaster.broadcast(BroadcastMessages.APPLICATION_KICKSTART); - setTimeout(function () { - Persistance.initialize(); // NOTE: We are not using this stuff! - }, 0); + // iPad IOS7 hack + if (Client.isPad && Client.isOS7 && window.innerHeight != document.documentElement.clientHeight) { + document.documentElement.style.height = window.innerHeight + "px"; + } + + Application.lock(this); + fileEventBroadcasterSubscriptions(true); + EventBroadcaster.subscribe(BroadcastMessages.APPLICATION_SHUTDOWN, this); + + SetupService = WebServiceProxy.createProxy(Constants.URL_WSDL_SETUPSERVICE); + ReadyService = WebServiceProxy.createProxy(Constants.URL_WSDL_READYSERVICE); + LoginService = WebServiceProxy.createProxy(Constants.URL_WSDL_LOGINSERVICE); + InstallationService = WebServiceProxy.createProxy(Constants.URL_WSDL_INSTALLSERVICE); + StringService = WebServiceProxy.createProxy(Constants.URL_WSDL_STRINGSERVICE); + + EventBroadcaster.broadcast(BroadcastMessages.APPLICATION_KICKSTART); + + setTimeout(function () { + Persistance.initialize(); // NOTE: We are not using this stuff! + }, 0); }; /* * Indicate user just logged to console */ this.justLogged = false; - + /** * @implements {IBroadcastListener} * @param {string} broadcast */ - this.handleBroadcast = function ( broadcast ) { - - switch ( broadcast ) { - - case BroadcastMessages.PERSISTANCE_INITIALIZED : - kickStart ( broadcast ); + this.handleBroadcast = function (broadcast) { + + switch (broadcast) { + + case BroadcastMessages.PERSISTANCE_INITIALIZED: + kickStart(broadcast); break; - - case BroadcastMessages.APPLICATION_STARTUP : + + case BroadcastMessages.APPLICATION_STARTUP: // doStartUp (); hmmmm.... break; - + case BroadcastMessages.KEY_ENTER: if (bindingMap.decks != null) { var selecteddeck = bindingMap.decks.getSelectedDeckBinding(); @@ -70,129 +70,127 @@ var KickStart = new function () { case "logindeck": this.login(); break; - case "changepassworddeck": + case "changepassworddeck": this.changePassword(); break; default: } } - + } break; - - case BroadcastMessages.APPLICATION_LOGIN : + + case BroadcastMessages.APPLICATION_LOGIN: var appwindow = window.bindingMap.appwindow; //Workarrond for iPad - "div layout creashed on some reflex" if (Client.isPad) { appwindow.bindingElement.style.borderLeft = "1px solid #333"; } - appwindow.setURL ( "app.aspx" ); + appwindow.setURL("app.aspx"); break; - + case BroadcastMessages.APPLICATION_OPERATIONAL: showWorkbench(); - setTimeout(function() { + setTimeout(function () { StageBinding.bindingInstance.handleHash(window); }, 0); break; - - case BroadcastMessages.APPLICATION_SHUTDOWN : - if ( bindingMap.decks != null ) { - bindingMap.decks.select ( "shutdowndeck" ); + + case BroadcastMessages.APPLICATION_SHUTDOWN: + if (bindingMap.decks != null) { + bindingMap.decks.select("shutdowndeck"); } - bindingMap.cover.show (); + bindingMap.cover.show(); break; } } - + /** * File and unfile EventBroadcaster subscriptions. * @param {boolean} isSubscribe */ - function fileEventBroadcasterSubscriptions ( isSubscribe ) { - - new List ([ - + function fileEventBroadcasterSubscriptions(isSubscribe) { + + new List([ + BroadcastMessages.PERSISTANCE_INITIALIZED, BroadcastMessages.APPLICATION_STARTUP, BroadcastMessages.APPLICATION_LOGIN, BroadcastMessages.APPLICATION_OPERATIONAL - - ]).each ( - function ( broadcast ) { - if ( isSubscribe ) { - EventBroadcaster.subscribe ( broadcast, KickStart ); + + ]).each( + function (broadcast) { + if (isSubscribe) { + EventBroadcaster.subscribe(broadcast, KickStart); } else { - EventBroadcaster.unsubscribe ( broadcast, KickStart ); + EventBroadcaster.unsubscribe(broadcast, KickStart); } } ); } - + /** * Freeze storyboard until Localstore initialize. * If not registered, show registration. Otherwise show login. * @param {string} broadcast */ - function kickStart ( broadcast ) { - - switch ( broadcast ) { - case BroadcastMessages.PERSISTANCE_INITIALIZED : + function kickStart(broadcast) { + + switch (broadcast) { + case BroadcastMessages.PERSISTANCE_INITIALIZED: isLocalStoreReady = true; break; } - - if ( isLocalStoreReady ) { - if ( bindingMap.decks != null && LoginService.IsLoggedIn ( true )) { - accessGranted (); + + if (isLocalStoreReady) { + if (bindingMap.decks != null && LoginService.IsLoggedIn(true)) { + accessGranted(); } else { - if ( bindingMap.decks != null ) { - showLogin (); + if (bindingMap.decks != null) { + showLogin(); } else { - showWelcome (); + showWelcome(); } } } splashScreenData(); } - + /** * Splash screen data. */ - function splashScreenData () { + function splashScreenData() { var elems = document.getElementsByClassName("js-applicationname"); - for(var index = 0; index < elems.length; ++index) - { + for (var index = 0; index < elems.length; ++index) { elems[index].innerHTML = Installation.applicationName; } } - + /* * Show welcome screens on first time startup. */ - function showWelcome () { - - Application.unlock ( KickStart ); - if ( window.Welcome != null ) { - Welcome.test (); + function showWelcome() { + + Application.unlock(KickStart); + if (window.Welcome != null) { + Welcome.test(); } } - + /* * Show login screen. */ - function showLogin () { - - EventBroadcaster.subscribe ( BroadcastMessages.KEY_ENTER, KickStart ); - Application.unlock ( KickStart ); - bindingMap.decks.select ( "logindeck" ); - - setTimeout ( function () { + function showLogin() { + + EventBroadcaster.subscribe(BroadcastMessages.KEY_ENTER, KickStart); + Application.unlock(KickStart); + bindingMap.decks.select("logindeck"); + + setTimeout(function () { if (Application.isLocalHost) { - if(Application.isDeveloperMode || Client.isPerformanceTest) - { + if (Application.isDeveloperMode || Client.isPerformanceTest) { DataManager.getDataBinding("username").setValue(DEFAULT_USERNAME); DataManager.getDataBinding("password").setValue(DEFAULT_PASSWORD); } @@ -201,42 +199,42 @@ var KickStart = new function () { KickStart.login(); } } - setTimeout ( function () { - DataManager.getDataBinding ( "username" ).focus (); - }, 250 ); - }, 0 ); + setTimeout(function () { + DataManager.getDataBinding("username").focus(); + }, 250); + }, 0); } - + /** * When registered, monitor the servers readystate and continue to login screen when done. */ - function watchProgress () { - - window.progressOnRegistrationInterval = window.setInterval ( function () { - if ( ReadyService.IsServerReady ( true )) { - window.clearInterval ( window.progressOnRegistrationInterval ); + function watchProgress() { + + window.progressOnRegistrationInterval = window.setInterval(function () { + if (ReadyService.IsServerReady(true)) { + window.clearInterval(window.progressOnRegistrationInterval); window.progressOnRegistrationInterval = null; - splashScreenData (); - showLogin (); + splashScreenData(); + showLogin(); } - }, 2000 ); + }, 2000); } - + /** * Show it. */ - function showWorkbench () { - - setTimeout ( function () { - bindingMap.cover.hide (); - fileEventBroadcasterSubscriptions ( false ); - Application.unlock ( KickStart ); - }, PageBinding.TIMEOUT ); + function showWorkbench() { + + setTimeout(function () { + bindingMap.cover.hide(); + fileEventBroadcasterSubscriptions(false); + Application.unlock(KickStart); + }, PageBinding.TIMEOUT); } this.changePassword = function () { - + if (bindingMap.toppage.validateAllDataBindings()) { var username = DataManager.getDataBinding("username").getResult(); @@ -256,7 +254,7 @@ var KickStart = new function () { alert(result.getFaultString()); } else { if (result.length == 0) { - setTimeout(function() { + setTimeout(function () { top.window.location.reload(true); }, 0); } else { @@ -270,7 +268,7 @@ var KickStart = new function () { } } else { - + this.showPasswordErrors([Resolver.resolve("${string:Composite.C1Console.Users:ChangePasswordForm.ConfirmationPasswordMimatch}")]); } } @@ -281,14 +279,14 @@ var KickStart = new function () { var errorsElement = document.getElementById("passworderror"); errorsElement.innerHTML = ""; - errors.each(function(error) { + errors.each(function (error) { var errorElement = document.createElement("div"); errorElement.textContent = error; errorElement.className = "text-error text-sm"; errorsElement.appendChild(errorElement); }); - + errorsElement.style.display = "block"; @@ -298,12 +296,12 @@ var KickStart = new function () { handleAction: function (action) { document.getElementById("passworderror").style.display = "none"; action.target.removeActionListener( - Binding.ACTION_DIRTY, handler + Binding.ACTION_DIRTY, handler ); } } bindingMap.passwordfields.addActionListener( - Binding.ACTION_DIRTY, handler + Binding.ACTION_DIRTY, handler ); DataManager.getDataBinding("passwordold").clean(); @@ -312,31 +310,31 @@ var KickStart = new function () { } - + /** * Note that we disable SOAP debugging during login. * Note that we may be able to do something intelligent with the SOAP response. * Note that we didn't manage to come up with intelligent handling of the SOAP response. */ this.login = function () { - - Application.lock ( KickStart ); // unlocked by showWorkbench or if fields don't validate - + + Application.lock(KickStart); // unlocked by showWorkbench or if fields don't validate + /* * The timeout is here to block GUI with wait cursor. */ - setTimeout ( function () { - - if ( bindingMap.toppage.validateAllDataBindings ()) { - KickStart.doLogin ( - DataManager.getDataBinding ( "username" ).getResult (), - DataManager.getDataBinding ( "password" ).getResult () + setTimeout(function () { + + if (bindingMap.toppage.validateAllDataBindings()) { + KickStart.doLogin( + DataManager.getDataBinding("username").getResult(), + DataManager.getDataBinding("password").getResult() ); } else { - Application.unlock ( KickStart ); + Application.unlock(KickStart); } - - }, 25 ); + + }, 25); } /** @@ -344,68 +342,68 @@ var KickStart = new function () { * @param {String} username * @param {String} password */ - this.doLogin = function ( username, password ) { - + this.doLogin = function (username, password) { + var wasEnabled = WebServiceProxy.isLoggingEnabled; WebServiceProxy.isLoggingEnabled = false; WebServiceProxy.isFaultHandler = false; - + var isAllowed = false; var isChangePasswordRequired = false; - var result = LoginService.ValidateAndLogin ( username, password ); - if ( result instanceof SOAPFault ) { - alert ( result.getFaultString ()); + var result = LoginService.ValidateAndLogin(username, password); + if (result instanceof SOAPFault) { + alert(result.getFaultString()); } else { - if (result == "lockedAfterMaxAttempts") { - // TODO: unhardcode - alert("The account was locked after maximum login attempts. Please contact administrator."); - } + if (result == "lockedAfterMaxAttempts") { + // TODO: unhardcode + alert("The account was locked after maximum login attempts. Please contact administrator."); + } - if (result == "lockedByAnAdministrator") { - // TODO: unhardcode - alert("The account was locked by an administrator."); - } + if (result == "lockedByAnAdministrator") { + // TODO: unhardcode + alert("The account was locked by an administrator."); + } - if (result == "passwordUpdateRequired") { - isChangePasswordRequired = true; + if (result == "passwordUpdateRequired") { + isChangePasswordRequired = true; - } + } - if (result == "success") { - isAllowed = true; - } + if (result == "success") { + isAllowed = true; + } } if (isChangePasswordRequired) { changePasswordRequired(); - }else if ( isAllowed ) { - EventBroadcaster.unsubscribe ( BroadcastMessages.KEY_ENTER, KickStart ); + } else if (isAllowed) { + EventBroadcaster.unsubscribe(BroadcastMessages.KEY_ENTER, KickStart); this.justLogged = true; - accessGranted (); + accessGranted(); } else { - Application.unlock ( KickStart ); - if ( bindingMap.decks != null ) { // on Welcome we may get trapped here! - accesssDenied (); + Application.unlock(KickStart); + if (bindingMap.decks != null) { // on Welcome we may get trapped here! + accesssDenied(); } } WebServiceProxy.isFaultHandler = true; - if ( wasEnabled ) { + if (wasEnabled) { WebServiceProxy.isLoggingEnabled = true; } } - + /** * Access granted. */ - function accessGranted () { - setTimeout ( function () { - if ( bindingMap.decks != null ) { - bindingMap.decks.select ( "loadingdeck" ); + function accessGranted() { + setTimeout(function () { + if (bindingMap.decks != null) { + bindingMap.decks.select("loadingdeck"); } - setTimeout ( function () { - Application.login (); - }, 0 ); - }, 0 ); + setTimeout(function () { + Application.login(); + }, 0); + }, 0); } /** @@ -416,12 +414,12 @@ var KickStart = new function () { setTimeout(function () { Application.unlock(KickStart); if (bindingMap.decks != null) { - bindingMap.decks.select("changepassworddeck"); + bindingMap.decks.select("changepassworddeck"); bindingMap.cover.attachClassName("widesplash"); setTimeout(function () { - var passwordexpired = document.getElementById("passwordexpired"); - passwordexpired.textContent = passwordexpired.textContent.replace("{0}", Installation.passwordExpirationTimeInDays); + var passwordexpired = document.getElementById("passwordexpired"); + passwordexpired.textContent = passwordexpired.textContent.replace("{0}", Installation.passwordExpirationTimeInDays); DataManager.getDataBinding("usernameold").setValue(DataManager.getDataBinding("username").getResult()); DataManager.getDataBinding("passwordold").focus(); @@ -430,49 +428,49 @@ var KickStart = new function () { } }, 25); } - + /** * Access denied. */ - function accesssDenied () { - - var username = DataManager.getDataBinding ( "username" ); - var password = DataManager.getDataBinding ( "password" ); - - username.blur (); - password.blur (); - username.setValue ( "" ); - password.setValue ( "" ); - username.clean (); - password.clean (); - username.focus (); - - document.getElementById ( "loginerror" ).style.display = "block"; - + function accesssDenied() { + + var username = DataManager.getDataBinding("username"); + var password = DataManager.getDataBinding("password"); + + username.blur(); + password.blur(); + username.setValue(""); + password.setValue(""); + username.clean(); + password.clean(); + username.focus(); + + document.getElementById("loginerror").style.display = "block"; + var handler = { - handleAction : function ( action ) { - document.getElementById ( "loginerror" ).style.display = "none"; - action.target.removeActionListener ( - Binding.ACTION_DIRTY, handler + handleAction: function (action) { + document.getElementById("loginerror").style.display = "none"; + action.target.removeActionListener( + Binding.ACTION_DIRTY, handler ); } } - bindingMap.loginfields.addActionListener ( - Binding.ACTION_DIRTY, handler + bindingMap.loginfields.addActionListener( + Binding.ACTION_DIRTY, handler ); } - + /* * Fire on load! */ - WindowManager.fireOnLoad ( this ); - + WindowManager.fireOnLoad(this); + /* * Non-qualified browsers would run into a javascript * error in the UpdateManager when switching to the * not supported page. Let's disable the UpdateManager. */ - if ( !isQualified ) { + if (!isQualified) { UpdateManager.isEnabled = false; } } diff --git a/Website/Composite/scripts/source/top/ui/UserInterfaceMapping.js b/Website/Composite/scripts/source/top/ui/UserInterfaceMapping.js index 09d48f1640..0a104c9112 100644 --- a/Website/Composite/scripts/source/top/ui/UserInterfaceMapping.js +++ b/Website/Composite/scripts/source/top/ui/UserInterfaceMapping.js @@ -12,17 +12,12 @@ function UserInterfaceMapping ( map ) { /** * @type {HashMap} */ - if (Client.isExplorer) { - this.map = {}; - for (var m in map) { - this.map[m.replace("ui:", "")] = map[m]; - } - } - else { - this.map = map; - } + this.map = {}; - + for (var m in map) { + this.map[m.replace('ui:', '')] = map[m]; + this.map[m] = map[m]; + } } /** diff --git a/Website/Composite/services/Localization/LocalizationService.asmx b/Website/Composite/services/Localization/LocalizationService.asmx index e6b902a689..6067a1c6e3 100644 --- a/Website/Composite/services/Localization/LocalizationService.asmx +++ b/Website/Composite/services/Localization/LocalizationService.asmx @@ -1,4 +1,4 @@ -<%@ WebService Language="C#" Class="Composite.Services.LocalizationService" %> +<%@ WebService Language="C#" Class="Composite.Services.LocalizationService" %> using System; using System.Linq; @@ -139,7 +139,7 @@ namespace Composite.Services [WebMethod] public string GetTextDirection(bool dummy) { - return UserSettings.ActiveLocaleCultureInfo.TextInfo.IsRightToLeft ? "rtl" : "ltr"; + return UserSettings.ActiveLocaleCultureInfo == null ? "ltr" : (UserSettings.ActiveLocaleCultureInfo.TextInfo.IsRightToLeft ? "rtl" : "ltr"); } [WebMethod] diff --git a/Website/WebSite.csproj b/Website/WebSite.csproj index 2667746362..820e32b677 100644 --- a/Website/WebSite.csproj +++ b/Website/WebSite.csproj @@ -658,6 +658,7 @@ + diff --git a/Website/package.json b/Website/package.json index 9018cceffd..95bb8dc981 100644 --- a/Website/package.json +++ b/Website/package.json @@ -27,7 +27,7 @@ "grunt-contrib-watch": "~0.6.1", "grunt-postcss": "^0.5.5", "grunt-svgmin": "3.2.0", - "iedriver": "2.53.1", + "iedriver": "3.14.1", "istanbul": "^0.4.3", "istanbul-lib-coverage": "1.0.0", "jsdom": "9.4.1",