diff --git a/.gitignore b/.gitignore
index fb9ec3b3b4..95b556a942 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-
+
/.vs
/Composite/bin
/Composite/obj
@@ -36,3 +36,5 @@
/Packages/
selenium-debug.log
/Website/test/e2e/reports/
+
+GitCommitInfo.cs
\ No newline at end of file
diff --git a/Composite.Workflows/C1Console/Scheduling/BaseSchedulerWorkflow.cs b/Composite.Workflows/C1Console/Scheduling/BaseSchedulerWorkflow.cs
index b7a89eb4e3..f633bdb92b 100644
--- a/Composite.Workflows/C1Console/Scheduling/BaseSchedulerWorkflow.cs
+++ b/Composite.Workflows/C1Console/Scheduling/BaseSchedulerWorkflow.cs
@@ -39,6 +39,7 @@ private void initializeCodeActivity_ExecuteCode(object sender, EventArgs e)
private void finalizeCodeActivity_ExecuteCode(object sender, EventArgs e)
{
using (ThreadDataManager.Initialize())
+ using (ServiceLocator.EnsureThreadDataServiceScope())
{
Execute();
}
diff --git a/Composite.Workflows/C1Console/Scheduling/DataPublishSchedulerWorkflow.cs b/Composite.Workflows/C1Console/Scheduling/DataPublishSchedulerWorkflow.cs
index c5fa9e2081..d8506b1756 100644
--- a/Composite.Workflows/C1Console/Scheduling/DataPublishSchedulerWorkflow.cs
+++ b/Composite.Workflows/C1Console/Scheduling/DataPublishSchedulerWorkflow.cs
@@ -36,8 +36,14 @@ protected override void Execute()
var publishSchedule = PublishScheduleHelper.GetPublishSchedule(type, DataId, LocaleName);
DataFacade.Delete(publishSchedule);
- var data = (IPublishControlled)DataFacade.GetDataByUniqueKey(type, DataId);
- Verify.IsNotNull(data, "The data with the id '{0}' does not exist", DataId);
+ var data = (IPublishControlled)DataFacade.TryGetDataByUniqueKey(type, DataId);
+ if (data == null)
+ {
+ Log.LogWarning(LogTitle, $"Failed to find data of type '{type}' by id '{DataId}'.");
+
+ transaction.Complete();
+ return;
+ }
dataEntityToken = data.GetDataEntityToken();
@@ -49,11 +55,11 @@ protected override void Execute()
DataFacade.Update(data);
- Log.LogVerbose(LogTitle, "Scheduled publishing of data with label '{0}' is complete", data.GetLabel());
+ Log.LogVerbose(LogTitle, $"Scheduled publishing of data with label '{data.GetLabel()}' is complete");
}
else
{
- Log.LogWarning(LogTitle, "Scheduled publishing of data with label '{0}' could not be done because the data is not in a publisheble state", data.GetLabel());
+ Log.LogWarning(LogTitle, $"Scheduled publishing of data with label '{data.GetLabel()}' could not be done because the data is not in a publisheble state");
}
transaction.Complete();
diff --git a/Composite.Workflows/C1Console/Scheduling/DataUnpublishSchedulerWorkflow.cs b/Composite.Workflows/C1Console/Scheduling/DataUnpublishSchedulerWorkflow.cs
index d9be03a759..1dedbee6e5 100644
--- a/Composite.Workflows/C1Console/Scheduling/DataUnpublishSchedulerWorkflow.cs
+++ b/Composite.Workflows/C1Console/Scheduling/DataUnpublishSchedulerWorkflow.cs
@@ -37,10 +37,16 @@ protected override void Execute()
DataFacade.Delete(unpublishSchedule);
- var deletePublished = false;
+ var data = (IPublishControlled)DataFacade.TryGetDataByUniqueKey(type, DataId);
+ if (data == null)
+ {
+ Log.LogWarning(LogTitle, $"Failed to find data of type '{type}' by id '{DataId}'.");
- var data = (IPublishControlled)DataFacade.GetDataByUniqueKey(type, DataId);
- Verify.IsNotNull(data, "The data with the id {0} does not exist", DataId);
+ transaction.Complete();
+ return;
+ }
+
+ var deletePublished = false;
dataEntityToken = data.GetDataEntityToken();
@@ -56,7 +62,7 @@ protected override void Execute()
}
else
{
- Log.LogWarning(LogTitle, "Scheduled unpublishing of data with label '{0}' could not be done because the data is not in a unpublisheble state", data.GetLabel());
+ Log.LogWarning(LogTitle, $"Scheduled unpublishing of data with label '{data.GetLabel()}' could not be done because the data is not in a unpublisheble state");
}
@@ -69,7 +75,7 @@ protected override void Execute()
{
DataFacade.Delete(deletedData, CascadeDeleteType.Disable);
- Log.LogVerbose(LogTitle, "Scheduled unpublishing of data with label '{0}' is complete", deletedData.GetLabel());
+ Log.LogVerbose(LogTitle, $"Scheduled unpublishing of data with label '{deletedData.GetLabel()}' is complete");
}
}
}
diff --git a/Composite.Workflows/Composite.Workflows.csproj b/Composite.Workflows/Composite.Workflows.csproj
index ccb14bf104..178935dd0e 100644
--- a/Composite.Workflows/Composite.Workflows.csproj
+++ b/Composite.Workflows/Composite.Workflows.csproj
@@ -11,7 +11,7 @@
Composite
Composite.Workflows
{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- v4.6.1
+ v4.7.1
512
SAK
SAK
@@ -965,6 +965,7 @@
ChangeOwnPasswordWorkflow.cs
+
@@ -1362,11 +1363,26 @@
+
+
+ $(ProjectDir)git_branch.txt
+ $(ProjectDir)git_commithash.txt
+
+
+
+
+ $([System.IO.File]::ReadAllText("$(GitBranchFile)").Trim())
+ $([System.IO.File]::ReadAllText("$(GitCommitHashFile)").Trim())
+ [assembly: System.Reflection.AssemblyInformationalVersion("$(GitBranch). Commit Hash: $(GitCommitHash)")]
+
+
+
+
+
+
copy "$(TargetPath)" "$(ProjectDir)..\bin\"
copy "$(TargetPath)" "$(ProjectDir)..\Website\bin\"
diff --git a/Composite/C1Console/Security/Foundation/PluginFacades/LoginSessionStorePluginFacade.cs b/Composite/C1Console/Security/Foundation/PluginFacades/LoginSessionStorePluginFacade.cs
index e0143d3290..32bc26763d 100644
--- a/Composite/C1Console/Security/Foundation/PluginFacades/LoginSessionStorePluginFacade.cs
+++ b/Composite/C1Console/Security/Foundation/PluginFacades/LoginSessionStorePluginFacade.cs
@@ -26,8 +26,7 @@ static LoginSessionStorePluginFacade()
public static bool HasConfiguration()
{
- return (ConfigurationServices.ConfigurationSource != null) &&
- (ConfigurationServices.ConfigurationSource.GetSection(LoginSessionStoreSettings.SectionName) != null);
+ return ConfigurationServices.ConfigurationSource?.GetSection(LoginSessionStoreSettings.SectionName) != null;
}
@@ -53,26 +52,23 @@ public static void StoreUsername(string userName, bool persistAcrossSessions)
}
- public static void FlushUsername()
+ public static string Logout()
{
using (_resourceLocker.ReadLocker)
{
- _resourceLocker.Resources.Provider.FlushUsername();
- }
- }
+ var provider = _resourceLocker.Resources.Provider;
+ provider.FlushUsername();
-
- public static string StoredUsername
- {
- get
- {
- return _resourceLocker.Resources.Provider.StoredUsername;
+ return (provider as ILoginSessionStoreRedirectedLogout)?.LogoutUrl;
}
}
+ public static string StoredUsername => _resourceLocker.Resources.Provider.StoredUsername;
+
+
public static IPAddress UserIpAddress
{
get
@@ -97,15 +93,15 @@ private static void HandleConfigurationError(Exception ex)
{
Flush();
- throw new ConfigurationErrorsException(string.Format("Failed to load the configuration section '{0}' from the configuration.", LoginSessionStoreSettings.SectionName), ex);
+ throw new ConfigurationErrorsException($"Failed to load the configuration section '{LoginSessionStoreSettings.SectionName}' from the configuration.", ex);
}
private sealed class Resources
{
- public LoginSessionStoreFactory Factory { get; set; }
- public ILoginSessionStore Provider { get; set; }
+ private LoginSessionStoreFactory Factory { get; set; }
+ public ILoginSessionStore Provider { get; private set; }
public static void DoInitializeResources(Resources resources)
{
@@ -127,7 +123,7 @@ public static void DoInitializeResources(Resources resources)
}
else if (RuntimeInformation.IsUnittest)
{
- // This is a fall bakc for unittests
+ // This is a fallback for unittests
resources.Provider = new BuildinLoginSessionStore();
}
}
diff --git a/Composite/C1Console/Security/Plugins/LoginSessionStore/ILoginSessionStoreRedirectedLogout.cs b/Composite/C1Console/Security/Plugins/LoginSessionStore/ILoginSessionStoreRedirectedLogout.cs
new file mode 100644
index 0000000000..35fb52f3d5
--- /dev/null
+++ b/Composite/C1Console/Security/Plugins/LoginSessionStore/ILoginSessionStoreRedirectedLogout.cs
@@ -0,0 +1,11 @@
+namespace Composite.C1Console.Security.Plugins.LoginSessionStore
+{
+ ///
+ /// Allows specifying a logout URL.
+ ///
+ public interface ILoginSessionStoreRedirectedLogout
+ {
+ ///
+ string LogoutUrl { get; }
+ }
+}
diff --git a/Composite/C1Console/Security/Plugins/LoginSessionStore/Runtime/LoginSessionStoreResolver.cs b/Composite/C1Console/Security/Plugins/LoginSessionStore/Runtime/LoginSessionStoreResolver.cs
index eecaafb1a4..195863827b 100644
--- a/Composite/C1Console/Security/Plugins/LoginSessionStore/Runtime/LoginSessionStoreResolver.cs
+++ b/Composite/C1Console/Security/Plugins/LoginSessionStore/Runtime/LoginSessionStoreResolver.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@@ -6,7 +6,7 @@
namespace Composite.C1Console.Security.Plugins.LoginSessionStore.Runtime
{
- internal class LoginSessionStoreResolver : ILoginSessionStore
+ internal class LoginSessionStoreResolver : ILoginSessionStore, ILoginSessionStoreRedirectedLogout
{
private readonly IEnumerable _loginSessionStores;
@@ -36,5 +36,10 @@ public void FlushUsername()
}
public IPAddress UserIpAddress => PreferredLoginSessionStore()?.UserIpAddress;
+
+ public string LogoutUrl => _loginSessionStores
+ .OfType()
+ .Select(_ => _.LogoutUrl)
+ .FirstOrDefault(url => !string.IsNullOrEmpty(url));
}
}
\ No newline at end of file
diff --git a/Composite/C1Console/Security/UserValidationFacade.cs b/Composite/C1Console/Security/UserValidationFacade.cs
index 158f2e07f5..8f2e89d58c 100644
--- a/Composite/C1Console/Security/UserValidationFacade.cs
+++ b/Composite/C1Console/Security/UserValidationFacade.cs
@@ -1,6 +1,5 @@
using System;
using System.Web;
-using Composite.Core.Extensions;
using Composite.Core.Logging;
using Composite.C1Console.Security.Foundation.PluginFacades;
using Composite.C1Console.Security.Plugins.LoginProvider;
@@ -54,29 +53,17 @@ public static ValidationType GetValidationType()
return ValidationType.Windows;
}
- throw new InvalidOperationException(string.Format("Validation plugin '{0}' does not implement a known validation interface", LoginProviderPluginFacade.GetValidationPluginType()));
+ throw new InvalidOperationException($"Validation plugin '{LoginProviderPluginFacade.GetValidationPluginType()}' does not implement a known validation interface");
}
///
- public static IEnumerable AllUsernames
- {
- get
- {
- return LoginProviderPluginFacade.AllUsernames;
- }
- }
+ public static IEnumerable AllUsernames => LoginProviderPluginFacade.AllUsernames;
///
- public static bool CanSetUserPassword
- {
- get
- {
- return LoginProviderPluginFacade.CanSetUserPassword;
- }
- }
+ public static bool CanSetUserPassword => LoginProviderPluginFacade.CanSetUserPassword;
///
@@ -106,7 +93,7 @@ public static LoginResult FormValidateUser(string userName, string password)
if (loginResult == LoginResult.Success)
{
- LoggingService.LogVerbose("UserValidation", String.Format("The user: [{0}], has been validated and accepted. {1}", userName, GetIpInformation()), LoggingService.Category.Audit);
+ LoggingService.LogVerbose("UserValidation", $"The user: [{userName}], has been validated and accepted. {GetIpInformation()}", LoggingService.Category.Audit);
PersistUsernameInSessionDataProvider(userName);
}
else if (LoginResultDescriptions.ContainsKey(loginResult))
@@ -120,7 +107,7 @@ public static LoginResult FormValidateUser(string userName, string password)
private static void LogLoginFailed(string userName, string message)
{
LoggingService.LogWarning("UserValidation",
- "Login as [{0}] failed. {1} {2}".FormatWith(userName, message, GetIpInformation()),
+ $"Login as [{userName}] failed. {message} {GetIpInformation()}",
LoggingService.Category.Audit);
}
@@ -145,7 +132,7 @@ private static string GetIpInformation()
return string.Empty;
}
- return " IP address: " + ipaddress;
+ return "IP address: " + ipaddress;
}
@@ -185,20 +172,21 @@ public static bool WindowsValidateUser(string userName, string domainName)
///
- /// Flushes the username from the login session
+ /// Flushes the username from the login session.
///
- public static void Logout()
- {
- LoginSessionStorePluginFacade.FlushUsername();
- }
+ [Obsolete("Use Logout() instead.")]
+ public static void FlushUsername() => Logout();
+
+
+ ///
+ /// Flushes the username from the login session (if applicable) and returns a logout URL.
+ ///
+ public static string Logout() => LoginSessionStorePluginFacade.Logout();
///
- public static bool IsLoggedIn()
- {
- return !string.IsNullOrEmpty(LoginSessionStorePluginFacade.StoredUsername);
- }
+ public static bool IsLoggedIn() => !string.IsNullOrEmpty(LoginSessionStorePluginFacade.StoredUsername);
diff --git a/Composite/Composite.csproj b/Composite/Composite.csproj
index 357a785b4c..385e2e52ca 100644
--- a/Composite/Composite.csproj
+++ b/Composite/Composite.csproj
@@ -11,7 +11,7 @@
Composite
{14822709-B5A1-4724-98CA-57A101D1B079};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
4
- v4.6.1
+ v4.7.1
SAK
SAK
SAK
@@ -125,14 +125,6 @@
-
- ..\packages\System.Net.WebSockets.4.0.0\lib\net46\System.Net.WebSockets.dll
- True
-
-
- ..\packages\System.Net.WebSockets.Client.4.0.0\lib\net46\System.Net.WebSockets.Client.dll
- True
-
..\packages\System.Reactive.Core.3.0.0\lib\net46\System.Reactive.Core.dll
True
@@ -163,10 +155,6 @@
3.5
-
- False
- ..\Packages\System.ValueTuple.4.3.0\lib\portable-net40+sl4+win8+wp8\System.ValueTuple.dll
-
..\packages\Microsoft.AspNet.Razor.3.2.3\lib\net45\System.Web.Razor.dll
@@ -245,11 +233,13 @@
+
+
@@ -263,6 +253,7 @@
+
@@ -2677,11 +2668,26 @@
+
+
+ $(ProjectDir)git_branch.txt
+ $(ProjectDir)git_commithash.txt
+
+
+
+
+ $([System.IO.File]::ReadAllText("$(GitBranchFile)").Trim())
+ $([System.IO.File]::ReadAllText("$(GitCommitHashFile)").Trim())
+ [assembly: System.Reflection.AssemblyInformationalVersion("$(GitBranch). Commit Hash: $(GitCommitHash)")]
+
+
+
+
+
+
copy "$(TargetPath)" "$(ProjectDir)..\bin\"
copy "$(TargetPath)" "$(ProjectDir)..\Website\bin\"
diff --git a/Composite/Core/EmptyDisposable.cs b/Composite/Core/EmptyDisposable.cs
new file mode 100644
index 0000000000..d1af318d77
--- /dev/null
+++ b/Composite/Core/EmptyDisposable.cs
@@ -0,0 +1,13 @@
+using System;
+
+namespace Composite.Core
+{
+ internal class EmptyDisposable : IDisposable
+ {
+ public static readonly EmptyDisposable Instance = new EmptyDisposable();
+
+ public void Dispose()
+ {
+ }
+ }
+}
diff --git a/Composite/Core/IO/Zip/ZipFileSystem.cs b/Composite/Core/IO/Zip/ZipFileSystem.cs
index b3086abfb5..5693138c1d 100644
--- a/Composite/Core/IO/Zip/ZipFileSystem.cs
+++ b/Composite/Core/IO/Zip/ZipFileSystem.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
@@ -11,7 +11,11 @@ internal sealed class ZipFileSystem : IZipFileSystem
private const int CopyBufferSize = 4096;
private readonly HashSet _entryNames = new HashSet();
- private string ZipFilename { get; set; }
+ private readonly Dictionary _denormalizedEntryNames = new Dictionary();
+
+ private string ZipFilename { get; }
+
+ private static string NormalizePathDelimiters(string path) => path.Replace('\\', '/');
public ZipFileSystem(string zipFilename)
{
@@ -26,7 +30,7 @@ public ZipFileSystem(string zipFilename)
public bool ContainsFile(string filename)
{
- filename = filename.Replace('\\', '/');
+ filename = NormalizePathDelimiters(filename);
return GetFilenames().Any(f => f.Equals(filename, StringComparison.OrdinalIgnoreCase));
}
@@ -35,7 +39,7 @@ public bool ContainsFile(string filename)
public bool ContainsDirectory(string directoryName)
{
- directoryName = directoryName.Replace('\\', '/');
+ directoryName = NormalizePathDelimiters(directoryName);
return GetDirectoryNames().Any(f => f.Equals(directoryName, StringComparison.OrdinalIgnoreCase));
}
@@ -54,7 +58,7 @@ public IEnumerable GetFilenames()
public IEnumerable GetFilenames(string directoryName)
{
- directoryName = directoryName.Replace('\\', '/');
+ directoryName = NormalizePathDelimiters(directoryName);
foreach (var filename in GetFilenames())
{
@@ -109,14 +113,20 @@ public Stream GetFileStream(string filename)
var zipArchive = new ZipArchive(C1File.Open(ZipFilename, FileMode.Open, FileAccess.Read));
- var entryPath = filename.Substring(2).Replace('\\', '/');
+
+ var normalizedEntryPath = NormalizePathDelimiters(filename.Substring(2));
+
+ if(!_denormalizedEntryNames.TryGetValue(normalizedEntryPath, out string entryPath))
+ {
+ throw new InvalidOperationException($"Entry '{normalizedEntryPath}' not found");
+ }
var entry = zipArchive.GetEntry(entryPath);
if (entry == null)
{
zipArchive.Dispose();
- throw new InvalidOperationException($"Entry '{entryPath}' not found");
+ throw new InvalidOperationException($"Failed to extract entry '{entryPath}' from zip archive");
}
return new StreamWrapper(entry.Open(), () => zipArchive.Dispose());
@@ -162,7 +172,10 @@ private void Initialize()
{
foreach (var entry in zipArchive.Entries)
{
- _entryNames.Add(entry.FullName);
+ var normalizedEntryPath = NormalizePathDelimiters(entry.FullName);
+ _denormalizedEntryNames[normalizedEntryPath] = entry.FullName;
+
+ _entryNames.Add(normalizedEntryPath);
}
}
}
@@ -177,17 +190,14 @@ private static string ParseFilename(string filename)
throw new ArgumentException("filename should start with a '~/' or '~\\'");
}
- filename = filename.Remove(0, 1);
- filename = filename.Replace('\\', '/');
+ filename = NormalizePathDelimiters(filename.Substring(1));
if (!filename.StartsWith("/"))
{
throw new ArgumentException("filename should start with a '~/' or '~\\'");
}
- filename = filename.Remove(0, 1);
-
- return filename;
+ return filename.Substring(1);
}
private class StreamWrapper : Stream, IDisposable
diff --git a/Composite/Core/Implementation/DataConnectionImplementation.cs b/Composite/Core/Implementation/DataConnectionImplementation.cs
index 4c0ac58939..254bc32c6d 100644
--- a/Composite/Core/Implementation/DataConnectionImplementation.cs
+++ b/Composite/Core/Implementation/DataConnectionImplementation.cs
@@ -1,6 +1,5 @@
-using System;
+using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.Globalization;
using System.Linq;
using Composite.Core.Threading;
@@ -15,6 +14,7 @@ namespace Composite.Core.Implementation
public class DataConnectionImplementation : DataConnectionBase, IDisposable
{
private IDisposable _threadDataManager;
+ private readonly IDisposable _serviceScope;
private readonly DataScope _dataScope;
internal DataScope DataScope => _dataScope;
@@ -31,6 +31,8 @@ public DataConnectionImplementation(PublicationScope scope, CultureInfo locale)
InitializeScope(scope, locale);
_dataScope = new DataScope(this.DataScopeIdentifier, locale);
+
+ _serviceScope = ServiceLocator.EnsureThreadDataServiceScope();
}
private void InitializeThreadData()
@@ -198,6 +200,8 @@ protected virtual void Dispose(bool disposing)
{
if (disposing)
{
+ _serviceScope?.Dispose();
+
_dataScope.Dispose();
_threadDataManager.Dispose();
diff --git a/Composite/Core/Instrumentation/Profiler.cs b/Composite/Core/Instrumentation/Profiler.cs
index 6883483895..18cbd3f35e 100644
--- a/Composite/Core/Instrumentation/Profiler.cs
+++ b/Composite/Core/Instrumentation/Profiler.cs
@@ -1,4 +1,4 @@
-#define ProfileMemory
+#define ProfileMemory
using System;
using System.Collections.Generic;
@@ -227,18 +227,6 @@ internal static void AddSubMeasurement(Measurement measurement)
}
}
- private static bool Disabled
- {
- get { return false; }
- }
-
- private class EmptyDisposable: IDisposable
- {
- public static readonly EmptyDisposable Instance = new EmptyDisposable();
-
- public void Dispose()
- {
- }
- }
+ private static bool Disabled => false;
}
}
diff --git a/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs b/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs
index c0024d080b..38d299cb77 100644
--- a/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs
+++ b/Composite/Core/PackageSystem/PackageFragmentInstallers/DataPackageFragmentInstaller.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
@@ -32,6 +32,8 @@ public sealed class DataPackageFragmentInstaller : BasePackageFragmentInstaller
private List _validationResult;
private Dictionary _dataKeysToBeInstalled;
+ private Dictionary _dataKeysToBeInstalledByTypeId;
+
private Dictionary>> _missingDataReferences;
private static Dictionary _pageVersionIds = new Dictionary();
@@ -41,6 +43,7 @@ public override IEnumerable Validate()
{
_validationResult = new List();
_dataKeysToBeInstalled = new Dictionary();
+ _dataKeysToBeInstalledByTypeId = new Dictionary();
_missingDataReferences = new Dictionary>>();
if (this.Configuration.Count(f => f.Name == "Types") > 1)
@@ -597,7 +600,7 @@ private void ValidateNonDynamicAddedType(DataType dataType)
}
- RegisterKeyToBeAdded(dataType, dataKeyPropertyCollection);
+ RegisterKeyToBeAdded(dataType, null, dataKeyPropertyCollection);
// Checking foreign key references
foreach (var foreignKeyProperty in DataAttributeFacade.GetDataReferenceProperties(dataType.InterfaceType))
@@ -632,16 +635,24 @@ private void CheckForBrokenReference(DataType refereeType, Type type, string pro
// Checking key in the keys to be installed
var keyValuePair = new KeyValuePair(keyPropertyName, referenceKey);
- if (_missingDataReferences.ContainsKey(referredType) && _missingDataReferences[referredType].Contains(keyValuePair))
+ if (_missingDataReferences.TryGetValue(referredType, out var refs) && refs.Contains(keyValuePair))
{
return;
}
- if (_dataKeysToBeInstalled.ContainsKey(referredType) && _dataKeysToBeInstalled[referredType].KeyRegistered(refereeType, keyValuePair))
+ if (_dataKeysToBeInstalled.TryGetValue(referredType, out var keys)
+ && keys.KeyRegistered(refereeType, keyValuePair))
{
return;
}
+ var typeId = referredType.GetImmutableTypeId();
+ if (_dataKeysToBeInstalledByTypeId.TryGetValue(typeId, out var dynamicTypeKeys)
+ && dynamicTypeKeys.KeyRegistered(refereeType, keyValuePair))
+ {
+ return;
+ }
+
using (GetDataScopeFromDataTypeElement(refereeType))
{
if (DataFacade.TryGetDataByUniqueKey(type, propertyValue) == null)
@@ -705,13 +716,26 @@ private DataScope GetDataScopeFromDataTypeElement(DataType dataType)
return new DataScope(dataType.DataScopeIdentifier, locale);
}
- private void RegisterKeyToBeAdded(DataType dataType, DataKeyPropertyCollection dataKeyPropertyCollection)
+
+
+ private void RegisterKeyToBeAdded(DataType dataType, DataTypeDescriptor dataTypeDescriptor, DataKeyPropertyCollection dataKeyPropertyCollection)
{
if (dataKeyPropertyCollection.Count != 1) return;
+ TypeKeyInstallationData typeKeyInstallationData;
- var typeKeyInstallationData = _dataKeysToBeInstalled.GetOrAdd(dataType.InterfaceType,
+ if (dataType.InterfaceType != null)
+ {
+ // Static types
+ typeKeyInstallationData = _dataKeysToBeInstalled.GetOrAdd(dataType.InterfaceType,
() => new TypeKeyInstallationData(dataType.InterfaceType));
+ }
+ else
+ {
+ // Dynamic types
+ typeKeyInstallationData = _dataKeysToBeInstalledByTypeId.GetOrAdd(dataTypeDescriptor.DataTypeId,
+ () => new TypeKeyInstallationData(dataTypeDescriptor));
+ }
var keyValuePair = dataKeyPropertyCollection.KeyProperties.First();
@@ -791,8 +815,7 @@ private void ValidateDynamicAddedType(DataType dataType)
// TODO: implement check if the same key has already been added
}
- // TODO: to be implemented for dynamic types
- // RegisterKeyToBeAdded(dataType, dataKeyPropertyCollection);
+ RegisterKeyToBeAdded(dataType, dataTypeDescriptor, dataKeyPropertyCollection);
// Checking foreign key references
foreach (var referenceField in dataTypeDescriptor.Fields.Where(f => f.ForeignKeyReferenceTypeName != null))
@@ -800,8 +823,8 @@ private void ValidateDynamicAddedType(DataType dataType)
object propertyValue;
if (!fieldValues.TryGetValue(referenceField.Name, out propertyValue)
|| propertyValue == null
- || (propertyValue is Guid && (Guid)propertyValue == Guid.Empty)
- || propertyValue is string && (string)propertyValue == "")
+ || (propertyValue is Guid guid && guid == Guid.Empty)
+ || (propertyValue is string str && str == ""))
{
continue;
}
@@ -858,21 +881,29 @@ private sealed class DataType
///
/// Information about data keys to be installed for a given data type
///
- [DebuggerDisplay("TypeKeyInstallationData {_type.FullName} . DataScopes: {_dataScopes.Count}")]
+ [DebuggerDisplay("TypeKeyInstallationData {_typeName} . DataScopes: {_dataScopes.Count}")]
private class TypeKeyInstallationData
{
private const string AllLocalesKey = "all";
private readonly bool _isLocalized;
private readonly bool _isPublishable;
- private readonly Type _type;
+ private readonly string _typeName;
+
private readonly Dictionary>> _dataScopes = new Dictionary>>();
public TypeKeyInstallationData(Type type)
{
_isLocalized = DataLocalizationFacade.IsLocalized(type);
_isPublishable = typeof(IPublishControlled).IsAssignableFrom(type);
- _type = type;
+ _typeName = type.FullName;
+ }
+
+ public TypeKeyInstallationData(DataTypeDescriptor typeDescriptor)
+ {
+ _isLocalized = typeDescriptor.SuperInterfaces.Contains(typeof(ILocalizedControlled));
+ _isPublishable = typeDescriptor.SuperInterfaces.Contains(typeof(IPublishControlled));
+ _typeName = typeDescriptor.Name;
}
public void RegisterKeyUsage(DataType dataType, KeyValuePair keyValuePair)
@@ -910,15 +941,10 @@ private void RegisterKeyUsage(DataType dataType, string localeName, DataScopeIde
{
string dataScopeKey = GetDataScopeKey(publicationScope, localeName);
- if (!_dataScopes.ContainsKey(dataScopeKey))
- {
- _dataScopes.Add(dataScopeKey, new HashSet>());
- }
-
- var hashset = _dataScopes[dataScopeKey];
+ var hashset = _dataScopes.GetOrAdd(dataScopeKey, () => new HashSet>());
Verify.That(!hashset.Contains(keyValuePair), "Item with the same key present twice. Data type: '{0}', field '{1}', value '{2}'",
- dataType.InterfaceTypeName ?? "null", keyValuePair.Key, keyValuePair.Value ?? "null");
+ dataType.InterfaceTypeName ?? dataType.InterfaceTypeName ?? "null", keyValuePair.Key, keyValuePair.Value ?? "null");
hashset.Add(keyValuePair);
}
diff --git a/Composite/Core/ServiceLocator.cs b/Composite/Core/ServiceLocator.cs
index 871ef6848d..654d3a6e47 100644
--- a/Composite/Core/ServiceLocator.cs
+++ b/Composite/Core/ServiceLocator.cs
@@ -1,10 +1,11 @@
-using System;
+using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Configuration;
using Composite.Core.Configuration;
+using Composite.Core.Threading;
using Microsoft.Extensions.DependencyInjection;
namespace Composite.Core
@@ -20,6 +21,7 @@ namespace Composite.Core
public static class ServiceLocator
{
private const string HttpContextKey = "HttpApplication.ServiceScope";
+ private const string ThreadDataKey = "HttpApplication.ServiceScope";
private static IServiceCollection _serviceCollection = new ServiceCollection();
private static IServiceProvider _serviceProvider;
private static ConcurrentDictionary _hasTypeLookup = new ConcurrentDictionary();
@@ -213,9 +215,23 @@ internal static void DisposeRequestServicesScope(HttpContext context)
if (scope != null)
{
scope.Dispose();
+ context.Items.Remove(HttpContextKey);
}
}
+ internal static IDisposable EnsureThreadDataServiceScope()
+ {
+ if (RequestScopedServiceProvider != null) return EmptyDisposable.Instance;
+
+ var current = ThreadDataManager.GetCurrentNotNull();
+
+ var serviceScopeFactory = (IServiceScopeFactory)_serviceProvider.GetService(typeof(IServiceScopeFactory));
+ var serviceScope = serviceScopeFactory.CreateScope();
+
+ current.SetValue(ThreadDataKey, serviceScope);
+
+ return new ThreadDataServiceScopeDisposable(current);
+ }
///
/// Return a IServiceScope - if a scope has been initialized on the request (HttpContext) a scoped provider is returned.
@@ -225,11 +241,39 @@ private static IServiceProvider RequestScopedServiceProvider
get
{
var context = HttpContext.Current;
- if (context == null) return null;
+ if (context != null)
+ {
+ var scope = (IServiceScope)context.Items[HttpContextKey];
- var scope = (IServiceScope)context.Items[HttpContextKey];
+ return scope?.ServiceProvider;
+ }
+
+ var threadData = ThreadDataManager.Current;
+ if (threadData != null)
+ {
+ var scope = (IServiceScope) threadData[ThreadDataKey];
+ return scope?.ServiceProvider;
+ }
+
+ return null;
+ }
+ }
+
+ private class ThreadDataServiceScopeDisposable : IDisposable
+ {
+ private readonly ThreadDataManagerData _threadData;
+
+ public ThreadDataServiceScopeDisposable(ThreadDataManagerData threadData)
+ {
+ _threadData = threadData;
+ }
+
+ public void Dispose()
+ {
+ var scope = (IServiceScope)_threadData[ThreadDataKey];
- return scope != null ? scope.ServiceProvider : null;
+ scope?.Dispose();
+ _threadData.SetValue(ThreadDataKey, null);
}
}
diff --git a/Composite/Core/Threading/ThreadDataManager.cs b/Composite/Core/Threading/ThreadDataManager.cs
index ae2c530a4c..b338e75cba 100644
--- a/Composite/Core/Threading/ThreadDataManager.cs
+++ b/Composite/Core/Threading/ThreadDataManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Web;
using Composite.C1Console.Security.Foundation.PluginFacades;
@@ -132,7 +132,7 @@ public static IDisposable Initialize(ThreadDataManagerData parentThreadData)
/// An scope
public static IDisposable EnsureInitialize()
{
- if (Current != null) return new EmptyDisposableObj();
+ if (Current != null) return EmptyDisposable.Instance;
return Initialize();
}
@@ -207,15 +207,6 @@ public static void FinalizeThroughHttpContext()
}
- private sealed class EmptyDisposableObj : IDisposable
- {
- public void Dispose()
- {
- // Do nothing here...
- }
-
- }
-
private sealed class ThreadDataManagerScope : IDisposable
{
private bool _disposed;
diff --git a/Composite/Core/WebClient/Captcha/CaptchaConfiguration.cs b/Composite/Core/WebClient/Captcha/CaptchaConfiguration.cs
index ac630c9367..6bdf07258b 100644
--- a/Composite/Core/WebClient/Captcha/CaptchaConfiguration.cs
+++ b/Composite/Core/WebClient/Captcha/CaptchaConfiguration.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.IO;
using System.Xml;
using Composite.Core.Extensions;
@@ -9,59 +9,54 @@ namespace Composite.Core.WebClient.Captcha
internal static class CaptchaConfiguration
{
private static readonly string CaptchaConfigurationFilePath = @"App_Data\Composite\Configuration\Captcha.xml";
- private static readonly object _syncRoot = new object();
- private static string _password;
+ public static string Password { get; }
- public static string Password
+ static CaptchaConfiguration()
{
- get
- {
- if (_password != null) return _password;
-
- lock (_syncRoot)
- {
- if (_password != null) return _password;
+ string configurationFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CaptchaConfigurationFilePath);
- string configurationFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, CaptchaConfigurationFilePath);
+ string password = null;
- if (C1File.Exists(configurationFilePath))
+ if (C1File.Exists(configurationFilePath))
+ {
+ var doc = new XmlDocument();
+ try
+ {
+ using (var sr = new C1StreamReader(configurationFilePath))
{
- var doc = new XmlDocument();
- try
- {
- using (var sr = new C1StreamReader(configurationFilePath))
- {
- doc.Load(sr);
- }
+ doc.Load(sr);
+ }
- var passwordNode = doc.SelectSingleNode("captcha/password");
- if (passwordNode != null && !string.IsNullOrEmpty(passwordNode.InnerText))
- {
- _password = passwordNode.InnerText;
- }
- }
- catch (Exception)
- {
- // Do nothing
- }
+ var passwordNode = doc.SelectSingleNode("captcha/password");
+ if (!string.IsNullOrEmpty(passwordNode?.InnerText))
+ {
+ password = passwordNode.InnerText;
+ }
+ }
+ catch (Exception)
+ {
+ // Do nothing
+ }
- if (_password != null) return _password;
+ if (password != null)
+ {
+ Password = password;
+ return;
+ }
- // Deleting configuration file
- C1File.Delete(configurationFilePath);
- }
+ // Deleting configuration file
+ C1File.Delete(configurationFilePath);
+ }
- _password = Guid.NewGuid().ToString();
+ password = Guid.NewGuid().ToString();
- string configFile = @" {0} ".FormatWith(_password);
+ string configFile = @" {0} ".FormatWith(password);
- C1File.WriteAllText(configurationFilePath, configFile);
+ C1File.WriteAllText(configurationFilePath, configFile);
- return _password;
- }
- }
+ Password = password;
}
}
}
diff --git a/Composite/Core/WebClient/Captcha/Encryption.cs b/Composite/Core/WebClient/Captcha/Encryption.cs
index dc0eded538..d725a15898 100644
--- a/Composite/Core/WebClient/Captcha/Encryption.cs
+++ b/Composite/Core/WebClient/Captcha/Encryption.cs
@@ -1,9 +1,9 @@
-using System;
+using System;
using System.Globalization;
using System.IO;
using System.Security.Cryptography;
using System.Text;
-using System.Web.Hosting;
+using Composite.Core.Configuration;
using Composite.Core.IO;
@@ -16,19 +16,25 @@ internal static class Encryption
static Encryption()
{
- var md5 = MD5.Create();
-
- string key = Environment.MachineName + CaptchaConfiguration.Password + HostingEnvironment.ApplicationPhysicalPath;
+ string key = InstallationInformationFacade.InstallationId + CaptchaConfiguration.Password;
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
- _encryptionKey = md5.ComputeHash(keyBytes);
+ using (var hashAlgorithm = MD5.Create())
+ {
+ _encryptionKey = hashAlgorithm.ComputeHash(keyBytes);
+ }
}
public static string Encrypt(string value)
{
Verify.ArgumentNotNullOrEmpty(value, nameof(value));
+ return ByteToHexString(RijndaelEncrypt(value));
+ }
+
+ private static byte[] RijndaelEncrypt(string value)
+ {
// Create a RijndaelManaged object
// with the specified key and IV.
using (var rima = new RijndaelManaged())
@@ -49,7 +55,7 @@ public static string Encrypt(string value)
swEncrypt.Write(value);
}
// Return the encrypted bytes from the memory stream.
- return ByteToHexString(msEncrypt.ToArray());
+ return msEncrypt.ToArray();
}
}
}
@@ -59,6 +65,11 @@ public static string Decrypt(string encryptedValue)
Verify.ArgumentNotNullOrEmpty(encryptedValue, nameof(encryptedValue));
byte[] encodedSequence = HexStringToByteArray(encryptedValue);
+ return RijndaelDecrypt(encodedSequence);
+ }
+
+ private static string RijndaelDecrypt(byte[] bytes)
+ {
using (var rima = new RijndaelManaged())
{
rima.Key = _encryptionKey;
@@ -68,7 +79,7 @@ public static string Decrypt(string encryptedValue)
ICryptoTransform decryptor = rima.CreateDecryptor();
// Create the streams used for decryption.
- using (var msDecrypt = new MemoryStream(encodedSequence))
+ using (var msDecrypt = new MemoryStream(bytes))
using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
using (var srDecrypt = new C1StreamReader(csDecrypt))
{
diff --git a/Composite/Core/WebClient/Renderings/Page/IPageContentFilter.cs b/Composite/Core/WebClient/Renderings/Page/IPageContentFilter.cs
index c919553581..91f9a6d7fa 100644
--- a/Composite/Core/WebClient/Renderings/Page/IPageContentFilter.cs
+++ b/Composite/Core/WebClient/Renderings/Page/IPageContentFilter.cs
@@ -17,7 +17,7 @@ public interface IPageContentFilter
void Filter(XhtmlDocument document, IPage page);
///
- /// Gets the execution order. Filters wil lower values will be executed first.
+ /// Gets the execution order. Filters with lower values will be executed first.
///
int Order { get; }
}
diff --git a/Composite/Data/PageManager.cs b/Composite/Data/PageManager.cs
index 30d81e121f..32f2565d45 100644
--- a/Composite/Data/PageManager.cs
+++ b/Composite/Data/PageManager.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
@@ -197,21 +197,24 @@ public static ReadOnlyCollection GetPlaceholderContent(
public static ReadOnlyCollection GetPlaceholderContent(Guid pageId, Guid versionId)
{
string cacheKey = GetCacheKey(pageId, versionId);
- var cachedValue = _placeholderCache.Get(cacheKey);
- if (cachedValue != null)
+ var result = _placeholderCache.Get(cacheKey);
+
+ if (result == null)
{
- return cachedValue;
- }
+ using (var conn = new DataConnection())
+ {
+ conn.DisableServices();
- var list = DataFacade.GetData(false)
- .Where(f => f.PageId == pageId
- && f.VersionId == versionId).ToList();
+ var list = DataFacade.GetData(false)
+ .Where(f => f.PageId == pageId && f.VersionId == versionId).ToList();
- var readonlyList = new ReadOnlyCollection(list);
+ result = new ReadOnlyCollection(list);
+ }
- _placeholderCache.Add(cacheKey, readonlyList);
+ _placeholderCache.Add(cacheKey, result);
+ }
- return readonlyList;
+ return result;
}
#endregion Public
diff --git a/Composite/Properties/SharedAssemblyInfo.cs b/Composite/Properties/SharedAssemblyInfo.cs
index d6d0f99ded..10265634b6 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.3")]
+[assembly: AssemblyTitle("C1 CMS 6.4")]
#else
-[assembly: AssemblyTitle("C1 CMS 6.3 (Internal Build)")]
+[assembly: AssemblyTitle("C1 CMS 6.4 (Internal Build)")]
#endif
[assembly: AssemblyCompany("Orckestra Inc")]
@@ -13,4 +13,4 @@
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
-[assembly: AssemblyVersion("6.3.*")]
+[assembly: AssemblyVersion("6.4.*")]
diff --git a/Composite/Search/DocumentSources/CmsPageDocumentSource.cs b/Composite/Search/DocumentSources/CmsPageDocumentSource.cs
index a918ecab7d..e38a3ca404 100644
--- a/Composite/Search/DocumentSources/CmsPageDocumentSource.cs
+++ b/Composite/Search/DocumentSources/CmsPageDocumentSource.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -49,7 +49,8 @@ public CmsPageDocumentSource(IEnumerable extens
var entityToken = GetAdministratedEntityToken(page);
return entityToken != null ? FromPage(page, entityToken, null) : null;
},
- data => GetDocumentId((IPage) data));
+ data => GetDocumentId((IPage) data),
+ PageShouldBeIndexed);
_changesIndexNotifier.Start();
}
@@ -64,12 +65,14 @@ public void Subscribe(IDocumentSourceListener sourceListener)
public IEnumerable GetSearchDocuments(CultureInfo culture, string continuationToken = null)
{
ICollection unpublishedPages;
+ IDictionary parentPageIDs;
var (lastPageId, lastPagesPublicationScope) = ParseContinuationToken(continuationToken);
using (var conn = new DataConnection(PublicationScope.Unpublished, culture))
{
unpublishedPages = conn.Get().Evaluate();
+ parentPageIDs = conn.Get().ToDictionary(ps => ps.Id, ps => ps.ParentId);
}
unpublishedPages = unpublishedPages
@@ -78,9 +81,12 @@ public IEnumerable GetSearchDocuments(CultureInfo
.ToList();
var publishedPages = new Dictionary, IPage>();
+ HashSet publishedPageIds;
+
using (var conn = new DataConnection(PublicationScope.Published, culture))
{
publishedPages = conn.Get().ToDictionary(page => new Tuple(page.Id, page.VersionId));
+ publishedPageIds = new HashSet(publishedPages.Select(p => p.Key.Item1));
}
var unpublishedMetaData = GetAllMetaData(PublicationScope.Unpublished, culture);
@@ -89,12 +95,13 @@ public IEnumerable GetSearchDocuments(CultureInfo
foreach (var unpublishedPage in unpublishedPages)
{
+ Guid pageId = unpublishedPage.Id;
var entityToken = unpublishedPage.GetDataEntityToken();
- IPage publishedPage;
- if (unpublishedPage.Id.CompareTo(lastPageId) > 0
- && publishedPages.TryGetValue(new Tuple(unpublishedPage.Id, unpublishedPage.VersionId),
- out publishedPage))
+ if (pageId.CompareTo(lastPageId) > 0
+ && publishedPages.TryGetValue(new Tuple(pageId, unpublishedPage.VersionId),
+ out IPage publishedPage)
+ && AllAncestorPagesArePublished(pageId, publishedPageIds, parentPageIDs))
{
yield return new DocumentWithContinuationToken
{
@@ -109,7 +116,7 @@ public IEnumerable GetSearchDocuments(CultureInfo
}
}
- if (unpublishedPage.Id.CompareTo(lastPageId) > 0
+ if (pageId.CompareTo(lastPageId) > 0
|| lastPagesPublicationScope == PublicationScope.Published)
{
yield return new DocumentWithContinuationToken
@@ -121,7 +128,7 @@ public IEnumerable GetSearchDocuments(CultureInfo
}
}
- private (Guid lastPage , PublicationScope publicationScope) ParseContinuationToken(string continuationToken)
+ private (Guid lastPage, PublicationScope publicationScope) ParseContinuationToken(string continuationToken)
{
if (continuationToken == null)
{
@@ -262,5 +269,82 @@ private List GetMetaData(Guid pageId, Guid versionId, PublicationScope pu
return result;
}
+
+ private bool AllAncestorPagesArePublished(Guid pageId, HashSet publishedPageIds,
+ IDictionary parentPageIDs)
+ {
+ int depth = 100;
+
+ while (depth > 0)
+ {
+ if (!parentPageIDs.TryGetValue(pageId, out Guid parentPageID))
+ {
+ // The the page is unreachable from the tree, no need to index it
+ return false;
+ }
+
+ if (parentPageID == Guid.Empty)
+ {
+ return true;
+ }
+
+ if (!publishedPageIds.Contains(parentPageID))
+ {
+ return false;
+ }
+
+ pageId = parentPageID;
+ depth--;
+ }
+
+ Log.LogError(nameof(CmsPageDocumentSource), $"There's a loop in page hierarchy. Page ID: '{pageId}'");
+ return false;
+ }
+
+ private bool AllAncestorPagesArePublished(Guid pageId, CultureInfo locale)
+ {
+ using (var dc = new DataConnection(PublicationScope.Published, locale))
+ {
+ dc.DisableServices();
+
+ int depth = 100;
+
+ while (depth > 0)
+ {
+ var parentPageId = PageManager.GetParentId(pageId);
+ if (parentPageId == Guid.Empty) return true;
+
+ var parentPage = PageManager.GetPageById(parentPageId, true);
+ if (parentPage == null)
+ {
+ return false;
+ }
+
+ pageId = parentPageId;
+ depth--;
+ }
+ }
+
+ Log.LogError(nameof(CmsPageDocumentSource), $"There's a loop in page hierarchy. Page ID: '{pageId}'");
+ return false;
+ }
+
+ private bool PageShouldBeIndexed(IData data)
+ {
+ if (!(data is IPage page))
+ {
+ return true;
+ }
+
+ if (data.DataSourceId.PublicationScope == PublicationScope.Published)
+ {
+ return AllAncestorPagesArePublished(page.Id, page.DataSourceId.LocaleScope);
+ }
+
+ // Indexing the unpublished version fo the page only if the page is not in the "published" state,
+ // or the published version isn't indexed
+ return page.PublicationStatus != GenericPublishProcessController.Published
+ || !AllAncestorPagesArePublished(page.Id, page.DataSourceId.LocaleScope);
+ }
}
}
diff --git a/Composite/Search/DocumentSources/DataChangesIndexNotifier.cs b/Composite/Search/DocumentSources/DataChangesIndexNotifier.cs
index ad8ed622cf..6f79096ab6 100644
--- a/Composite/Search/DocumentSources/DataChangesIndexNotifier.cs
+++ b/Composite/Search/DocumentSources/DataChangesIndexNotifier.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -19,31 +19,34 @@ internal class DataChangesIndexNotifier
private Func GetDocument { get; }
private Func GetDocumentId { get; }
+ private Predicate DataItemShouldBeIndexed { get; }
public DataChangesIndexNotifier(
IEnumerable listeners,
Type interfaceType,
Func getDocumentFunc,
- Func getDocumentIdFunc)
+ Func getDocumentIdFunc,
+ Predicate dataItemShouldBeIndexed = null)
{
_listeners = listeners;
_interfaceType = interfaceType;
GetDocument = getDocumentFunc;
GetDocumentId = getDocumentIdFunc;
+ DataItemShouldBeIndexed = dataItemShouldBeIndexed ?? (_ => !IsPublishedDataFromUnpublishedScope(_));
}
public void Start()
{
DataEventSystemFacade.SubscribeToDataAfterAdd(_interfaceType,
- (sender, args) => GetActionContainer().Add(() => Data_OnAfterAdd(sender, args)),
+ (sender, args) => CatchAll(() => GetActionContainer().Add(() => Data_OnAfterAdd(sender, args))),
true);
DataEventSystemFacade.SubscribeToDataAfterUpdate(_interfaceType,
- (sender, args) => GetActionContainer().Add(() => Data_OnAfterUpdate(sender, args)),
+ (sender, args) => CatchAll(() => GetActionContainer().Add(() => Data_OnAfterUpdate(sender, args))),
true);
DataEventSystemFacade.SubscribeToDataDeleted(_interfaceType,
- (sender, args) => GetActionContainer().Add(() => Data_OnDeleted(sender, args)),
+ (sender, args) => CatchAll(() => GetActionContainer().Add(() => Data_OnDeleted(sender, args))),
true);
}
@@ -65,17 +68,17 @@ private IEnumerable GetCultures(IData data)
return DataLocalizationFacade.ActiveLocalizationCultures;
}
- private bool IgnoreNotification(DataEventArgs args) => !_listeners.Any();
+ private bool IgnoreNotifications() => !_listeners.Any();
private void Data_OnAfterAdd(object sender, DataEventArgs dataEventArgs)
{
- if (IgnoreNotification(dataEventArgs)) return;
+ if (IgnoreNotifications()) return;
try
{
var data = dataEventArgs.Data;
- if (IsPublishedDataFromUnpublishedScope(data))
+ if (!DataItemShouldBeIndexed(data))
{
return;
}
@@ -96,12 +99,12 @@ private void Data_OnAfterAdd(object sender, DataEventArgs dataEventArgs)
private void Data_OnAfterUpdate(object sender, DataEventArgs dataEventArgs)
{
- if (IgnoreNotification(dataEventArgs)) return;
+ if (IgnoreNotifications()) return;
try
{
var data = dataEventArgs.Data;
- bool toBeDeleted = IsPublishedDataFromUnpublishedScope(data);
+ bool toBeDeleted = !DataItemShouldBeIndexed(data);
if (toBeDeleted)
{
@@ -125,7 +128,7 @@ private void Data_OnAfterUpdate(object sender, DataEventArgs dataEventArgs)
private void Data_OnDeleted(object sender, DataEventArgs dataEventArgs)
{
- if (IgnoreNotification(dataEventArgs)) return;
+ if (IgnoreNotifications()) return;
var data = dataEventArgs.Data;
DeleteDocuments(data);
@@ -154,5 +157,17 @@ private bool IsPublishedDataFromUnpublishedScope(IData data)
&& data.DataSourceId.PublicationScope == PublicationScope.Unpublished
&& ((IPublishControlled)data).PublicationStatus == GenericPublishProcessController.Published;
}
+
+ private void CatchAll(Action action)
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception ex)
+ {
+ Log.LogError(nameof(DataChangesIndexNotifier), ex);
+ }
+ }
}
}
diff --git a/Composite/packages.config b/Composite/packages.config
index eb202d024d..47ee7dc78c 100644
--- a/Composite/packages.config
+++ b/Composite/packages.config
@@ -1,4 +1,4 @@
-
+
@@ -9,16 +9,12 @@
-
-
-
-
diff --git a/Package.nuspec b/Package.nuspec
index 9d74c8761c..6054b350dd 100644
--- a/Package.nuspec
+++ b/Package.nuspec
@@ -12,7 +12,7 @@
true
Contains dll-s distributed with C1 CMS.
- Copyright 2017
+ Copyright 2018
C1CMS OrckestraCMS CompositeC1 cms
@@ -22,15 +22,15 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecharmap/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecharmap/plugin.min.js
index 22fda69c3a..563452e05b 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecharmap/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecharmap/plugin.min.js
@@ -17,9 +17,9 @@ new function () {
*/
getInfo : function() {
return {
- longname : "Composite Character Map Plugin",
- author : "Composite A/S",
- authorurl : "http://www.composite.net",
+ longname: "Orckestra Character Map Plugin",
+ author : "Orckestra A/S",
+ authorurl : "https://c1.orckestra.com/",
infourl : null,
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecomponent/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecomponent/plugin.min.js
index 8f55fbf268..9e80e6f8a3 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecomponent/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositecomponent/plugin.min.js
@@ -1,4 +1,4 @@
-/**
+/**
* Composite plugin.
*/
new function () {
@@ -23,8 +23,8 @@ new function () {
getInfo: function () {
return {
longname: "Composite Component Plugin",
- author: "Composite A/S",
- authorurl: "http://www.composite.net",
+ author: "Orckestra A/S",
+ authorurl: "https://c1.orckestra.com/",
infourl: null,
version: tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositefield/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositefield/plugin.min.js
index eaa1d522df..8e4e81c38a 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositefield/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositefield/plugin.min.js
@@ -16,8 +16,8 @@ new function () {
getInfo : function() {
return {
longname : "Composite Field Plugin",
- author : "Composite A/S",
- authorurl : "http://www.composite.net",
+ author : "Orckestra A/S",
+ authorurl : "https://c1.orckestra.com/",
infourl : null,
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeimage/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeimage/plugin.min.js
index 9b01d21e71..bd2ca783bd 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeimage/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeimage/plugin.min.js
@@ -25,8 +25,8 @@ new function () {
getInfo: function () {
return {
longname: "Composite Image Plugin",
- author: "Composite A/S",
- authorurl: "http://www.composite.net",
+ author: "Orckestra A/S",
+ authorurl: "https://c1.orckestra.com/",
infourl: null,
version: tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositelink/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositelink/plugin.min.js
index 683f58fd43..998dc0626e 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositelink/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositelink/plugin.min.js
@@ -10,8 +10,8 @@ new function () {
getInfo : function() {
return {
longname : "Composite Link",
- author : "Composite A/S",
- authorurl : "http://www.composite.net",
+ author : "Orckestra A/S",
+ authorurl : "https://c1.orckestra.com/",
infourl : null,
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeplugin/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeplugin/plugin.min.js
index a4018c0bb5..8e3b49a604 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeplugin/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositeplugin/plugin.min.js
@@ -16,8 +16,8 @@ new function () {
getInfo : function() {
return {
longname : "Composite Plugin",
- author : "Composite A/S",
- authorurl : "http://www.composite.net",
+ author : "Orckestra A/S",
+ authorurl : "https://c1.orckestra.com/",
infourl : null,
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositerendering/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositerendering/plugin.min.js
index 80da63c18b..971ef9a8af 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositerendering/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositerendering/plugin.min.js
@@ -59,8 +59,8 @@ new function () {
getInfo: function () {
return {
longname: "Composite Rendering Plugin",
- author: "Composite A/S",
- authorurl: "http://www.composite.net",
+ author: "Orckestra A/S",
+ authorurl: "https://c1.orckestra.com/",
infourl: null,
version: tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositespellcheck/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositespellcheck/plugin.min.js
index 1e7bf9437b..1df85e262a 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositespellcheck/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositespellcheck/plugin.min.js
@@ -1,4 +1,4 @@
-/**
+/**
* Composite plugin. This plugin does nothing, it is simply a template.
*/
new function () {
@@ -16,8 +16,8 @@ new function () {
getInfo: function () {
return {
longname: "Composite SpellCheck",
- author: "Composite A/S",
- authorurl: "http://www.composite.net",
+ author: "Orckestra A/S",
+ authorurl: "https://c1.orckestra.com/",
infourl: null,
version: tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetable/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetable/plugin.min.js
index 77f4cab0ab..37424d8b82 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetable/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetable/plugin.min.js
@@ -28,8 +28,8 @@ new function () {
getInfo: function () {
return {
longname: "Composite Table Plugin",
- author: "Composite A/S",
- authorurl: "http://www.composite.net",
+ author: "Orckestra A/S",
+ authorurl: "https://c1.orckestra.com/",
infourl: null,
version: tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetext/plugin.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetext/plugin.min.js
index 50623f8891..99270f97b4 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetext/plugin.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/plugins/compositetext/plugin.min.js
@@ -18,8 +18,8 @@ new function () {
getInfo : function() {
return {
longname : "Composite Text Plugin",
- author : "Composite A/S",
- authorurl : "http://www.composite.net",
+ author : "Orckestra A/S",
+ authorurl : "https://c1.orckestra.com/",
infourl : null,
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
diff --git a/Website/Composite/content/misc/editors/visualeditor/tinymce/themes/composite/theme.min.js b/Website/Composite/content/misc/editors/visualeditor/tinymce/themes/composite/theme.min.js
index d577c4b2f5..cfc0222d1a 100644
--- a/Website/Composite/content/misc/editors/visualeditor/tinymce/themes/composite/theme.min.js
+++ b/Website/Composite/content/misc/editors/visualeditor/tinymce/themes/composite/theme.min.js
@@ -36,8 +36,8 @@ new function () {
this.getInfo = function() {
return {
longname : 'Composite theme',
- author : 'Composite A/S',
- authorurl : 'http://www.composite.net',
+ author : 'Orckestra A/S',
+ authorurl : 'https://c1.orckestra.com/',
version : tinymce.majorVersion + "." + tinymce.minorVersion
}
}
diff --git a/Website/Composite/scripts/source/top/core/Application.js b/Website/Composite/scripts/source/top/core/Application.js
index 0183c7b396..b1b97f1b21 100644
--- a/Website/Composite/scripts/source/top/core/Application.js
+++ b/Website/Composite/scripts/source/top/core/Application.js
@@ -387,21 +387,30 @@ _Application.prototype = {
},
/**
- * Log out. Notice that LogoutService currently returns true no matter what...
- * @return {boolean}
- */
- logout : function () {
-
- var result = false;
- if ( this.isLoggedIn ) {
- this.isLoggedIn = false;
- this.isLoggedOut = true;
- result = LoginService.Logout ( true );
- if ( !result ) {
- alert ( "Logout failed." );
+ * Log out.
+ * @return {Promise}
+ */
+ logout: function () {
+ var self = this;
+ var promise = new Promise(function (resolve, reject) {
+ if (self.isLoggedIn) {
+ self.isLoggedIn = false;
+ self.isLoggedOut = true;
+ LoginService.Logout(true, function (result, fault) {
+ if (fault) {
+ self.isLoggedIn = true;
+ self.isLoggedOut = false;
+ alert("Logout failed.");
+ reject();
+ } else {
+ resolve(result);
+ }
+ });
+
}
- }
- return result;
+ });
+
+ return promise;
},
/**
@@ -920,10 +929,14 @@ _Application.prototype = {
if ( FlowControllerService != null ) {
FlowControllerService.ReleaseAllConsoleResources ( Application.CONSOLE_ID );
}
- if ( this.logout ()) {
- top.close();
- location.reload();
- }
+
+ this.logout().then(function (redirectLocation) {
+ if (redirectLocation) {
+ location = redirectLocation;
+ } else {
+ location.reload();
+ }
+ });
},
/**
diff --git a/Website/Composite/scripts/source/top/services/WebServiceProxy.js b/Website/Composite/scripts/source/top/services/WebServiceProxy.js
index afd96a7eec..1de2ca81dd 100644
--- a/Website/Composite/scripts/source/top/services/WebServiceProxy.js
+++ b/Website/Composite/scripts/source/top/services/WebServiceProxy.js
@@ -84,13 +84,10 @@ WebServiceProxy.createProxyOperation = function (operation) {
var self = this;
var response = request.asyncInvoke(operation.address, function (response) {
self._log(operation, response);
-
+ var soapFault = null;
if (response) {
if (response.fault) {
- result = SOAPFault.newInstance(operation, response.fault);
- if (WebServiceProxy.isFaultHandler) {
- WebServiceProxy.handleFault(result, request, response);
- }
+ soapFault = SOAPFault.newInstance(operation, response.fault);
} else {
if (WebServiceProxy.isDOMResult) {
result = response.document;
@@ -100,7 +97,7 @@ WebServiceProxy.createProxyOperation = function (operation) {
}
}
request.dispose();
- onresponse(result);
+ onresponse(result, soapFault);
});
} else {
var request = operation.encoder.encode(
diff --git a/Website/Composite/services/Admin/DownloadFile.ashx b/Website/Composite/services/Admin/DownloadFile.ashx
index 2f2102a77a..b3c8117065 100644
--- a/Website/Composite/services/Admin/DownloadFile.ashx
+++ b/Website/Composite/services/Admin/DownloadFile.ashx
@@ -5,6 +5,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
+using System.Runtime.Versioning;
+using System.Runtime.InteropServices;
using System.Web;
using Composite;
using Composite.Core.IO;
@@ -127,13 +129,15 @@ public class DownloadFile : IHttpHandler
if (assemblyInfo != null)
{
+ var attributes = assemblyInfo.CustomAttributes;
+
lines.AddRange(new []
{
"--------- Version and metadata ---------",
"",
"Assembly Name : " + assemblyInfo.GetName().Name,
"Version : " + assemblyInfo.GetName().Version,
- "Is Debug Dll : " + assemblyInfo.CustomAttributes.Any(
+ "Is Debug Dll : " + attributes.Any(
a => a.AttributeType == typeof(DebuggableAttribute)
&& a.ConstructorArguments.Any(
attr => attr.ArgumentType == typeof(DebuggableAttribute.DebuggingModes)
@@ -142,6 +146,18 @@ public class DownloadFile : IHttpHandler
"ImageRuntimeVersion : " + assemblyInfo.ImageRuntimeVersion,
"Full Name : " + assemblyInfo.FullName
});
+
+ var guidAttribute = attributes.FirstOrDefault(attr => attr.AttributeType == typeof(GuidAttribute));
+ if (guidAttribute != null)
+ {
+ lines.Add("Guid : " + (string) guidAttribute.ConstructorArguments.First().Value);
+ }
+
+ var targetFrameworkAttr = attributes.FirstOrDefault(attr => attr.AttributeType == typeof(TargetFrameworkAttribute));
+ if (targetFrameworkAttr != null)
+ {
+ lines.Add("Target Framework : " + (string) targetFrameworkAttr.ConstructorArguments.First().Value);
+ }
}
else
{
@@ -195,7 +211,7 @@ public class DownloadFile : IHttpHandler
- var text = string.Join(Environment.NewLine, lines.Select(line => context.Server.HtmlEncode(line)));
+ var text = string.Join(Environment.NewLine, lines);
context.Response.Write(text);
}
diff --git a/Website/Composite/services/Login/Login.asmx b/Website/Composite/services/Login/Login.asmx
index 13180ab1c5..ac8a4576f7 100644
--- a/Website/Composite/services/Login/Login.asmx
+++ b/Website/Composite/services/Login/Login.asmx
@@ -1,4 +1,4 @@
-<%@ WebService Language="C#" Class="Composite.Services.Login" %>
+<%@ WebService Language="C#" Class="Composite.Services.Login" %>
using System;
using System.Collections.Generic;
@@ -73,10 +73,9 @@ namespace Composite.Services
}
[WebMethod]
- public bool Logout(bool dummy)
+ public string Logout(bool dummy)
{
- UserValidationFacade.Logout();
- return true;
+ return UserValidationFacade.Logout();
}
[WebMethod]
diff --git a/Website/DebugBuild.Web.config b/Website/DebugBuild.Web.config
index faf345f6fa..cc15d41f19 100644
--- a/Website/DebugBuild.Web.config
+++ b/Website/DebugBuild.Web.config
@@ -1,4 +1,4 @@
-
+
@@ -29,7 +29,7 @@
-
+
@@ -87,18 +87,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Website/ReleaseBuild.Web.config b/Website/ReleaseBuild.Web.config
index b3b5930b61..c271a9b782 100644
--- a/Website/ReleaseBuild.Web.config
+++ b/Website/ReleaseBuild.Web.config
@@ -1,4 +1,4 @@
-
+
@@ -29,7 +29,7 @@
-
+
@@ -87,18 +87,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/Website/WebSite.csproj b/Website/WebSite.csproj
index c2189c3750..3f8d4aeeda 100644
--- a/Website/WebSite.csproj
+++ b/Website/WebSite.csproj
@@ -11,7 +11,7 @@
Library
Composite
Composite.Website
- v4.6.1
+ v4.7.1
SAK
SAK
SAK
@@ -28,6 +28,7 @@
+
true
@@ -94,12 +95,6 @@
-
- ..\packages\System.IO.FileSystem.4.0.1\lib\net46\System.IO.FileSystem.dll
-
-
- ..\packages\System.IO.FileSystem.Primitives.4.0.1\lib\net46\System.IO.FileSystem.Primitives.dll
-
..\packages\System.Reactive.Core.3.0.0\lib\net46\System.Reactive.Core.dll
True
@@ -116,12 +111,6 @@
False
..\Packages\System.Reflection.Metadata.1.4.2\lib\portable-net45+win8\System.Reflection.Metadata.dll
-
- ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
-
-
- ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
-
@@ -2771,7 +2760,6 @@
-