Skip to content

Commit

Permalink
v5.0.4 (#475)
Browse files Browse the repository at this point in the history
* Allow plus addressing in Multi Inbox (#464)

Extends regex pattern support on MultipleMailboxes.xaml

* Remove "Resolved By User" Relationship when WorkItem is reactivated (#467)

* Update smletsExchangeConnector.ps1

* Update smletsExchangeConnector.ps1

* Remove-SCSMRelationshipObject one liner

After testing it does not appear there is a scenario wherein the try/catch will engage.

---------

Co-authored-by: Adam <adhocadam@protonmail.com>

* logging, cireson integration (#469)

Introduce logging events for functions that are responsible for obtaining suggestion URLs (KB/RO) from one's Cireson portal

* escape potential regex (#473)

in the event the Suggestions are enabled and the email subject/body contain regex patterns, this will ensure they are escaped correctly

* update version notes and contributors (#474)

* version notes

update inline notes and contributors

* contributors

updating contributors per release

---------

Co-authored-by: Konstantin Slavin-Borovskij <betalyte@gmail.com>
Co-authored-by: SimonZein <128088337+SimonZein@users.noreply.github.com>
  • Loading branch information
3 people committed Sep 10, 2023
1 parent 860b095 commit 02be0b1
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 65 deletions.
Expand Up @@ -14,7 +14,7 @@
<!-- Contributors -->
<TextBlock Margin="0,5,0,0" TextWrapping="Wrap" FontStyle="Italic" Text="SMLets Exchange Connector (PowerShell) Contributors" />
<TextBlock Margin="10,0" TextWrapping="Wrap" Text="Martin Blomgren, Tom Hendricks, Leigh Kilday, nradler2, Justin Workman, Brad Zima, bennyguk, Jan Schulz,
Peter Miklian, Daniel Polivka, Alexander Axberg, Simon Zeinhofer" />
Peter Miklian, Daniel Polivka, Alexander Axberg, Simon Zeinhofer, Konstantin Slavin-Borovskij" />
<!-- Reviewers -->
<TextBlock Margin="0,5,0,0" TextWrapping="Wrap" FontStyle="Italic" Text="SMLets Exchange Connector (PowerShell) Reviewers" />
<TextBlock Margin="10,0" TextWrapping="Wrap" Text="Tom Hendricks, Brian Wiest" />
Expand Down
Expand Up @@ -43,7 +43,7 @@
<GridViewColumn Header="Email Address">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding MailboxAddress}" Custom:Validation.RegexPattern="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$" />
<TextBox Text="{Binding MailboxAddress}" Custom:Validation.RegexPattern="^([0-9a-zA-Z]([-.\w]*[0-9a-zA-Z])*(\+[0-9a-zA-Z]([-.\w]*[0-9a-zA-Z]))*@([0-9a-zA-Z][-\w]*[0-9a-zA-Z]\.)+[a-zA-Z]{2,9})$" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Expand Down
169 changes: 106 additions & 63 deletions smletsExchangeConnector.ps1
Expand Up @@ -10,7 +10,7 @@ enabling other organizational level processes via email
.NOTES
Author: Adam Dzyacky
Contributors: Martin Blomgren, Leigh Kilday, Tom Hendricks, nradler2, Justin Workman, Brad Zima, bennyguk, Jan Schulz, Peter Miklian, Daniel Polivka, Alexander Axberg, Simon Zeinhofer
Contributors: Martin Blomgren, Leigh Kilday, Tom Hendricks, nradler2, Justin Workman, Brad Zima, bennyguk, Jan Schulz, Peter Miklian, Daniel Polivka, Alexander Axberg, Simon Zeinhofer, Konstantin Slavin-Borovskij
Reviewers: Tom Hendricks, Brian Wiest
Inspiration: The Cireson Community, Anders Asp, Stefan Roth, and (of course) Travis Wright for SMlets examples
Requires: PowerShell 4+, SMlets, and Exchange Web Services API (already installed on SCSM workflow server by virtue of stock Exchange Connector).
Expand All @@ -20,6 +20,10 @@ Requires: PowerShell 4+, SMlets, and Exchange Web Services API (already installe
Signed/Encrypted option: .NET 4.5 is required to use MimeKit.dll
Misc: The Release Record functionality does not exist in this as no out of box (or 3rd party) Type Projection exists to serve this purpose.
You would have to create your own Type Projection in order to leverage this.
Version: 5.0.4 = #464 - Enhancement, Allow plus addressing in multi-mailbox
#467 - Bug, Resolved by User relationship should be nulled when reactivating a Work Item
#469 - Enhancement, More logging events around Cireson based integration and suggesting KA/RO
#473 - Bug, Emails with signature graphics that feature alt text prevent the suggestion feature from executing
Version: 5.0.3 = #443 - Bug, Custom Events are incorrectly tied to a Logging Level
#453 - Enhancement, Merge Reply functionality supports multi-language environments
#452 - Bug, Fix for updating Work Items when the Comment is greater than 4000 characters
Expand Down Expand Up @@ -3059,15 +3063,22 @@ function Get-CiresonPortalUser
)

$isAuthUserAPIurl = "api/V3/User/IsUserAuthorized?userName=$username&domain=$domain"
if ($ciresonPortalWindowsAuth -eq $true)
{
$ciresonPortalUserObject = Invoke-RestMethod -Uri ($ciresonPortalServer+$isAuthUserAPIurl) -Method post -UseDefaultCredentials
try {
if ($ciresonPortalWindowsAuth -eq $true)
{
$ciresonPortalUserObject = Invoke-RestMethod -Uri ($ciresonPortalServer+$isAuthUserAPIurl) -Method post -UseDefaultCredentials
}
else
{
$ciresonPortalUserObject = Invoke-RestMethod -Uri ($ciresonPortalServer+$isAuthUserAPIurl) -Method post -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}
if ($loggingLevel -ge 4) {New-SMEXCOEvent -Source "Get-CiresonPortalUser" -EventID 0 -Severity Information -LogMessage "User/Sender found in Cireson Portal"}
return $ciresonPortalUserObject
}
else
{
$ciresonPortalUserObject = Invoke-RestMethod -Uri ($ciresonPortalServer+$isAuthUserAPIurl) -Method post -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
catch {
if ($loggingLevel -ge 3) {New-SMEXCOEvent -Source "Get-CiresonPortalUser" -EventID 1 -Severity Error -LogMessage $_.Exception}
return $null
}
return $ciresonPortalUserObject
}

#retrieve a group from SCSM through the Cireson Web Portal API
Expand All @@ -3081,17 +3092,24 @@ function Get-CiresonPortalGroup

$adGroup = Get-ADGroup @adParams -Filter "Mail -eq $groupEmail"

if($ciresonPortalWindowsAuth)
{
#wanted to use a get groups style request, but "api/V3/User/GetConsoleGroups" feels costly instead of a search
$cwpGroupResponse = Invoke-RestMethod -Uri ($ciresonPortalServer+"api/V3/User/GetUserList?userFilter=$($adGroup.Name)&filterByAnalyst=false&groupsOnly=true&maxNumberOfResults=25") -UseDefaultCredentials
try {
if($ciresonPortalWindowsAuth)
{
#wanted to use a get groups style request, but "api/V3/User/GetConsoleGroups" feels costly instead of a search
$cwpGroupResponse = Invoke-RestMethod -Uri ($ciresonPortalServer+"api/V3/User/GetUserList?userFilter=$($adGroup.Name)&filterByAnalyst=false&groupsOnly=true&maxNumberOfResults=25") -UseDefaultCredentials
}
else
{
$cwpGroupResponse = Invoke-RestMethod -Uri ($ciresonPortalServer+"api/V3/User/GetUserList?userFilter=$($adGroup.Name)&filterByAnalyst=false&groupsOnly=true&maxNumberOfResults=25") -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}
$ciresonPortalGroup = $cwpGroupResponse | select-object @{Name='AccessGroupId'; Expression={$_.Id}}, name | Where-Object{$_.name -eq $($adGroup.Name)}
if ($loggingLevel -ge 4) {New-SMEXCOEvent -Source "Get-CiresonPortalGroup" -EventID 0 -Severity Information -LogMessage "Group/Sender found in Cireson Portal"}
return $ciresonPortalGroup
}
else
{
$cwpGroupResponse = Invoke-RestMethod -Uri ($ciresonPortalServer+"api/V3/User/GetUserList?userFilter=$($adGroup.Name)&filterByAnalyst=false&groupsOnly=true&maxNumberOfResults=25") -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
catch {
if ($loggingLevel -ge 3) {New-SMEXCOEvent -Source "Get-CiresonPortalGroup" -EventID 1 -Severity Error -LogMessage $_.Exception}
return $null
}
$ciresonPortalGroup = $cwpGroupResponse | select-object @{Name='AccessGroupId'; Expression={$_.Id}}, name | Where-Object{$_.name -eq $($adGroup.Name)}
return $ciresonPortalGroup
}

#retrieve all the announcements on the portal
Expand Down Expand Up @@ -3128,36 +3146,47 @@ function Search-AvailableCiresonPortalOffering
)

$serviceCatalogAPIurl = "api/V3/ServiceCatalog/GetServiceCatalog?userId=$($ciresonPortalUser.id)&isScoped=$($ciresonPortalUser.Security.IsServiceCatalogScoped)"
if ($ciresonPortalWindowsAuth -eq $true)
{
$serviceCatalogResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$serviceCatalogAPIurl) -Method get -UseDefaultCredentials
}
else
{
$serviceCatalogResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$serviceCatalogAPIurl) -Method get -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}
try {
if ($ciresonPortalWindowsAuth -eq $true)
{
$serviceCatalogResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$serviceCatalogAPIurl) -Method get -UseDefaultCredentials
}
else
{
$serviceCatalogResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$serviceCatalogAPIurl) -Method get -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}

#### If the user has access to some Request Offerings, find which RO Titles/Description contain words from their original message ####
if ($serviceCatalogResults)
{
#prepare the results by generating a URL array for the email
$matchingRequestURLs = @()
foreach ($serviceCatalogResult in $serviceCatalogResults)
#### If the user has access to some Request Offerings, find which RO Titles/Description contain words from their original message ####
if ($serviceCatalogResults)
{
$wordsMatched = ($searchQuery.Trim().Split() | Where-Object{($serviceCatalogResult.RequestOfferingTitle -match "\b$_\b") -or ($serviceCatalogResult.RequestOfferingDescription -match "\b$_\b")}).count
if ($wordsMatched -ge $numberOfWordsToMatchFromEmailToRO)
#prepare the results by generating a URL array for the email
$matchingRequestURLs = @()
foreach ($serviceCatalogResult in $serviceCatalogResults)
{
$ciresonPortalRequestURL = "`"" + $ciresonPortalServer + "SC/ServiceCatalog/RequestOffering/" + $serviceCatalogResult.RequestOfferingId + "," + $serviceCatalogResult.ServiceOfferingId + "`""
$RequestOfferingURL = "<a href=$ciresonPortalRequestURL/>$($serviceCatalogResult.RequestOfferingTitle)</a><br />"
$requestOfferingSuggestion = [PSCustomObject] @{
RequestOfferingURL = $RequestOfferingURL
WordsMatched = $wordsMatched
$wordsMatched = ($searchQuery.Trim().Split() | ForEach-Object {[regex]::Escape($_)} | Where-Object{($serviceCatalogResult.RequestOfferingTitle -match "\b$_\b") -or ($serviceCatalogResult.RequestOfferingDescription -match "\b$_\b")}).count
if ($wordsMatched -ge $numberOfWordsToMatchFromEmailToRO)
{
$ciresonPortalRequestURL = "`"" + $ciresonPortalServer + "SC/ServiceCatalog/RequestOffering/" + $serviceCatalogResult.RequestOfferingId + "," + $serviceCatalogResult.ServiceOfferingId + "`""
$RequestOfferingURL = "<a href=$ciresonPortalRequestURL/>$($serviceCatalogResult.RequestOfferingTitle)</a><br />"
$requestOfferingSuggestion = [PSCustomObject] @{
RequestOfferingURL = $RequestOfferingURL
WordsMatched = $wordsMatched
}
$matchingRequestURLs += $requestOfferingSuggestion
}
$matchingRequestURLs += $requestOfferingSuggestion
}
$matchingRequestURLs = ($matchingRequestURLs | sort-object WordsMatched -Descending).RequestOfferingURL
if ($loggingLevel -ge 4) {New-SMEXCOEvent -Source "Search-AvailableCiresonPortalOffering" -EventID 0 -Severity Information -LogMessage "$($matchingRequestURLs.Count) relevant ROs found"}
return $matchingRequestURLs
}
else {
if ($loggingLevel -ge 2) {New-SMEXCOEvent -Source "Search-AvailableCiresonPortalOffering" -EventID 1 -Severity Warning -LogMessage "No relevant ROs were found"}
return $null
}
$matchingRequestURLs = ($matchingRequestURLs | sort-object WordsMatched -Descending).RequestOfferingURL
return $matchingRequestURLs
}
catch {
if ($loggingLevel -ge 3) {New-SMEXCOEvent -Source "Search-AvailableCiresonPortalOffering" -EventID 2 -Severity Error -LogMessage $_.Exception}
return $null
}
}

Expand All @@ -3171,35 +3200,46 @@ function Search-CiresonKnowledgeBase
)

