├── release-notes.txt
├── lib
└── Resources
│ └── logo.png
├── MFiles.VAF.Extensions
├── sample-dashboard.png
├── interval-taskqueue.png
├── scheduled-taskqueue.png
├── runondemand-taskqueue.png
├── Resources
│ └── Images
│ │ ├── Failed.png
│ │ ├── Running.png
│ │ ├── Waiting.png
│ │ ├── enabled.png
│ │ ├── error.png
│ │ ├── warning.png
│ │ ├── Completed.png
│ │ ├── Download.png
│ │ ├── canceled.png
│ │ ├── viewlogs.png
│ │ └── notenabled.png
├── TaskQueueBackgroundOperations
│ ├── TaskQueueBackgroundOperationManager
│ │ ├── run-now-dashboard.png
│ │ ├── GetDashboardContent.cs
│ │ └── RemoveBackgroundOperation.cs
│ └── Readme.md
├── Configuration
│ ├── IRecurringOperationConfigurationAttribute.cs
│ ├── UsesLoggingResourcesAttribute.cs
│ ├── Upgrading
│ │ ├── AllowDefaultValueSerializationAttribute.cs
│ │ ├── IConfigurationUpgradeManager.cs
│ │ ├── Rules
│ │ │ ├── MoveConfigurationUpgradeRule.cs
│ │ │ ├── VAF20ToVAF23UpgradeRule.cs
│ │ │ ├── VAF10ToVAF23UpgradeRule.cs
│ │ │ └── IUpgradeRule.cs
│ │ ├── JsonConversion
│ │ │ ├── JsonConvert.cs
│ │ │ └── IJsonConvert.cs
│ │ ├── EntireNamespaceNamedValueItem.cs
│ │ ├── INamedValueStorageManagerExtensionMethods.cs
│ │ └── INamedValueStorageManager.cs
│ ├── UsesConfigurationResourcesAttribute.cs
│ ├── RecurrenceType.cs
│ ├── ScheduledExecution
│ │ ├── TriggerTimeType.cs
│ │ ├── ScheduleTriggerType.cs
│ │ ├── TimeZoneStableValueOptionsProvider.cs
│ │ └── Readme.md
│ ├── IConfigurationWithLoggingConfiguration.cs
│ ├── ConfigurationCollectionItemBase.cs
│ ├── IRecurrenceConfiguration.cs
│ ├── RecurringOperationConfigurationAttribute.cs
│ ├── ConfigurationVersionAttribute.cs
│ ├── IVersionedConfiguration.cs
│ ├── ConfigurationBase.cs
│ └── JsonConverterBase.cs
├── Dashboards
│ ├── AsynchronousDashboardContent
│ │ ├── AsynchronousDashboardContentSettings.cs
│ │ ├── IAsynchronousExecutionsDashboardContentRenderer.cs
│ │ ├── IAsynchronousDashboardContentProvider.cs
│ │ ├── DashboardQueueAndTaskDetails.cs
│ │ └── IAsynchronousDashboardContentRenderer.cs
│ ├── TaskQueues
│ │ └── HideOnDashboardAttribute.cs
│ ├── LoggingDashboardContent
│ │ └── ILoggingDashboardContentRenderer.cs
│ ├── DevelopmentDashboardContent
│ │ └── IDevelopmentDashboardContentRenderer.cs
│ ├── DashboardCustomContentEx.cs
│ ├── DashboardListItemEx.cs
│ ├── Commands
│ │ └── CustomDomainCommandResolution
│ │ │ ├── AggregatedCustomDomainCommandResolver.cs
│ │ │ ├── DefaultCustomDomainCommandResolver.cs
│ │ │ ├── LogCustomDomainCommandResolver.cs
│ │ │ └── ICustomDomainCommandResolver.cs
│ ├── ApplicationOverviewDashboardContent
│ │ └── IApplicationOverviewDashboardContentRenderer.cs
│ ├── ILogoSource.cs
│ ├── ExceptionDashboardPanel.cs
│ └── DashboardHelpersEx.cs
├── MFBuiltInUsers.cs
├── ExtensionMethods
│ ├── VaultObjectFileOperations
│ │ ├── Readme.md
│ │ └── AddFile.cs
│ ├── EnvironmentBaseExtensionMethods.cs
│ ├── TaskProcessorJobExExtensionMethods.cs
│ ├── MFTaskStateExtensionMethods.cs
│ ├── DictionaryExtensionMethods.cs
│ ├── StringExtensionMethods.cs
│ ├── ObjVerEx
│ │ ├── ToLookup.cs
│ │ ├── GetOwner.cs
│ │ ├── GetLookupIDs.cs
│ │ └── GetPropertyAsValueListItem.cs
│ ├── TimestampExtensionMethods.cs
│ ├── MFSearchBuilderExtensionMethods
│ │ ├── HasFiles.cs
│ │ ├── CheckedOut.cs
│ │ ├── FindCount.cs
│ │ ├── ExternalID.cs
│ │ ├── FileSize.cs
│ │ ├── FileExtension.cs
│ │ ├── FullText.cs
│ │ └── Owner.cs
│ ├── IEnumerableExtensionMethods.cs
│ ├── EnumExtensionMethods.cs
│ ├── ITaskProcessingJobOfTExtensionMethods.cs
│ └── TaskQueueManagerExtensionMethods.cs
├── Email
│ ├── Configuration
│ │ └── Credentials.cs
│ └── Readme.md
├── Directives
│ ├── TaskDirectiveWithDisplayName.cs
│ └── GenericTaskDirective.cs
├── Attributes
│ └── CustomCommandAttribute.cs
├── Logging
│ └── Sensitivity
│ │ └── Filters
│ │ ├── INamedValueItemSensitivityFilter.cs
│ │ └── ObjVerExLogSensitivityFilter.cs
├── ConfigurableVaultApplicationBase.GetCommands.cs
├── MFiles.VAF.Extensions.csproj.DotSettings
├── ConfigurableVaultApplicationBase.ConfigurationUpgrading.cs
├── ConfigurableVaultApplicationBase.LoadTypedVaultExtensionMethods.cs
└── nuget-readme.md
├── .editorconfig
├── MFiles.VAF.Extensions.Tests
├── Readme.md
├── Email
│ ├── Readme.md
│ ├── Credentials.cs
│ └── VAFSmtpConfiguration.cs
├── Dashboards
│ ├── TaskInformationTests.cs
│ ├── DashboardListItemWithNormalWhitespaceTests.cs
│ ├── StyleComparisonHelper.cs
│ ├── DashboardTableTests.cs
│ ├── Commands
│ │ └── CustomDomainCommandResolution
│ │ │ └── DefaultCustomDomainCommandResolverTests.cs
│ ├── DashboardCustomContentExTests.cs
│ ├── DashboardTableRowTests.cs
│ └── DashboardTableCellTests.cs
├── ExtensionMethods
│ ├── IEnumerableExtensionMethodsTests.cs
│ ├── ObjVerEx
│ │ ├── IsEnteringState.cs
│ │ └── ToLookup.cs
│ ├── MFSearchBuilderExtensionMethods
│ │ ├── MFSearchBuilderExtensionMethodTestBase.cs
│ │ ├── FindCount.cs
│ │ └── ExternalID.cs
│ ├── AssertExtensionMethods.cs
│ ├── EnvironmentBaseExtensionMethodsTests.cs
│ ├── FormattingExtensionMethods.cs
│ └── TimeZoneInformationExtensionMethodsTests.cs
├── TestBaseWithVaultMock.cs
├── LocalTimeZoneInfoMocker.cs
├── Configuration
│ ├── DataMemberRequiredAttribute.cs
│ ├── SecurityRequiredAttribute.cs
│ ├── ConfigurationBaseTests.cs
│ ├── JsonConfEditorRequiredAttribute.cs
│ ├── JsonConvertTests.cs
│ └── Upgrading
│ │ ├── Rules
│ │ ├── UpgradeRuleTestBase.cs
│ │ ├── VAF20ToVAF23UpgradeRuleTests.cs
│ │ └── VAF10ToVAF23UpgradeRuleTests.cs
│ │ └── ConfigurationUpgradeManager.cs
├── MFiles.VAF.Extensions.Tests.csproj
├── Logging
│ └── Configuration.cs
├── Directives
│ ├── ObjVerExTaskDirectiveTests.cs
│ └── TaskDirectiveWithDisplayNameTestsBase.cs
├── Initialization.cs
├── NewtonsoftJsonConvert.cs
└── ConfigurableVaultApplicationBase.IsCurrentConfigurationValid.cs
├── MFiles.VAF.Extensions.sln.DotSettings
├── .github
└── workflows
│ ├── build.yml
│ └── build-and-publish.yml
├── LICENSE.md
├── .gitattributes
└── BRANCHING.md
/release-notes.txt:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/lib/Resources/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/lib/Resources/logo.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/sample-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/sample-dashboard.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/interval-taskqueue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/interval-taskqueue.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/scheduled-taskqueue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/scheduled-taskqueue.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/runondemand-taskqueue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/runondemand-taskqueue.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/Failed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/Failed.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/Running.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/Running.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/Waiting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/Waiting.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/enabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/enabled.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/error.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/warning.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/Completed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/Completed.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/Download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/Download.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/canceled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/canceled.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/viewlogs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/viewlogs.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Resources/Images/notenabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/Resources/Images/notenabled.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [.editorconfig]
4 | end_of_line = lf
5 |
6 | [*.cs]
7 | charset = utf-8-bom
8 | end_of_line = lf
9 | indent_style = tab
10 | indent_size = 4
11 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/TaskQueueBackgroundOperations/TaskQueueBackgroundOperationManager/run-now-dashboard.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/M-Files/VAF.Extensions.Community/HEAD/MFiles.VAF.Extensions/TaskQueueBackgroundOperations/TaskQueueBackgroundOperationManager/run-now-dashboard.png
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Readme.md:
--------------------------------------------------------------------------------
1 | # M-Files Vault Application Framework Extensions (Community) Tests
2 |
3 | This project provides unit test coverage for the M-Files Vault Application Framework Extensions (Community) project extension methods and classes. Submissions to the project should include unit test coverage for the new members.
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Email/Readme.md:
--------------------------------------------------------------------------------
1 | # M-Files Vault Application Framework Email Extensions (Community) Tests
2 |
3 | This project provides unit test coverage for the M-Files Vault Application Framework API Email Extensions (Community) project extension methods and classes. Submissions to the project should include unit test coverage for the new members.
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/IRecurringOperationConfigurationAttribute.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable once CheckNamespace
2 |
3 | using System;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | public interface IRecurringOperationConfigurationAttribute
8 | {
9 | string QueueID { get; set; }
10 | string TaskType { get; set; }
11 | Type[] ExpectedPropertyOrFieldTypes { get; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | VAF
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/AsynchronousDashboardContent/AsynchronousDashboardContentSettings.cs:
--------------------------------------------------------------------------------
1 | namespace MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent
2 | {
3 | public static class AsynchronousDashboardContentSettings
4 | {
5 | ///
6 | /// The number of waiting tasks in a single queue at which point the dashboard is shown degraded.
7 | ///
8 | public const int DegradedDashboardThreshold = 3000;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/MFBuiltInUsers.cs:
--------------------------------------------------------------------------------
1 | namespace MFiles.VAF.Extensions
2 | {
3 | ///
4 | /// Internal class for enum-style access to "built-in" users.
5 | ///
6 | internal static class MFBuiltInUsers
7 | {
8 | ///
9 | /// The user ID reported when code is run as the M-Files user
10 | /// (e.g. automatic state transitions, task processing).
11 | ///
12 | internal const int MFilesServerUserID = -102;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/UsesLoggingResourcesAttribute.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Resources;
2 | using System.Resources;
3 |
4 | namespace MFiles.VAF.Extensions
5 | {
6 | ///
7 | /// Used to mark a configuration class as using resources from .
8 | ///
9 | public class UsesLoggingResourcesAttribute
10 | : UsesResourcesAttribute
11 | {
12 | public UsesLoggingResourcesAttribute()
13 | : base(typeof(Resources.Logging)) { }
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/TaskQueueBackgroundOperations/TaskQueueBackgroundOperationManager/GetDashboardContent.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using MFiles.VAF.Extensions.Dashboards;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions
11 | {
12 | public partial class TaskQueueBackgroundOperationManager
13 | {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/AllowDefaultValueSerializationAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MFiles.VAF.Extensions.Configuration.Upgrading
8 | {
9 | [AttributeUsage
10 | (
11 | AttributeTargets.Property | AttributeTargets.Field,
12 | AllowMultiple = false,
13 | Inherited = true
14 | )]
15 | public class AllowDefaultValueSerializationAttribute
16 | : Attribute
17 | {
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/UsesConfigurationResourcesAttribute.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Resources;
2 | using System.Resources;
3 |
4 | namespace MFiles.VAF.Extensions
5 | {
6 | ///
7 | /// Used to mark a configuration class as using resources from .
8 | ///
9 | public class UsesConfigurationResourcesAttribute
10 | : UsesResourcesAttribute
11 | {
12 | public UsesConfigurationResourcesAttribute()
13 | : base(typeof(Resources.Configuration)) { }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/RecurrenceType.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 |
3 | namespace MFiles.VAF.Extensions
4 | {
5 | [UsesConfigurationResources]
6 | public enum RecurrenceType
7 | {
8 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.RecurrenceType_Unknown))]
9 | Unknown = 0,
10 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.RecurrenceType_Interval))]
11 | Interval = 1,
12 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.RecurrenceType_Schedule))]
13 | Schedule = 2
14 | }
15 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Email/Credentials.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Tests.Configuration;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace MFiles.VAF.Extensions.Tests.Email
5 | {
6 | [TestClass]
7 | [DataMemberRequired
8 | (
9 | nameof(Extensions.Email.Credentials.AccountName),
10 | nameof(Extensions.Email.Credentials.Password)
11 | )]
12 | [SecurityRequired
13 | (
14 | nameof(Extensions.Email.Credentials.Password),
15 | true
16 | )]
17 | public class Credentials
18 | : ConfigurationClassTestBase
19 | {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/IConfigurationUpgradeManager.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 |
5 | namespace MFiles.VAF.Extensions.Configuration.Upgrading
6 | {
7 | public interface IConfigurationUpgradeManager
8 | {
9 | ///
10 | /// Upgrades the configuration in the vault.
11 | ///
12 | /// The vault reference to use to access named-value storage.
13 | void UpgradeConfiguration(Vault vault)
14 | where TSecureConfiguration : class, new();
15 |
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ScheduledExecution/TriggerTimeType.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 |
3 | namespace MFiles.VAF.Extensions.ScheduledExecution
4 | {
5 | [UsesConfigurationResources]
6 | public enum TriggerTimeType
7 | {
8 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.TriggerTimeType_ServerTime))]
9 | ServerTime = 0,
10 |
11 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.TriggerTimeType_Utc))]
12 | Utc = 1,
13 |
14 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.TriggerTimeType_Custom))]
15 | Custom = 2
16 | }
17 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/VaultObjectFileOperations/Readme.md:
--------------------------------------------------------------------------------
1 | # VaultObjectFileOperations extension methods
2 |
3 | ## AddFile
4 |
5 | Adds a file to an existing M-Files object.
6 |
7 | ```csharp
8 | // Assume that "sourceStream" contains the new file contents:
9 | using (var sourceStream = ...)
10 | {
11 | env.Vault.ObjectFileOperations.AddFile
12 | (
13 | env.ObjVerEx,
14 | "My new file",
15 | ".pdf,
16 | sourceStream
17 | );
18 | }
19 | ```
20 |
21 | *Note: the object must be already checked out and have the "Single File Document" property appropriately set prior to calling this method.*
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/TaskInformationTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Tests.Dashboards
10 | {
11 | [TestClass]
12 | public class TaskInformationTests
13 | {
14 | [TestMethod]
15 | public void NoPercentage_UnencodedValueDoesNotThrow()
16 | {
17 | var x = new TaskInformation() { StatusDetails = "hello & world" };
18 | x.AsDashboardContent(true).ToXmlString();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/IConfigurationWithLoggingConfiguration.cs:
--------------------------------------------------------------------------------
1 |
2 | using MFiles.VAF.Configuration.Logging;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration
5 | {
6 | ///
7 | /// An interface that is used to denote that the configuration class exposes
8 | /// logging configuration somehow.
9 | ///
10 | public interface IConfigurationWithLoggingConfiguration
11 | {
12 | ///
13 | /// Returns the logging configuration from whereever it may be configured.
14 | ///
15 | ///
16 | /// The logging configuration.
17 | ///
18 | ILoggingConfiguration GetLoggingConfiguration();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/TaskQueues/HideOnDashboardAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MFiles.VAF.Extensions.Dashboards
4 | {
5 | ///
6 | /// Used to hide a queue or task processor on the dashboard.
7 | ///
8 | ///
9 | /// If both and attributes
10 | /// are present then the takes effect.
11 | ///
12 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
13 | public class HideOnDashboardAttribute
14 | : Attribute
15 | {
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/LoggingDashboardContent/ILoggingDashboardContentRenderer.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using MFiles.VAF.Configuration.Logging;
4 |
5 | namespace MFiles.VAF.Extensions.Dashboards.LoggingDashboardContent
6 | {
7 | public interface ILoggingDashboardContentRenderer
8 | {
9 | ///
10 | /// Gets the logging dashboard content.
11 | ///
12 | /// The content, or null if nothing to render.
13 | IDashboardContent GetDashboardContent
14 | (
15 | IConfigurationRequestContext context,
16 | ILoggingConfiguration loggingConfiguration
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/IEnumerableExtensionMethodsTests.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods
8 | {
9 | [TestClass]
10 | public class IEnumerableExtensionMethodsTests
11 | {
12 | [TestMethod]
13 | public void AsNotNull_ReturnsOriginalCollection()
14 | {
15 | var collection = new List();
16 | Assert.AreSame(collection, collection.AsNotNull());
17 | }
18 |
19 | [TestMethod]
20 | public void AsNotNull_DoesNotReturnNull()
21 | {
22 | Assert.IsNotNull(((IEnumerable)null).AsNotNull());
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/ObjVerEx/IsEnteringState.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using MFilesAPI;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Moq;
5 | using System;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods.ObjVerEx
8 | {
9 | [TestClass]
10 | public class IsEnteringState
11 | : TestBaseWithVaultMock
12 | {
13 |
14 | [TestMethod]
15 | [ExpectedException(typeof(ArgumentNullException))]
16 | public void ThrowsIfNullObjVerEx()
17 | {
18 | ((Common.ObjVerEx)null).IsEnteringState(123);
19 | }
20 |
21 | [TestMethod]
22 | public void ReturnsFalseIfNullMFIdentifier()
23 | {
24 | Assert.IsFalse(new MFiles.VAF.Common.ObjVerEx().IsEnteringState(123));
25 | }
26 |
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ScheduledExecution/ScheduleTriggerType.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 |
3 | namespace MFiles.VAF.Extensions.ScheduledExecution
4 |
5 | {
6 | [UsesConfigurationResources]
7 | public enum ScheduleTriggerType
8 | {
9 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.ScheduleTriggerType_Unknown))]
10 | Unknown = 0,
11 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.ScheduleTriggerType_Daily))]
12 | Daily = 1,
13 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.ScheduleTriggerType_Weekly))]
14 | Weekly = 2,
15 | [JsonConfEditor(Label = ResourceMarker.Id + nameof(Resources.Configuration.ScheduleTriggerType_Monthly))]
16 | Monthly = 3
17 | }
18 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ScheduledExecution/TimeZoneStableValueOptionsProvider.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Configuration.JsonEditor;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Configuration.ScheduledExecution
10 | {
11 | public class TimeZoneStableValueOptionsProvider
12 | : IStableValueOptionsProvider
13 | {
14 | public IEnumerable GetOptions(IConfigurationRequestContext context)
15 | {
16 | foreach(var timezone in TimeZoneInfo.GetSystemTimeZones())
17 | {
18 | yield return new ValueOption()
19 | {
20 | Value = timezone.Id,
21 | Label = timezone.DisplayName
22 | };
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/DevelopmentDashboardContent/IDevelopmentDashboardContentRenderer.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 |
3 | namespace MFiles.VAF.Extensions.Dashboards.DevelopmentDashboardContent
4 | {
5 | #if DEBUG
6 | public interface IDevelopmentDashboardContentRenderer
7 | {
8 | ///
9 | /// Gets the development dashboard content.
10 | ///
11 | /// The content, or null if nothing to render.
12 | IDashboardContent GetDashboardContent();
13 |
14 | ///
15 | /// Populates internal dictionaries with referenced assemblies.
16 | /// Should be called once at startup.
17 | ///
18 | ///
19 | void PopulateReferencedAssemblies();
20 | }
21 | #endif
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ScheduledExecution/Readme.md:
--------------------------------------------------------------------------------
1 | # Scheduled Execution
2 |
3 | Creation of a `Schdeule` object, and associated triggers, can be used to define when a background operation should be run. Multiple triggers can be combined to express more complex combinations (e.g. "Weekdays at 8pm, and weekends at 4pm").
4 |
5 | ### Schedule.cs
6 |
7 | The class denoting a schedule. Contains a list of triggers.
8 |
9 | ### DailyTrigger.cs
10 |
11 | A trigger that runs every day at one or more times.
12 |
13 | ### WeeklyTrigger.cs
14 |
15 | A trigger that runs on one or more days of the week, at one or more times (the same times every day).
16 |
17 | ### DayOfMonthTrigger.cs
18 |
19 | A trigger that runs on a specific numbered day of the month (e.g. every 1st of the month), at one or more times (the same times every day).
20 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/AsynchronousDashboardContent/IAsynchronousExecutionsDashboardContentRenderer.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using System.Collections.Generic;
4 |
5 | namespace MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent
6 | {
7 | ///
8 | /// Renders details about executions of an asynchronous operation into a dashboard.
9 | /// Note: rendering of overall process is done via ,
10 | /// and the two work in tandem.
11 | ///
12 | public interface IAsynchronousExecutionsDashboardContentRenderer
13 | {
14 | IDashboardContent GetDashboardContent
15 | (
16 | DashboardQueueAndTaskDetails details,
17 | IEnumerable> executions
18 | );
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/TestBaseWithVaultMock.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using Moq;
3 |
4 | namespace MFiles.VAF.Extensions.Tests
5 | {
6 | public abstract class TestBaseWithVaultMock
7 | {
8 | ///
9 | /// Returns a mock that can be used to retrieve data as appropriate.
10 | ///
11 | ///
12 | protected virtual Mock GetVaultMock()
13 | => this.GetVaultMock(MockBehavior.Default);
14 |
15 | ///
16 | /// Returns a mock that can be used to retrieve data as appropriate.
17 | ///
18 | ///
19 | protected virtual Mock GetVaultMock(MockBehavior behaviour)
20 | {
21 | var mock = new Mock(behaviour)
22 | {
23 | DefaultValue = DefaultValue.Empty
24 | };
25 | mock.SetupAllProperties();
26 | return mock;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/Rules/MoveConfigurationUpgradeRule.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Logging;
2 | using MFilesAPI;
3 | using System;
4 | using System.Linq;
5 |
6 | namespace MFiles.VAF.Extensions.Configuration.Upgrading.Rules
7 | {
8 | public class MoveConfigurationUpgradeRule
9 | : SingleNamedValueItemUpgradeRuleBase
10 | {
11 | public MoveConfigurationUpgradeRule(ISingleNamedValueItem readFrom, ISingleNamedValueItem writeTo, Version migrateFromVersion, Version migrateToVersion)
12 | : base(readFrom, writeTo, migrateFromVersion, migrateToVersion)
13 | {
14 | }
15 |
16 | private ILogger Logger { get; } = LogManager.GetLogger();
17 |
18 | ///
19 | /// This method does not make any changes to the content.
20 | protected override string Convert(string input)
21 | => input;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/LocalTimeZoneInfoMocker.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 |
4 | namespace MFiles.VAF.Extensions.Tests
5 | {
6 | public class LocalTimeZoneInfoMocker : IDisposable
7 | {
8 | public LocalTimeZoneInfoMocker(string timeZoneId)
9 | : this(TimeZoneInfo.FindSystemTimeZoneById(timeZoneId))
10 | {
11 | }
12 | public LocalTimeZoneInfoMocker(TimeZoneInfo mockTimeZoneInfo)
13 | {
14 | var info = typeof(TimeZoneInfo).GetField("s_cachedData", BindingFlags.NonPublic | BindingFlags.Static);
15 | var cachedData = info.GetValue(null);
16 | var field = cachedData.GetType().GetField("m_localTimeZone",
17 | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Instance);
18 | field.SetValue(cachedData, mockTimeZoneInfo);
19 | }
20 |
21 | public void Dispose()
22 | {
23 | TimeZoneInfo.ClearCachedData();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/EnvironmentBaseExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.ExtensionMethods
5 | {
6 | public static class EnvironmentBaseExtensionMethods
7 | {
8 | ///
9 | /// Returns true if
10 | /// is set to a system process (i.e. an ID less than or equal to zero).
11 | ///
12 | /// The environment to check.
13 | /// true if is less than or equal to zero
14 | /// Thrown if is null.
15 | public static bool IsCurrentUserSystemProcess(this EnvironmentBase env)
16 | // IDs under zero are system processes.
17 | => (env ?? throw new ArgumentNullException(nameof(env))).CurrentUserID <= 0;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/DataMemberRequiredAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.Serialization;
4 |
5 | namespace MFiles.VAF.Extensions.Tests.Configuration
6 | {
7 | ///
8 | /// Defines that specific properties of a class must be decorated with a .
9 | ///
10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
11 | public class DataMemberRequiredAttribute
12 | : Attribute
13 | {
14 | ///
15 | /// The properties that need the data member attribute.
16 | ///
17 | public List PropertyNames {get; set; }
18 | = new List();
19 |
20 | public DataMemberRequiredAttribute
21 | (
22 | params string[] propertyNames
23 | )
24 | : base()
25 | {
26 | this.PropertyNames.AddRange(propertyNames ?? new string[0]);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/TaskProcessorJobExExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MFiles.VAF.Extensions
4 | {
5 | ///
6 | /// Contains extension methods for working with task processor jobs.
7 | ///
8 | public static class TaskProcessorJobExExtensionMethods
9 | {
10 | ///
11 | /// Retrieves the background operation name.
12 | ///
13 | public static string GetBackgroundOperationName
14 | (
15 | this TaskProcessorJobEx taskProcessorJobEx
16 | )
17 | where TDirective : BackgroundOperationTaskDirective
18 | where TSecureConfiguration : class, new()
19 | {
20 | // Sanity.
21 | if (null == taskProcessorJobEx)
22 | throw new ArgumentNullException(nameof(taskProcessorJobEx));
23 |
24 | return taskProcessorJobEx.Job?.Directive?.BackgroundOperationName;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/DashboardCustomContentEx.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Xml;
8 |
9 | namespace MFiles.VAF.Extensions.Dashboards
10 | {
11 | public class DashboardCustomContentEx
12 | : DashboardContentBase
13 | {
14 | public IDashboardContent InnerContent { get; set; }
15 | public DashboardCustomContentEx(IDashboardContent innerContent)
16 | {
17 | this.InnerContent = innerContent;
18 | }
19 | public DashboardCustomContentEx(string htmlContent)
20 | {
21 | this.InnerContent = new DashboardCustomContent(htmlContent);
22 | }
23 | protected override XmlDocumentFragment GenerateXmlDocumentFragment(XmlDocument xml)
24 | {
25 | if (null == this.InnerContent)
26 | return null;
27 | return this.InnerContent.Generate(xml);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/DashboardListItemWithNormalWhitespaceTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 |
5 | namespace MFiles.VAF.Extensions.Tests.Dashboards
6 | {
7 | [TestClass]
8 | public class DashboardListItemExTests
9 | {
10 | [TestMethod]
11 | public void WhiteSpaceExplicitlySet()
12 | {
13 | var innerContent = new DashboardCustomContent("hello world.
");
14 | var content = new DashboardListItemEx()
15 | {
16 | InnerContent = innerContent
17 | };
18 |
19 | // Get the "content" div in the list item.
20 | var element = content.ToXmlFragment()?.FirstChild?.SelectSingleNode("div[@class='content']");
21 | Assert.IsNotNull(element);
22 |
23 | // Ensure that the whitespace is overridden.
24 | Assert.AreEqual("white-space: normal", element.Attributes["style"]?.Value);
25 |
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/MFSearchBuilderExtensionMethods/MFSearchBuilderExtensionMethodTestBase.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 |
3 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods.MFSearchBuilderExtensionMethods
4 | {
5 | ///
6 | /// A base class that tests of the
7 | /// can use.
8 | ///
9 | // ReSharper disable once InconsistentNaming
10 | public abstract class MFSearchBuilderExtensionMethodTestBase
11 | : TestBaseWithVaultMock
12 | {
13 |
14 | ///
15 | /// Returns a that will be used for the tests.
16 | ///
17 | ///
18 | protected virtual MFSearchBuilder GetSearchBuilder()
19 | {
20 | // Get the vault mock and populate it if needed.
21 | var vaultMock = this.GetVaultMock();
22 |
23 | // Return the search builder.
24 | return new MFSearchBuilder(vaultMock.Object);
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Email/Configuration/Credentials.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.Serialization;
2 | using MFiles.VAF.Configuration;
3 |
4 | namespace MFiles.VAF.Extensions.Email
5 | {
6 | ///
7 | /// Configuration settings for authentication to the Smtp server.
8 | ///
9 | [DataContract]
10 | public class Credentials
11 | : MFilesAPI.Extensions.Email.Credentials
12 | {
13 | ///
14 | /// The account name to connect as.
15 | ///
16 | [DataMember(Order = 0 )]
17 | [JsonConfEditor(Label = "Username")]
18 | public override string AccountName
19 | {
20 | get => base.AccountName;
21 | set => base.AccountName = value;
22 | }
23 |
24 | ///
25 | /// The password for the account.
26 | ///
27 | [DataMember(Order = 1 )]
28 | [JsonConfEditor(Label = "Password")]
29 | [Security(IsPassword = true)]
30 | public override string Password
31 | {
32 | get => base.Password;
33 | set => base.Password = value;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ConfigurationCollectionItemBase.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using System.Runtime.Serialization;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration
5 | {
6 | [DataContract]
7 | public abstract class ConfigurationCollectionItemBase
8 | {
9 | ///
10 | /// ArrayElementGuid is needed for the VAF to track items in a collection.
11 | /// This base class should be used where a collection of POCO configuration items
12 | /// are exposed. (e.g. in "public List MyCollection { get;set; }" the XXXX class
13 | /// should inherit from this class).
14 | ///
15 | /// Hidden. Not to be edited by user.
16 | [DataMember(Name = "arrayElementGuid", EmitDefaultValue = false, Order = 99)]
17 | [JsonConfEditor
18 | (
19 | TypeEditor = "guid",
20 | Hidden = true,
21 | IsRequired = true,
22 | ClearOnCopy = true
23 | )]
24 | public string ArrayElementGuid { get; set; }
25 | = System.Guid.NewGuid().ToString();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/AssertExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using Newtonsoft.Json.Linq;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods
10 | {
11 | internal static class AssertExtensionMethods
12 | {
13 | public static void AreEqualJson(this Assert assert, string expected, string actual)
14 | {
15 | if (null == expected)
16 | {
17 | Assert.IsNull(actual);
18 | return;
19 | }
20 | Assert.AreEqual
21 | (
22 | JObject.Parse(expected).ToString(),
23 | JObject.Parse(actual).ToString()
24 | );
25 | }
26 | public static void AreEqualJson(this Assert assert, JObject expected, JObject actual)
27 | {
28 | if (null == expected)
29 | {
30 | Assert.IsNull(actual);
31 | return;
32 | }
33 | Assert.AreEqual
34 | (
35 | expected.ToString(),
36 | actual.ToString()
37 | );
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/IRecurrenceConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MFiles.VAF.Extensions
4 | {
5 | public interface IRecurrenceConfiguration
6 | {
7 | ///
8 | /// Returns a string that describes the current recurrence configuration. Must be HTML-formatted.
9 | ///
10 | /// The string
11 | /// <p>Runs every 10 minutes.</p>
12 | string ToDashboardDisplayString();
13 |
14 | ///
15 | /// Returns the next time the recurrence should run.
16 | ///
17 | /// The current execution time, or null to use the current time.
18 | /// The next-run time. May be null if there are no valid next-run times.
19 | DateTimeOffset? GetNextExecution(DateTimeOffset? after = null);
20 |
21 | ///
22 | /// Whether the processor should run on vault startup (in addition to any other schedule or interval).
23 | ///
24 | bool? RunOnVaultStartup { get; }
25 | }
26 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/AsynchronousDashboardContent/IAsynchronousDashboardContentProvider.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using MFiles.VAF.Configuration.AdminConfigurations;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using System.Diagnostics;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using static MFiles.VAF.Common.ApplicationTaskQueue.TaskQueueManager;
9 |
10 | namespace MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent
11 | {
12 | ///
13 | /// Provides content around asynchronous dashboards, to be subsequently rendered on a dashboard.
14 | ///
15 | public interface IAsynchronousDashboardContentProvider
16 | {
17 | ///
18 | /// Returns data from this provider about asynchronous operations that should be rendered onto a dashboard.
19 | ///
20 | /// The data, or null if none should be rendered.
21 | IEnumerable>>> GetAsynchronousDashboardContent();
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/MFiles.VAF.Extensions.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net472
5 |
6 | false
7 |
8 | AnyCPU
9 |
10 |
11 |
12 | AnyCPU
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFTaskStateExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MFiles.VAF.Extensions
9 | {
10 | internal static class MFTaskStateExtensionMethods
11 | {
12 | public static string ForDisplay(this MFTaskState taskState)
13 | {
14 | switch (taskState)
15 | {
16 | case MFTaskState.MFTaskStateWaiting:
17 | return Resources.Dashboard.MFTaskStateWaiting;
18 | case MFTaskState.MFTaskStateInProgress:
19 | return Resources.Dashboard.MFTaskStateInProgress;
20 | case MFTaskState.MFTaskStateCompleted:
21 | return Resources.Dashboard.MFTaskStateCompleted;
22 | case MFTaskState.MFTaskStateFailed:
23 | return Resources.Dashboard.MFTaskStateFailed;
24 | case MFTaskState.MFTaskStateCanceled:
25 | return Resources.Dashboard.MFTaskStateCanceled;
26 | case MFTaskState.MFTaskStateNone:
27 | return Resources.Dashboard.MFTaskStateNone;
28 | }
29 | return "Undefined";
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | push:
5 | branches: [ master, main ] # Build on main/master branch
6 | pull_request:
7 | branches: [ master, main ] # Build on PR/MR that target main/master
8 |
9 | jobs:
10 | build:
11 | timeout-minutes: 10
12 | runs-on: 'windows-2022'
13 |
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Setup MSBuild path
21 | uses: microsoft/setup-msbuild@v1.1
22 | with:
23 | vs-version: '[17.0,)'
24 |
25 | - name: Setup dot net
26 | uses: actions/setup-dotnet@v2
27 | with:
28 | dotnet-version: 6.0.x
29 |
30 | - name: Restore NuGet packages
31 | run: nuget restore MFiles.VAF.Extensions.sln
32 |
33 | - name: Build solution - RELEASE configuration
34 | run: msbuild MFiles.VAF.Extensions.sln /nologo /verbosity:m /p:Configuration=Release /t:Build /p:DefineConstants="DONOTDEPLOY"
35 |
36 | # Tests cannot be run as MFAPI is not installed.
37 | # - name: Test
38 | # run: dotnet test --no-restore --verbosity normal
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/DashboardListItemEx.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using System.Xml;
3 |
4 | namespace MFiles.VAF.Extensions.Dashboards
5 | {
6 | public class DashboardListItemEx : DashboardListItem
7 | {
8 | ///
9 | /// If set, may be used to order the list items prior to rendering.
10 | ///
11 | public int? Order { get; set; }
12 |
13 | ///
14 | /// If true, ensures that the "white-space" CSS value is set to normal.
15 | ///
16 | public bool SetWhitespaceToNormal { get; set; } = true;
17 |
18 | ///
19 | public override XmlDocumentFragment Generate(XmlDocument xml)
20 | {
21 | var fragment = base.Generate(xml);
22 |
23 | // Get a handle on the various elements.
24 | XmlElement listItem = (XmlElement)fragment.SelectNodes("li")[0];
25 | XmlElement content = (XmlElement)listItem.SelectNodes("*[@class=\"content\"]")[0];
26 |
27 | // Explicitly set the whitespace to normal.
28 | content.SetAttribute("style", "white-space: normal");
29 |
30 | return fragment;
31 | }
32 |
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # The MIT License (MIT)
2 |
3 | Copyright (c) 2017 M-Files Oy
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | **THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.**
22 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Directives/TaskDirectiveWithDisplayName.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using MFiles.VAF.MultiserverMode;
3 | using System.Runtime.Serialization;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | public interface ITaskDirectiveWithDisplayName
8 | {
9 | ///
10 | /// The display name for the action/directive.
11 | /// This may be shown on dashboards to identify what this task does.
12 | ///
13 | /// "Convert Document 123 to PDF"
14 | /// "Import AAABBBCCC.pdf"
15 | string DisplayName { get; set; }
16 | }
17 |
18 | ///
19 | /// A task queue directive with a display name.
20 | ///
21 | [DataContract]
22 | public abstract class TaskDirectiveWithDisplayName
23 | : TaskDirective, ITaskDirectiveWithDisplayName
24 | {
25 | ///
26 | /// The display name for the action/directive.
27 | /// This may be shown on dashboards to identify what this task does.
28 | ///
29 | /// "Convert Document 123 to PDF"
30 | /// "Import AAABBBCCC.pdf"
31 | [DataMember]
32 | public string DisplayName { get; set; }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Attributes/CustomCommandAttribute.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Configuration.Interfaces.Domain;
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | namespace MFiles.VAF.Extensions
7 | {
8 | ///
9 | /// Declares that the associated method should be exposed via a command,
10 | /// and run when the command is executed.
11 | ///
12 | [AttributeUsage(AttributeTargets.Method, Inherited = true)]
13 | public class CustomCommandAttribute
14 | : Attribute
15 | {
16 | ///
17 | public string CommandId { get; set; }
18 |
19 | ///
20 | public string Label { get; set; }
21 |
22 | ///
23 | public string HelpText { get; set; }
24 |
25 | ///
26 | public string ConfirmMessage { get; set; }
27 |
28 | ///
29 | public bool Blocking { get; set; }
30 |
31 | public CustomCommandAttribute(string label)
32 | {
33 | this.Label = label;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/DictionaryExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MFiles.VAF.Extensions.ExtensionMethods
8 | {
9 | public static class DictionaryExtensionMethods
10 | {
11 | ///
12 | /// Adds or updates an item in a dictionary.
13 | ///
14 | /// The type of the key in the dictionary.
15 | /// The type of the value in the dictionary.
16 | /// The dictionary to update.
17 | /// The key of the item.
18 | /// The value for the item.
19 | public static void AddOrUpdate(this Dictionary dictionary, TKey key, TValue value)
20 | {
21 | // Sanity.
22 | if (null == dictionary)
23 | throw new ArgumentNullException(nameof(dictionary));
24 | if (null == key)
25 | throw new ArgumentNullException(nameof(key));
26 | if (dictionary.ContainsKey(key))
27 | dictionary[key] = value;
28 | else
29 | dictionary.Add(key, value);
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/EnvironmentBaseExtensionMethodsTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFiles.VAF.Extensions.ExtensionMethods;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using System;
5 |
6 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods
7 | {
8 | [TestClass]
9 | public class EnvironmentBaseExtensionMethodsTests
10 | {
11 | [TestMethod]
12 | [ExpectedException(typeof(ArgumentNullException))]
13 | public void IsCurrentUserSystemProcess_ThrowsWithNullArgument()
14 | {
15 | ((EnvironmentBase)null).IsCurrentUserSystemProcess();
16 | }
17 |
18 | [TestMethod]
19 | public void IsCurrentUserSystemProcess_ReturnsTrueForMFilesServerUser()
20 | {
21 | var environmentBase = new EnvironmentBase()
22 | {
23 | CurrentUserID = MFBuiltInUsers.MFilesServerUserID
24 | };
25 | Assert.IsTrue(environmentBase.IsCurrentUserSystemProcess());
26 | }
27 |
28 | [TestMethod]
29 | public void IsCurrentUserSystemProcess_ReturnsFalseForRandomValidUserId()
30 | {
31 | var environmentBase = new EnvironmentBase()
32 | {
33 | CurrentUserID = 4 // chosen by fair dice roll; guaranteed to be random.
34 | };
35 | Assert.IsFalse(environmentBase.IsCurrentUserSystemProcess());
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/SecurityRequiredAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MFiles.VAF.Configuration;
3 |
4 | namespace MFiles.VAF.Extensions.Tests.Configuration
5 | {
6 | ///
7 | /// Defines that a specific property of a class must be decorated with a .
8 | ///
9 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
10 | public class SecurityRequiredAttribute
11 | : Attribute
12 | {
13 | ///
14 | /// The property that needs the security attribute.
15 | ///
16 | public string PropertyName { get; set; }
17 |
18 | public bool IsPassword { get;set; }
19 | public SecurityAttribute.UserLevel ChangeBy { get; set; }
20 | public SecurityAttribute.UserLevel ViewBy { get; set; }
21 |
22 | public SecurityRequiredAttribute
23 | (
24 | string propertyName,
25 | bool isPassword = false,
26 | SecurityAttribute.UserLevel changeBy = SecurityAttribute.UserLevel.SystemAdmin,
27 | SecurityAttribute.UserLevel viewBy = SecurityAttribute.UserLevel.Undefined
28 | )
29 | : base()
30 | {
31 | this.PropertyName = propertyName;
32 | this.IsPassword = isPassword;
33 | this.ChangeBy = changeBy;
34 | this.ViewBy = viewBy;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/RecurringOperationConfigurationAttribute.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable once CheckNamespace
2 | using MFiles.VAF.Configuration;
3 | using System;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | ///
8 | /// Defines that the following property or field controls how a task processor should recur.
9 | /// The property or field that follows should be a .
10 | ///
11 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true, Inherited = true)]
12 | public class RecurringOperationConfigurationAttribute
13 | : JsonConfEditorAttribute, IRecurringOperationConfigurationAttribute
14 | {
15 | ///
16 | public string QueueID { get; set; }
17 |
18 | ///
19 | public string TaskType { get; set; }
20 |
21 | ///
22 | public Type[] ExpectedPropertyOrFieldTypes { get; private set; }
23 |
24 | public RecurringOperationConfigurationAttribute
25 | (
26 | string queueId,
27 | string taskType
28 | )
29 | {
30 | this.QueueID = queueId;
31 | this.TaskType = taskType;
32 | this.ExpectedPropertyOrFieldTypes = new[]
33 | {
34 | typeof(TimeSpan),
35 | typeof(IRecurrenceConfiguration)
36 | };
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Logging/Configuration.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MFiles.VAF.Extensions.Tests.Logging
9 | {
10 | [TestClass]
11 | public class ConfigurationTests
12 | {
13 | [TestMethod]
14 | public void Serialization()
15 | {
16 | var configuration = new ConfigurationProxy();
17 | var newString = Newtonsoft.Json.JsonConvert.SerializeObject
18 | (
19 | configuration,
20 | Newtonsoft.Json.Formatting.None,
21 | new NewtonsoftJsonConvert().JsonSerializerSettings
22 | );
23 | Assert.AreEqual("{}", newString);
24 | }
25 | [TestMethod]
26 | public void Serialization_WithVersion()
27 | {
28 | var configuration = new ConfigurationProxy() { Version = new Version("1.0" )};
29 | var newString = Newtonsoft.Json.JsonConvert.SerializeObject
30 | (
31 | configuration,
32 | Newtonsoft.Json.Formatting.None,
33 | new NewtonsoftJsonConvert().JsonSerializerSettings
34 | );
35 | Assert.AreEqual("{\"Version\":\"1.0\"}", newString);
36 | }
37 | public class ConfigurationProxy
38 | : MFiles.VAF.Extensions.Configuration.ConfigurationBase
39 | {
40 |
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/Rules/VAF20ToVAF23UpgradeRule.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration.Upgrading.Rules
5 | {
6 | ///
7 | /// A rule to move configuration from the location used in VAF 2.0 to the location used in VAF 2.3.
8 | ///
9 | public class VAF20ToVAF23UpgradeRule
10 | : MoveConfigurationUpgradeRule
11 | {
12 | ///
13 | /// The namespace used for all VAF 2.0 configurations.
14 | ///
15 | public const string SourceNamespaceLocation = "M-Files.Configuration.SavedConfigurations";
16 |
17 | ///
18 | /// The named value name (key) used by all VAF 2.3 applications.
19 | ///
20 | public const string TargetNamedValueName = "configuration";
21 |
22 | public VAF20ToVAF23UpgradeRule(VaultApplicationBase vaultApplication, string configurationNodeName, Version migrateFromVersion, Version migrateToVersion)
23 | : base
24 | (
25 | new SingleNamedValueItem
26 | (
27 | MFNamedValueType.MFConfigurationValue,
28 | SourceNamespaceLocation,
29 | configurationNodeName
30 | ),
31 | SingleNamedValueItem.ForLatestVAFVersion(vaultApplication),
32 | migrateFromVersion,
33 | migrateToVersion
34 | )
35 | {
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/TaskQueueBackgroundOperations/TaskQueueBackgroundOperationManager/RemoveBackgroundOperation.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MFiles.VAF;
3 | using MFiles.VAF.MultiserverMode;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | public partial class TaskQueueBackgroundOperationManager
8 | {
9 | ///
10 | /// Removes a background operation by the name.
11 | ///
12 | /// The name of the operation.
13 | /// if the operation could be removed.
14 | public bool RemoveBackgroundOperation
15 | (
16 | string name
17 | )
18 | {
19 | lock (TaskQueueBackgroundOperationManager._lock)
20 | {
21 | if (this.BackgroundOperations.ContainsKey(name) == false)
22 | throw new ArgumentException
23 | (
24 | String.Format
25 | (
26 | Resources.Exceptions.TaskQueueBackgroundOperations.BackgroundOperationDoesNotExist,
27 | name,
28 | this.QueueId
29 | ),
30 | nameof(name)
31 | );
32 |
33 | // Cancel all Future Executions
34 | this.BackgroundOperations[name].CancelFutureExecutions();
35 |
36 | // Remove it from the dictionary.
37 | return this.BackgroundOperations.Remove(name);
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/JsonConversion/JsonConvert.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace MFiles.VAF.Extensions
5 | {
6 | public abstract class JsonConvert
7 | : IJsonConvert
8 | {
9 | ///
10 | public abstract T Deserialize(string input);
11 |
12 | ///
13 | public abstract object Deserialize(string input, Type type);
14 |
15 | ///
16 | public abstract string Serialize(T input);
17 |
18 | ///
19 | public abstract string Serialize(object input, Type t);
20 |
21 | ///
22 | /// If these types are found then their default values are output when
23 | /// serializing instances.
24 | ///
25 | ///
26 | public static List DefaultValueSkippedTypes { get; } = new List();
27 |
28 | ///
29 | /// If these types are found in the JSON then the raw JSON will be maintained.
30 | /// Useful for types that are not expected to go through .NET serialization
31 | /// such as .
32 | ///
33 | public static List LeaveJsonAloneTypes { get; } = new List()
34 | {
35 | "MFiles.VAF.Configuration.JsonAdaptor.SearchConditionsJA"
36 | };
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/ObjVerEx/ToLookup.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 |
5 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods.ObjVerEx
6 | {
7 | [TestClass]
8 | public class ToLookup : TestBaseWithVaultMock
9 | {
10 | ///
11 | /// A null should throw an exception.
12 | ///
13 | [TestMethod]
14 | [ExpectedException(typeof(ArgumentNullException))]
15 | public void NullObjVerExThrows()
16 | {
17 | ((Common.ObjVerEx) null).ToLookup(true);
18 | }
19 |
20 | [TestMethod]
21 | [DataRow(false, 0, 1, 1)]
22 | [DataRow(true, 2, 3, -1)]
23 | public void ToLookupTest
24 | (
25 | bool latestVersion,
26 | int objectType,
27 | int objectID,
28 | int expectedOutput
29 | )
30 | {
31 | // Get the vault mock and populate it if needed.
32 | var vaultMock = this.GetVaultMock();
33 |
34 | // Create the ObjVerEx and set the properties.
35 | var objVerEx = new Common.ObjVerEx(vaultMock.Object, objectType, objectID, 1);
36 | Lookup lkp = objVerEx.ToLookup(latestVersion);
37 |
38 | // Assert.
39 | Assert.AreEqual(objectType, lkp.ObjectType);
40 | Assert.AreEqual(objectID, lkp.Item);
41 | Assert.AreEqual(expectedOutput, lkp.Version);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/ConfigurationBaseTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Tests.Configuration
10 | {
11 | [TestClass]
12 | public class ConfigurationBaseTests
13 | {
14 | [TestMethod]
15 | public void EnsureLoggingPropertyHasCorrectSecurityAttribute()
16 | {
17 | // Get the property itself.
18 | var type = typeof(VAF.Extensions.Configuration.ConfigurationBase);
19 | var propertyInfo = type.GetProperty(nameof(VAF.Extensions.Configuration.ConfigurationBase.Logging));
20 | Assert.IsNotNull(propertyInfo, $"Property {type.GetProperty(nameof(VAF.Extensions.Configuration.ConfigurationBase.Logging))} not found.");
21 |
22 | // Get the security attribute on the property.
23 | var securityAttribute = propertyInfo.GetCustomAttribute();
24 | Assert.IsNotNull(securityAttribute, "Security attribute not found.");
25 |
26 | // Ensure that it's configurable by vault admin.
27 | Assert.AreEqual(SecurityAttribute.UserLevel.VaultAdmin, securityAttribute.ChangeBy);
28 | Assert.AreEqual(SecurityAttribute.UserLevel.VaultAdmin, securityAttribute.ViewBy);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ConfigurationVersionAttribute.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace MFiles.VAF.Extensions.Configuration
7 | {
8 | ///
9 | /// Allows definition of the version of this configuration structure.
10 | /// Expected to be used on a class that implemented .
11 | ///
12 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
13 | public class ConfigurationVersionAttribute : Attribute
14 | {
15 | public Version Version { get; set; }
16 | public bool UsesCustomNVSLocation { get; set; }
17 | public string Namespace { get; set; }
18 | public string Key { get; set; }
19 | public MFNamedValueType NamedValueType { get; set; } = MFNamedValueType.MFSystemAdminConfiguration;
20 | public Type PreviousVersionType { get; set; }
21 |
22 | public ConfigurationVersionAttribute(string version)
23 | {
24 | this.Version = Version.Parse(version);
25 | }
26 | }
27 |
28 | ///
29 | /// Allows a declarative approach to configuration upgrading.
30 | ///
31 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
32 | public class ConfigurationUpgradeMethodAttribute: Attribute
33 | {
34 | public ConfigurationUpgradeMethodAttribute()
35 | {
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/Rules/VAF10ToVAF23UpgradeRule.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration.Upgrading.Rules
5 | {
6 | ///
7 | /// A rule to move configuration from the location used in VAF 1.0 to the location used in VAF 2.3 and higher.
8 | ///
9 | ///
10 | /// In VAF 1.0 was used to define the location of the configuration.
11 | /// In VAF 2.3, the location of the configuration is based upon the vault application class namespace.
12 | ///
13 | public class VAF10ToVAF23UpgradeRule
14 | : MoveConfigurationUpgradeRule
15 | {
16 | ///
17 | /// The named value name (key) used by all VAF 2.3 applications.
18 | ///
19 | public const string TargetNamedValueName = "configuration";
20 |
21 | public VAF10ToVAF23UpgradeRule(VaultApplicationBase vaultApplication, string @namespace, string name, Version migrateFromVersion, Version migrateToVersion)
22 | : base
23 | (
24 | new SingleNamedValueItem
25 | (
26 | MFNamedValueType.MFConfigurationValue,
27 | @namespace,
28 | name
29 | ),
30 | SingleNamedValueItem.ForLatestVAFVersion(vaultApplication),
31 | migrateFromVersion,
32 | migrateToVersion
33 | )
34 | {
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/StringExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace MFiles.VAF.Extensions
4 | {
5 | public static class StringExtensionMethods
6 | {
7 | ///
8 | /// Escapes so that it can be used in XML.
9 | ///
10 | /// The string to escape.
11 | /// The escaped string, or null if is null.
12 | public static string EscapeXmlForDashboard(this string input)
13 | {
14 | return input.EscapeXmlForDashboard(null);
15 | }
16 |
17 | ///
18 | /// Runs through ,
19 | /// then escapes it so that it can be used in XML.
20 | ///
21 | /// The string to escape.
22 | /// The arguments to use in the call.
23 | /// The escaped string, or null if is null.
24 | public static string EscapeXmlForDashboard(this string format, params object[] args)
25 | {
26 | if (null == format)
27 | return null;
28 | return args == null || args.Length == 0
29 | ? System.Security.SecurityElement.Escape(format)
30 | : System.Security.SecurityElement.Escape(String.Format(format, args));
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/ObjVerEx/ToLookup.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFilesAPI;
3 | using System;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | public static partial class ObjVerExExtensionMethods
8 | {
9 | ///
10 | /// Function to return MFilesAPI.Lookup representation of this MFiles.VAF.Common.ObjVerEx with a specific lookup version or the always latest
11 | ///
12 | /// The object version to check.
13 | /// false return the specific latest version
14 | /// MFilesAPI.Lookup representation of this MFiles.VAF.Common.ObjVerEx
15 | public static Lookup ToLookup(this ObjVerEx objVerEx, bool latestVersion)
16 | {
17 | // Sanity.
18 | if (null == objVerEx)
19 | throw new ArgumentNullException(nameof(objVerEx));
20 |
21 | // Get the standard implementation with version data.
22 | var lookup = objVerEx.ToLookup();
23 |
24 | // If we do not want the version data then we're all good.
25 | if (!latestVersion)
26 | return lookup;
27 |
28 | // Latest version: remove the version data and return.
29 | lookup.Version = -1;
30 |
31 | return lookup;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Directives/ObjVerExTaskDirectiveTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFiles.VAF.Extensions;
3 | using MFilesAPI;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.Directives
8 | {
9 | [TestClass]
10 | public class ObjVerExTaskDirectiveTests
11 | : TaskDirectiveWithDisplayNameTestsBase
12 | {
13 | [TestMethod]
14 | public void ObjVerExIsReadWrite()
15 | {
16 | var type = typeof(ObjVerExTaskDirective);
17 | var property = type.GetProperty(nameof(ObjVerExTaskDirective.ObjVerEx));
18 | Assert.IsNotNull(property);
19 | Assert.IsTrue(property.CanRead);
20 | Assert.IsTrue(property.CanWrite);
21 | }
22 |
23 | [TestMethod]
24 | [ExpectedException(typeof(ArgumentNullException))]
25 | public void FromObjVerThrowsIfArgumentNull()
26 | {
27 | ((ObjVer)null).ToObjVerExTaskDirective();
28 | }
29 |
30 | [TestMethod]
31 | [ExpectedException(typeof(ArgumentNullException))]
32 | public void FromObjVerExThrowsIfArgumentNull()
33 | {
34 | ((ObjVerEx)null).ToObjVerExTaskDirective();
35 | }
36 |
37 | [TestMethod]
38 | public void FromObjVerCorrectString()
39 | {
40 | var objVer = new ObjVer();
41 | objVer.SetIDs(0, 1, 2);
42 | Assert.AreEqual
43 | (
44 | "(0-1-2)",
45 | objVer.ToObjVerExTaskDirective().ObjVerEx
46 | );
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/JsonConfEditorRequiredAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MFiles.VAF.Configuration;
3 |
4 | namespace MFiles.VAF.Extensions.Tests.Configuration
5 | {
6 | ///
7 | /// Defines that specific properties of a class must be decorated with a
8 | /// with specific values.
9 | ///
10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
11 | public class JsonConfEditorRequiredAttribute
12 | : Attribute
13 | {
14 | ///
15 | /// The property that needs the security attribute.
16 | ///
17 | public string PropertyName { get; set; }
18 |
19 | public object DefaultValue { get; set; }
20 |
21 | public bool Hidden { get; set; }
22 |
23 | public string ShowWhen { get; set; }
24 |
25 | public string HideWhen { get; set; }
26 | public string ChildTypeEditor { get; set; }
27 |
28 | public JsonConfEditorRequiredAttribute
29 | (
30 | string propertyName,
31 | object defaultValue = null,
32 | bool hidden = false,
33 | string showWhen = null,
34 | string hideWhen = null,
35 | string childTypeEditor = null
36 | )
37 | {
38 | this.PropertyName = propertyName;
39 | this.DefaultValue = defaultValue;
40 | this.Hidden = hidden;
41 | this.ShowWhen = showWhen;
42 | this.HideWhen = hideWhen;
43 | this.ChildTypeEditor = childTypeEditor;
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/Commands/CustomDomainCommandResolution/AggregatedCustomDomainCommandResolver.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution
7 | {
8 | ///
9 | /// An implementation of that
10 | /// can be used to return custom domain commands from multiple other instances
11 | /// of .
12 | ///
13 | public class AggregatedCustomDomainCommandResolver
14 | : ICustomDomainCommandResolver
15 | {
16 | public List CustomDomainCommandResolvers { get; }
17 | = new List();
18 |
19 | ///
20 | public virtual IEnumerable GetCustomDomainCommands()
21 | => CustomDomainCommandResolvers?
22 | .SelectMany(r => r.GetCustomDomainCommands()?.AsNotNull())?
23 | .AsNotNull();
24 |
25 | ///
26 | public virtual DashboardDomainCommandEx GetDashboardDomainCommand(string commandId, DashboardCommandStyle style = DashboardCommandStyle.Link)
27 | => CustomDomainCommandResolvers?
28 | .Select(c => c.GetDashboardDomainCommand(commandId, style))
29 | .FirstOrDefault(c => c != null);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Directives/GenericTaskDirective.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.Serialization;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MFiles.VAF.Extensions
9 | {
10 | [DataContract]
11 | public class GenericTaskDirective : TaskDirectiveWithDisplayName
12 | {
13 | public GenericTaskDirective()
14 | {
15 |
16 | }
17 | public GenericTaskDirective(TA item1, string displayName = null)
18 | : this()
19 | {
20 | this.DisplayName = displayName;
21 | this.Item1 = item1;
22 | }
23 |
24 | [DataMember]
25 | public TA Item1 { get; set; }
26 | }
27 |
28 | [DataContract]
29 | public class GenericTaskDirective : GenericTaskDirective
30 | {
31 | public GenericTaskDirective()
32 | : base()
33 | {
34 |
35 | }
36 | public GenericTaskDirective(TA item1, TB item2, string displayName = null)
37 | : base(item1, displayName)
38 | {
39 | this.Item2 = item2;
40 | }
41 |
42 | [DataMember]
43 | public TB Item2 { get; set; }
44 | }
45 |
46 | [DataContract]
47 | public class GenericTaskDirective : GenericTaskDirective
48 | {
49 | public GenericTaskDirective()
50 | : base()
51 | {
52 |
53 | }
54 | public GenericTaskDirective(TA item1, TB item2, TC item3, string displayName = null)
55 | : base(item1, item2, displayName)
56 | {
57 | this.Item3 = item3;
58 | }
59 |
60 | [DataMember]
61 | public TC Item3 { get; set; }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Logging/Sensitivity/Filters/INamedValueItemSensitivityFilter.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Logging;
2 | using MFiles.VAF.Configuration.Logging.SensitivityFilters;
3 | using MFiles.VAF.Extensions.Configuration.Upgrading;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Logging.Sensitivity.Filters
11 | {
12 | ///
13 | /// Implements a log sensitivity filter for .
14 | ///
15 | public class INamedValueItemSensitivityFilter
16 | : LogSensitivityFilterBase
17 | {
18 | public override string FilterValueForLogging
19 | (
20 | INamedValueItem input,
21 | LogSensitivity level,
22 | IEnumerable customFlags,
23 | string format,
24 | IFormatProvider formatProvider
25 | )
26 | {
27 | if (null == input)
28 | return String.Empty;
29 |
30 | if (input is ISingleNamedValueItem singleNamedValueItem)
31 | {
32 | return $"value {singleNamedValueItem.Name} in namespace {singleNamedValueItem.Namespace} of type {singleNamedValueItem.NamedValueType}";
33 | }
34 | else if (input is IEntireNamespaceNamedValueItem entireNamespaceNamedValueItem)
35 | {
36 | return $"all values in namespace {entireNamespaceNamedValueItem.Namespace} of type {entireNamespaceNamedValueItem.NamedValueType}";
37 | }
38 | else
39 | {
40 | return $"(unhandled input type: {input.GetType().FullName}";
41 | }
42 |
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ConfigurableVaultApplicationBase.GetCommands.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Reflection;
7 |
8 | namespace MFiles.VAF.Extensions
9 | {
10 | public abstract partial class ConfigurableVaultApplicationBase
11 | {
12 | ///
13 | public override IEnumerable GetCommands(IConfigurationRequestContext context)
14 | {
15 | // Return the base commands, if any.
16 | foreach (var c in base.GetCommands(context)?.AsNotNull())
17 | yield return c;
18 |
19 | // Return any commands that the resolver provides.
20 | {
21 | var resolver = this.GetCustomDomainCommandResolver();
22 | if (resolver != null)
23 | {
24 | foreach (var c in resolver.GetCustomDomainCommands()?.AsNotNull())
25 | yield return c;
26 | }
27 | }
28 | }
29 |
30 | ///
31 | /// Returns an object - or - that searches known object types
32 | /// to find methods decorated with .
33 | ///
34 | /// The resolver, or if none is configured.
35 | /// Returns by default.
36 | public virtual ICustomDomainCommandResolver GetCustomDomainCommandResolver()
37 | {
38 | return new DefaultCustomDomainCommandResolver(this);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/Rules/IUpgradeRule.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using MFilesAPI;
3 | using System;
4 |
5 | namespace MFiles.VAF.Extensions.Configuration.Upgrading.Rules
6 | {
7 | ///
8 | /// Defines a rule that can be run ()
9 | /// to somehow upgrade the configuration.
10 | ///
11 | public interface IUpgradeRule
12 | {
13 | ///
14 | /// Returns whether the rules are correctly configured to allow execution.
15 | ///
16 | /// if execution can be attempted.
17 | bool IsValid();
18 |
19 | ///
20 | /// Runs this rule against the provided .
21 | ///
22 | /// The vault to run the code against.
23 | /// if the rule ran successfully, if the rule chose not to run (e.g. there was nothing to migrate).
24 | /// May throw exceptions.
25 | bool Execute(Vault vault);
26 |
27 | ///
28 | /// The version that this rule migrates from.
29 | /// If the version in the configuration is higher than this then this rule will be skipped.
30 | ///
31 | Version MigrateFromVersion { get; }
32 |
33 | ///
34 | /// The version of the configuration after this migration has completed.
35 | ///
36 | Version MigrateToVersion { get; }
37 |
38 | ///
39 | /// The converter to serialize/deserialize JSON.
40 | ///
41 | IJsonConvert JsonConvert { get; set; }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/EntireNamespaceNamedValueItem.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration.Upgrading
5 | {
6 | public interface IEntireNamespaceNamedValueItem
7 | : INamedValueItem
8 | {
9 |
10 | }
11 |
12 | ///
13 | /// Represents all items within a namespace.
14 | ///
15 | public class EntireNamespaceNamedValueItem
16 | : NamedValueItemBase, IEntireNamespaceNamedValueItem
17 | {
18 | ///
19 | /// Creates a reference to all items in a given namespace.
20 | ///
21 | /// The type of item(s) to be read/written.
22 | /// The location of the item(s).
23 | public EntireNamespaceNamedValueItem(MFNamedValueType namedValueType, string @namespace)
24 | : base(namedValueType, @namespace)
25 | {
26 | }
27 |
28 | ///
29 | public override NamedValues GetNamedValues(INamedValueStorageManager manager, Vault vault)
30 | {
31 | manager = manager ?? throw new ArgumentNullException(nameof(manager));
32 | return manager.GetNamedValues(vault, this.NamedValueType, this.Namespace);
33 | }
34 |
35 | ///
36 | public override bool IsValid()
37 | => false == string.IsNullOrWhiteSpace(this.Namespace);
38 |
39 | ///
40 | public override void SetNamedValues(INamedValueStorageManager manager, Vault vault, NamedValues namedValues)
41 | {
42 | manager = manager ?? throw new ArgumentNullException(nameof(manager));
43 | manager.SetNamedValues(vault, this.NamedValueType, this.Namespace, namedValues);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/ApplicationOverviewDashboardContent/IApplicationOverviewDashboardContentRenderer.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent;
3 | using System;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace MFiles.VAF.Extensions.Dashboards.ApplicationOverviewDashboardContent
8 | {
9 | public interface IApplicationOverviewDashboardContentRenderer
10 | {
11 | ///
12 | /// The title for the application details panel.
13 | ///
14 | string ApplicationDetailsTitle { get; set; }
15 |
16 | ///
17 | /// The title for the licensing status panel.
18 | ///
19 | string LicensingStatusTitle { get; set; }
20 |
21 | ///
22 | /// Whether to show the version section.
23 | ///
24 | bool ShowVersion { get; set; }
25 |
26 | ///
27 | /// Whether to show the publisher section.
28 | ///
29 | bool ShowPublisher { get; set; }
30 |
31 | ///
32 | /// Whether to show the copyright section.
33 | ///
34 | bool ShowCopyright { get; set; }
35 |
36 | ///
37 | /// Whether to show the MSM status.
38 | ///
39 | bool ShowMultiServerModeStatus { get; set; }
40 |
41 | ///
42 | /// Whether to show the licensing status.
43 | ///
44 | bool ShowLicenseStatus { get; set; }
45 |
46 | ///
47 | /// Gets the application overview dashboard content.
48 | ///
49 | /// The content, or null if nothing to render.
50 | IDashboardContent GetDashboardContent();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/ObjVerEx/GetOwner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MFiles.VAF.Common;
3 |
4 | namespace MFiles.VAF.Extensions
5 | {
6 | public static partial class ObjVerExExtensionMethods
7 | {
8 | ///
9 | /// Gets the "owner" of this object.
10 | ///
11 | /// The child/owned object.
12 | /// The parent/owning object.
13 | /// Can return null if the owner is deleted.
14 | /// Thrown if the represents an object without an owner.
15 | public static ObjVerEx GetOwner
16 | (
17 | this ObjVerEx objVerEx
18 | )
19 | {
20 | // Sanity.
21 | if (null == objVerEx)
22 | throw new ArgumentNullException(nameof(objVerEx));
23 |
24 | // Load the current object's ObjType to find the owning type.
25 | var objType = objVerEx
26 | .Vault
27 | .ObjectTypeOperations
28 | .GetObjectType(objVerEx.Type);
29 |
30 | // Does this have an owning type?
31 | if (false == objType.HasOwnerType)
32 | throw new ArgumentException
33 | (
34 | string.Format
35 | (
36 | Resources.Exceptions.ObjVerExExtensionMethods.GetOwner_ObjectTypeDoesNotHaveOwner,
37 | objType.NamePlural
38 | ),
39 | nameof(objVerEx)
40 | );
41 |
42 | // Get the owning type.
43 | var owningObjType = objVerEx
44 | .Vault
45 | .ObjectTypeOperations
46 | .GetObjectType(objType.OwnerType);
47 |
48 | // Get the direct reference on this ObjVerEx to the owner.
49 | return objVerEx.GetDirectReference(owningObjType.OwnerPropertyDef);
50 | }
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Initialization.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using MFiles.VAF.Configuration.Logging;
4 | using MFiles.VAF.Configuration.Logging.Targets;
5 | using MFiles.VAF.Configuration.Logging.NLog;
6 |
7 | namespace MFiles.VAF.Extensions.Tests
8 | {
9 | [TestClass]
10 | public class Initialization
11 | {
12 | private class MSTestContextTarget
13 | : global::NLog.Targets.TargetWithLayout
14 | {
15 | protected TestContext TestContext { get; }
16 | public MSTestContextTarget(TestContext testContext)
17 | {
18 | this.TestContext = testContext;
19 | }
20 | protected override void Write(NLog.LogEventInfo logEvent)
21 | {
22 | this.TestContext.WriteLine(this.RenderLogEvent(Layout, logEvent));
23 | }
24 | }
25 | [AssemblyInitialize]
26 | public static void MyTestInitialize(TestContext testContext)
27 | {
28 | var layout = "${level}:\t${message}\t${logger}${onexception:${newline}${exception:format=ToString:innerformat=ToString:separator=\r\n}}";
29 |
30 | LogManager.Current = new MFiles.VAF.Configuration.Logging.NLog.NLogLogManager();
31 | LogManager.Initialize(Moq.Mock.Of());
32 | global::NLog.LogManager.Configuration = new NLog.Config.LoggingConfiguration();
33 | // Output some stuff to the standard output.
34 | // This means it's associated with each test; click a test to see the standard output (debug lines!)
35 | global::NLog.LogManager.Configuration.AddRule
36 | (
37 | global::NLog.LogLevel.Trace,
38 | global::NLog.LogLevel.Fatal,
39 | new MSTestContextTarget(testContext)
40 | {
41 | Layout = layout
42 | }
43 | );
44 | global::NLog.LogManager.ReconfigExistingLoggers();
45 |
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Directives/TaskDirectiveWithDisplayNameTestsBase.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Runtime.Serialization;
5 |
6 | namespace MFiles.VAF.Extensions.Tests.Directives
7 | {
8 | public abstract class TaskDirectiveWithDisplayNameTestsBase
9 | where TDirective : TaskDirectiveWithDisplayName, new()
10 | {
11 | [TestMethod]
12 | public void DisplayNameIsReadWrite()
13 | {
14 | var type = typeof(TDirective);
15 | var property = type.GetProperty("DisplayName");
16 | Assert.IsNotNull(property);
17 | Assert.IsTrue(property.CanRead);
18 | Assert.IsTrue(property.CanWrite);
19 | }
20 |
21 | [TestMethod]
22 | public void DisplayName_HasDataMemberAttribute()
23 | {
24 | this.AssertPropertyHasDataMemberAttribute(nameof(TaskDirectiveWithDisplayName.DisplayName));
25 | }
26 |
27 | [TestMethod]
28 | public void DisplayNamePersistsData()
29 | {
30 | var instance = new TDirective();
31 | Assert.IsNull(instance.DisplayName);
32 | instance.DisplayName = "hello world";
33 | Assert.AreEqual("hello world", instance.DisplayName);
34 | }
35 |
36 | [TestMethod]
37 | public void DirectiveType_HasDataContractAttribute()
38 | {
39 | var type = typeof(TDirective);
40 | Assert.IsNotNull(type.GetCustomAttributes(false).FirstOrDefault(a => a is DataContractAttribute));
41 | }
42 |
43 | protected void AssertPropertyHasDataMemberAttribute(string propertyName, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance)
44 | {
45 | var type = typeof(TDirective);
46 | Assert.IsNotNull(type.GetProperty(propertyName, bindingFlags).GetCustomAttribute(typeof(DataMemberAttribute)));
47 | }
48 |
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/TimestampExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.ExtensionMethods
5 | {
6 | public static class TimestampExtensionMethods
7 | {
8 | ///
9 | /// Converts a to a
10 | /// instance, maintaining the highest level of precision we can.
11 | ///
12 | /// The datetime to represent.
13 | /// The timestamp.
14 | public static Timestamp ToPreciseTimestamp(this DateTime dateTime)
15 | {
16 | return new TimestampClass
17 | {
18 | Year = (uint)dateTime.Year,
19 | Month = (uint)dateTime.Month,
20 | Day = (uint)dateTime.Day,
21 | Hour = (uint)dateTime.Hour,
22 | Minute = (uint)dateTime.Minute,
23 | Second = (uint)dateTime.Second,
24 | Fraction = (uint)(dateTime.Ticks % 1e7M * 100) // 10,000,000 ticks in a second, 100 nanoseconds in a tick
25 | };
26 | }
27 |
28 | ///
29 | /// Converts a to a
30 | /// instance, maintaining the highest level of precision we can.
31 | ///
32 | /// The timestamp to represent.
33 | /// The DateTime.
34 | public static DateTime ToPreciseDateTime
35 | (
36 | this Timestamp timestamp,
37 | DateTimeKind kind = DateTimeKind.Local
38 | )
39 | {
40 | return new DateTime
41 | (
42 | (int)timestamp.Year,
43 | (int)timestamp.Month,
44 | (int)timestamp.Day,
45 | (int)timestamp.Hour,
46 | (int)timestamp.Minute,
47 | (int)timestamp.Second,
48 | kind
49 | )
50 | .AddTicks(timestamp.Fraction / 100); // 100 nanoseconds in a tick.
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/JsonConvertTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Logging;
2 | using MFiles.VAF.Configuration.Logging.NLog.Configuration;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.Linq;
9 | using System.Runtime.Serialization;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace MFiles.VAF.Extensions.Tests.Configuration
14 | {
15 | [TestClass]
16 | public class JsonConvertTests
17 | {
18 | [TestMethod]
19 | public void SerializesMinimumLogLevelCorrectly()
20 | {
21 | var jsonConvert = new NewtonsoftJsonConvert();
22 | var loggingConfiguration = jsonConvert.Deserialize(
23 | @"
24 | {
25 | ""FileTargetConfigurations"": [
26 | {
27 | ""MinimumLogLevel"": ""Trace""
28 | }
29 | ]
30 | }"
31 | );
32 |
33 | Assert.AreEqual
34 | (
35 | LogLevel.Trace,
36 | loggingConfiguration.FileTargetConfigurations.First().MinimumLogLevel,
37 | "LogLevel default value 'Trace' was not retained for MinimumLogLevel during deserialization."
38 | );
39 |
40 | // Parse the serialised data into a JObject.
41 | // This allows us to test what was actually serialised, and ignore anything that
42 | // was populated/created during the serialisation process.
43 | var jObject = JObject.Parse(jsonConvert.Serialize(loggingConfiguration));
44 |
45 | Assert.AreEqual
46 | (
47 | LogLevel.Trace,
48 | Enum.Parse(typeof(LogLevel), ((JArray)jObject["FileTargetConfigurations"])[0].Value("MinimumLogLevel")),
49 | "LogLevel default value 'Trace' was not retained for MinimumLogLevel during serialization."
50 | );
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/ObjVerEx/GetLookupIDs.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFiles.VAF.Configuration;
3 | using MFilesAPI;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace MFiles.VAF.Extensions
8 | {
9 | public static partial class ObjVerExExtensionMethods
10 | {
11 | ///
12 | /// Return list of lookup ids for a property, works on lookup and lookups.
13 | /// Returns empty list, if property is missing from the object or no valid lookups were found.
14 | ///
15 | /// Object to get lookups
16 | /// property to get lookups for
17 | /// Are deleted lookups included or not. Default is that deleted lookups are not included.
18 | /// list of ids, empty list if no lookups found or property is missing
19 | public static List GetLookupIDs(this ObjVerEx objVerEx, MFIdentifier property, bool includeDeleted = false)
20 | {
21 | var lookupIDs = new List();
22 | PropertyValue pv = objVerEx.GetProperty(property);
23 | // Check the property's lookups.
24 | if (pv != null && !pv.Value.IsNULL() &&
25 | (pv.Value.DataType == MFDataType.MFDatatypeLookup ||
26 | pv.Value.DataType == MFDataType.MFDatatypeMultiSelectLookup))
27 | {
28 | Lookups lks = pv.Value.GetValueAsLookups();
29 | foreach (Lookup lookup in lks)
30 | {
31 | if (!lookupIDs.Contains(lookup.Item))
32 | {
33 | // If the lookup is deleted, then based on the includeDeleted parameter determines is the value added or not
34 | if (lookup.Deleted && !includeDeleted)
35 | continue;
36 |
37 | lookupIDs.Add(lookup.Item);
38 | }
39 | }
40 | }
41 |
42 | return lookupIDs;
43 | }
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/NewtonsoftJsonConvert.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.JsonAdaptor;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Runtime.Serialization;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Tests
11 | {
12 | [TestClass]
13 | public class NewtonsoftJsonConvertTests
14 | {
15 | [DataContract]
16 | public class Configuration
17 | {
18 | [DataMember]
19 | public SearchConditionsJA SearchConditions { get; set; }
20 | }
21 |
22 | [TestMethod]
23 | public void SearchConditions()
24 | {
25 | Configuration config = new Configuration();
26 | config.SearchConditions = new SearchConditionsJA();
27 | config.SearchConditions.Add(new SearchConditionJA()
28 | {
29 | ConditionType = MFilesAPI.MFConditionType.MFConditionTypeEqual,
30 | Expression = new ExpressionJA()
31 | {
32 | PropertyDef = 0,
33 | DataType = MFilesAPI.MFDataType.MFDatatypeText
34 | },
35 | TypedValue = new TypedValueJA()
36 | {
37 | DataType = MFilesAPI.MFDataType.MFDatatypeText,
38 | Value = "hello world"
39 | }
40 | });
41 | config.SearchConditions.Add(new SearchConditionJA()
42 | {
43 | ConditionType = MFilesAPI.MFConditionType.MFConditionTypeEqual,
44 | Expression = new ExpressionJA()
45 | {
46 | PropertyDef = 123,
47 | DataType = MFilesAPI.MFDataType.MFDatatypeBoolean
48 | },
49 | TypedValue = new TypedValueJA()
50 | {
51 | DataType = MFilesAPI.MFDataType.MFDatatypeBoolean,
52 | Value = true
53 | }
54 | });
55 |
56 | var serializer = new NewtonsoftJsonConvert();
57 | var x = serializer.Serialize(config);
58 | Assert.IsNotNull(x);
59 |
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/AsynchronousDashboardContent/DashboardQueueAndTaskDetails.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using System.Collections.Generic;
3 |
4 | namespace MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent
5 | {
6 | public class DashboardQueueAndTaskDetails
7 | {
8 | ///
9 | /// The queue ID being represented.
10 | ///
11 | public string QueueId { get; set; }
12 |
13 | ///
14 | /// The task type being represented.
15 | ///
16 | public string TaskType { get; set; }
17 |
18 | ///
19 | /// The (display) name for this queue/task-type to show on the dashboard.
20 | ///
21 | public string Name { get; set; }
22 |
23 | ///
24 | /// The description to show on the dashboard.
25 | ///
26 | public string Description { get; set; }
27 |
28 | ///
29 | /// Whether this section should be shown degraded or not.
30 | ///
31 | public bool ShowDegradedDashboard => TasksInQueue >= AsynchronousDashboardContentSettings.DegradedDashboardThreshold;
32 |
33 | ///
34 | /// The number of tasks of this type in the queue.
35 | ///
36 | public int TasksInQueue { get; set; }
37 |
38 | ///
39 | /// Any commands to render related to this queue/task-type.
40 | ///
41 | public List Commands { get; set; } = new List();
42 |
43 | ///
44 | /// If this is a recurring process then details on the recurring frequency.
45 | ///
46 | public IRecurrenceConfiguration RecurrenceConfiguration { get; set; }
47 |
48 | ///
49 | /// Determines the order the operations are shown on the dashboard.
50 | ///
51 | public int Order { get; set; }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Logging/Sensitivity/Filters/ObjVerExLogSensitivityFilter.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFiles.VAF.Configuration.Logging;
3 | using MFiles.VAF.Configuration.Logging.SensitivityFilters;
4 | using MFilesAPI;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace MFiles.VAF.Extensions.Logging.Sensitivity.Filters
12 | {
13 | ///
14 | /// Implements a log sensitivity filter for .
15 | ///
16 | public class ObjVerExLogSensitivityFilter
17 | : LogSensitivityFilterBase
18 | {
19 | ///
20 | /// Resolve the delegate filter we use.
21 | ///
22 | /// A sensitivity filter for s.
23 | protected ILogSensitivityFilter GetObjectVersionFilter()
24 | {
25 | return ResolveDelegateFilter(() => new ObjectVersionLogSensitivityFilter());
26 | }
27 |
28 | ///
29 | public override IEnumerable GetSupportedCustomFlags()
30 | {
31 | // Return any sensitivity flags from the underlying implementation.
32 | foreach (SensitivityFlag sf in GetObjectVersionFilter()?.GetSupportedCustomFlags() ?? Enumerable.Empty())
33 | yield return sf;
34 | }
35 |
36 | ///
37 | public override string FilterValueForLogging(ObjVerEx value, LogSensitivity level, IEnumerable customFlags, string format, IFormatProvider formatProvider)
38 | {
39 | // Sanity.
40 | if (null == value?.Info)
41 | return String.Empty;
42 |
43 | // Use the object version filter.
44 | return this.GetObjectVersionFilter()?
45 | .FilterValueForLogging(value.Info, level, customFlags, format, formatProvider);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/JsonConversion/IJsonConvert.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.JsonAdaptor;
2 | using MFiles.VAF.Configuration.JsonEditor;
3 | using MFiles.VAF.Extensions.Configuration.Upgrading.Rules;
4 | using System;
5 | using System.CodeDom;
6 | using System.Collections;
7 | using System.Globalization;
8 | using System.Linq.Expressions;
9 | using System.Runtime.CompilerServices;
10 | using System.Runtime.InteropServices;
11 |
12 | namespace MFiles.VAF.Extensions
13 | {
14 | public interface IJsonConvert
15 | {
16 | ///
17 | /// Deserializes into an instance of .
18 | ///
19 | /// The type to deserialize to.
20 | /// The serialized version.
21 | /// The instance.
22 | T Deserialize(string input);
23 |
24 | ///
25 | /// Deserializes to an instance of .
26 | ///
27 | /// The serialized version.
28 | /// The type to deserialize to.
29 | /// The instance.
30 | object Deserialize(string input, Type type);
31 |
32 | ///
33 | /// Serializes .
34 | ///
35 | /// The type to deserialize from.
36 | /// The object to deserialize.
37 | /// The instance.
38 | string Serialize(T input);
39 |
40 | ///
41 | /// Serializes .
42 | ///
43 | /// The type to serialize from.
44 | /// The object to deserialize.
45 | /// The instance.
46 | string Serialize(object input, Type t);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/HasFiles.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection to restrict files by their size.
19 | ///
20 | /// The to add the condition to.
21 | /// Whether to include items with files (true) or include items without files (false).
22 | /// The provided, for chaining.
23 | public static MFSearchBuilder HasFiles
24 | (
25 | this MFSearchBuilder searchBuilder,
26 | bool hasFiles
27 | )
28 | {
29 | // Sanity.
30 | if (null == searchBuilder)
31 | throw new ArgumentNullException(nameof(searchBuilder));
32 |
33 | // Create the search condition.
34 | var searchCondition = new SearchCondition
35 | {
36 | ConditionType = MFConditionType.MFConditionTypeEqual
37 | };
38 |
39 | // Set up the file value expression.
40 | searchCondition.Expression.SetFileValueExpression
41 | (
42 | MFFileValueType.MFFileValueTypeHasFiles
43 | );
44 |
45 | // Search by the size
46 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeBoolean, hasFiles);
47 |
48 | // Add the search condition to the collection.
49 | searchBuilder.Conditions.Add(-1, searchCondition);
50 |
51 | // Return the search builder for chaining.
52 | return searchBuilder;
53 | }
54 |
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/CheckedOut.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection to find items by their checkout status.
19 | ///
20 | /// The to add the condition to.
21 | /// Whether to include items that are checked out (true) or include items that are not checked out (false).
22 | /// The provided, for chaining.
23 | public static MFSearchBuilder IsCheckedOut
24 | (
25 | this MFSearchBuilder searchBuilder,
26 | bool isCheckedOut = false
27 | )
28 | {
29 | // Sanity.
30 | if (null == searchBuilder)
31 | throw new ArgumentNullException(nameof(searchBuilder));
32 |
33 |
34 | // Create the search condition.
35 | var searchCondition = new SearchCondition
36 | {
37 | ConditionType = MFConditionType.MFConditionTypeEqual
38 | };
39 |
40 | // Set up the status value expression.
41 | searchCondition.Expression.SetStatusValueExpression(MFStatusType.MFStatusTypeCheckedOut);
42 |
43 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeBoolean, isCheckedOut);
44 |
45 | // Add the search condition to the collection.
46 | searchBuilder.Conditions.Add(-1, searchCondition);
47 |
48 | // Return the search builder for chaining.
49 | return searchBuilder;
50 | }
51 |
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/FindCount.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFilesAPI;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | public static partial class MFSearchBuilderExtensionMethods
12 | {
13 |
14 | ///
15 | /// Finds the number of items that are returned by this search.
16 | /// Wraps https://www.m-files.com/api/documentation/#MFilesAPI~VaultObjectSearchOperations~GetObjectCountInSearch.html.
17 | ///
18 | /// The to add the condition to.
19 | /// Any search flags to use.
20 | /// The count.
21 | public static int FindCount
22 | (
23 | this MFSearchBuilder searchBuilder,
24 | MFSearchFlags searchFlags = MFSearchFlags.MFSearchFlagNone
25 | )
26 | {
27 | // Sanity.
28 | if (null == searchBuilder)
29 | throw new ArgumentNullException(nameof(searchBuilder));
30 | if (null == searchBuilder.Vault)
31 | throw new ArgumentException(Resources.Exceptions.MFSearchBuilderExtensionMethods.VaultReferenceNull, nameof(searchBuilder));
32 | if (null == searchBuilder.Vault.ObjectSearchOperations)
33 | throw new ArgumentException(Resources.Exceptions.MFSearchBuilderExtensionMethods.VaultObjectSearchOperationsReferenceNull, nameof(searchBuilder));
34 | if (null == searchBuilder.Conditions)
35 | throw new ArgumentException(Resources.Exceptions.MFSearchBuilderExtensionMethods.SearchConditionsNull, nameof(searchBuilder));
36 |
37 | // Use the GetObjectCountInSearch API method.
38 | return searchBuilder
39 | .Vault
40 | .ObjectSearchOperations
41 | .GetObjectCountInSearch
42 | (
43 | searchBuilder.Conditions,
44 | searchFlags
45 | );
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/TaskQueueBackgroundOperations/Readme.md:
--------------------------------------------------------------------------------
1 | # Task queue extensions
2 |
3 | ## Extension methods
4 |
5 | **View more details here: [ExtensionMethods](ExtensionMethods)**
6 |
7 | ## TaskQueueBackgroundOperationManager
8 |
9 | **View more details here: [TaskQueueBackgroundOperationManager](TaskQueueBackgroundOperationManager)**
10 |
11 | The Vault Application Framework's `BackgroundOperationManager`, and the associated concept of background operations, are depreciated when using version 2.2 and higher of the Vault Application Framework. This is because VAF 2.2 and onwards enable support for M-Files Multi-Server Mode and try and depreciate functionality that is not directly compatible with this functionality. VAF 1.0-style background operations will continue to function when targeting VAF 2.2, but their behaviour may be unexpected when running in Multi-Server environments.
12 |
13 | The [replacement approach](https://developer.m-files.com/Frameworks/Vault-Application-Framework/Multi-Server-Mode/Recurring-Tasks/) is, instead, to use a task queue. Your task queue would be populated with a single task representing the code you wish to execute and, if the operation is to recur, the task is set to [automatically re-queue itself upon completion](https://developer.m-files.com/Frameworks/Vault-Application-Framework/Multi-Server-Mode/Recurring-Tasks/#recurring). However, this approach requires a significant amount of boilerplate code.
14 |
15 | The `TaskQueueBackgroundOperationManager` class wraps the above approach, allowing a method signature very similar to the typical background operation approach.
16 |
17 | ## TaskQueueBackgroundOperation
18 |
19 | The `TaskQueueBackgroundOperation` class is returned by the `TaskQueueBackgroundOperationManager` and represents a single background operation.
20 |
21 | **View more details on creating and running background operations are here: [TaskQueueBackgroundOperationManager](TaskQueueBackgroundOperationManager)**
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/ILogoSource.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using MFiles.VAF.Extensions.Dashboards;
4 | using MFiles.VAF.Extensions.ExtensionMethods;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace MFiles.VAF.Extensions.Dashboards
12 | {
13 | public interface ILogoSource
14 | : IDashboardContent
15 | {
16 | }
17 | public class SvgLogoSource
18 | : LogoSourceBase
19 | {
20 | private SvgLogoSource()
21 | {
22 | }
23 | public static SvgLogoSource FromXmlString(string xmlString)
24 | => new SvgLogoSource()
25 | {
26 | ImageUriString = $"data:image/svg+xml;base64,{Convert.ToBase64String(xmlString.ToBytes(Encoding.UTF8))}"
27 | };
28 | }
29 | public abstract class LogoSourceBase
30 | : DashboardCustomContentEx, ILogoSource
31 | {
32 | public const int DefaultHeightInPixels = 100;
33 | public const int DefaultWidthInPixels = 200;
34 |
35 | protected LogoSourceBase()
36 | : base("")
37 | {
38 | this.Styles.Add("height", DefaultHeightInPixels + "px");
39 | this.Styles.Add("width", DefaultWidthInPixels + "px");
40 | this.Styles.Add("background-size", "contain");
41 | this.Styles.Add("background-repeat", "no-repeat");
42 | this.Styles.Add("background-position", "center top");
43 | }
44 | private string imageUriString;
45 | public string ImageUriString
46 | {
47 | get => this.imageUriString;
48 | set
49 | {
50 | this.imageUriString = value;
51 | if (string.IsNullOrWhiteSpace(value))
52 | this.Styles.Remove("background-image");
53 | else
54 | this.Styles.AddOrUpdate("background-image", $"url('{value}')");
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/ExternalID.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection to restrict items by their external ID.
19 | ///
20 | /// The to add the condition to.
21 | /// The external ID of the item.
22 | /// The provided, for chaining.
23 | public static MFSearchBuilder ExternalId
24 | (
25 | this MFSearchBuilder searchBuilder,
26 | string externalId
27 | )
28 | {
29 | // Sanity.
30 | if (null == searchBuilder)
31 | throw new ArgumentNullException(nameof(searchBuilder));
32 | if (null == externalId)
33 | throw new ArgumentNullException(nameof(externalId));
34 |
35 | // Create the search condition.
36 | var searchCondition = new SearchCondition
37 | {
38 | ConditionType = MFConditionType.MFConditionTypeEqual
39 | };
40 |
41 | // Set up the file value expression.
42 | searchCondition.Expression.SetStatusValueExpression
43 | (
44 | MFStatusType.MFStatusTypeExtID
45 | );
46 |
47 | // Search by external ID.
48 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeText, externalId);
49 |
50 | // Add the search condition to the collection.
51 | searchBuilder.Conditions.Add(-1, searchCondition);
52 |
53 | // Return the search builder for chaining.
54 | return searchBuilder;
55 | }
56 |
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/VaultObjectFileOperations/AddFile.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using MFiles.VAF.Common;
4 | using MFilesAPI;
5 | using MFilesAPI.Extensions;
6 |
7 | namespace MFiles.VAF.Extensions
8 | {
9 | public static partial class VaultObjectFileOperationsExtensionMethods
10 | {
11 | ///
12 | /// Adds a new file to the specified object.
13 | ///
14 | /// The instance of to use.
15 | /// The object version to add the file to. Must already be checked out.
16 | /// The title of the file (without an extension).
17 | /// The file extension. Can be supplied with or without preceeding ".".
18 | /// The contents of the file.
19 | public static void AddFile
20 | (
21 | this VaultObjectFileOperations objectFileOperations,
22 | ObjVerEx objVerEx,
23 | string title,
24 | string extension,
25 | Stream fileContents
26 | )
27 | {
28 | // Sanity.
29 | if (null == objectFileOperations)
30 | throw new ArgumentNullException(nameof(objectFileOperations));
31 | if (null == objVerEx)
32 | throw new ArgumentNullException(nameof(objVerEx));
33 | if (null == objVerEx.Vault)
34 | throw new ArgumentException(Resources.Exceptions.ObjVerExExtensionMethods.ObjVerExVaultReferenceNull, nameof(objVerEx));
35 | if (String.IsNullOrWhiteSpace(title))
36 | throw new ArgumentException(Resources.Exceptions.ObjVerExExtensionMethods.AddFile_FileMustHaveTitle, nameof(title));
37 | if (null == fileContents)
38 | throw new ArgumentNullException(nameof(fileContents));
39 |
40 | // Use the other extension method.
41 | objectFileOperations.AddFile
42 | (
43 | objVerEx.ObjVer,
44 | objVerEx.Vault,
45 | title,
46 | extension,
47 | fileContents
48 | );
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/StyleComparisonHelper.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace MFiles.VAF.Extensions.Tests.Dashboards
6 | {
7 | public class StyleComparisonHelper : Dictionary
8 | {
9 | public StyleComparisonHelper(string expectedStyle)
10 | : this(expectedStyle?.Split(";".ToCharArray())
11 | .Where(s => s.Length > 0)
12 | .Select(p => p.Split(":".ToCharArray()))
13 | .ToDictionary
14 | (
15 | a => (a[0] ?? "").Trim(),
16 | a => a.Length > 1
17 | ? (string.Join(":", a.Skip(1)) ?? "").Trim()
18 | : null
19 | ))
20 | {
21 | }
22 | public StyleComparisonHelper(IEnumerable> items)
23 | {
24 | if (null != items)
25 | foreach (var x in items)
26 | this.Add(x.Key, x.Value);
27 | }
28 | public bool TestAgainstString(string input)
29 | {
30 | // Everything in our dictionary must appear in the input.
31 | if (this.Count > 0 && string.IsNullOrWhiteSpace(input))
32 | return false;
33 | if (this.Count == 0 && string.IsNullOrWhiteSpace(input))
34 | return true;
35 |
36 | // Split by semi-colon, then colon.
37 | var dict = input.Split(";".ToCharArray())
38 | .Where(s => s.Length > 0)
39 | .Select(p => p.Split(":".ToCharArray()))
40 | .ToDictionary
41 | (
42 | a => (a[0] ?? "").Trim(),
43 | a => a.Length > 1
44 | ? (string.Join(":", a.Skip(1)) ?? "").Trim()
45 | : null
46 | );
47 |
48 | // Make sure they all exist and match.
49 | foreach (var k in this.Keys)
50 | {
51 | if (false == dict.ContainsKey(k))
52 | {
53 | Assert.Fail($"Target string does not contain key {k}.");
54 | }
55 | if (this[k] != dict[k])
56 | {
57 | Assert.Fail($"Value for {k} is not correct (expected:{this[k]}, actual: {dict[k]}).");
58 | return false;
59 | }
60 | }
61 |
62 | return true;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/MFSearchBuilderExtensionMethods/FindCount.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common;
2 | using MFilesAPI;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using Moq;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods.MFSearchBuilderExtensionMethods
12 | {
13 | [TestClass]
14 | public class FindCount
15 | : TestBaseWithVaultMock
16 | {
17 | [TestMethod]
18 | [ExpectedException(typeof(ArgumentNullException))]
19 | public void ThrowsWithNullSearchBuilder()
20 | {
21 | ((MFSearchBuilder)null).FindCount();
22 | }
23 |
24 | ///
25 | /// Ensures that the extension method delegates to
26 | /// .
27 | ///
28 | ///
29 | /// Inherits the same limitations as the referenced API method.
30 | /// If you truly need to count all objects in a large vault then you may need to use a segmented search,
31 | /// but that has potentially significant overhead on the server.
32 | ///
33 | [TestMethod]
34 | public void CallGetObjectCountInSearch()
35 | {
36 | // Setup the search operations mock.
37 | var vaultSearchOperationsMock = new Mock();
38 | vaultSearchOperationsMock.Setup
39 | (
40 | m => m.GetObjectCountInSearch(Moq.It.IsAny(), Moq.It.IsAny())
41 | )
42 | .Returns(1)
43 | .Verifiable();
44 |
45 | // Setup the vault mock.
46 | var vaultMock = this.GetVaultMock();
47 | vaultMock.Setup(m => m.ObjectSearchOperations).Returns(vaultSearchOperationsMock.Object);
48 |
49 | // Create the search builder and call FindCount.
50 | var searchBuilder = new MFSearchBuilder(vaultMock.Object);
51 | Assert.AreEqual(1, searchBuilder.FindCount());
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/AsynchronousDashboardContent/IAsynchronousDashboardContentRenderer.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using MFiles.VAF.Common.ApplicationTaskQueue;
3 | using MFiles.VAF.Configuration.Domain.Dashboards;
4 | using System.Collections;
5 | using System.Collections.Generic;
6 | using System.Runtime.Remoting.Contexts;
7 | using static MFiles.VAF.Common.ApplicationTaskQueue.TaskQueueManager;
8 |
9 | namespace MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent
10 | {
11 | ///
12 | /// Renders details about asynchronous operations into a dashboard.
13 | /// Note: rendering of the actual executions is done via ,
14 | /// and the two work in tandem.
15 | ///
16 | public interface IAsynchronousDashboardContentRenderer
17 | {
18 | ///
19 | /// Gets the dashboard content for the provided .
20 | ///
21 | /// The providers to render content from.
22 | /// The content, or null if nothing to render.
23 | IDashboardContent GetDashboardContent(IEnumerable providers);
24 |
25 | ///
26 | /// Gets the dashboard content for the provided . This data may come from multiple providers.
27 | ///
28 | /// The data to render.
29 | /// The content, or null if nothing to render.
30 | IDashboardContent GetDashboardContent(IEnumerable>>> data);
31 |
32 | ///
33 | /// Gets the content for a single task queue / task type.
34 | ///
35 | /// The item to render.
36 | /// The content, or null if nothing to render.
37 | IDashboardContent GetDashboardContent(KeyValuePair>> item);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/INamedValueStorageManagerExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 | using MFiles.VAF.Extensions.Configuration.Upgrading;
4 |
5 | namespace MFiles.VAF.Extensions.Configuration.Upgrading
6 | {
7 | public static class INamedValueStorageManagerExtensionMethods
8 | {
9 | public static string GetValue(this INamedValueStorageManager namedValueStorageManager, Vault vault, MFNamedValueType namedValueType, string @namespace, string @name, string defaultValue = null)
10 | {
11 | if (null == namedValueStorageManager)
12 | throw new ArgumentNullException(nameof(namedValueStorageManager));
13 | if (null == vault)
14 | throw new ArgumentNullException(nameof(vault));
15 | if (string.IsNullOrWhiteSpace(@namespace))
16 | throw new ArgumentException(nameof(@namespace));
17 | if (string.IsNullOrWhiteSpace(@name))
18 | throw new ArgumentException(nameof(@name));
19 |
20 | var namedValues = namedValueStorageManager.GetNamedValues(vault, namedValueType, @namespace);
21 | if (null == namedValues)
22 | return defaultValue;
23 | return namedValues.Contains(name) ? namedValues[name]?.ToString() : defaultValue;
24 | }
25 | public static void SetValue(this INamedValueStorageManager namedValueStorageManager, Vault vault, MFNamedValueType namedValueType, string @namespace, string @name, string value)
26 | {
27 | if (null == namedValueStorageManager)
28 | throw new ArgumentNullException(nameof(namedValueStorageManager));
29 | if (null == vault)
30 | throw new ArgumentNullException(nameof(vault));
31 | if (string.IsNullOrWhiteSpace(@namespace))
32 | throw new ArgumentException(nameof(@namespace));
33 | if (string.IsNullOrWhiteSpace(@name))
34 | throw new ArgumentException(nameof(@name));
35 |
36 | var namedValues = namedValueStorageManager.GetNamedValues(vault, namedValueType, @namespace) ?? new NamedValues();
37 | namedValues[name] = value;
38 | namedValueStorageManager.SetNamedValues(vault, namedValueType, @namespace, namedValues);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/DashboardTableTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Tests.Dashboards
11 | {
12 | [TestClass]
13 | public class DashboardTableTests
14 | : DashboardContentBaseTests
15 | {
16 |
17 | public override DashboardTable CreateDashboardContent()
18 | {
19 | return new DashboardTable();
20 | }
21 |
22 | [TestMethod]
23 | // Does not support icons.
24 | public override void Icon_PathToFile()
25 | {
26 | var dashboardContent = this.CreateDashboardContent();
27 | dashboardContent.Icon = "/some/file.png";
28 | var element = dashboardContent.Generate(new System.Xml.XmlDocument())?.FirstChild;
29 |
30 | // This component does not support icons.
31 | // We should have an element, but the class should not be set.
32 | Assert.IsNotNull(element);
33 | Assert.AreEqual("table-wrapper", element.Attributes["class"]?.Value ?? "");
34 | Assert.IsFalse
35 | (
36 | (element.Attributes["style"]?.Value ?? "").Contains("background-image:url('")
37 | );
38 | }
39 |
40 | [TestMethod]
41 | // Does not support icons.
42 | public override void Icon_FromResource()
43 | {
44 | var dashboardContent = this.CreateDashboardContent();
45 | dashboardContent.Icon = "/Resources/Images/Completed.png";
46 | var element = dashboardContent.Generate(new System.Xml.XmlDocument())?.FirstChild;
47 |
48 | // This component does not support icons.
49 | // We should have an element, but the class should not be set.
50 | Assert.IsNotNull(element);
51 | Assert.AreEqual("table-wrapper", element.Attributes["class"]?.Value ?? "");
52 | Assert.IsFalse
53 | (
54 | (element.Attributes["style"]?.Value ?? "").Contains("background-image:url(data:image/png;base64")
55 | );
56 | }
57 |
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/Upgrading/Rules/UpgradeRuleTestBase.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Configuration.Upgrading;
2 | using MFilesAPI;
3 | using Moq;
4 |
5 | namespace MFiles.VAF.Extensions.Tests.Configuration.Upgrading.Rules
6 | {
7 | public abstract class UpgradeRuleTestBase
8 | {
9 | protected const MFNamedValueType DefaultSourceNVSType = MFNamedValueType.MFConfigurationValue;
10 | protected const string DefaultSourceNamespace = "Source.Namespace";
11 |
12 | protected const MFNamedValueType DefaultTargetNVSType = MFNamedValueType.MFSystemAdminConfiguration;
13 | protected const string DefaultTargetNamespace = "Target.Namespace";
14 |
15 | public Mock CreateSingleNamedValueItemMock
16 | (
17 | bool isValid,
18 | MFNamedValueType namedValueType = DefaultSourceNVSType,
19 | string @namespace = DefaultSourceNamespace,
20 | string name = "config"
21 | )
22 | {
23 | var mock = new Mock();
24 | mock.SetupAllProperties();
25 | mock.Setup(m => m.IsValid()).Returns(isValid);
26 | mock.Setup(m => m.GetNamedValues(It.IsAny(), It.IsAny()))
27 | .Returns((INamedValueStorageManager manager, Vault vault) =>
28 | {
29 | return manager?.GetNamedValues(vault, namedValueType, @namespace);
30 | });
31 | mock.Setup(m => m.RemoveNamedValues(It.IsAny(), It.IsAny(), It.IsAny()))
32 | .Callback((INamedValueStorageManager manager, Vault vault, string[] names) =>
33 | {
34 | manager?.RemoveNamedValues(vault, namedValueType, @namespace, names);
35 | });
36 | mock.Setup(m => m.SetNamedValues(It.IsAny(), It.IsAny(), It.IsAny()))
37 | .Callback((INamedValueStorageManager manager, Vault vault, NamedValues nv) =>
38 | {
39 | manager?.SetNamedValues(vault, namedValueType, @namespace, nv);
40 | });
41 | mock.Object.NamedValueType = namedValueType;
42 | mock.Object.Namespace = @namespace;
43 | mock.Object.Name = name;
44 | return mock;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/FileSize.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection to restrict files by their size.
19 | ///
20 | /// The to add the condition to.
21 | /// The file size to restrict by (in bytes).
22 | /// What type of search to execute.
23 | /// The provided, for chaining.
24 | public static MFSearchBuilder FileSize
25 | (
26 | this MFSearchBuilder searchBuilder,
27 | long size,
28 | MFConditionType conditionType
29 | )
30 | {
31 | // Sanity.
32 | if (null == searchBuilder)
33 | throw new ArgumentNullException(nameof(searchBuilder));
34 | if (size < 0)
35 | throw new ArgumentOutOfRangeException(Resources.Exceptions.MFSearchBuilderExtensionMethods.FileSize_Negative, nameof(size));
36 |
37 | // Create the search condition.
38 | var searchCondition = new SearchCondition
39 | {
40 | ConditionType = conditionType
41 | };
42 |
43 | // Set up the file value expression.
44 | searchCondition.Expression.SetFileValueExpression
45 | (
46 | MFFileValueType.MFFileValueTypeFileSize
47 | );
48 |
49 | // Search by the size
50 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeInteger64, size);
51 |
52 | // Add the search condition to the collection.
53 | searchBuilder.Conditions.Add(-1, searchCondition);
54 |
55 | // Return the search builder for chaining.
56 | return searchBuilder;
57 | }
58 |
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/MFiles.VAF.Extensions.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | True
4 | True
5 | True
6 | True
7 | True
8 | True
9 | True
10 | True
11 | True
12 | True
13 | True
14 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ConfigurableVaultApplicationBase.ConfigurationUpgrading.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Configuration;
2 | using MFiles.VAF.Extensions.Configuration.Upgrading;
3 | using MFiles.VAF.Extensions.Configuration.Upgrading.Rules;
4 | using MFilesAPI;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Reflection;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 |
12 | namespace MFiles.VAF.Extensions
13 | {
14 | public abstract partial class ConfigurableVaultApplicationBase
15 | {
16 | ///
17 | /// The configuration upgrade manager.
18 | /// May be null before is called.
19 | ///
20 | /// Populated with the result of calling .
21 | protected IConfigurationUpgradeManager ConfigurationUpgradeManager { get; private set; }
22 |
23 | ///
24 | /// Returns the instance of that will be used
25 | /// to upgrade any configuration found.
26 | ///
27 | ///
28 | /// by default.
29 | /// Return an instance of something that inherits ,
30 | /// for example , to control configuration upgrading.
31 | ///
32 | public virtual IConfigurationUpgradeManager GetConfigurationUpgradeManager()
33 | => null;
34 |
35 | ///
36 | /// Will call then call the base implementation.
37 | protected override void PopulateConfigurationObjects(Vault vault)
38 | {
39 | // Create the configuration upgrade manager if needed.
40 | this.ConfigurationUpgradeManager = this.ConfigurationUpgradeManager
41 | ?? this.GetConfigurationUpgradeManager();
42 |
43 | // Run any configuration upgrade rules.
44 | this.ConfigurationUpgradeManager?.UpgradeConfiguration(vault);
45 |
46 | // Use the base implementation.
47 | base.PopulateConfigurationObjects(vault);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/IVersionedConfiguration.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Runtime.Serialization;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Configuration
10 | {
11 | ///
12 | /// A root configuration structure with version data.
13 | ///
14 | public interface IVersionedConfiguration
15 | {
16 | ///
17 | /// The current version of the configuration.
18 | /// Used for managing upgrading of configuration structures.
19 | ///
20 | Version Version { get; set; }
21 | }
22 |
23 | ///
24 | /// A base class for configuration that implements .
25 | /// When is used on a derived class, the version
26 | /// data will be loaded from the attribute and will be populated.
27 | ///
28 | [DataContract]
29 | public class VersionedConfigurationBase
30 | : IVersionedConfiguration
31 | {
32 | public VersionedConfigurationBase()
33 | {
34 | // Set the version from the attribute.
35 | this.Version = this
36 | .GetType()
37 | .GetCustomAttributes(false)?
38 | .Where(a => a is ConfigurationVersionAttribute)
39 | .Cast()
40 | .FirstOrDefault()?
41 | .Version ?? new Version("0.0");
42 | }
43 |
44 | ///
45 | [IgnoreDataMember]
46 | public Version Version { get;set; }
47 |
48 | ///
49 | /// The current version of the configuration.
50 | /// Used for managing upgrading of configuration structures.
51 | ///
52 | [DataMember(EmitDefaultValue = true, Name="Version")]
53 | [JsonConfEditor(Hidden = true)]
54 | public string VersionString
55 | {
56 | get => this.Version?.ToString();
57 | set => this.Version = value == null ? new Version("0.0") : Version.Parse(value);
58 | }
59 |
60 | public bool ShouldSerializeVersionString()
61 | {
62 | return this.Version != null
63 | && this.Version.ToString() != "0.0";
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/Commands/CustomDomainCommandResolution/DefaultCustomDomainCommandResolver.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution
6 | {
7 | public class DefaultCustomDomainCommandResolver
8 | : AggregatedCustomDomainCommandResolver
9 | where TSecureConfiguration : class, new()
10 | {
11 |
12 | ///
13 | /// The vault application that this resolver is running within.
14 | ///
15 | protected ConfigurableVaultApplicationBase VaultApplication { get; }
16 |
17 | ///
18 | /// Creates an instance of
19 | /// and includes the provided in the list of things to resolve against.
20 | ///
21 | /// The vault application this is running within.
22 | /// If is .
23 | public DefaultCustomDomainCommandResolver(ConfigurableVaultApplicationBase vaultApplication)
24 | : base()
25 | {
26 | this.VaultApplication = vaultApplication
27 | ?? throw new ArgumentNullException(nameof(vaultApplication));
28 |
29 | foreach(var r in this.GetDefaultCustomDomainCommandResolvers()?.AsNotNull())
30 | if (null != r)
31 | this.CustomDomainCommandResolvers.Add(r);
32 | }
33 |
34 | ///
35 | /// Retrieves the custom domain resolvers that should be used by default.
36 | ///
37 | /// The custom domain command resolvers.
38 | public virtual IEnumerable GetDefaultCustomDomainCommandResolvers()
39 | {
40 | yield return new AsynchronousOperationCustomDomainCommandResolver(VaultApplication);
41 | yield return new LogCustomDomainCommandResolver(VaultApplication);
42 | yield return new AttributeCustomDomainCommandResolver(VaultApplication);
43 | }
44 |
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/FileExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection to restrict items just to files of the provided type.
19 | ///
20 | /// The to add the condition to.
21 | /// The file extension to restrict by. If this does not start with a "." then the method will add it.
22 | /// The provided, for chaining.
23 | public static MFSearchBuilder FileExtension
24 | (
25 | this MFSearchBuilder searchBuilder,
26 | string extension
27 | )
28 | {
29 | // Sanity.
30 | if (null == searchBuilder)
31 | throw new ArgumentNullException(nameof(searchBuilder));
32 | if (null == extension)
33 | throw new ArgumentNullException(nameof(extension));
34 |
35 | // Ensure it starts with a dot.
36 | if(false == extension.StartsWith("."))
37 | extension = "." + extension;
38 |
39 | // Create the search condition.
40 | var searchCondition = new SearchCondition
41 | {
42 | ConditionType = MFConditionType.MFConditionTypeContains
43 | };
44 |
45 | // Set up the file value expression.
46 | searchCondition.Expression.SetFileValueExpression
47 | (
48 | MFFileValueType.MFFileValueTypeFileName
49 | );
50 |
51 | // Search for the extension.
52 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeText, extension);
53 |
54 | // Add the search condition to the collection.
55 | searchBuilder.Conditions.Add(-1, searchCondition);
56 |
57 | // Return the search builder for chaining.
58 | return searchBuilder;
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/Upgrading/Rules/VAF20ToVAF23UpgradeRuleTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Configuration.Upgrading;
2 | using MFiles.VAF.Extensions.Configuration.Upgrading.Rules;
3 | using MFilesAPI;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.Configuration.Upgrading.Rules
8 | {
9 | [TestClass]
10 | public class VAF20ToVAF23UpgradeRuleTests
11 | {
12 | [TestMethod]
13 | public void EnsureBaseClass()
14 | {
15 | Assert.IsTrue(typeof(MoveConfigurationUpgradeRule).IsAssignableFrom(typeof(VAF20ToVAF23UpgradeRule)));
16 | }
17 |
18 | [TestMethod]
19 | public void Options_SourceValid()
20 | {
21 | var vaultApplication = new VaultApplicationProxy();
22 | var configurationNodeName = "Hello World";
23 |
24 | var instance = new VAF20ToVAF23UpgradeRule(vaultApplication, configurationNodeName, new Version("0.0"), new Version("0.0"));
25 | var source = instance.ReadFrom as SingleNamedValueItem;
26 | Assert.IsNotNull(source, "The source is not a single named value item.");
27 | Assert.AreEqual(MFNamedValueType.MFConfigurationValue, source.NamedValueType);
28 | Assert.AreEqual(VAF20ToVAF23UpgradeRule.SourceNamespaceLocation, source.Namespace);
29 | Assert.AreEqual(configurationNodeName, source.Name);
30 | }
31 |
32 | [TestMethod]
33 | public void Options_TargetValid()
34 | {
35 | var vaultApplication = new VaultApplicationProxy();
36 | var configurationNodeName = "Hello World";
37 |
38 | var instance = new VAF20ToVAF23UpgradeRule(vaultApplication, configurationNodeName, new Version("0.0"), new Version("0.0"));
39 | var source = instance.WriteTo as SingleNamedValueItem;
40 | Assert.IsNotNull(source, "The target is not a single named value item.");
41 | Assert.AreEqual(MFNamedValueType.MFSystemAdminConfiguration, source.NamedValueType);
42 | Assert.AreEqual("MFiles.VAF.Extensions.Tests.Configuration.Upgrading.Rules.VAF20ToVAF23UpgradeRuleTests+VaultApplicationProxy", source.Namespace);
43 | Assert.AreEqual(VAF20ToVAF23UpgradeRule.TargetNamedValueName, source.Name);
44 |
45 | }
46 | public class VaultApplicationProxy
47 | : VaultApplicationBase
48 | {
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/ConfigurationBase.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using MFiles.VAF.Configuration.Logging;
3 | using MFiles.VAF.Configuration.Logging.NLog;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Runtime.Serialization;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace MFiles.VAF.Extensions.Configuration
12 | {
13 | ///
14 | /// A base class for configuration that implements .
15 | ///
16 | [DataContract]
17 | [UsesConfigurationResources]
18 | [UsesLoggingResources]
19 | public abstract class ConfigurationBase
20 | : VersionedConfigurationBase, IConfigurationWithLoggingConfiguration
21 | {
22 | [DataMember(EmitDefaultValue = false)]
23 | [JsonConfEditor
24 | (
25 | Label = ResourceMarker.Id + nameof(Resources.Configuration.LoggingConfiguration_Label),
26 | HelpText = ResourceMarker.Id + nameof(Resources.Configuration.LoggingConfiguration_HelpText)
27 | )]
28 | [Security(ChangeBy = SecurityAttribute.UserLevel.VaultAdmin, ViewBy = SecurityAttribute.UserLevel.VaultAdmin)]
29 | public NLogLoggingConfiguration Logging { get; set; }
30 |
31 | ///
32 | public ILoggingConfiguration GetLoggingConfiguration()
33 | => this.Logging ?? new NLogLoggingConfiguration();
34 | }
35 |
36 | [DataContract]
37 | public class NLogLoggingConfiguration
38 | : MFiles.VAF.Configuration.Logging.NLog.Configuration.NLogLoggingConfiguration
39 | {
40 | ///
41 | public override IEnumerable GetAllLoggingExclusionRules()
42 | {
43 | // Include any other exclusion rules.
44 | foreach (var r in base.GetAllLoggingExclusionRules() ?? Enumerable.Empty())
45 | yield return r;
46 |
47 | // If we're set to exclude internal messages then also exclude the task manager ex (spammy).
48 | if (false == (this.Advanced?.RenderInternalLogMessages ?? false))
49 | {
50 | yield return new NLogLoggingExclusionRule()
51 | {
52 | LoggerName = "MFiles.VAF.Extensions.TaskManagerEx*",
53 | MinimumLogLevelOverride = LogLevel.Fatal
54 | };
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/Upgrading/Rules/VAF10ToVAF23UpgradeRuleTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Configuration.Upgrading;
2 | using MFiles.VAF.Extensions.Configuration.Upgrading.Rules;
3 | using MFilesAPI;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.Configuration.Upgrading.Rules
8 | {
9 | [TestClass]
10 | public class VAF10ToVAF23UpgradeRuleTests
11 | {
12 | [TestMethod]
13 | public void EnsureBaseClass()
14 | {
15 | Assert.IsTrue(typeof(MoveConfigurationUpgradeRule).IsAssignableFrom(typeof(VAF10ToVAF23UpgradeRule)));
16 | }
17 |
18 | [TestMethod]
19 | public void Options_SourceValid()
20 | {
21 | var vaultApplication = new VaultApplicationProxy();
22 | var sourceNamespace = "MySourceNamespace";
23 | var keyName = "config";
24 |
25 | var instance = new VAF10ToVAF23UpgradeRule(vaultApplication, sourceNamespace, keyName, new Version("0.0"), new Version("0.0"));
26 | var source = instance.ReadFrom as SingleNamedValueItem;
27 | Assert.IsNotNull(source, "The source is not a single named value item.");
28 | Assert.AreEqual(MFNamedValueType.MFConfigurationValue, source.NamedValueType);
29 | Assert.AreEqual(sourceNamespace, source.Namespace);
30 | Assert.AreEqual(keyName, source.Name);
31 | }
32 |
33 | [TestMethod]
34 | public void Options_TargetValid()
35 | {
36 | var vaultApplication = new VaultApplicationProxy();
37 | var sourceNamespace = "MySourceNamespace";
38 | var keyName = "Hello World";
39 |
40 | var instance = new VAF10ToVAF23UpgradeRule(vaultApplication, sourceNamespace, keyName, new Version("0.0"), new Version("0.0"));
41 | var source = instance.WriteTo as SingleNamedValueItem;
42 | Assert.IsNotNull(source, "The target is not a single named value item.");
43 | Assert.AreEqual(MFNamedValueType.MFSystemAdminConfiguration, source.NamedValueType);
44 | Assert.AreEqual("MFiles.VAF.Extensions.Tests.Configuration.Upgrading.Rules.VAF10ToVAF23UpgradeRuleTests+VaultApplicationProxy", source.Namespace);
45 | Assert.AreEqual(VAF10ToVAF23UpgradeRule.TargetNamedValueName, source.Name);
46 |
47 | }
48 | public class VaultApplicationProxy
49 | : VaultApplicationBase
50 | {
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Code files should have platform appropriate line endings.
2 | # Do not force git to consider the files as text; there are some exotically encoded (eg. UCS-16)
3 | # files lurking in our tree. The attribute text=auto will make git treat files as text whenever
4 | # they look like text to it, which means strange files will not be touched.
5 | *.cs text=auto eol=auto
6 | *.cpp text=auto eol=auto
7 | *.h text=auto eol=auto
8 | *.H text=auto eol=auto
9 | *.hpp text=auto eol=auto
10 | *.c text=auto eol=auto
11 | *.js text=auto eol=auto
12 | *.kt text=auto eol=auto
13 | *.kts text=auto eol=auto
14 | *.html text=auto eol=auto
15 | *.css text=auto eol=auto
16 | *.ps1 text=auto eol=auto
17 | *.psm1 text=auto eol=auto
18 | *.java text=auto eol=auto
19 | *.md text=auto eol=auto
20 | *.nuspec text=auto eol=auto
21 | *.py text=auto eol=auto
22 | *.sh text=auto eol=auto
23 | *.txt text=auto eol=auto
24 | *.TXT text=auto eol=auto
25 | *.xml text=auto eol=auto
26 | *.XML text=auto eol=auto
27 | *.xsl text=auto eol=auto
28 | *.xslt text=auto eol=auto
29 | *.yml text=auto eol=auto
30 | *.tt text=auto eol=auto
31 | *.proto text=auto eol=auto
32 | *.ts text=auto eol=auto
33 | *.cc text=auto eol=auto
34 | *.rc text=auto eol=auto
35 | *.resx text=auto eol=auto
36 | *.config text=auto eol=auto
37 | *.json text=auto eol=auto
38 | *.peg text=auto eol=auto
39 | *.idl text=auto eol=auto
40 | *.wxs text=auto eol=auto
41 | *.wxl text=auto eol=auto
42 | *.vcxitems text=auto eol=auto
43 |
44 | # Git configuration files. These really must be in a text format understood by git.
45 | .gitignore text eol=auto
46 | .gitattributes text eol=auto
47 | .gitmodules text eol=auto
48 |
49 | # Visual studio projects should always have CRLF endings.
50 | *.*proj text=auto eol=crlf
51 | *.props text=auto eol=crlf
52 | *.properties text=auto eol=crlf
53 | *.targets text=auto eol=crlf
54 | *.sln text=auto eol=crlf
55 | *.filters text=auto eol=crlf
56 |
57 | # Bat files only run in windows environment, so they should always have CRLF endings.
58 | *.bat text=auto eol=crlf
59 | *.bat_ text=auto eol=crlf
60 |
61 | # Disable delta compression for binary-like files.
62 | *.jpg -delta
63 | *.pdf -delta
64 | *.bmp -delta
65 | *.png -delta
66 | *.dll -delta
67 | *.pdb -delta
68 | *.exe -delta
69 | *.zip -delta
70 | *.msi -delta
71 | *.mfappx -delta
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/Commands/CustomDomainCommandResolution/DefaultCustomDomainCommandResolverTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Moq;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using static MFiles.VAF.Extensions.Tests.Dashboards.Commands.CustomDomainCommandResolution.AttributeCustomDomainCommandResolverTests;
10 |
11 | namespace MFiles.VAF.Extensions.Tests.Dashboards.Commands.CustomDomainCommandResolution
12 | {
13 | [TestClass]
14 | public class DefaultCustomDomainCommandResolverTests
15 | {
16 |
17 | [TestMethod]
18 | public void GetDefaultCustomDomainCommandResolvers_ContainsAsynchronousCustomDomainCommandResolver()
19 | {
20 | var resolver = new DefaultCustomDomainCommandResolver
"));
40 | this.InnerContent = collection;
41 |
42 | // Set the title.
43 | var title = new DashboardCustomContentEx(string.IsNullOrWhiteSpace(titleText) ? "Exception" : titleText)
44 | {
45 | Icon = "Resources/Images/Failed.png"
46 | };
47 | title.Styles.AddOrUpdate("color", "red");
48 | this.TitleDashboardContent = title;
49 |
50 | // General styling.
51 | this.Styles.AddOrUpdate("padding", "5px 0px");
52 | this.Styles.AddOrUpdate("border-top", "1px solid red");
53 | this.Styles.AddOrUpdate("border-bottom", "1px solid red");
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/FullText.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Text;
6 | using MFiles.VAF.Common;
7 | using MFilesAPI;
8 |
9 | namespace MFiles.VAF.Extensions
10 | {
11 | ///
12 | /// Extension methods for the class.
13 | ///
14 | // ReSharper disable once InconsistentNaming
15 | public static partial class MFSearchBuilderExtensionMethods
16 | {
17 | ///
18 | /// Adds a to the collection for a full-text search on the value given.
19 | ///
20 | /// The to add the condition to.
21 | /// The value to search for.
22 | /// The type of full text search to execute (defaults to | ).
23 | /// The provided, for chaining.
24 | public static MFSearchBuilder FullText
25 | (
26 | this MFSearchBuilder searchBuilder,
27 | string value,
28 | MFFullTextSearchFlags searchFlags = MFFullTextSearchFlags.MFFullTextSearchFlagsLookInFileData
29 | // ReSharper disable once BitwiseOperatorOnEnumWithoutFlags
30 | | MFFullTextSearchFlags.MFFullTextSearchFlagsLookInMetaData
31 | )
32 | {
33 | // Sanity.
34 | if (null == searchBuilder)
35 | throw new ArgumentNullException(nameof(searchBuilder));
36 | if (null == value)
37 | throw new ArgumentNullException(nameof(value));
38 |
39 | // Create the search condition.
40 | var searchCondition = new SearchCondition
41 | {
42 | ConditionType = MFConditionType.MFConditionTypeContains
43 | };
44 |
45 | // Set up the any-field (full-text) expression.
46 | searchCondition.Expression.SetAnyFieldExpression
47 | (
48 | searchFlags
49 | );
50 |
51 | // Search for the given term.
52 | searchCondition.TypedValue.SetValue(MFDataType.MFDatatypeText, value);
53 |
54 | // Add the search condition to the collection.
55 | searchBuilder.Conditions.Add(-1, searchCondition);
56 |
57 | // Return the search builder for chaining.
58 | return searchBuilder;
59 | }
60 |
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Email/Readme.md:
--------------------------------------------------------------------------------
1 | # Email Extensions
2 |
3 | This builds upon the [COM API Extensions](https://github.com/M-Files/COMAPI.Extensions.Community/tree/master/MFilesAPI.Extensions/Email) functionality, and the readme there should be read before this one.
4 |
5 | ## Configuration
6 |
7 | The VAF Community Extensions project provides an implementation of `MFilesAPI.Extensions.Email.Configuration.SmtpConfiguration` that has been decorated with the attributes required for use in VAF 2.1 configuration areas.
8 |
9 | ### Adding to a Vault Application Framework Configuration file
10 |
11 | The VAF extensions library contains a class definition that can be simply added to an existing VAF 2.1 configuration class to expose the required SMTP configuration elements:
12 |
13 | ```csharp
14 | [DataContract]
15 | public class Configuration
16 | {
17 | [DataMember]
18 | public MFiles.VAF.Extensions.Email.VAFSmtpConfiguration SmtpConfiguration { get; set; }
19 | = new MFiles.VAF.Extensions.Email.VAFSmtpConfiguration();
20 | }
21 | ```
22 | *Note: After installing your application you must go and configure your SMTP settings. These cannot be read from M-Files Server Notification details, unfortunately.**
23 |
24 | ## Sending an email
25 |
26 | The VAF extensions library adds an extension method for `ObjVerEx.AddAllFiles`, allowing files to be easily attached from an existing `ObjVerEx` instance:
27 |
28 | ```csharp
29 |
30 | using MFilesAPI.Extensions.Email;
31 | using MFiles.VAF.Extensions.Email;
32 |
33 | namespace extensionstest2
34 | {
35 | public class VaultApplication
36 | : MFiles.VAF.Extensions.ConfigurableVaultApplicationBase
37 | {
38 |
39 | [StateAction("WFS.test.SendEmail")]
40 | public void SendEmailWorkflowHandler(StateEnvironment env)
41 | {
42 | // Create a message.
43 | using (var emailMessage = new EmailMessage(this.Configuration.SmtpConfiguration))
44 | {
45 | // To.
46 | emailMessage.AddRecipient(AddressType.To, "craig.hawker@m-files.com");
47 |
48 | // Configure the message metadata.
49 | emailMessage.Subject = "hello world";
50 | emailMessage.HtmlBody = $"This is a HTML for document {env.ObjVerEx.Title}.";
51 |
52 | // Add all files from the current object.
53 | emailMessage.AddAllFiles(env.ObjVerEx, MFFileFormat.MFFileFormatPDF);
54 |
55 | // Send the message.
56 | emailMessage.Send();
57 | }
58 | }
59 |
60 | }
61 |
62 | }
63 |
64 | ```
65 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/JsonConverterBase.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using System.Linq;
3 | using System.Runtime.Serialization;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | internal abstract class JsonConverterBase
8 | : JsonConverter
9 | {
10 |
11 | ///
12 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
13 | {
14 | if (value == null)
15 | return;
16 |
17 | var valueType = value.GetType();
18 |
19 | // Start the object.
20 | writer.WriteStartObject();
21 |
22 | // Output any properties.
23 | foreach (var p in valueType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
24 | {
25 | // Skip null values.
26 | var memberValue = p.GetValue(value);
27 | if (null == memberValue)
28 | continue;
29 |
30 | // Only process data members.
31 | var dataMemberAttribute = p.GetCustomAttributes(typeof(DataMemberAttribute), true).FirstOrDefault() as DataMemberAttribute;
32 | if (null == dataMemberAttribute)
33 | continue;
34 |
35 | // What should this be called?
36 | var name = string.IsNullOrWhiteSpace(dataMemberAttribute.Name)
37 | ? p.Name
38 | : dataMemberAttribute.Name;
39 |
40 | // Add it to the object.
41 | writer.WritePropertyName(name);
42 | writer.WriteRawValue(Newtonsoft.Json.JsonConvert.SerializeObject(memberValue, Formatting.Indented));
43 |
44 | }
45 |
46 | // Output any fields.
47 | foreach (var f in valueType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
48 | {
49 | // Skip null values.
50 | var memberValue = f.GetValue(value);
51 | if (null == memberValue)
52 | continue;
53 |
54 | // Only process data members.
55 | var dataMemberAttribute = f.GetCustomAttributes(typeof(DataMemberAttribute), true).FirstOrDefault() as DataMemberAttribute;
56 | if (null == dataMemberAttribute)
57 | continue;
58 |
59 | // What should this be called?
60 | var name = string.IsNullOrWhiteSpace(dataMemberAttribute.Name)
61 | ? f.Name
62 | : dataMemberAttribute.Name;
63 |
64 | // Add it to the object.
65 | writer.WritePropertyName(name);
66 | writer.WriteRawValue(Newtonsoft.Json.JsonConvert.SerializeObject(memberValue, Formatting.Indented));
67 |
68 | }
69 |
70 | // End the object.
71 | writer.WriteEndObject();
72 |
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ConfigurableVaultApplicationBase.IsCurrentConfigurationValid.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Tests
10 | {
11 | [TestClass]
12 | public class ConfigurableVaultApplicationBase
13 | {
14 | [TestMethod]
15 | [DataRow(true, false, false, true)] // Valid, no cache.
16 | [DataRow(false, false, false, true)] // Invalid, no cache.
17 | [DataRow(true, true, false, false)] // Valid, with cache.
18 | [DataRow(false, true, false, false)] // Invalid, with cache.
19 | [DataRow(true, true, true, true)] // Valid, with cache, force refresh.
20 | [DataRow(false, true, true, true)] // Invalid, with cache, force refresh.
21 | public void IsCurrentConfigurationValid
22 | (
23 | bool isConfigurationValid, // What IsValid will return.
24 | bool warmCache, // If true, cache will be populated before tests.
25 | bool forceCacheRefresh, // If true then the cache will be populated.
26 | bool isValidCallExpected // If true, IsValid must be called.
27 | )
28 | {
29 | var proxy = new Proxy(isConfigurationValid); // IsValid will return true.
30 |
31 | // Should we warm the cache?
32 | if (warmCache)
33 | {
34 | proxy.IsCurrentConfigurationValid(null, forceCacheRefresh); // Call it once so that it populates the cache.
35 | proxy.IsValidCalled = false; // Reset our flag.
36 | }
37 |
38 | // Does it return the correct valid?
39 | Assert.AreEqual(isConfigurationValid, proxy.IsCurrentConfigurationValid(null, forceCacheRefresh));
40 |
41 | // Did it call IsValid?
42 | Assert.AreEqual(isValidCallExpected, proxy.IsValidCalled);
43 | }
44 |
45 | private class Proxy
46 | : ConfigurableVaultApplicationBaseProxy
47 | {
48 | public bool IsConfigurationValid { get; } = false;
49 | public bool IsValidCalled { get; set; } = false;
50 | public Proxy(bool isConfigurationValid)
51 | {
52 | this.IsConfigurationValid = isConfigurationValid;
53 | }
54 | public override bool IsValid(Vault vault)
55 | {
56 | this.IsValidCalled = true;
57 | return this.IsConfigurationValid;
58 | }
59 | public new bool IsCurrentConfigurationValid(Vault vault, bool force = false)
60 | {
61 | return base.IsCurrentConfigurationValid(vault, force);
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/Commands/CustomDomainCommandResolution/LogCustomDomainCommandResolver.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using System;
3 | using System.Collections.Generic;
4 |
5 | namespace MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution
6 | {
7 | public class LogCustomDomainCommandResolver
8 | : CustomDomainCommandResolverBase
9 | where TSecureConfiguration : class, new()
10 | {
11 |
12 | ///
13 | /// The vault application that this resolver is running within.
14 | ///
15 | protected ConfigurableVaultApplicationBase VaultApplication { get; }
16 |
17 | ///
18 | /// Creates an instance of
19 | /// and includes the provided in the list of things to resolve against.
20 | ///
21 | /// The vault application this is running within.
22 | /// If is .
23 | public LogCustomDomainCommandResolver(ConfigurableVaultApplicationBase vaultApplication)
24 | : base()
25 | {
26 | VaultApplication = vaultApplication
27 | ?? throw new ArgumentNullException(nameof(vaultApplication));
28 | }
29 |
30 | public override IEnumerable GetCustomDomainCommands()
31 | {
32 |
33 | // Return the commands associated with downloading logs from the default file target.
34 | foreach (var c in GetDefaultLogTargetDownloadCommands()?.AsNotNull())
35 | yield return c;
36 | }
37 |
38 | ///
39 | /// Returns the commands associated with downloading logs from the default file target.
40 | ///
41 | ///
42 | protected virtual IEnumerable GetDefaultLogTargetDownloadCommands()
43 | {
44 | // One to allow them to select which logs...
45 | yield return ShowSelectLogDownloadDashboardCommand.Create();
46 |
47 | // ...and one that actually does the collation/download.
48 | yield return DownloadSelectedLogsDashboardCommand.Create();
49 |
50 | // Allow the user to see the latest log entries.
51 | yield return ShowLatestLogEntriesDashboardCommand.Create();
52 | yield return RetrieveLatestLogEntriesCommand.Create();
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/.github/workflows/build-and-publish.yml:
--------------------------------------------------------------------------------
1 | name: Build and publish
2 |
3 | on:
4 | push:
5 | branches: [ release ]
6 |
7 | jobs:
8 | build:
9 | timeout-minutes: 10
10 | runs-on: 'windows-2022'
11 |
12 | steps:
13 | - uses: actions/checkout@v3
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Get version number
18 | shell: pwsh
19 | run: |
20 | $versionNumber = Get-Date -Format "yy.M.${{ github.run_number }}"
21 | echo "versionNumber=$versionNumber" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf-8 -Append
22 |
23 | - name: Write release notes
24 | shell: pwsh
25 | run: |
26 | $lastTag = git describe --tags --abbrev=0
27 | $cmd = "git log --pretty=""format:%nhttps://github.com/M-Files/VAF.Extensions.Community/commit/%H%n%B"" --max-count=50 --date-order --no-merges $lastTag..@"
28 | $changes = cmd /c $cmd
29 | Add-Content -Path "release-notes.txt" -Value "Changes included in ${{ env.versionNumber }}"
30 | Add-Content -Path "release-notes.txt" -Value $changes
31 |
32 | - name: Setup MSBuild path
33 | uses: microsoft/setup-msbuild@v1.1
34 | with:
35 | vs-version: '[17.0,)'
36 |
37 | - name: Setup dot net
38 | uses: actions/setup-dotnet@v2
39 | with:
40 | dotnet-version: 6.0.x
41 |
42 | - name: Create nuget package
43 | run: dotnet pack ./MFiles.VAF.Extensions/MFiles.VAF.Extensions.csproj --configuration Release -p:Version=${{ env.versionNumber }}
44 |
45 | # Tests cannot be run as MFAPI is not installed.
46 | # - name: Test
47 | # run: dotnet test --no-restore --verbosity normal
48 |
49 | - name: Push with dotnet
50 | run: dotnet nuget push ./MFiles.VAF.Extensions/bin/Release/MFiles.VAF.Extensions.${{ env.versionNumber }}.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
51 |
52 | - name: Create release
53 | id: createRelease
54 | uses: ncipollo/release-action@v1
55 | env:
56 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57 | with:
58 | artifactErrorsFailBuild: true
59 | artifacts: "./MFiles.VAF.Extensions/bin/Release/MFiles.VAF.Extensions.${{ env.versionNumber }}.nupkg"
60 | name: ${{ env.versionNumber }}
61 | tag: ${{ env.versionNumber }}
62 | draft: false
63 | makeLatest: true
64 | omitBody: true
65 | prerelease: false
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/DashboardCustomContentExTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Tests.Dashboards
11 | {
12 | [TestClass]
13 | public class DashboardCustomContentExTests
14 | : DashboardContentBaseTests
15 | {
16 | [TestMethod]
17 | public void NullInnerContentDoesNotThrow()
18 | {
19 | new DashboardCustomContentEx(innerContent: null);
20 | }
21 | [TestMethod]
22 | public void NullInnerContentDoesNotThrow2()
23 | {
24 | new DashboardCustomContentEx(htmlContent: null);
25 | }
26 | [TestMethod]
27 | [ExpectedException(typeof(System.Xml.XmlException))]
28 | public void UnencodedContentDoesThrow()
29 | {
30 | var content = new DashboardCustomContentEx(htmlContent: "hello & world ");
31 | content.ToXmlString();
32 | }
33 | [TestMethod]
34 | public void EncodedContentDoesNotThrow()
35 | {
36 | var content = new DashboardCustomContentEx(htmlContent: "hello & world ");
37 | content.ToXmlString();
38 | }
39 |
40 | [TestMethod]
41 | public void InnerContentSetCorrectly()
42 | {
43 | var innerContent = new DashboardCustomContent("hello world.
");
44 | var content = new DashboardCustomContentEx(innerContent);
45 | Assert.AreEqual(innerContent, content?.InnerContent);
46 | }
47 |
48 | [TestMethod]
49 | public void StringInnerContentWrapped()
50 | {
51 | var innerContent = "hello world.
";
52 | var content = new DashboardCustomContentEx(innerContent);
53 | Assert.IsInstanceOfType(content?.InnerContent, typeof(DashboardCustomContent));
54 | Assert.AreEqual(innerContent, content?.InnerContent?.ToXmlString());
55 | }
56 |
57 | [TestMethod]
58 | public void ToXmlStringReturnsInnerContent()
59 | {
60 | var innerContent = "hello world.
";
61 | var content = new DashboardCustomContentEx(innerContent);
62 | Assert.IsInstanceOfType(content?.InnerContent, typeof(DashboardCustomContent));
63 | Assert.AreEqual(content?.InnerContent?.ToXmlString(), content.ToXmlString());
64 | }
65 |
66 |
67 | public override DashboardCustomContentEx CreateDashboardContent()
68 | {
69 | return new DashboardCustomContentEx("hello world.
");
70 | }
71 |
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/Commands/CustomDomainCommandResolution/ICustomDomainCommandResolver.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.AdminConfigurations;
2 | using MFiles.VAF.Configuration.Domain.Dashboards;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 |
6 | namespace MFiles.VAF.Extensions.Dashboards.Commands.CustomDomainCommandResolution
7 | {
8 | ///
9 | /// Returns custom domain commands.
10 | ///
11 | public interface ICustomDomainCommandResolver
12 | {
13 | ///
14 | /// Gets all custom domain commands.
15 | ///
16 | /// The found domain commands.
17 | IEnumerable GetCustomDomainCommands();
18 |
19 | ///
20 | /// Returns a dashboard domain command for the given command Id.
21 | ///
22 | /// The ID of the command to return.
23 | /// The style for the command.
24 | /// The command, or null if not found.
25 | DashboardDomainCommandEx GetDashboardDomainCommand
26 | (
27 | string commandId,
28 | DashboardCommandStyle style = default
29 | );
30 | }
31 |
32 | ///
33 | /// A base implementation of
34 | /// that provides an implementation of
35 | /// that iterates over to find a command with the supplied ID.
36 | ///
37 | public abstract class CustomDomainCommandResolverBase
38 | : ICustomDomainCommandResolver
39 | {
40 |
41 | ///
42 | public abstract IEnumerable GetCustomDomainCommands();
43 |
44 | ///
45 | public virtual DashboardDomainCommandEx GetDashboardDomainCommand
46 | (
47 | string commandId,
48 | DashboardCommandStyle style = default
49 | )
50 | {
51 | // Sanity.
52 | if (string.IsNullOrWhiteSpace(commandId))
53 | return null;
54 |
55 | // Try to get the domain command for this method.
56 | var command = GetCustomDomainCommands()
57 | .FirstOrDefault(c => c.ID == commandId);
58 |
59 | // Sanity.
60 | if (null == command)
61 | return null;
62 |
63 | // Return the command.
64 | return new DashboardDomainCommandEx
65 | {
66 | DomainCommandID = command.ID,
67 | Title = command.DisplayName,
68 | Style = style
69 | };
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Configuration/Upgrading/INamedValueStorageManager.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using System;
3 |
4 | namespace MFiles.VAF.Extensions.Configuration.Upgrading
5 | {
6 | public interface INamedValueStorageManager
7 | {
8 | NamedValues GetNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace);
9 | void SetNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace, NamedValues namedValues);
10 | void RemoveNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace, params string[] namedValues);
11 | }
12 | internal class VaultNamedValueStorageManager
13 | : INamedValueStorageManager
14 | {
15 | public NamedValues GetNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace)
16 | {
17 | // Sanity.
18 | if (null == vault)
19 | throw new ArgumentNullException(nameof(vault));
20 | if (null == vault.NamedValueStorageOperations)
21 | throw new ArgumentException("The NamedValueStorage instance was null.", nameof(vault));
22 |
23 | // Use the API implementation.
24 | return vault
25 | .NamedValueStorageOperations
26 | .GetNamedValues(namedValueType, @namespace);
27 | }
28 | public void SetNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace, NamedValues namedValues)
29 | {
30 | // Sanity.
31 | if (null == vault)
32 | throw new ArgumentNullException(nameof(vault));
33 | if (null == vault.NamedValueStorageOperations)
34 | throw new ArgumentException("The NamedValueStorage instance was null.", nameof(vault));
35 |
36 | // Use the API implementation.
37 | vault
38 | .NamedValueStorageOperations
39 | .SetNamedValues(namedValueType, @namespace, namedValues);
40 | }
41 | public void RemoveNamedValues(Vault vault, MFNamedValueType namedValueType, string @namespace, params string[] namedValueNames)
42 | {
43 | // Sanity.
44 | if (null == vault)
45 | throw new ArgumentNullException(nameof(vault));
46 | if (null == vault.NamedValueStorageOperations)
47 | throw new ArgumentException("The NamedValueStorage instance was null.", nameof(vault));
48 | if (null == namedValueNames || 0 == namedValueNames.Length)
49 | return;
50 |
51 | // Create the strings collection.
52 | var strings = new Strings();
53 | foreach (var name in namedValueNames)
54 | strings.Add(0, name);
55 |
56 | // Use the API implementation.
57 | vault
58 | .NamedValueStorageOperations
59 | .RemoveNamedValues(namedValueType, @namespace, strings);
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Email/VAFSmtpConfiguration.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Extensions.Tests.Configuration;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 |
4 | namespace MFiles.VAF.Extensions.Tests.Email
5 | {
6 | [TestClass]
7 | [DataMemberRequired
8 | (
9 | nameof(Extensions.Email.VAFSmtpConfiguration.DefaultSender),
10 | nameof(Extensions.Email.VAFSmtpConfiguration.UseLocalPickupFolder),
11 | nameof(Extensions.Email.VAFSmtpConfiguration.LocalPickupFolder),
12 | nameof(Extensions.Email.VAFSmtpConfiguration.ServerAddress),
13 | nameof(Extensions.Email.VAFSmtpConfiguration.Port),
14 | nameof(Extensions.Email.VAFSmtpConfiguration.UseEncryptedConnection),
15 | nameof(Extensions.Email.VAFSmtpConfiguration.Credentials)
16 | )]
17 | [JsonConfEditorRequired
18 | (
19 | nameof(Extensions.Email.VAFSmtpConfiguration.UseLocalPickupFolder),
20 | defaultValue: false
21 | )]
22 | [JsonConfEditorRequired
23 | (
24 | nameof(Extensions.Email.VAFSmtpConfiguration.LocalPickupFolder),
25 | defaultValue: null,
26 | hidden: true,
27 | showWhen: ".parent._children{.key == 'UseLocalPickupFolder' && .value == true }"
28 | )]
29 | [JsonConfEditorRequired
30 | (
31 | nameof(Extensions.Email.VAFSmtpConfiguration.ServerAddress),
32 | defaultValue: null,
33 | hidden: false,
34 | hideWhen: ".parent._children{.key == 'UseLocalPickupFolder' && .value == true }"
35 | )]
36 | [JsonConfEditorRequired
37 | (
38 | nameof(Extensions.Email.VAFSmtpConfiguration.Port),
39 | defaultValue: MFilesAPI.Extensions.Email.SmtpConfiguration.DefaultPort,
40 | Hidden = false,
41 | HideWhen = ".parent._children{.key == 'UseLocalPickupFolder' && .value == true }"
42 | )]
43 | [JsonConfEditorRequired
44 | (
45 | nameof(Extensions.Email.VAFSmtpConfiguration.UseEncryptedConnection),
46 | defaultValue: true,
47 | hidden: false,
48 | hideWhen: ".parent._children{.key == 'UseLocalPickupFolder' && .value == true }"
49 | )]
50 | [JsonConfEditorRequired
51 | (
52 | nameof(Extensions.Email.VAFSmtpConfiguration.RequiresAuthentication),
53 | defaultValue: true,
54 | hidden: false,
55 | hideWhen: ".parent._children{.key == 'UseLocalPickupFolder' && .value == true }"
56 | )]
57 | [JsonConfEditorRequired
58 | (
59 | nameof(Extensions.Email.VAFSmtpConfiguration.Credentials),
60 | defaultValue: null,
61 | hidden: true,
62 | showWhen: ".parent._children{.key == 'RequiresAuthentication' && .value == true }"
63 | )]
64 | public class VAFSmtpConfiguration
65 | : ConfigurationClassTestBase
66 | {
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/ObjVerEx/GetPropertyAsValueListItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using MFiles.VAF.Common;
4 | using MFiles.VAF.Configuration;
5 |
6 | using MFilesAPI;
7 |
8 | namespace MFiles.VAF.Extensions
9 | {
10 | public static partial class ObjVerExExtensionMethods
11 | {
12 | ///
13 | /// Get the value list item for the specified property def id.
14 | ///
15 | /// The child/owned object.
16 | /// The property definition id.
17 | /// Value list item object if available
18 | /// Can return null if the value list item is deleted or not set.
19 | /// Thrown if does not point to a suitable property definition.
20 | public static ValueListItem GetPropertyAsValueListItem(
21 | this ObjVerEx objVerEx,
22 | int propDefId
23 | )
24 | {
25 | // Sanity.
26 | if (null == objVerEx)
27 | throw new ArgumentNullException(nameof(objVerEx));
28 |
29 | // Validity of the property def id
30 | if (0 > propDefId)
31 | {
32 | throw new ArgumentOutOfRangeException
33 | (
34 | nameof(propDefId),
35 | Resources.Exceptions.VaultInteraction.PropertyDefinition_NotResolved
36 | );
37 | }
38 |
39 | // Get the value list id of the property def
40 | PropertyDef propDef = objVerEx
41 | .Vault
42 | .PropertyDefOperations
43 | .GetPropertyDef(propDefId);
44 |
45 | // Exception if property was not found
46 | if (null == propDef)
47 | {
48 | throw new ArgumentException
49 | (
50 | String.Format(Resources.Exceptions.VaultInteraction.PropertyDefinition_NotFound, propDefId),
51 | nameof(propDefId)
52 | );
53 | }
54 |
55 | // Does this have an owning type?
56 | if (false == propDef.BasedOnValueList || 0 > propDef.ValueList)
57 | {
58 | throw new ArgumentException
59 | (
60 | String.Format
61 | (
62 | Resources.Exceptions.VaultInteraction.PropertyDefinition_NotBasedOnValueList,
63 | propDefId,
64 | propDef.Name
65 | ),
66 | nameof(propDefId)
67 | );
68 | }
69 |
70 | // Get the lookup id of the property
71 | int lookupId = objVerEx.GetLookupID(propDefId);
72 |
73 | // Return null if lookup was not found
74 | if (lookupId < 0)
75 | return null;
76 |
77 | // return the value list item for the lookup id
78 | return objVerEx
79 | .Vault
80 | .ValueListItemOperations
81 | .GetValueListItemByID(propDef.ValueList, lookupId);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/DashboardTableRowTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Tests.Dashboards
11 | {
12 | [TestClass]
13 | public class DashboardTableRowTests
14 | {
15 | [TestMethod]
16 | public void EmptyCellsByDefault()
17 | {
18 | Assert.AreEqual(0, new DashboardTableRow().Cells.Count);
19 | }
20 |
21 | [TestMethod]
22 | public void EmptyCommandsByDefault()
23 | {
24 | Assert.AreEqual(0, new DashboardTableRow().Commands.Count);
25 | }
26 |
27 | [TestMethod]
28 | public void BodyByDefault()
29 | {
30 | Assert.AreEqual(DashboardTableRowType.Body, new DashboardTableRow().DashboardTableRowType);
31 | }
32 |
33 | [TestMethod]
34 | public void GeneratesEmptyRowByDefault()
35 | {
36 | var row = new DashboardTableRow();
37 | var element = row.ToXmlFragment()?.FirstChild;
38 | Assert.IsNotNull(element);
39 | Assert.AreEqual("tr", element.LocalName);
40 | Assert.AreEqual(0, element.ChildNodes.Count);
41 | }
42 |
43 | [TestMethod]
44 | public void AddCellIncrementsCellCount()
45 | {
46 | var row = new DashboardTableRow();
47 | var cell = row.AddCell("hello");
48 | Assert.IsNotNull(cell);
49 | Assert.AreEqual(1, row.Cells.Count);
50 | Assert.AreEqual(cell, row.Cells[0]);
51 | }
52 |
53 | [TestMethod]
54 | public void AddCellGeneratesRowWithOneCell()
55 | {
56 | var row = new DashboardTableRow();
57 | row.AddCell("hello");
58 | var element = row.ToXmlFragment()?.FirstChild;
59 | Assert.IsNotNull(element);
60 | Assert.AreEqual("tr", element.LocalName);
61 | Assert.AreEqual(1, element.ChildNodes.Count);
62 | }
63 |
64 | [TestMethod]
65 | public void AddCellsIncrementsCellCount()
66 | {
67 | var row = new DashboardTableRow();
68 | var cellList = row.AddCells("hello", "world", "new", "cells");
69 | Assert.IsNotNull(cellList);
70 | Assert.AreEqual(4, cellList.Count);
71 | Assert.AreEqual(4, row.Cells.Count);
72 | }
73 |
74 | [TestMethod]
75 | public void AddCellsGeneratesRowWithAppropriateCells()
76 | {
77 | var row = new DashboardTableRow();
78 | row.AddCells("hello", "world", "new", "cells");
79 | var element = row.ToXmlFragment()?.FirstChild;
80 | Assert.IsNotNull(element);
81 | Assert.AreEqual("tr", element.LocalName);
82 | Assert.AreEqual(4, element.ChildNodes.Count);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ConfigurableVaultApplicationBase.LoadTypedVaultExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable once CheckNamespace
2 | using MFiles.VAF.Common.ApplicationTaskQueue;
3 | using MFiles.VAF.Configuration.AdminConfigurations;
4 | using MFiles.VAF.Configuration.Domain.Dashboards;
5 | using MFiles.VAF.Core;
6 | using MFiles.VAF.Extensions;
7 | using MFiles.VAF;
8 | using MFilesAPI;
9 | using System;
10 | using System.Linq;
11 | using MFiles.VAF.MultiserverMode;
12 | using MFiles.VAF.AppTasks;
13 | using MFiles.VAF.Common;
14 | using System.Reflection;
15 | using System.Collections;
16 | using MFiles.VAF.Configuration.Logging;
17 | using MFiles.VAF.Extensions.Dashboards.AsynchronousDashboardContent;
18 | using System.Collections.Generic;
19 | using MFiles.VAF.Extensions.Dashboards.LoggingDashboardContent;
20 | using MFiles.VAF.Extensions.Dashboards.DevelopmentDashboardContent;
21 | using System.Threading.Tasks;
22 | using MFiles.VAF.Extensions.Logging;
23 |
24 | namespace MFiles.VAF.Extensions
25 | {
26 | public abstract partial class ConfigurableVaultApplicationBase
27 | {
28 |
29 | ///
30 | protected override void LoadHandlerMethods(Vault vault)
31 | {
32 | base.LoadHandlerMethods(vault);
33 | this.LoadTypedVaultExtensionMethods(vault, this);
34 | }
35 |
36 | ///
37 | /// Identifies vault extension methods decorated with
38 | /// and ensures that they are wired up correctly.
39 | ///
40 | /// The vault to use for any vault access.
41 | /// The object to check for vault extension methods.
42 | protected virtual void LoadTypedVaultExtensionMethods(Vault vault, object source)
43 | {
44 | // Sanity.
45 | if (null == vault)
46 | throw new ArgumentNullException(nameof(vault));
47 | if (null == source)
48 | return;
49 | if (null == this.vaultExtensionMethods)
50 | throw new InvalidOperationException("The vault extensions method dictionary was null.");
51 |
52 | // Add matching methods.
53 | foreach (var method in source.GetType().GetMethods(BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic))
54 | {
55 | // Is it one we care about?
56 | var attribute = method.GetCustomAttribute();
57 | if (null == attribute)
58 | continue;
59 |
60 | // Okay, register it.
61 | this.vaultExtensionMethods[attribute.VaultExtensionMethodName]
62 | = attribute.AsVaultExtensionMethodInfo(method, source);
63 | }
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/ITaskProcessingJobOfTExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.AppTasks;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace MFiles.VAF.Extensions.ExtensionMethods
9 | {
10 | public static class ITaskProcessingJobOfTExtensionMethods
11 | {
12 | ///
13 | /// An action which can be used with
14 | /// to indicate that the job does not have anything to do in the commit action.
15 | ///
16 | private static Action NoAction { get; } = (v) => { };
17 |
18 | ///
19 | /// Commits the job, but does not write anything to the vault.
20 | ///
21 | /// The type of directive this job accepts.
22 | /// The job to commit.
23 | ///
24 | /// The default transaction mode is , which forces the task processor to call
25 | /// prior to the processing completing.
26 | /// In some situations the task processing may not need to actually make any updates to the server. This extension
27 | /// method is simply a shorthand for passing a lambda with no body to
28 | /// .
29 | ///
30 | public static void CommitWithNoAction(this ITaskProcessingJob job)
31 | where TDirective : TaskDirective
32 | {
33 | // Sanity.
34 | if (null == job)
35 | throw new ArgumentNullException(nameof(job));
36 |
37 | // No action in the commit phase.
38 | job.Commit(ITaskProcessingJobOfTExtensionMethods.NoAction);
39 | }
40 |
41 | ///
42 | /// Updates the in-progress status of the task.
43 | ///
44 | /// The type of directive this job accepts.
45 | /// The job.
46 | /// Information about the task.
47 | public static void Update(this ITaskProcessingJob job, TaskInformation taskInformation)
48 | where TDirective : TaskDirective
49 | {
50 | // Sanity.
51 | if (null == job)
52 | throw new ArgumentNullException(nameof(job));
53 | if (null == taskInformation)
54 | return;
55 |
56 | // Use the standard method.
57 | job.Update
58 | (
59 | percentComplete: taskInformation.PercentageComplete,
60 | details: taskInformation.StatusDetails,
61 | data: taskInformation.ToJObject()
62 | );
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/nuget-readme.md:
--------------------------------------------------------------------------------
1 | # M-Files Vault Application Framework Extensions library
2 |
3 | *Please note that this library is provided "as-is" and with no warranty, explicit or otherwise. You should ensure that the functionality meets your requirements, and thoroughly test them, prior to using in any production scenarios.*
4 |
5 | The following helper library is a community-driven set of functionality that extends the base [M-Files Vault Application Framework](https://developer.m-files.com/Frameworks/Vault-Application-Framework/). This library is [open-source](https://github.com/M-Files/VAF.Extensions.Community) and not directly supported by M-Files. Contributions are accepted according to our contribution guide.
6 |
7 | ## Using the library
8 |
9 | 1. Install the latest production release from nuget.
10 | 2. Update your `VaultApplication.cs` file, ensuring that your vault application inherits from `MFiles.VAF.Extensions.ConfigurableVaultApplicationBase`. A more complete example of an empty vault application class is shown below.
11 | 3. Ensure that your `Configuration.cs` file inherits from `MFiles.VAF.Configuration.ConfigurationBase`. A more complete example of an empty configuration class is shown below.
12 |
13 | ```
14 | public class VaultApplication
15 | : MFiles.VAF.Extensions.ConfigurableVaultApplicationBase
16 | {
17 |
18 | }
19 | public class Configuration
20 | : MFiles.VAF.Configuration.ConfigurationBase
21 | {
22 |
23 | }
24 | ```
25 |
26 | ## Naming formats
27 |
28 | Releases follow a naming format based upon the M-Files versioning; releases are named using a combination of the year and month they are released in and an incrementing build number. Releases may also optionally contain a suffix (starting with a hyphen) denoting that the release is a preview release and should not be used in production environments.
29 |
30 | * `22.6.123` - this full release was made in June 2022. Full releases come from the `release` branch.
31 | * `22.6.140` - this full release was also made in June 2022, but is newer than the one above.
32 | * `22.7.141-preview` - this release was made in July 2022 from the main branch. Releases from the main branch are often close to release quality, but should only be used for testing.
33 | * `22.7.0.13-test-feature-1` - this release was made in July 2022 from a specific feature branch. This release will contain in-development functionality and should only be used when needing to test the specific feature being developed. Significant breaking changes may still be made when this functionality progresses to preview or release builds.
34 |
35 | Any problems can be logged as [issues against the repository](https://github.com/M-Files/VAF.Extensions.Community/issues), or discussed on the [M-Files Community](https://community.m-files.com).
36 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/Dashboards/DashboardHelpersEx.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace MFiles.VAF.Extensions.Dashboards
10 | {
11 | public static class DashboardHelpersEx
12 | {
13 | ///
14 | /// Converts an icon image file to a valid path.
15 | /// Also works with resources.
16 | ///
17 | /// The icon URI.
18 | /// A value that can be used as an icon source.
19 | public static string ImageFileToDataUri(string iconUri, Assembly assembly = null)
20 | {
21 | // Sanity.
22 | if(string.IsNullOrWhiteSpace(iconUri))
23 | return string.Empty;
24 |
25 | // If it starts with http/https then assume remote and reference it.
26 | if (iconUri.StartsWith("http:") || iconUri.StartsWith("https:"))
27 | return iconUri;
28 | // If it exists on the disk then load that and convert to base64.
29 | else if (System.IO.File.Exists(iconUri))
30 | return DashboardHelper.ImageFileToDataUri(iconUri);
31 | else
32 | {
33 |
34 | // Default to the calling assembly.
35 | assembly = assembly ?? Assembly.GetCallingAssembly() ?? Assembly.GetExecutingAssembly();
36 | if (null == assembly)
37 | return String.Empty;
38 |
39 | // Is it in a resource?
40 | foreach (var resource in assembly.GetManifestResourceNames())
41 | {
42 | // Is this good enough?
43 | if (resource.EndsWith(iconUri.Replace("/", ".")))
44 | {
45 | // Resolve the mime type.
46 | string mimeType = "image/unknown";
47 | string ext = iconUri.Substring(iconUri.LastIndexOf(".")).ToLower();
48 | Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.ClassesRoot.OpenSubKey(ext);
49 | if (regKey != null && regKey.GetValue("Content Type") != null)
50 | mimeType = regKey.GetValue("Content Type").ToString();
51 |
52 | // Base64 encode the image file content.
53 | using (var memoryStream = new System.IO.MemoryStream())
54 | {
55 | using (var stream = assembly.GetManifestResourceStream(resource))
56 | {
57 | stream.CopyTo(memoryStream);
58 | }
59 | // Create and return the data uri.
60 | return String.Format
61 | (
62 | "data:{0};base64,{1}",
63 | mimeType,
64 | Convert.ToBase64String(memoryStream.ToArray())
65 | );
66 | }
67 |
68 | }
69 | }
70 |
71 | // If we're not in the executing assembly then check that.
72 | if (assembly != Assembly.GetExecutingAssembly())
73 | return ImageFileToDataUri(iconUri, Assembly.GetExecutingAssembly());
74 |
75 | return String.Format("'{0}'", iconUri);
76 | }
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/TaskQueueManagerExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Common.ApplicationTaskQueue;
2 | using MFiles.VAF;
3 | using MFilesAPI;
4 | using System;
5 | using MFiles.VAF.MultiserverMode;
6 |
7 | namespace MFiles.VAF.Extensions
8 | {
9 | ///
10 | /// Contains helper methods for .
11 | ///
12 | public static class TaskQueueManagerExtensionMethods
13 | {
14 | ///
15 | /// Updates the data associated with ,
16 | /// setting the state to and the progress
17 | /// data to .
18 | ///
19 | /// The task manager to update the job using.
20 | /// The job to update.
21 | /// The new job state.
22 | /// Any progress data to report.
23 | [Obsolete("You should migrate to using VAF 2.3+ task queues (in the MFiles.VAF.AppTasks namespace), not VAF 2.2 task queues (in the MFiles.VAF.MultiserverMode namespace)")]
24 | public static void UpdateTask
25 | (
26 | this TaskQueueManager taskQueueManager,
27 | TaskProcessorJob job,
28 | MFTaskState state,
29 | string progressData = ""
30 | )
31 | {
32 | // Sanity.
33 | if (null == taskQueueManager)
34 | throw new ArgumentNullException(nameof(taskQueueManager));
35 | if (null == job)
36 | throw new ArgumentNullException(nameof(job));
37 |
38 | // Use the default UpdateTask implementation.
39 | taskQueueManager.UpdateTask(job.AppTaskId, state, progressData);
40 | }
41 |
42 | ///
43 | /// Updates the data associated with .
44 | /// Only use this overload to represent an exception having occurred whilst processing a job.
45 | ///
46 | /// The task manager to update the job using.
47 | /// The job to update.
48 | /// The exception that was thrown.
49 | [Obsolete("You should migrate to using VAF 2.3+ task queues (in the MFiles.VAF.AppTasks namespace), not VAF 2.2 task queues (in the MFiles.VAF.MultiserverMode namespace)")]
50 | public static void UpdateTask
51 | (
52 | this TaskQueueManager taskQueueManager,
53 | TaskProcessorJob job,
54 | Exception exception
55 | )
56 | {
57 | // Sanity.
58 | if (null == taskQueueManager)
59 | throw new ArgumentNullException(nameof(taskQueueManager));
60 | if (null == job)
61 | throw new ArgumentNullException(nameof(job));
62 | if (null == exception)
63 | throw new ArgumentNullException(nameof(exception));
64 |
65 | // Use the other overload.
66 | taskQueueManager.UpdateTask
67 | (
68 | job.AppTaskId,
69 | MFTaskState.MFTaskStateFailed,
70 | exception.ToString()
71 | );
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/FormattingExtensionMethods.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Threading;
5 |
6 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods
7 | {
8 | [TestClass]
9 | public class FormattingExtensionMethods
10 | {
11 | public FormattingExtensionMethods()
12 | {
13 | Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
14 | Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
15 | }
16 |
17 | [TestMethod]
18 | public void ToDashboardDisplayString_HoursMinutesSeconds()
19 | {
20 | var interval = new TimeSpan(1, 30, 32);
21 | Assert.AreEqual
22 | (
23 | "Runs on vault startup and every 1 hour, 30 minutes, and 32 seconds.
",
24 | interval.ToDashboardDisplayString()
25 | );
26 | }
27 |
28 | [TestMethod]
29 | public void ToDashboardDisplayString_RunOnStartup_False()
30 | {
31 | var interval = new TimeSpanEx(new TimeSpan(1, 30, 32))
32 | {
33 | RunOnVaultStartup = false
34 | };
35 | Assert.AreEqual
36 | (
37 | "Runs every 1 hour, 30 minutes, and 32 seconds.
",
38 | interval.ToDashboardDisplayString()
39 | );
40 | }
41 |
42 | [TestMethod]
43 | public void ToDashboardDisplayString_RunOnStartup_True()
44 | {
45 | var interval = new TimeSpanEx(new TimeSpan(1, 30, 32))
46 | {
47 | RunOnVaultStartup = true
48 | };
49 | Assert.AreEqual
50 | (
51 | "Runs on vault startup and every 1 hour, 30 minutes, and 32 seconds.
",
52 | interval.ToDashboardDisplayString()
53 | );
54 | }
55 |
56 | [TestMethod]
57 | public void ToDisplayString_2hours_0minutes_23seconds()
58 | {
59 | TimeSpan? interval = new TimeSpan(2, 0, 23);
60 | Assert.AreEqual
61 | (
62 | "2 hours, and 23 seconds",
63 | interval.ToDisplayString()
64 | );
65 | }
66 |
67 | [TestMethod]
68 | public void ToDisplayString_2hours_1minute_23seconds()
69 | {
70 | TimeSpan? interval = new TimeSpan(2, 1, 23);
71 | Assert.AreEqual
72 | (
73 | "2 hours, 1 minute, and 23 seconds",
74 | interval.ToDisplayString()
75 | );
76 | }
77 |
78 | [TestMethod]
79 | public void ToDisplayString_2hours_10minutes_23seconds()
80 | {
81 | TimeSpan? interval = new TimeSpan(2, 10, 23);
82 | Assert.AreEqual
83 | (
84 | "2 hours, 10 minutes, and 23 seconds",
85 | interval.ToDisplayString()
86 | );
87 | }
88 |
89 | [TestMethod]
90 | public void ToDisplayString_2days_2hours_10minutes_23seconds()
91 | {
92 | TimeSpan? interval = new TimeSpan(2, 2, 10, 23);
93 | Assert.AreEqual
94 | (
95 | "2 days, 2 hours, 10 minutes, and 23 seconds",
96 | interval.ToDisplayString()
97 | );
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions/ExtensionMethods/MFSearchBuilderExtensionMethods/Owner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using MFiles.VAF.Common;
3 | using MFilesAPI;
4 |
5 | namespace MFiles.VAF.Extensions
6 | {
7 | ///
8 | /// Extension methods for the class.
9 | ///
10 | // ReSharper disable once InconsistentNaming
11 | public static partial class MFSearchBuilderExtensionMethods
12 | {
13 | ///
14 | /// Adds a to the collection to find items with the given owner.
15 | ///
16 | /// The to add the condition to.
17 | /// The owner item.
18 | /// The provided, for chaining.
19 | public static MFSearchBuilder Owner
20 | (
21 | this MFSearchBuilder searchBuilder,
22 | ObjVerEx owner
23 | )
24 | {
25 | // Sanity.
26 | if (null == owner)
27 | throw new ArgumentNullException(nameof(owner));
28 |
29 | // Use the other overload.
30 | return searchBuilder.Owner(owner.ObjVer);
31 | }
32 |
33 | ///
34 | /// Adds a to the collection to find items with the given owner.
35 | ///
36 | /// The to add the condition to.
37 | /// The owner item.
38 | /// The provided, for chaining.
39 | public static MFSearchBuilder Owner
40 | (
41 | this MFSearchBuilder searchBuilder,
42 | ObjVer owner
43 | )
44 | {
45 | // Sanity.
46 | if (null == owner)
47 | throw new ArgumentNullException(nameof(owner));
48 |
49 | // Use the other overload.
50 | return searchBuilder.Owner(owner.ObjID);
51 | }
52 |
53 | ///
54 | /// Adds a to the collection to find items with the given owner.
55 | ///
56 | /// The to add the condition to.
57 | /// The owner item.
58 | /// The provided, for chaining.
59 | public static MFSearchBuilder Owner
60 | (
61 | this MFSearchBuilder searchBuilder,
62 | ObjID objID
63 | )
64 | {
65 | // Sanity.
66 | if (null == objID)
67 | throw new ArgumentNullException(nameof(objID));
68 |
69 | // Get the owner object type.
70 | var ownerObjType = searchBuilder
71 | .Vault
72 | .ObjectTypeOperations
73 | .GetObjectType(objID.Type);
74 |
75 | // Use the other method.
76 | return searchBuilder.AddPropertyValueSearchCondition
77 | (
78 | ownerObjType.OwnerPropertyDef,
79 | MFDataType.MFDatatypeLookup, // Owner must be single-select
80 | objID.ID,
81 | MFConditionType.MFConditionTypeEqual
82 | );
83 | }
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/TimeZoneInformationExtensionMethodsTests.cs:
--------------------------------------------------------------------------------
1 | using MFilesAPI;
2 | using Microsoft.VisualStudio.TestTools.UnitTesting;
3 | using Moq;
4 | using System;
5 | using System.Collections.Generic;
6 |
7 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods
8 | {
9 | [TestClass]
10 | public class TimeZoneInformationExtensionMethodsTests
11 | {
12 | [TestMethod]
13 | [DynamicData(nameof(GetEnsureLocalTimeData), DynamicDataSourceType.Method)]
14 | public void EnsureLocalTime
15 | (
16 | string timezoneName,
17 | DateTime input,
18 | DateTime expected
19 | )
20 | {
21 | var tzMock = new Mock();
22 | tzMock.Setup(m => m.StandardName).Returns(timezoneName);
23 |
24 | var output = tzMock.Object.EnsureLocalTime(input);
25 | Assert.AreEqual(expected.ToString(), output.ToString());
26 | Assert.AreEqual(expected.Kind, output.Kind);
27 | }
28 |
29 | public static IEnumerable GetEnsureLocalTimeData()
30 | {
31 | yield return new object[]
32 | {
33 | "GMT Standard Time",
34 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Utc),
35 | new DateTime(2023, 08, 01, 11, 00, 00, DateTimeKind.Local)
36 | };
37 | yield return new object[]
38 | {
39 | "GMT Standard Time",
40 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Local),
41 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Local),
42 | };
43 | yield return new object[]
44 | {
45 | "GMT Standard Time",
46 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Unspecified),
47 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Unspecified),
48 | };
49 | }
50 |
51 | [TestMethod]
52 | [DynamicData(nameof(GetEnsureUtcTimeData), DynamicDataSourceType.Method)]
53 | public void EnsureUtcTime
54 | (
55 | string timezoneName,
56 | DateTime input,
57 | DateTime expected
58 | )
59 | {
60 | var tzMock = new Mock();
61 | tzMock.Setup(m => m.StandardName).Returns(timezoneName);
62 |
63 | var output = tzMock.Object.EnsureUTCTime(input);
64 | Assert.AreEqual(expected.ToString(), output.ToString());
65 | Assert.AreEqual(expected.Kind, output.Kind);
66 | }
67 |
68 | public static IEnumerable GetEnsureUtcTimeData()
69 | {
70 | yield return new object[]
71 | {
72 | "GMT Standard Time",
73 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Local),
74 | new DateTime(2023, 08, 01, 09, 00, 00, DateTimeKind.Utc)
75 | };
76 | yield return new object[]
77 | {
78 | "GMT Standard Time",
79 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Utc),
80 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Utc)
81 | };
82 | yield return new object[]
83 | {
84 | "GMT Standard Time",
85 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Unspecified),
86 | new DateTime(2023, 08, 01, 10, 00, 00, DateTimeKind.Unspecified)
87 | };
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/ExtensionMethods/MFSearchBuilderExtensionMethods/ExternalID.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using MFilesAPI;
7 | using Microsoft.VisualStudio.TestTools.UnitTesting;
8 |
9 | namespace MFiles.VAF.Extensions.Tests.ExtensionMethods.MFSearchBuilderExtensionMethods
10 | {
11 | [TestClass]
12 | public class ExternalId
13 | : MFSearchBuilderExtensionMethodTestBase
14 | {
15 | ///
16 | /// Tests that calling
17 | ///
18 | /// adds a search condition.
19 | ///
20 | [TestMethod]
21 | public void AddsSearchCondition()
22 | {
23 | // Create the search builder.
24 | var mfSearchBuilder = this.GetSearchBuilder();
25 |
26 | // Ensure it has no items in the collection.
27 | Assert.AreEqual(0, mfSearchBuilder.Conditions.Count);
28 |
29 | // Add the search condition for the external ID.
30 | mfSearchBuilder.ExternalId("hello-world");
31 |
32 | // Ensure that there is one item in the collection.
33 | Assert.AreEqual(1, mfSearchBuilder.Conditions.Count);
34 | }
35 |
36 | [TestMethod]
37 | [ExpectedException(typeof(ArgumentNullException))]
38 | public void NullExternalIdThrows()
39 | {
40 | // Create the search builder.
41 | var mfSearchBuilder = this.GetSearchBuilder();
42 |
43 | // Attempt to search by null.
44 | mfSearchBuilder.ExternalId(null);
45 | }
46 |
47 | ///
48 | /// Tests that calling
49 | ///
50 | /// adds a valid search condition.
51 | ///
52 | [TestMethod]
53 | public void SearchConditionIsCorrect()
54 | {
55 | // Create the search builder.
56 | var mfSearchBuilder = this.GetSearchBuilder();
57 |
58 | // Add the search condition for the external ID.
59 | mfSearchBuilder.ExternalId("hello-world");
60 |
61 | // If there's anything other than one condition then fail.
62 | if (mfSearchBuilder.Conditions.Count != 1)
63 | Assert.Inconclusive("Only one search condition should exist");
64 |
65 | // Retrieve the just-added condition.
66 | var condition = mfSearchBuilder.Conditions[mfSearchBuilder.Conditions.Count];
67 |
68 | // Ensure the condition type is correct.
69 | Assert.AreEqual(MFConditionType.MFConditionTypeEqual, condition.ConditionType);
70 |
71 | // Ensure the expression type is correct.
72 | Assert.AreEqual(MFExpressionType.MFExpressionTypeStatusValue, condition.Expression.Type);
73 |
74 | // Ensure the status value is correct.
75 | Assert.AreEqual(MFStatusType.MFStatusTypeExtID, condition.Expression.DataStatusValueType);
76 |
77 | // Ensure that the typed value is correct.
78 | Assert.AreEqual(MFDataType.MFDatatypeText, condition.TypedValue.DataType);
79 | Assert.AreEqual("hello-world", condition.TypedValue.Value as string);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Configuration/Upgrading/ConfigurationUpgradeManager.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration;
2 | using MFiles.VAF.Extensions.Configuration.Upgrading;
3 | using MFilesAPI;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using Moq;
6 | using Newtonsoft.Json.Linq;
7 | using System;
8 | using System.Collections.Generic;
9 | using System.Linq;
10 | using System.Text;
11 | using System.Threading.Tasks;
12 |
13 | namespace MFiles.VAF.Extensions.Tests.Configuration.Upgrading
14 | {
15 | [TestClass]
16 | public partial class ConfigurationUpgradeManager
17 | : TestBaseWithVaultMock
18 | {
19 | delegate void readConfigurationDataCallback(Vault v, string @namespace, string @name, out string value);
20 |
21 | protected IConfigurationStorage GetConfigurationStorage(TConfigurationType configuration)
22 | => GetConfigurationStorage(configuration == null ? (string)null : Newtonsoft.Json.JsonConvert.SerializeObject(configuration));
23 |
24 | protected IConfigurationStorage GetConfigurationStorage(string configurationData = null)
25 | {
26 | var storage = new Mock();
27 |
28 | // Reading of data.
29 | storage
30 | .Setup(m => m.ReadConfigurationData(It.IsAny(), It.IsAny(), It.IsAny()))
31 | .Returns(() => configurationData);
32 | storage
33 | .Setup(m => m.ReadConfigurationData(It.IsAny(), It.IsAny(), It.IsAny(), out It.Ref.IsAny))
34 | .Callback(new readConfigurationDataCallback((Vault v, string @namespace, string @name, out string value) =>
35 | {
36 | value = configurationData;
37 | }))
38 | .Returns(configurationData != null);
39 |
40 | // Writing of data.
41 | storage
42 | .Setup(m => m.SaveConfigurationData(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()))
43 | .Callback((Vault v, string @namespace, string @data, string @key) =>
44 | {
45 | configurationData = @data;
46 | });
47 |
48 | // Deserialisation.
49 | // Note: this isn't a true mock, but we'll assume that Newtonsoft is capable of doing these actions as it makes the method more flexible.
50 | storage
51 | .Setup(m => m.Deserialize(It.IsAny(), It.IsAny()))
52 | .Returns((Type t, string s) => Newtonsoft.Json.JsonConvert.DeserializeObject(s, t));
53 | storage
54 | .Setup(m => m.Deserialize(It.IsAny()))
55 | .Returns
56 | (
57 | // The generic method is a bit awkward to mock...
58 | new InvocationFunc((invocation) =>
59 | {
60 | return Newtonsoft.Json.JsonConvert.DeserializeObject(invocation.Arguments[0] as string, invocation.Method.GetGenericArguments()[0]);
61 | })
62 | );
63 |
64 | // Serialisation.
65 | storage
66 | .Setup(m => m.Serialize(It.IsAny()))
67 | .Returns((object o) => Newtonsoft.Json.JsonConvert.SerializeObject(o));
68 |
69 | //Okay.
70 | return storage.Object;
71 | }
72 |
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/BRANCHING.md:
--------------------------------------------------------------------------------
1 | # Branching
2 |
3 | This page documents the branching strategy used by this repository. Please read the documentation on [contributing to this repository](CONTRIBUTING.md) prior to submitting any contributions.
4 |
5 | Whilst code can be downloaded from any public branch, it is strongly recommended that only [release](#General-releases) code is run in production environments. [Prerelease code](#prereleases) can be run in test environments (e.g. to test a new feature). It is recommended that code from the [default branch](#default-branch) is only used for creating new feature branches.
6 |
7 | # Default branch
8 |
9 | The default branch is always deemed to be most up-to-date, including changes merged in from other branches. Whilst every effort is made to ensure that this code is adequately tested (via unit tests and manually), users should acknowledge that this code may be of lower quality than the releases. Code from this branch is not recommended for production use.
10 |
11 | # Feature branches
12 |
13 | New features (extension methods, helper classes, etc.) should be made in a separate branch. Unless otherwise marked, feature branches should be considered 'under active development' and may be broken or have known (even known and undocumented) issues.
14 |
15 | Once complete (including adequate unit testing), a pull request should be made asking for the changes to be included into the repository's default branch. Any accepted pull requests will be merged into the default branch as per the [contribution guidance](CONTRIBUTING.md).
16 |
17 | Feature branches should be removed once the changes are merged into the default branch.
18 |
19 | # Releases
20 |
21 | Periodically, M-Files will organise an update of the [nuget package](https://www.nuget.org/packages/MFiles.VAF.Extensions) so that users can more easily test out the new features. To do this, changes will be merged into the `prerelease` or `release` branch accordingly.
22 |
23 | ## Prereleases
24 |
25 | Changes from the default branch will be periodically merged into the `prerelease` branch. Code merged into this branch on GitHub will trigger a GitHub action that will publish the package to nuget. Code from this branch should follow semantic versioning conventions and have a [suitable suffix](https://docs.microsoft.com/en-gb/nuget/concepts/package-versioning#pre-release-versions) to denote that it is a prerelease version.
26 |
27 | *It is **not recommended** to use prerelease packages in production environments. These packages are designed only for use in test or internal environments.*
28 |
29 | ## General releases
30 |
31 | Once prerelease code has undergone community testing and commenting, changes will be merged into the `release` branch. Code merged into this branch on GitHub will trigger a GitHub action that will publish the package to nuget. Code from this branch should follow semantic versioning conventions and should not contain any suffixes to mark it as pre-release.
32 |
33 | *It is recommended to **only use general release packages in production environments**.*
34 |
--------------------------------------------------------------------------------
/MFiles.VAF.Extensions.Tests/Dashboards/DashboardTableCellTests.cs:
--------------------------------------------------------------------------------
1 | using MFiles.VAF.Configuration.Domain.Dashboards;
2 | using MFiles.VAF.Extensions.Dashboards;
3 | using Microsoft.VisualStudio.TestTools.UnitTesting;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace MFiles.VAF.Extensions.Tests.Dashboards
11 | {
12 | [TestClass]
13 | public class DashboardTableCellTests
14 | : DashboardContentBaseTests
15 | {
16 | [TestMethod]
17 | public void NullInnerContentDoesNotThrow()
18 | {
19 | new DashboardTableCell(innerContent: null);
20 | }
21 | [TestMethod]
22 | public void NullInnerContentDoesNotThrow2()
23 | {
24 | new DashboardTableCell(htmlContent: null);
25 | }
26 |
27 | [TestMethod]
28 | public void InnerContentSetCorrectly()
29 | {
30 | var innerContent = new DashboardTableCell("hello world.
");
31 | var content = new DashboardTableCell(innerContent);
32 | Assert.AreEqual(innerContent, content?.InnerContent);
33 | }
34 |
35 | [TestMethod]
36 | public void StringInnerContentWrapped()
37 | {
38 | var innerContent = "hello world.
";
39 | var content = new DashboardTableCell(innerContent);
40 | Assert.IsNotNull(content?.InnerContent as DashboardCustomContentEx);
41 | Assert.AreEqual(innerContent, content?.InnerContent?.ToXmlString());
42 | }
43 |
44 | [TestMethod]
45 | public void ElementNameCorrect_TD()
46 | {
47 | var content = new DashboardTableCell("hello");
48 | var output = content.ToXmlFragment()?.FirstChild;
49 | Assert.IsNotNull(output);
50 | Assert.AreEqual("td", output.LocalName);
51 | }
52 |
53 | [TestMethod]
54 | public void ElementNameCorrect_TH()
55 | {
56 | var content = new DashboardTableCell("hello") { DashboardTableCellType = DashboardTableCellType.Header };
57 | var output = content.ToXmlFragment()?.FirstChild;
58 | Assert.IsNotNull(output);
59 | Assert.AreEqual("th", output.LocalName);
60 | }
61 |
62 | [TestMethod]
63 | public void DefaultStyles_TD()
64 | {
65 | var content = new DashboardTableCell("hello");
66 | var output = content.ToXmlFragment()?.FirstChild;
67 | Assert.IsTrue
68 | (
69 | new StyleComparisonHelper("font-size: 12px; padding: 2px 3px; text-align: left;")
70 | .TestAgainstString(output?.Attributes["style"]?.Value)
71 | );
72 | }
73 |
74 | [TestMethod]
75 | public void DefaultStyles_TH()
76 | {
77 | var content = new DashboardTableCell("hello") { DashboardTableCellType = DashboardTableCellType.Header };
78 | var output = content.ToXmlFragment()?.FirstChild;
79 | Assert.IsTrue
80 | (
81 | new StyleComparisonHelper("font-size: 12px; padding: 2px 3px; text-align: left;border-bottom: 1px solid #CCC;")
82 | .TestAgainstString(output?.Attributes["style"]?.Value)
83 | );
84 | }
85 |
86 | public override DashboardTableCell CreateDashboardContent()
87 | {
88 | return new DashboardTableCell("hello world.
");
89 | }
90 |
91 | }
92 | }
93 |
--------------------------------------------------------------------------------