diff --git a/Filesystem/File.vb b/Filesystem/File.vb
new file mode 100644
index 00000000..30cd5203
--- /dev/null
+++ b/Filesystem/File.vb
@@ -0,0 +1,487 @@
+Imports System.IO
+Imports System.Security.Cryptography
+Imports System.Text
+Imports System.Text.RegularExpressions
+Imports DigitalData.Modules.Logging
+
+''' File
+''' 0.0.0.1
+''' 11.10.2018
+'''
+''' Module that provides variouse File operations
+'''
+'''
+''' NLog, >= 4.5.8
+'''
+'''
+''' LogConfig, DigitalData.Module.Logging.LogConfig
+''' A LogConfig object
+'''
+'''
+'''
+'''
+'''
+'''
+'''
+Public Class File
+ Private ReadOnly _Logger As Logger
+ Private ReadOnly _LogConfig As LogConfig
+
+ Private ReadOnly _invalidFilenameChars As String
+ Private ReadOnly _invalidPathChars As String
+
+ Private Const REGEX_CLEAN_FILENAME As String = "[\\/:""<>|\b\0\r\n\t]"
+ Private Const REGEX_CLEAN_PATH As String = "[""<>|\b\0\r\n\t]"
+
+ ' The limit enforced by windows for filenpaths is 260,
+ ' so we use a slightly smaller number to have some Error margin.
+ '
+ ' Source: https://docs.microsoft.com/de-de/windows/win32/fileio/naming-a-file?redirectedfrom=MSDN#maximum-path-length-limitation
+ Private Const MAX_FILE_PATH_LENGTH = 250
+
+ Private Const FILE_NAME_ACCESS_TEST = "accessTest.txt"
+
+ Public Sub New(LogConfig As LogConfig)
+ _LogConfig = LogConfig
+ _Logger = LogConfig.GetLogger()
+
+ _invalidFilenameChars = String.Join("", Path.GetInvalidFileNameChars())
+ _invalidPathChars = String.Join("", Path.GetInvalidPathChars())
+ End Sub
+
+ Public Function GetCleanFilename(FileName As String) As String
+ _Logger.Debug("Filename before cleaning: [{0}]", FileName)
+
+ Dim oCleanName As String = FileName
+ oCleanName = Regex.Replace(oCleanName, _invalidFilenameChars, String.Empty)
+ oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_FILENAME, String.Empty, RegexOptions.Singleline)
+ oCleanName = Regex.Replace(oCleanName, "\s{2,}", " ")
+ oCleanName = Regex.Replace(oCleanName, "\.{2,}", ".")
+
+ _Logger.Debug("Filename after cleaning: [{0}]", oCleanName)
+
+ Return oCleanName
+ End Function
+
+ Public Function GetCleanPath(FilePath As String) As String
+ _Logger.Debug("Path before cleaning: [{0}]", FilePath)
+
+ Dim oCleanName As String = FilePath
+ oCleanName = Regex.Replace(oCleanName, _invalidPathChars, String.Empty)
+ oCleanName = Regex.Replace(oCleanName, REGEX_CLEAN_PATH, String.Empty, RegexOptions.Singleline)
+
+ _Logger.Debug("Path after cleaning: [{0}]", oCleanName)
+
+ Return oCleanName
+ End Function
+
+ '''
+ ''' Reads the file at `FilePath` and computes a SHA256 Hash from its contents
+ '''
+ '''
+ '''
+ Public Function GetChecksum(FilePath As String) As String
+ Try
+ Using oFileStream = IO.File.OpenRead(FilePath)
+ Using oStream As New BufferedStream(oFileStream, 1200000)
+ Dim oChecksum() As Byte = SHA256.Create.ComputeHash(oStream)
+ Return FormatHash(oChecksum)
+ End Using
+ End Using
+ Catch ex As Exception
+ _Logger.Error(ex)
+ Return Nothing
+ End Try
+ End Function
+
+ Public Function GetChecksumFromString(pStringToCheck As String) As String
+ Dim oBytes() As Byte = Encoding.UTF8.GetBytes(pStringToCheck)
+ Dim oChecksum() As Byte = SHA256.Create.ComputeHash(oBytes)
+ Return FormatHash(oChecksum)
+ End Function
+
+ Private Function FormatHash(pChecksum)
+ Return BitConverter.
+ ToString(pChecksum).
+ Replace("-", String.Empty)
+ End Function
+
+ '''
+ ''' Adds file version string to given filename `Destination` if that file already exists.
+ '''
+ '''
+ '''
+ Public Function GetVersionedFilename(Destination As String) As String
+ Try
+ Dim oFileName As String = Destination
+ Dim oFinalFileName = oFileName
+
+ Dim oDestinationDir = Path.GetDirectoryName(oFileName)
+ Dim oExtension = Path.GetExtension(oFileName)
+
+ Dim oVersionSeparator As Char = "~"c
+
+ ' Split Filename without extension at version separator to:
+ ' - Check if file is already versioned
+ ' - Get the file version of an already versioned file
+ '
+ ' Example:
+ ' test1.pdf --> test1 --> ['test1'] --> no fileversion
+ ' test1~2.pdf --> test1~2 --> ['test1', '2'] --> version 2
+ ' test1~12345~2.pdf --> test1~12345~2 --> ['test1', '12345', '2'] --> still version 2
+ 'Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(oFileName)
+ 'Dim oSplitFilename = oFileNameWithoutExtension.Split(oVersionSeparator).ToList()
+
+ ' if file is already versioned, extract file version
+ ' else just use the filename and set version to 1
+ 'If oSplitFilename.Count > 1 Then
+ ' Dim oVersion As Integer = 1
+ ' Try
+ ' oVersion = Integer.Parse(oSplitFilename.Last())
+ ' oFileNameWithoutExtension = String.Join("", oSplitFilename.Take(oSplitFilename.Count - 1))
+ ' Catch ex As Exception
+ ' ' oFilenameWithoutExtension does NOT change
+ ' oFileNameWithoutExtension = oFileNameWithoutExtension
+ ' Finally
+ ' oFileVersion = oVersion
+ ' End Try
+ 'Else
+ ' oFileVersion = 1
+ 'End If
+
+ Dim oFileNameWithoutExtension = Path.GetFileNameWithoutExtension(oFileName)
+ Dim oSplitResult = GetVersionedString(oFileNameWithoutExtension, oVersionSeparator)
+
+ oFileNameWithoutExtension = oSplitResult.Item1
+ Dim oFileVersion = oSplitResult.Item2
+
+ ' Shorten the filename (only filename, without extension or version)
+ ' by cutting the length in half. This should work no matter how long the path and/or filename are.
+ If oFileName.Length > MAX_FILE_PATH_LENGTH Then
+ _Logger.Info("Filename is too long. Filename will be cut to prevent further errors.")
+ _Logger.Info("Original Filename is: {0}", oFileNameWithoutExtension)
+ Dim oNewLength As Integer = Math.Round(oFileNameWithoutExtension.Length / 2)
+ Dim oNewFileNameWithoutExtension = oFileNameWithoutExtension.Substring(0, oNewLength)
+ _Logger.Info("New Filename will be: {0}", oNewFileNameWithoutExtension)
+
+ oFileNameWithoutExtension = oNewFileNameWithoutExtension
+ End If
+
+ ' while file exists, increment version
+ Do
+ oFinalFileName = Path.Combine(oDestinationDir, GetFilenameWithVersion(oFileNameWithoutExtension, oVersionSeparator, oFileVersion, oExtension))
+ _Logger.Debug("Intermediate Filename is {0}", oFinalFileName)
+ _Logger.Debug("File version: {0}", oFileVersion)
+ oFileVersion += 1
+ Loop While (IO.File.Exists(oFinalFileName))
+
+ _Logger.Debug("Final Filename is {0}", oFinalFileName)
+
+ Return oFinalFileName
+ Catch ex As Exception
+ _Logger.Warn("Filename {0} could not be versioned. Original filename will be returned!", Destination)
+ _Logger.Error(ex)
+ Return Destination
+ End Try
+ End Function
+
+ '''
+ ''' Split String at version separator to:
+ ''' check if string is already versioned,
+ ''' get the string version of an already versioned string
+ '''
+ '''
+ ''' Examples:
+ ''' test1.pdf --> test1 --> ['test1'] --> no fileversion
+ ''' test1~2.pdf --> test1~2 --> ['test1', '2'] --> version 2
+ ''' test1~12345~2.pdf --> test1~12345~2 --> ['test1', '12345', '2'] --> still version 2
+ ''' somestring~3 --> somestring~3 --> ['somestring', '3'] --> version 3
+ '''
+ ''' The string to versioned
+ ''' The character to split at
+ ''' Tuple of string and version
+ Public Function GetVersionedString(pString As String, pSeparator As Char) As Tuple(Of String, Integer)
+ Dim oSplitString = pString.Split(pSeparator).ToList()
+ Dim oStringVersion As Integer
+
+ ' if string is already versioned, extract string version
+ ' else just use the string and set version to 1
+ If oSplitString.Count > 1 Then
+ Dim oVersion As Integer = 1
+ Try
+ oVersion = Integer.Parse(oSplitString.Last())
+ pString = String.Join("", oSplitString.Take(oSplitString.Count - 1))
+ Catch ex As Exception
+ ' pString does NOT change
+ pString = pString
+ Finally
+ oStringVersion = oVersion
+ End Try
+ Else
+ oStringVersion = 1
+ End If
+
+ Return New Tuple(Of String, Integer)(pString, oStringVersion)
+ End Function
+
+ Public Function GetAppDataPath(CompanyName As String, ProductName As String)
+ Dim oLocalAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
+ Return Path.Combine(oLocalAppData, CompanyName, ProductName)
+ End Function
+
+ Private Function GetFilenameWithVersion(FileNameWithoutExtension As String, VersionSeparator As Char, FileVersion As Integer, Extension As String) As String
+ If FileVersion <= 1 Then
+ Return $"{FileNameWithoutExtension}{Extension}"
+ Else
+ Return $"{FileNameWithoutExtension}{VersionSeparator}{FileVersion}{Extension}"
+ End If
+ End Function
+
+ '''
+ ''' Removes files in a directory filtered by filename, extension and last write date
+ '''
+ ''' The directory in which files will be deleted
+ ''' Only delete files which are older than x days. Must be between 0 and 1000 days.
+ ''' A filename filter which will be checked
+ ''' A file extension which will be checked
+ ''' Should the function continue with deleting when a file could not be deleted?
+ ''' True if all files were deleted or if no files were deleted, otherwise false
+ Public Function RemoveFiles(Path As String, FileKeepTime As Integer, FileBaseName As String, Optional FileExtension As String = "log", Optional ContinueOnError As Boolean = True) As Boolean
+ If Not TestPathIsDirectory(Path) Then
+ Throw New ArgumentException($"Path {Path} is not a directory!")
+ End If
+
+ If Not Directory.Exists(Path) Then
+ Throw New DirectoryNotFoundException($"Path {Path} does not exist!")
+ End If
+
+ If FileKeepTime < 0 Or FileKeepTime > 1000 Then
+ Throw New ArgumentOutOfRangeException("FileKeepTime must be an integer between 0 and 1000!")
+ End If
+
+ Dim oUnableToDeleteCounter = 0
+ Dim oDirectory As New DirectoryInfo(Path)
+ Dim oDateLimit As DateTime = DateTime.Now.AddDays(FileKeepTime)
+ Dim oFiles As List(Of FileInfo) = oDirectory.
+ EnumerateFiles($"*{FileBaseName}*").
+ Where(Function(oFileInfo As FileInfo)
+ Return oFileInfo.Extension = FileExtension And oFileInfo.LastWriteTime < oDateLimit
+ End Function).
+ ToList()
+
+ If oFiles.Count = 0 Then
+ _Logger.Debug("No files found that match the criterias.")
+ Return True
+ End If
+
+ _Logger.Debug("Deleting old files (Found {0}).", oFiles.Count)
+
+ For Each oFile As FileInfo In oFiles
+ Try
+ oFile.Delete()
+ Catch ex As Exception
+ If ContinueOnError = False Then
+ _Logger.Warn("Deleting files was aborted at file {0}.", oFile.FullName)
+ Return False
+ End If
+ oUnableToDeleteCounter = oUnableToDeleteCounter + 1
+ _Logger.Warn("File {0} could not be deleted!")
+ End Try
+ Next
+
+ If oUnableToDeleteCounter > 0 Then
+ _Logger.Debug("Old files partially removed. {0} files could not be removed.", oUnableToDeleteCounter)
+ Else
+ _Logger.Debug("Old files removed.")
+ End If
+
+ Return True
+ End Function
+
+
+ Public Sub MoveTo(FilePath As String, Directory As String)
+ Dim oFileInfo As New FileInfo(FilePath)
+ IO.File.Move(FilePath, Path.Combine(Directory, oFileInfo.Name))
+ End Sub
+
+
+ Public Sub MoveTo(FilePath As String, NewFileName As String, Directory As String)
+ IO.File.Move(FilePath, Path.Combine(Directory, NewFileName))
+ End Sub
+
+ '''
+ ''' Copied from https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories
+ '''
+ '''
+ '''
+ '''
+ Public Sub CopyDirectory(ByVal SourceDirName As String, ByVal DestDirName As String, ByVal CopySubDirs As Boolean)
+ Dim oDirectory As DirectoryInfo = New DirectoryInfo(SourceDirName)
+
+ If Not oDirectory.Exists Then
+ Throw New DirectoryNotFoundException("Source directory does not exist or could not be found: " & SourceDirName)
+ End If
+
+ Dim oDirectories As DirectoryInfo() = oDirectory.GetDirectories()
+ Directory.CreateDirectory(DestDirName)
+ Dim oFiles As FileInfo() = oDirectory.GetFiles()
+
+ For Each oFile As FileInfo In oFiles
+ Dim tempPath As String = Path.Combine(DestDirName, oFile.Name)
+ oFile.CopyTo(tempPath, False)
+ Next
+
+ If CopySubDirs Then
+ For Each oSubDirectory As DirectoryInfo In oDirectories
+ Dim oTempPath As String = Path.Combine(DestDirName, oSubDirectory.Name)
+ CopyDirectory(oSubDirectory.FullName, oTempPath, CopySubDirs)
+ Next
+ End If
+ End Sub
+
+ '''
+ ''' Tries to create a directory and returns its path.
+ ''' Returns a temp path if `DirectoryPath` can not be created or written to.
+ '''
+ ''' The directory to create
+ ''' Should a write access test be performed?
+ ''' The used path
+ Public Function CreateDirectory(DirectoryPath As String, Optional TestWriteAccess As Boolean = True) As String
+ Dim oFinalPath As String
+ If Directory.Exists(DirectoryPath) Then
+ _Logger.Debug("Directory {0} already exists. Skipping.", DirectoryPath)
+ oFinalPath = DirectoryPath
+ Else
+ Try
+ Directory.CreateDirectory(DirectoryPath)
+ oFinalPath = DirectoryPath
+ Catch ex As Exception
+ _Logger.Error(ex)
+ _Logger.Warn("Directory {0} could not be created. Temp path will be used instead.", DirectoryPath)
+ oFinalPath = Path.GetTempPath()
+ End Try
+ End If
+
+ If TestWriteAccess AndAlso Not TestPathIsWritable(DirectoryPath) Then
+ _Logger.Warn("Directory {0} is not writable. Temp path will be used instead.", DirectoryPath)
+ oFinalPath = Path.GetTempPath()
+ Else
+ oFinalPath = DirectoryPath
+ End If
+
+ _Logger.Debug("Using path {0}", oFinalPath)
+
+ Return oFinalPath
+ End Function
+
+ Public Function TestPathIsWritable(DirectoryPath As String) As Boolean
+ Try
+ Dim fileAccessPath = Path.Combine(DirectoryPath, FILE_NAME_ACCESS_TEST)
+ Using fs As FileStream = IO.File.Create(fileAccessPath)
+ fs.WriteByte(0)
+ End Using
+
+ IO.File.Delete(fileAccessPath)
+ Return True
+ Catch ex As Exception
+ Return False
+ End Try
+ End Function
+
+ '''
+ ''' Checks if a file is locked, ie. in use by another process.
+ '''
+ '''
+ ''' https://docs.microsoft.com/en-us/dotnet/standard/io/handling-io-errors
+ ''' https://stackoverflow.com/questions/876473/is-there-a-way-to-check-if-a-file-is-in-use
+ '''
+ Public Function TestFileIsLocked(pFilePath As String) As Boolean
+ Try
+ Using stream As FileStream = IO.File.Open(pFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)
+ stream.Close()
+ End Using
+ Catch ex As Exception When ((ex.HResult And &HFFFF) = 32)
+ Return True
+ Catch ex As Exception
+ Return True
+ End Try
+
+ Return False
+ End Function
+
+ Public Function TestPathIsDirectory(Path As String) As Boolean
+ If Not Directory.Exists(Path) Then
+ Return False
+ End If
+
+ Dim oIsDirectory As Boolean = (System.IO.File.GetAttributes(Path) And FileAttributes.Directory) = FileAttributes.Directory
+ Return oIsDirectory
+ End Function
+
+ Public Function GetDateDirectory(pBaseDirectory As String, pDate As Date) As String
+ Dim oDateDirectory = GetDateString(pDate)
+ Dim oFinalDirectory As String = IO.Path.Combine(pBaseDirectory, oDateDirectory)
+ Return oFinalDirectory
+ End Function
+
+ Public Function GetDateDirectory(pBaseDirectory As String) As String
+ Return GetDateDirectory(pBaseDirectory, Now)
+ End Function
+
+ Public Function CreateDateDirectory(pBaseDirectory As String, pDate As Date) As String
+ Dim oDateDirectory = GetDateString(pDate)
+ Dim oFinalDirectory As String = IO.Path.Combine(pBaseDirectory, oDateDirectory)
+
+ If IO.Directory.Exists(oFinalDirectory) = False Then
+ _Logger.Debug("Path does not exist, creating: [{0}]", oFinalDirectory)
+ Try
+ Directory.CreateDirectory(oFinalDirectory)
+ _Logger.Debug("Created folder [{0}]", oFinalDirectory)
+ Catch ex As Exception
+ _Logger.Warn("Final path [{0}] could not be created!", oFinalDirectory)
+ _Logger.Error(ex)
+ End Try
+ End If
+
+ Return oFinalDirectory
+ End Function
+
+ Public Function CreateDateDirectory(pBaseDirectory As String) As String
+ Return CreateDateDirectory(pBaseDirectory, Now)
+ End Function
+
+ Public Function GetDateString() As String
+ Return $"{Now:yyyy\\MM\\dd}"
+ End Function
+
+ Public Function GetDateString(pDate As Date) As String
+ Return $"{pDate:yyyy\\MM\\dd}"
+ End Function
+
+ Public Function GetDateTimeString() As String
+ Return $"{Now:yyyy-MM-dd_hh-mm-ffff}"
+ End Function
+
+ Public Function GetDateTimeString(pDate As Date) As String
+ Return $"{pDate:yyyy-MM-dd_hh-mm-ffff}"
+ End Function
+
+ Public Function GetFilenameWithSuffix(pFilePath As String, pSuffix As String)
+ Dim oFileInfo = New IO.FileInfo(pFilePath)
+ Return GetFilenameWithSuffix(IO.Path.GetFileNameWithoutExtension(pFilePath), pSuffix, oFileInfo.Extension.Substring(1))
+ End Function
+
+ Public Function GetFilenameWithSuffix(pBaseString As String, pSuffix As String, pExtension As String)
+ Return $"{pBaseString}-{pSuffix}.{pExtension}"
+ End Function
+
+ Public Function GetFilenameWithPrefix(pFilePath As String, pPrefix As String)
+ Dim oFileInfo = New IO.FileInfo(pFilePath)
+ Return GetFilenameWithSuffix(IO.Path.GetFileNameWithoutExtension(pFilePath), pPrefix, oFileInfo.Extension.Substring(1))
+ End Function
+
+ Public Function GetFilenameWithPrefix(pBaseString As String, pPrefix As String, pExtension As String)
+ Return $"{pPrefix}-{pBaseString}.{pExtension}"
+ End Function
+
+End Class
diff --git a/Filesystem/FileContainer/DocumentObject.vb b/Filesystem/FileContainer/DocumentObject.vb
new file mode 100644
index 00000000..c9da13f0
--- /dev/null
+++ b/Filesystem/FileContainer/DocumentObject.vb
@@ -0,0 +1,18 @@
+Imports System.Runtime.Serialization
+
+
+Public Class DocumentObject
+
+
+ Public ReadOnly FileName As String
+
+ Public ReadOnly ContainerId As String
+
+ Public ReadOnly DocumentId As Int64
+
+ Public Sub New(ContainerId As String, DocumentId As Int64, FileName As String)
+ Me.ContainerId = ContainerId
+ Me.DocumentId = DocumentId
+ Me.FileName = FileName
+ End Sub
+End Class
diff --git a/Filesystem/FileContainer/FileContainer.vb b/Filesystem/FileContainer/FileContainer.vb
new file mode 100644
index 00000000..54d6ce4a
--- /dev/null
+++ b/Filesystem/FileContainer/FileContainer.vb
@@ -0,0 +1,193 @@
+Imports System.IO
+Imports DigitalData.Modules.Logging
+Imports DigitalData.Modules.Encryption
+Imports ProtoBuf
+
+''' FileContainer
+''' 0.0.0.2
+''' 21.11.2018
+'''
+''' File Container for securely saving files
+'''
+'''
+''' NLog, >= 4.5.8
+'''
+'''
+''' LogConfig, DigitalData.Module.Logging.LogConfig
+''' A LogConfig object
+''' Password, String
+''' The Password to Encrypt
+''' Path, String
+''' The Path to save/load the container
+'''
+'''
+''' dim oContainer = Container.Create(logConfig, "pass", "E:\some.container")
+''' dim oContainer = Container.Load(logConfig, "pass", "E:\some.container")
+'''
+''' dim oContainer = new Container(logConfig, "pass", "E:\some.container")
+''' oContainer.Save()
+'''
+''' dim oContainer = new Container(logConfig, "pass", "E:\some.container")
+''' oContainer.Contents = oSomeData
+''' oContainer.Save()
+'''
+''' dim oContainer = new Container(logConfig, "pass", "E:\some.container")
+''' oContainer.Load()
+''' dim oContents = oContainer.Contents
+'''
+''' dim oContainer = new Container(logConfig, "pass", "E:\some.container")
+''' oContainer.Load()
+''' oContainer.Contents = oSomeOtherData
+''' oContainer.Save()
+''' oContainer.SaveAs("E:\some2.container")
+'''
+Public Class FileContainer
+ Private _crypto As Encryption.Encryption
+ Private _compression As Compression
+ Private _inner As FileContainerInner
+ Private _logger As Logger
+ Private _logConfig As LogConfig
+ Private _path As String
+
+ Public Property Contents As Byte()
+ Get
+ Return _inner.Contents
+ End Get
+ Set(value As Byte())
+ _inner.Contents = value
+ End Set
+ End Property
+ Public ReadOnly Property ContainerId As String
+ Get
+ Return _inner.FileId
+ End Get
+ End Property
+ Public ReadOnly Property CreatedAt As String
+ Get
+ Return _inner.CreatedAt
+ End Get
+ End Property
+ Public ReadOnly Property UpdatedAt As String
+ Get
+ Return _inner.UpdatedAt
+ End Get
+ End Property
+
+ Public Shared Function Create(LogConfig As LogConfig, Password As String) As FileContainer
+ Dim oContainer = New FileContainer(LogConfig, Password)
+ Return oContainer
+ End Function
+
+ Public Shared Function Load(LogConfig As LogConfig, Password As String, Path As String) As FileContainer
+ Dim oContainer = New FileContainer(LogConfig, Password, Path)
+ oContainer.Load()
+ Return oContainer
+ End Function
+
+ Public Sub New(LogConfig As LogConfig, Password As String)
+ _logger = LogConfig.GetLogger()
+ _crypto = New Encryption.Encryption(LogConfig, Password)
+ _compression = New Compression(LogConfig)
+ _inner = New FileContainerInner()
+ End Sub
+
+ Public Sub New(LogConfig As LogConfig, Password As String, Path As String)
+ MyClass.New(LogConfig, Password)
+ _path = Path
+ End Sub
+
+ Public Sub SetFile(Contents As Byte(), FileName As String)
+ _inner.Contents = Contents
+ _inner.UpdatedAt = Date.Now
+ _inner.FileName = FileName
+ End Sub
+
+ Public Function GetFile() As FileContainerInner
+ Return _inner
+ End Function
+
+ Public Sub Save()
+ If IsNothing(_path) Then
+ Throw New ArgumentException("Path not set")
+ End If
+
+ SaveAs(_path)
+ End Sub
+
+ Public Sub SaveAs(Path As String)
+ Try
+ WriteBytesToFile(TransformToBytes(_inner), Path)
+ Catch ex As Exception
+ _logger.Error(ex)
+ Throw ex
+ End Try
+ End Sub
+
+ Public Sub Load()
+ If IsNothing(_path) Then
+ Throw New ArgumentException("Path not set")
+ End If
+
+ LoadFrom(_path)
+ End Sub
+
+ Public Sub LoadFrom(Path As String)
+ Try
+ _inner = TransformToObject(ReadBytesFromFile(_path))
+ Catch ex As Exception
+ _logger.Error(ex)
+ Throw ex
+ End Try
+ End Sub
+
+ Private Function TransformToBytes([Object] As FileContainerInner) As Byte()
+ Dim oBytes = Serialize([Object])
+ Dim oCompressed = _compression.Compress(oBytes)
+ Dim oEncrypted = _crypto.Encrypt(oCompressed)
+ Return oEncrypted
+ End Function
+
+ Private Function TransformToObject(Bytes As Byte()) As FileContainerInner
+ Dim oDecrypted = _crypto.Decrypt(Bytes)
+ Dim oDecompressed = _compression.Decompress(oDecrypted)
+ Dim oObject = Deserialize(oDecompressed)
+ Return oObject
+ End Function
+
+ Private Function Serialize(InnerData As FileContainerInner) As Byte()
+ Dim oBinaryData As Byte()
+
+ Using oStream As New MemoryStream
+ Serializer.Serialize(oStream, InnerData)
+ oBinaryData = oStream.ToArray()
+ End Using
+
+ Return oBinaryData
+ End Function
+
+ Private Function Deserialize(InnerData As Byte()) As FileContainerInner
+ Dim oObject As FileContainerInner
+
+ Using oStream As New MemoryStream(InnerData)
+ oObject = Serializer.Deserialize(Of FileContainerInner)(oStream)
+ End Using
+
+ Return oObject
+ End Function
+
+ Private Sub WriteBytesToFile(Data As Byte(), FilePath As String)
+ Using oSourceStream As New FileStream(FilePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None)
+ oSourceStream.Write(Data, 0, Data.Length)
+ oSourceStream.Flush()
+ End Using
+ End Sub
+
+ Private Function ReadBytesFromFile(FilePath As String) As Byte()
+ Using oFileStream = New FileStream(FilePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096)
+ Dim oBuffer As Byte() = New Byte(oFileStream.Length - 1) {}
+ oFileStream.Read(oBuffer, 0, oFileStream.Length)
+ oFileStream.Close()
+ Return oBuffer
+ End Using
+ End Function
+End Class
diff --git a/Filesystem/FileContainer/FileContainerInner.vb b/Filesystem/FileContainer/FileContainerInner.vb
new file mode 100644
index 00000000..fc787ce0
--- /dev/null
+++ b/Filesystem/FileContainer/FileContainerInner.vb
@@ -0,0 +1,23 @@
+Imports ProtoBuf
+
+
+
+Public Class FileContainerInner
+
+ Public FileId As String
+
+ Public Contents As Byte()
+
+ Public CreatedAt As DateTime
+
+ Public UpdatedAt As DateTime
+
+ Public FileName As String
+
+ Public Sub New()
+ FileId = Guid.NewGuid().ToString
+ CreatedAt = Date.Now
+ UpdatedAt = Date.Now
+ End Sub
+
+End Class
\ No newline at end of file
diff --git a/Filesystem/FileWatcher/FileWatcher.vb b/Filesystem/FileWatcher/FileWatcher.vb
new file mode 100644
index 00000000..6f82047a
--- /dev/null
+++ b/Filesystem/FileWatcher/FileWatcher.vb
@@ -0,0 +1,132 @@
+Imports System.IO
+Imports DigitalData.Modules.Filesystem
+Imports DigitalData.Modules.Filesystem.FileWatcherFilters
+Imports DigitalData.Modules.Logging
+
+
+Public Class FileWatcher
+ ' Internals
+ Private ReadOnly _Logger As Logger
+ Private ReadOnly _Watchers As List(Of FileSystemWatcher)
+ Private ReadOnly _Files As Dictionary(Of String, FileWatcherProperties)
+ Private ReadOnly _Filters As List(Of BaseFileFilter)
+
+ ' Options
+ Private _Path As String
+
+ ' Public Events
+ Public Event FileSaved(ByVal FullName As String, ByVal IsSpecial As Boolean)
+
+ Public Sub New(LogConfig As LogConfig, Path As String, Optional Filters As List(Of BaseFileFilter) = Nothing)
+ _Logger = LogConfig.GetLogger()
+ _Files = New Dictionary(Of String, FileWatcherProperties)
+ _Watchers = New List(Of FileSystemWatcher)
+ _Filters = IIf(IsNothing(Filters), GetDefaultFilters(), Filters)
+ _Path = Path
+
+ For Each oFilePath In Directory.EnumerateFiles(_Path)
+ Try
+ If IO.File.Exists(oFilePath) Then
+ _Files.Add(oFilePath, New FileWatcherProperties With {
+ .CreatedAt = DateTime.Now,
+ .ChangedAt = Nothing
+ })
+ End If
+ Catch ex As Exception
+ _Logger.Error(ex)
+ _Logger.Warn("File {0} cannot be watched!")
+ End Try
+ Next
+ End Sub
+
+ Public Sub Add(Filter As String)
+ _Watchers.Add(CreateWatcher(Filter))
+ End Sub
+
+ Public Sub Start()
+ For Each oWatcher In _Watchers
+ oWatcher.EnableRaisingEvents = True
+ Next
+ End Sub
+
+ Public Sub [Stop]()
+ For Each oWatcher In _Watchers
+ If Not IsNothing(oWatcher) Then
+ oWatcher.EnableRaisingEvents = False
+ oWatcher.Dispose()
+ End If
+ Next
+ End Sub
+
+ Private Function GetDefaultFilters()
+ Return New List(Of BaseFileFilter) From {
+ New TempFileFilter,
+ New OfficeFileFilter
+ }
+ End Function
+
+ Private Function CreateWatcher(Filter As String)
+ Dim oWatcher = New FileSystemWatcher() With {
+ .Path = _Path,
+ .Filter = Filter,
+ .NotifyFilter = NotifyFilters.LastAccess _
+ Or NotifyFilters.LastWrite _
+ Or NotifyFilters.FileName _
+ Or NotifyFilters.Size _
+ Or NotifyFilters.FileName _
+ Or NotifyFilters.Attributes
+ }
+
+ AddHandler oWatcher.Created, AddressOf HandleFileCreated
+ AddHandler oWatcher.Changed, AddressOf HandleFileChanged
+ AddHandler oWatcher.Deleted, AddressOf HandleFileDeleted
+ AddHandler oWatcher.Renamed, AddressOf HandleFileRenamed
+
+ Return oWatcher
+ End Function
+
+ Private Sub HandleFileCreated(sender As Object, e As FileSystemEventArgs)
+ _Files.Add(e.FullPath, New FileWatcherProperties())
+ _Logger.Debug("[Created] " & e.FullPath)
+ End Sub
+
+ '''
+ ''' This may fire twice for a single save operation,
+ ''' see: https://blogs.msdn.microsoft.com/oldnewthing/20140507-00/?p=1053/
+ '''
+ Private Sub HandleFileChanged(sender As Object, e As FileSystemEventArgs)
+ _Files.Item(e.FullPath).ChangedAt = DateTime.Now
+ _Logger.Debug("[Changed] " & e.FullPath)
+
+ Dim oShouldRaiseSave As Boolean = Not _Filters.Any(Function(oFilter)
+ Return oFilter.ShouldFilter(e)
+ End Function)
+
+ If oShouldRaiseSave Then
+ RaiseEvent FileSaved(e.FullPath, False)
+ End If
+ End Sub
+ Private Sub HandleFileDeleted(sender As Object, e As FileSystemEventArgs)
+ _Files.Remove(e.FullPath)
+ _Logger.Debug("[Removed] " & e.FullPath)
+ End Sub
+
+ Private Sub HandleFileRenamed(sender As Object, e As RenamedEventArgs)
+ Dim oProperties = _Files.Item(e.OldFullPath)
+ _Files.Remove(e.OldFullPath)
+ _Files.Add(e.FullPath, oProperties)
+ ' Soll eine umbenannte datei als NEU gelten?
+
+ Dim oShouldRaiseSave = _Filters.Any(Function(oFilter)
+ Return oFilter.ShouldRaiseSave(e)
+ End Function)
+
+ If oShouldRaiseSave Then
+ RaiseEvent FileSaved(e.OldFullPath, True)
+ End If
+
+ _Logger.Debug("[Renamed] {0} --> {1}", e.OldFullPath, e.FullPath)
+ End Sub
+
+
+End Class
diff --git a/Filesystem/FileWatcher/FileWatcherFilters.vb b/Filesystem/FileWatcher/FileWatcherFilters.vb
new file mode 100644
index 00000000..84f70b62
--- /dev/null
+++ b/Filesystem/FileWatcher/FileWatcherFilters.vb
@@ -0,0 +1,61 @@
+Imports System.IO
+
+'''
+''' Built-in filters for FileWatcher that are useful for correctly detecting changes on Office documents (currently Office 2016)
+'''
+Public Class FileWatcherFilters
+ '''
+ ''' Base Filter that all filters must inherit from
+ ''' Provides two functions that may be overridden and some useful file extension lists
+ '''
+ Public MustInherit Class BaseFileFilter
+ Public TempFiles As New List(Of String) From {".tmp", ""}
+
+ Public Overridable Function ShouldFilter(e As FileSystemEventArgs) As Boolean
+ Return False
+ End Function
+ Public Overridable Function ShouldRaiseSave(e As RenamedEventArgs) As Boolean
+ Return False
+ End Function
+ End Class
+
+ '''
+ ''' Simple Filter that filters changes made on temporary files
+ '''
+ Public Class TempFileFilter
+ Inherits BaseFileFilter
+
+ Public Overrides Function ShouldFilter(e As FileSystemEventArgs) As Boolean
+ Dim oFileInfo As New FileInfo(e.FullPath)
+ Return TempFiles.Contains(oFileInfo.Extension)
+ End Function
+ End Class
+
+ '''
+ ''' Filter to detect changes on Office files
+ '''
+ Public Class OfficeFileFilter
+ Inherits BaseFileFilter
+
+ Public OfficeFiles As New List(Of String) From {".docx", ".pptx", ".xlsx"}
+
+ Public Overrides Function ShouldFilter(e As FileSystemEventArgs) As Boolean
+ Dim oFileInfo As New FileInfo(e.FullPath)
+ Return OfficeFiles.Contains(oFileInfo.Extension) And oFileInfo.Name.StartsWith("~")
+ End Function
+
+ Public Overrides Function ShouldRaiseSave(e As RenamedEventArgs) As Boolean
+ Dim oIsTransform = OfficeFiles.Any(Function(Extension As String)
+ Return e.OldName.EndsWith(Extension)
+ End Function)
+
+ ' Check if it is renamed to a temp file
+ Dim oIsTempFile = TempFiles.Any(Function(Extension)
+ Return e.Name.EndsWith(Extension)
+ End Function)
+
+
+ Return oIsTransform And oIsTempFile
+ End Function
+ End Class
+End Class
diff --git a/Filesystem/FileWatcher/FileWatcherProperties.vb b/Filesystem/FileWatcher/FileWatcherProperties.vb
new file mode 100644
index 00000000..5eadecbe
--- /dev/null
+++ b/Filesystem/FileWatcher/FileWatcherProperties.vb
@@ -0,0 +1,10 @@
+Public Class FileWatcherProperties
+ Public Property CreatedAt As DateTime
+ Public Property ChangedAt As DateTime
+ Public ReadOnly Property HasChanged As Boolean
+ Public Sub New()
+ CreatedAt = DateTime.Now
+ ChangedAt = Nothing
+ HasChanged = False
+ End Sub
+End Class
diff --git a/Filesystem/Filesystem.vbproj b/Filesystem/Filesystem.vbproj
new file mode 100644
index 00000000..77010a3d
--- /dev/null
+++ b/Filesystem/Filesystem.vbproj
@@ -0,0 +1,133 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {991D0231-4623-496D-8BD0-9CA906029CBC}
+ Library
+ DigitalData.Modules.Filesystem
+ DigitalData.Modules.Filesystem
+ 512
+ Windows
+ v4.6.2
+
+
+
+ true
+ full
+ true
+ true
+ bin\Debug\
+ DigitalData.Modules.Filesystem.xml
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ pdbonly
+ false
+ true
+ true
+ bin\Release\
+ DigitalData.Modules.Filesystem.xml
+ 42016,41999,42017,42018,42019,42032,42036,42020,42021,42022
+
+
+ On
+
+
+ Binary
+
+
+ Off
+
+
+ On
+
+
+
+ ..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll
+
+
+
+ ..\packages\NLog.5.0.5\lib\net46\NLog.dll
+
+
+ ..\packages\protobuf-net.2.4.0\lib\net40\protobuf-net.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ True
+ Application.myapp
+ True
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+ VbMyResourcesResXFileCodeGenerator
+ Resources.Designer.vb
+ My.Resources
+ Designer
+
+
+
+
+ MyApplicationCodeGenerator
+ Application.Designer.vb
+
+
+ SettingsSingleFileGenerator
+ My
+ Settings.Designer.vb
+
+
+
+
+
+ {8a8f20fc-c46e-41ac-bee7-218366cfff99}
+ Encryption
+
+
+
+
\ No newline at end of file
diff --git a/Filesystem/My Project/Application.Designer.vb b/Filesystem/My Project/Application.Designer.vb
new file mode 100644
index 00000000..8ab460ba
--- /dev/null
+++ b/Filesystem/My Project/Application.Designer.vb
@@ -0,0 +1,13 @@
+'------------------------------------------------------------------------------
+'
+' Dieser Code wurde von einem Tool generiert.
+' Laufzeitversion:4.0.30319.42000
+'
+' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+' der Code erneut generiert wird.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
diff --git a/Filesystem/My Project/Application.myapp b/Filesystem/My Project/Application.myapp
new file mode 100644
index 00000000..758895de
--- /dev/null
+++ b/Filesystem/My Project/Application.myapp
@@ -0,0 +1,10 @@
+
+
+ false
+ false
+ 0
+ true
+ 0
+ 1
+ true
+
diff --git a/Filesystem/My Project/AssemblyInfo.vb b/Filesystem/My Project/AssemblyInfo.vb
new file mode 100644
index 00000000..86b1df22
--- /dev/null
+++ b/Filesystem/My Project/AssemblyInfo.vb
@@ -0,0 +1,35 @@
+Imports System
+Imports System.Reflection
+Imports System.Runtime.InteropServices
+
+' Allgemeine Informationen über eine Assembly werden über die folgenden
+' Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+' die einer Assembly zugeordnet sind.
+
+' Werte der Assemblyattribute überprüfen
+
+
+
+
+
+
+
+
+
+
+'Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird.
+
+
+' Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+'
+' Hauptversion
+' Nebenversion
+' Buildnummer
+' Revision
+'
+' Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+' übernehmen, indem Sie "*" eingeben:
+'
+
+
+
diff --git a/Filesystem/My Project/Resources.Designer.vb b/Filesystem/My Project/Resources.Designer.vb
new file mode 100644
index 00000000..26ca448a
--- /dev/null
+++ b/Filesystem/My Project/Resources.Designer.vb
@@ -0,0 +1,63 @@
+'------------------------------------------------------------------------------
+'
+' Dieser Code wurde von einem Tool generiert.
+' Laufzeitversion:4.0.30319.42000
+'
+' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+' der Code erneut generiert wird.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+Imports System
+
+Namespace My.Resources
+
+ 'Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
+ '-Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
+ 'Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
+ 'mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
+ '''
+ ''' Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ '''
+ _
+ Friend Module Resources
+
+ Private resourceMan As Global.System.Resources.ResourceManager
+
+ Private resourceCulture As Global.System.Globalization.CultureInfo
+
+ '''
+ ''' Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ '''
+ _
+ Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
+ Get
+ If Object.ReferenceEquals(resourceMan, Nothing) Then
+ Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("DigitalData.Modules.Filesystem.Resources", GetType(Resources).Assembly)
+ resourceMan = temp
+ End If
+ Return resourceMan
+ End Get
+ End Property
+
+ '''
+ ''' Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
+ ''' Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ '''
+ _
+ Friend Property Culture() As Global.System.Globalization.CultureInfo
+ Get
+ Return resourceCulture
+ End Get
+ Set
+ resourceCulture = value
+ End Set
+ End Property
+ End Module
+End Namespace
diff --git a/Filesystem/My Project/Resources.resx b/Filesystem/My Project/Resources.resx
new file mode 100644
index 00000000..af7dbebb
--- /dev/null
+++ b/Filesystem/My Project/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Filesystem/My Project/Settings.Designer.vb b/Filesystem/My Project/Settings.Designer.vb
new file mode 100644
index 00000000..cec9af04
--- /dev/null
+++ b/Filesystem/My Project/Settings.Designer.vb
@@ -0,0 +1,73 @@
+'------------------------------------------------------------------------------
+'
+' Dieser Code wurde von einem Tool generiert.
+' Laufzeitversion:4.0.30319.42000
+'
+' Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
+' der Code erneut generiert wird.
+'
+'------------------------------------------------------------------------------
+
+Option Strict On
+Option Explicit On
+
+
+Namespace My
+
+ _
+ Partial Friend NotInheritable Class MySettings
+ Inherits Global.System.Configuration.ApplicationSettingsBase
+
+ Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
+
+#Region "Automatische My.Settings-Speicherfunktion"
+#If _MyType = "WindowsForms" Then
+ Private Shared addedHandler As Boolean
+
+ Private Shared addedHandlerLockObject As New Object
+
+ _
+ Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
+ If My.Application.SaveMySettingsOnExit Then
+ My.Settings.Save()
+ End If
+ End Sub
+#End If
+#End Region
+
+ Public Shared ReadOnly Property [Default]() As MySettings
+ Get
+
+#If _MyType = "WindowsForms" Then
+ If Not addedHandler Then
+ SyncLock addedHandlerLockObject
+ If Not addedHandler Then
+ AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
+ addedHandler = True
+ End If
+ End SyncLock
+ End If
+#End If
+ Return defaultInstance
+ End Get
+ End Property
+ End Class
+End Namespace
+
+Namespace My
+
+ _
+ Friend Module MySettingsProperty
+
+ _
+ Friend ReadOnly Property Settings() As Global.DigitalData.Modules.Filesystem.My.MySettings
+ Get
+ Return Global.DigitalData.Modules.Filesystem.My.MySettings.Default
+ End Get
+ End Property
+ End Module
+End Namespace
diff --git a/Filesystem/My Project/Settings.settings b/Filesystem/My Project/Settings.settings
new file mode 100644
index 00000000..85b890b3
--- /dev/null
+++ b/Filesystem/My Project/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Filesystem/packages.config b/Filesystem/packages.config
new file mode 100644
index 00000000..3b7636d2
--- /dev/null
+++ b/Filesystem/packages.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Modules.sln b/Modules.sln
index 470454c8..a39b3c42 100644
--- a/Modules.sln
+++ b/Modules.sln
@@ -35,6 +35,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Messaging", "Messaging\Mess
EndProject
Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Database.Test", "Database.Test\Database.Test.vbproj", "{91B4DFC0-543C-43A7-A9E0-6817DCA277EC}"
EndProject
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Filesystem", "Filesystem\Filesystem.vbproj", "{991D0231-4623-496D-8BD0-9CA906029CBC}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -105,6 +107,10 @@ Global
{91B4DFC0-543C-43A7-A9E0-6817DCA277EC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{91B4DFC0-543C-43A7-A9E0-6817DCA277EC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{91B4DFC0-543C-43A7-A9E0-6817DCA277EC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {991D0231-4623-496D-8BD0-9CA906029CBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {991D0231-4623-496D-8BD0-9CA906029CBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {991D0231-4623-496D-8BD0-9CA906029CBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {991D0231-4623-496D-8BD0-9CA906029CBC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Patterns/My Project/AssemblyInfo.vb b/Patterns/My Project/AssemblyInfo.vb
index b1a14898..45d3546b 100644
--- a/Patterns/My Project/AssemblyInfo.vb
+++ b/Patterns/My Project/AssemblyInfo.vb
@@ -8,12 +8,12 @@ Imports System.Runtime.InteropServices
' Werte der Assemblyattribute überprüfen
-
-
-
-
-
-
+
+
+
+
+
+
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
'
-
-
+
+
diff --git a/Patterns/Patterns.vb b/Patterns/Patterns.vb
index fc23fe65..c5fac360 100644
--- a/Patterns/Patterns.vb
+++ b/Patterns/Patterns.vb
@@ -39,6 +39,10 @@ Public Class ClassPatterns
Public Const CLIPBOARD_VALUE_DE = "@Zwischenablage"
Public Const CLIPBOARD_VALUE_EN = "@Clipboard"
+ Public Const PATTERN_WMDOCID = "{@WMDocID}"
+ Public Const PATTERN_IDBOBJID = "{@IDBObjID}"
+
+
Public Const MAX_TRY_COUNT = 100
Public ReadOnly Property PatternRegex As Regex
@@ -51,7 +55,7 @@ Public Class ClassPatterns
Private ReadOnly _LogConfig As LogConfig
Private ReadOnly _Regex As Regex = New Regex("{#(\w+)#([\w\s_-]+)}+")
- Private ReadOnly _AllPatterns As New List(Of String) From {PATTERN_WMI, PATTERN_CTRL, PATTERN_USER, PATTERN_INT}
+ Private ReadOnly _AllPatterns As New List(Of String) From {PATTERN_WMI, PATTERN_CTRL, PATTERN_USER, PATTERN_INT, PATTERN_WMDOCID, PATTERN_IDBOBJID}
Private ReadOnly _ComplexPatterns As New List(Of String) From {PATTERN_WMI, PATTERN_CTRL}
Private ReadOnly _SimplePatterns As New List(Of String) From {PATTERN_USER, PATTERN_INT}
@@ -67,12 +71,13 @@ Public Class ClassPatterns
_Logger = pLogConfig.GetLogger
End Sub
- Public Function ReplaceAllValues(pInput As String, pUser As State.UserState, pClipboardContents As String) As String
+ Public Function ReplaceAllValues(pInput As String, pUser As State.UserState, pClipboardContents As String, pObjectID As String) As String
Try
Dim result = pInput
result = ReplaceClipboardContents(result, pClipboardContents)
result = ReplaceInternalValues(result)
+ result = ReplaceObjectIDValues(result, pObjectID)
result = ReplaceUserValues(result, pUser)
Return result
@@ -95,6 +100,13 @@ Public Class ClassPatterns
Return oResult
End Function
+ Public Function ReplaceObjectIDValues(pInput As String, pObjectID As String) As String
+ Dim oResult = pInput
+
+ oResult = oResult.Replace(CLIPBOARD_VALUE_DE, pObjectID)
+
+ Return oResult
+ End Function
Public Function ReplaceInternalValues(pInput As String) As String
Try
diff --git a/Patterns/Patterns.vbproj b/Patterns/Patterns.vbproj
index 2da07c8c..c3dc5057 100644
--- a/Patterns/Patterns.vbproj
+++ b/Patterns/Patterns.vbproj
@@ -53,6 +53,9 @@
False
..\..\DDMonorepo\Controls.LookupGrid\bin\Debug\DigitalData.Controls.LookupGrid.dll
+
+ ..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll
+
P:\Visual Studio Projekte\Bibliotheken\windream\Interop.WINDREAMLib.dll
True
@@ -142,10 +145,6 @@
-
- {903b2d7d-3b80-4be9-8713-7447b704e1b0}
- Logging
-
{81cac44f-3711-4c8f-ae98-e02a7448782a}
ZooFlow
diff --git a/Patterns/packages.config b/Patterns/packages.config
index 37cb9210..e25334c4 100644
--- a/Patterns/packages.config
+++ b/Patterns/packages.config
@@ -1,4 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Windows/My Project/AssemblyInfo.vb b/Windows/My Project/AssemblyInfo.vb
index 43a51866..2677a8ed 100644
--- a/Windows/My Project/AssemblyInfo.vb
+++ b/Windows/My Project/AssemblyInfo.vb
@@ -8,12 +8,12 @@ Imports System.Runtime.InteropServices
' Werte der Assemblyattribute überprüfen
-
-
-
-
-
-
+
+
+
+
+
+
@@ -31,5 +31,5 @@ Imports System.Runtime.InteropServices
' übernehmen, indem Sie "*" eingeben:
'
-
-
+
+
diff --git a/Windows/Windows.vbproj b/Windows/Windows.vbproj
index 8d73efe6..cfa781bc 100644
--- a/Windows/Windows.vbproj
+++ b/Windows/Windows.vbproj
@@ -44,6 +44,9 @@
On
+
+ ..\packages\DigitalData.Modules.Logging.2.6.5\lib\net462\DigitalData.Modules.Logging.dll
+
..\packages\NLog.5.0.5\lib\net46\NLog.dll
@@ -138,10 +141,6 @@
{6ea0c51f-c2b1-4462-8198-3de0b32b74f8}
Base
-
- {903B2D7D-3B80-4BE9-8713-7447B704E1B0}
- Logging
-
diff --git a/Windows/packages.config b/Windows/packages.config
index 37cb9210..e25334c4 100644
--- a/Windows/packages.config
+++ b/Windows/packages.config
@@ -1,4 +1,6 @@
+
+
\ No newline at end of file