$kbAPIurl = "api/V3/Article/FullTextSearch?&searchValue=$searchQuery"
if ($ciresonPortalWindowsAuth -eq $true)
{
$kbResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$kbAPIurl) -UseDefaultCredentials
}
else
{
$kbResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$kbAPIurl) -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}
try {
if ($ciresonPortalWindowsAuth -eq $true)
{
$kbResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$kbAPIurl) -UseDefaultCredentials
}
else
{
$kbResults = Invoke-RestMethod -Uri ($ciresonPortalServer+$kbAPIurl) -Headers @{"Authorization"=Get-CiresonPortalAPIToken}
}

$kbResults = $kbResults | Where-Object{$_.endusercontent -ne ""} | select-object articleid, title
$kbResults = $kbResults | Where-Object{$_.endusercontent -ne ""} | select-object articleid, title

if ($kbResults)
{
$matchingKBURLs = @()
foreach ($kbResult in $kbResults)
if ($kbResults)
{
$wordsMatched = ($searchQuery.Trim().Split() | Where-Object{($kbResult.title -match "\b$_\b")}).count
if ($wordsMatched -ge $numberOfWordsToMatchFromEmailToKA)
$matchingKBURLs = @()
foreach ($kbResult in $kbResults)
{
$KnowledgeArticleURL = "<a href=$ciresonPortalServer" + "KnowledgeBase/View/$($kbResult.articleid)#/>$($kbResult.title)</a><br />"
$knowledgeSuggestion = [PSCustomObject] @{
KnowledgeArticleURL = $KnowledgeArticleURL
WordsMatched = $wordsMatched
$wordsMatched = ($searchQuery.Trim().Split() | ForEach-Object {[regex]::Escape($_)} | Where-Object{($kbResult.title -match "\b$_\b")}).count
if ($wordsMatched -ge $numberOfWordsToMatchFromEmailToKA)
{
$KnowledgeArticleURL = "<a href=$ciresonPortalServer" + "KnowledgeBase/View/$($kbResult.articleid)#/>$($kbResult.title)</a><br />"
$knowledgeSuggestion = [PSCustomObject] @{
KnowledgeArticleURL = $KnowledgeArticleURL
WordsMatched = $wordsMatched
}
$matchingKBURLs += $knowledgeSuggestion
}
$matchingKBURLs += $knowledgeSuggestion
}
$matchingKBURLs = ($matchingKBURLs | sort-object WordsMatched -Descending).KnowledgeArticleURL
if ($loggingLevel -ge 4) {New-SMEXCOEvent -Source "Search-CiresonKnowledgeBase" -EventID 0 -Severity Information -LogMessage "$($matchingKBURLs.Count) relevant KBs were found"}
return $matchingKBURLs
}
else {
if ($loggingLevel -ge 2) {New-SMEXCOEvent -Source "Search-CiresonKnowledgeBase" -EventID 1 -Severity Warning -LogMessage "No relevant KBs were found"}
return $null
}
$matchingKBURLs = ($matchingKBURLs | sort-object WordsMatched -Descending).KnowledgeArticleURL
return $matchingKBURLs
}
catch {
if ($loggingLevel -ge 3) {New-SMEXCOEvent -Source "Search-CiresonKnowledgeBase" -EventID 2 -Severity Error -LogMessage $_.Exception}
return $null
}
}

