Calender Settings / MDaemon Connector / Outlook
-
Hello,
It is possible to automatically delete old calendar entries via MDaemon Connector.
I tried auto-archiving to 24 months in the calendar properties in Outlook Set the option to “permanently
delete old items”. Unfortunately this doesn't work.What I notice when setting up user accounts is that it always shows all calendar entries loaded, with us
depending on the calendar entries between 1000-70000.
Here I would like to have a calendar setting in the MDaemon connector where you can set up settings
for the existing calendar or public calendar.- 2 weeks
- last month
- 3 months
- 6 months
- AllBest regards, Martin
-
Arron Staff
There are no settings in MDaemon Connector to prune old calendar entries, but we do have a PoweShell script you run on the server to prune them. This script will automatically try to find the mailbox directories by looking at the default path for the New Account template, if that is not the correct location, you'll have to alter the script by setting $PathtoUserFolders to the correct path on line 243. 243 is current a blank line, setting the $PathtoUserFolders at this location will overwrite the value obtained from the AccountTemplates.dat file. If you set the value to d:\mdaemon\users\, the script will recursively find all calendars and prune them. If you want to run it on a single account or single calendar you should point it directly to the account or calendar. For example, c:\mdaemon\users\company.test\arron\calendar.imap.
By default the script will remove any calendar entries that are older than 3 years. You can adjust this by changing the $Years variable on line 254. The value is in years and must be a whole integer. If you set the value to 0 it will delete all entries.
As alwasy, I highly reccomend that you thoroughly test this script and make backups before running it on your live mailboxes.
Let us know if you have any questions.
######################################################################################################################## ######################################################################################################################## ## Prune Calendar Events V1.1 ## ## September 19, 2022 ## ## Copyright MDaemon Technologies 2022 ## ## ## ## This script is designed prune events from the Calendar.mrk file that are older than X years. It searches the default ## ## mail path configured for New Account templates and checks each Calendar.mrk file for old events. It checks for recurrence ## ## removes the associated .MSG files, and removes the associated .ICS files. A backup is made of the Calendar.mrk file ## ## before any changes are made to it. It is reccomended that you backup your entire user folder structure before running ## ## this script. ## ## ## ## MDaemon Technologies offers no warranty, provides no support, and is not responsible for any damages that may arise from ## ## the use of this script. Use at your own risk. ## ## ## ######################################################################################################################## ######################################################################################################################## Function Get-IniContent { <# .Synopsis Gets the content of an INI file .Description Gets the content of an INI file and returns it as a hashtable .Notes Author : Oliver Lipkau <oliver@lipkau.net> Source : https://github.com/lipkau/PsIni http://gallery.technet.microsoft.com/scriptcenter/ea40c1ef-c856-434b-b8fb-ebd7a76e8d91 Version : 1.0.0 - 2010/03/12 - OL - Initial release 1.0.1 - 2014/12/11 - OL - Typo (Thx SLDR) Typo (Thx Dave Stiff) 1.0.2 - 2015/06/06 - OL - Improvment to switch (Thx Tallandtree) 1.0.3 - 2015/06/18 - OL - Migrate to semantic versioning (GitHub issue#4) 1.0.4 - 2015/06/18 - OL - Remove check for .ini extension (GitHub Issue#6) 1.1.0 - 2015/07/14 - CB - Improve round-tripping and be a bit more liberal (GitHub Pull #7) OL - Small Improvments and cleanup 1.1.1 - 2015/07/14 - CB - changed .outputs section to be OrderedDictionary #Requires -Version 2.0 .Inputs System.String .Outputs System.Collections.Specialized.OrderedDictionary .Parameter FilePath Specifies the path to the input file. .Parameter CommentChar Specify what characters should be describe a comment. Lines starting with the characters provided will be rendered as comments. Default: ";" .Parameter IgnoreComments Remove lines determined to be comments from the resulting dictionary. .Example $FileContent = Get-IniContent "C:\myinifile.ini" ----------- Description Saves the content of the c:\myinifile.ini in a hashtable called $FileContent .Example $inifilepath | $FileContent = Get-IniContent ----------- Description Gets the content of the ini file passed through the pipe into a hashtable called $FileContent .Example C:\PS>$FileContent = Get-IniContent "c:\settings.ini" C:\PS>$FileContent["Section"]["Key"] ----------- Description Returns the key "Key" of the section "Section" from the C:\settings.ini file .Link Out-IniFile #> [CmdletBinding()] Param( [ValidateNotNullOrEmpty()] [ValidateScript({(Test-Path $_)})] [Parameter(ValueFromPipeline=$True,Mandatory=$True)] [string]$FilePath, [char[]]$CommentChar = @(";"), [switch]$IgnoreComments ) Begin { Write-Verbose "$($MyInvocation.MyCommand.Name):: Function started" $commentRegex = "^([$($CommentChar -join '')].*)$" } Process { Write-Verbose "$($MyInvocation.MyCommand.Name):: Processing file: $Filepath" $ini = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase) $commentCount = 0 switch -regex -file $FilePath { "^\s*\[(.+)\]\s*$" # Section { $section = $matches[1] $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase) $CommentCount = 0 continue } $commentRegex # Comment { if (!$IgnoreComments) { if (!(test-path "variable:section")) { $section = "_" $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase) } $value = $matches[1] $CommentCount++ $name = "Comment" + $CommentCount $ini[$section][$name] = $value } continue } "(.+?)\s*=\s*(.*)" # Key { if (!(test-path "variable:section")) { $section = "_" $ini[$section] = New-Object System.Collections.Specialized.OrderedDictionary([System.StringComparer]::OrdinalIgnoreCase) } $name,$value = $matches[1..2] $ini[$section][$name] = $value continue } } Write-Verbose "$($MyInvocation.MyCommand.Name):: Finished Processing file: $FilePath" Return $ini } End {Write-Verbose "$($MyInvocation.MyCommand.Name):: Function ended"} } function ConverttoSystemDateTime($DTString){ #Split the EndDateTime variable to a Date and a Time, the Time is Trash here. #$EndDate,$Trash = Split-String -Input $DTString -Separator " " $EndDate,$Trash = $DTString -split " " #Split the Date into year, month, and day values. #$EndYear,$EndMonth,$EndDay = Split-String -Input $EndDate -Separator "-" $EndYear,$EndMonth,$EndDay = $EndDate -split "-" #Using the year, month, and day values create a new EndDate object of type System.DateTime $EndDate = New-Object System.DateTime $EndYear,$EndMonth,$EndDay return $EndDate } function RemoveTNEFFile($Event){ if($Event.TNEF) { if($Event.TNEF.'#cdata-section') { #Write-Host "Found CDATA section setting the EndDateTime variable to the CDATA value." $TNEFFile = Join-Path $File.DirectoryName $Event.TNEF.'#cdata-section' } else { #Write-Host "If there is no CDATA section then just get the value." $TNEFFile = Join-Path $File.DirectoryName $Event.TNEF } if(Test-Path $TNEFFile) { Write-Host "Removing $TNEFFile" Remove-Item $TNEFFile -force -ErrorAction SilentlyContinue } } } function RemoveICSFile($Event){ if($Event.DAVDataFile) { $ICSFile = Join-Path $File.DirectoryName "_DAV" if($Event.DAVDataFile.'#cdata-section') { #Write-Host "Found CDATA section setting the EndDateTime variable to the CDATA value." $ICSFile = Join-Path $ICSFile $Event.DAVDataFile.'#cdata-section' } else { #Write-Host "If there is no CDATA section then just get the value." $ICSFile = Join-Path $ICSFile $Event.DAVDataFile } if(Test-Path $ICSFFile) { Write-Host "Removing $ICSFFile" Remove-Item $ICSFFile -force -ErrorAction SilentlyContinue } } } #Get the path to the MDaemon\app folder. if(Test-Path "HKLM:\SOFTWARE\Alt-N Technologies\MDaemon") { $MDAPPPath = (Get-ItemProperty "HKLM:\SOFTWARE\Alt-N Technologies\MDaemon" -Name AppPath).AppPath $MDINIPath = (Get-ItemProperty "HKLM:\SOFTWARE\Alt-N Technologies\MDaemon" -Name IniPath).IniPath } elseif(Test-Path "HKLM:\SOFTWARE\Wow6432Node\Alt-N Technologies\MDaemon") { $MDAPPPath = (Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Alt-N Technologies\MDaemon" -Name AppPath).AppPath $MDINIPath = (Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Alt-N Technologies\MDaemon" -Name IniPath).IniPath } else { Write-Host "Oh No, I couldn't find the registry keys for MDaemon!" Write-Host "The script will now exit." Exit } $AccountTemplatePath = Join-Path $MDAPPPath "AccountTemplates.dat" $MDINIContent = Get-IniContent $MDINIPath $AccounteTemplatesContent = Get-IniContent $AccountTemplatePath #Path to the MDaemon\Users folder. $PathtoUserFolders = $AccounteTemplatesContent["New Accounts"]["RootMailPath"] $PathtoUserFolders = Split-Path $PathtoUserFolders $PathtoUserFolders = Split-Path $PathtoUserFolders write-Host "The path to the users folders is set to $PathtoUserFolders" if(!(Test-Path $PathtoUserFolders)) { Write-Host "The path to the user folders does not exist. Exiting." Exit } #Number of years to keep. This should be a negative value. If you wnat to remove events that ended more than 2 years #ago set the value to -2." $Years = -3 if($Years -ge 0) { Write-Host "The Years variable needs to be set to a negative value. $Years" Exit } $ClearQuotaCounts = "" #Searches the directory structure for Calendar.mrk files. ForEach ($File in Get-ChildItem -Path $PathtoUserFolders -Include Calendar.mrk -Recurse) { $AddUsertoClearQuotaCountsSEM = "No" #Locking the MRK file so it can't be changed by anything else." $LockMRKFile = Join-Path $File.DirectoryName "Calendar.lck" Write-Host "Creating lock file for Calendar.mrk" New-Item $LockMRKFile -type File -Force #Making a backup copy of the MRK file, just in case." $BackupMRK = Join-Path $File.DirectoryName "Calendar.mrk.bck" Write-Host "Making a backup of the Calendar.mrk file." Copy-Item $File $BackupMRK -Force Write-Host "Loading $File" $Data = [Xml] (Get-Content $File) $MyDate = Get-Date $EndDateTime = $null $EndDate = $null #Looks at each event in the calendar to determine if it should be removed. foreach($Event in $Data.Calendar.event) { if($Event.End.'#cdata-section') { #Write-Host "Found CDATA section setting the EndDateTime variable to the CDATA value." $EndDateTime = $Event.End.'#cdata-section' } else { #Write-Host "If there is no CDATA section then just get the value." $EndDateTime = $Event.End } $EndDate = ConverttoSystemDateTime $EndDateTime #if the end date on the event is more than X years ago. if($EndDate -lt ($MyDate.AddYears($Years))) { #If the event is recurring if($Event.Recurrence) { $RecurrEndDate = ConverttoSystemDateTime $Event.Recurrence.End #If the recurrence ends more than X years ago if($RecurrEndDate -lt ($MyDate.AddYears($Years))) { $AddUsertoClearQuotaCountsSEM = "Yes" #Removing event and associated files because the last ocurrence of the event was more than 3 years ago. Write-Host "Removing recurring event" $Event.ID $Event.ParentNode.RemoveChild($Event) if($Event.TNEF) { RemoveTNEFFile($Event) } if($Event.DAVDataFile) { RemoveICSFile($Event) } } #If the recurrence do nothing. else { #Not doing anything here because the recurring event is not older than the date set. } } else { $AddUsertoClearQuotaCountsSEM = "Yes" #Removing Event and associated files because the event was more than 3 years ago. Write-Host "Removing event" $Event.ID $Event.ParentNode.RemoveChild($Event) if($Event.TNEF) { RemoveTNEFFile($Event) } if($Event.DAVDataFile) { RemoveICSFile($Event) } } } } $Output = Join-Path $File.DirectoryName "MRKOutput.mrk" Write-Host "Writing out new file at $File" $Data.save($File) Remove-Item $LockMRKFile -Force if($AddUsertoClearQuotaCountsSEM -eq "Yes") { #Building the email address of the user. $Mailbox = Split-Path -Path (Split-Path -Path $File.Directory) -Leaf $Domain = Split-Path -Path (Split-Path -Path (Split-Path -Path $File.Directory)) -Leaf $Email = $Mailbox + "@" + $Domain Write-Host "$Email will be added to the reloadquotacounts.sem file." $ClearQuotaCounts = $ClearQuotaCounts + $Email + "`r`n" } } $QuataCountsSub = $MDINIContent["Special"]["QuotaCountsSubFolders"] $CountGroupwareFolders = $MDINIContent["Special"]["CountGroupwareFolders"] if($QuataCountsSub -eq "Yes" -and $CountGroupwareFolders -eq "Yes") { $SemPath = Join-Path $MDAPPPath "ClearQuotaCounts.sem" Write-Host "Creating SEM files to update Quota Caches." New-Item -Path $SemPath -type File -Value $ClearQuotaCounts -Force } else { Write-Host "Quotas are not configured to count Groupware folder or sub folders so the cache will not be updated." }
-
Hello Arron,
thank you very much for the PS script.
I changed line 243:
write-Host "C:\MDaemon\Users\company.test\test\Kalender.IMAP"The following error message then appears, but after that it continues.
C:\MDaemon\Users\company.test\test\Kalender.IMAP Test-Path : The argument cannot be bound to the "Path" parameter because it is NULL. In C:\MDaemon-Kalender-CleanUP.ps1:245 Zeichen:16 + if(!(Test-Path $PathtoUserFolders)) + ~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCommand Creating lock file for Calendar.mrk Verzeichnis: C:\mdaemon\Public Folders\company.test.IMAP\Kalender.IMAP Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 14.10.2023 13:08 0 Calendar.lck Making a backup of the Calendar.mrk file. Loading C:\mdaemon\Public Folders\company.test.IMAP\Kalender.IMAP\Calendar.mrk Writing out new file at C:\mdaemon\Public Folders\company.test.IMAP\Kalender.IMAP\Calendar.mrk Creating lock file for Calendar.mrk
Why is he going to User02 here?
Verzeichnis: C:\mdaemon\Users\company.test\user02\Kalender.IMAP Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 14.10.2023 13:08 0 Calendar.lck Making a backup of the Calendar.mrk file. Loading C:\mdaemon\Users\company.test\user02\Kalender.IMAP\Calendar.mrk Writing out new file at C:\mdaemon\Users\company.test\user02\Kalender.IMAP\Calendar.mrk Creating lock file for Calendar.mrk Verzeichnis: C:\mdaemon\Users\company.test\test\Kalender.IMAP Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 14.10.2023 13:08 0 Calendar.lck Making a backup of the Calendar.mrk file. Loading C:\mdaemon\Users\company.test\test\Kalender.IMAP\Calendar.mrk Writing out new file at C:\mdaemon\Users\company.test\test\Kalender.IMAP\Calendar.mrk Quotas are not configured to count Groupware folder or sub folders so the cache will not be updated.
Best regards, Martin
-
Arron Staff
Write-Host only writes the value in quotes to the screen.
Instead of using this on line 243:
write-Host "C:\MDaemon\Users\company.test\test\Kalender.IMAP"
You need to use this:
$PathtoUserFolders = "C:\MDaemon\Users\company.test\test\Kalender.IMAP"
This should be added just after the second occurrence of this line:
$PathtoUserFolders = Split-Path $PathtoUserFolders
And in case you are wondering why that line is there twice, its because its splitting the last folder off the path and there are two folders that needed to be removed.
-
Hello Arron,
I tested it, it works.
My series elements are retained, that's good.Is this feature planned for a future version?
Martin
-
Arron Staff
At this time we do not have plans to expand the functionality.