Expand Down Expand Up @@ -3236,6 +3276,8 @@ function Get-CiresonSuggestionURL
return $null
}

if ($loggingLevel -ge 4) {New-SMEXCOEvent -Source "Get-CiresonSuggestionURL" -EventID 0 -Severity Information -LogMessage "Retrieving Suggestion URLs from Cireson Portal. Suggest KA: $SuggestKA. SuggestRO: $SuggestRO."}

#check string length
$wiTitle = if ($WorkItem.title.length -ge 1) {$WorkItem.title.trim()} else {$null}
$wiDesc = if ($WorkItem.description.length -ge 1) {$WorkItem.description.trim()} else {$null}
Expand Down Expand Up @@ -3678,6 +3720,7 @@ function Undo-WorkItemResolution
"System.WorkItem.Problem" {$enumValue = "ProblemStatusEnum.Active$"; $resName = "Resolution"}
}
Set-SCSMObject -SMObject $WorkItem -propertyhashtable @{"Status" = $enumValue; $resName = $null; "ResolutionDescription" = $null; "ResolvedDate" = $null} @scsmMGMTParams;
Get-SCSMRelationshipObject -BySource $workItem -Filter "RelationshipId -eq '$($workResolvedByUserRelClass.Id)'" @scsmMGMTParams | Remove-SCSMRelationshipObject
}

function Read-MIMEMessage
Expand Down

0 comments on commit 02be0b1

Please sign in to comment.