Public/New-AtwsAttachment.ps1

#Requires -Version 4.0

<#
 
.COPYRIGHT
Copyright (c) Office Center Hønefoss AS. All rights reserved. Based on code from Jan Egil Ring (Crayon). Licensed under the MIT license.
See https://github.com/officecenter/Autotask/blob/master/LICENSE.md for license information.
 
#>

Function New-AtwsAttachment {


  <#
      .SYNOPSIS
      This function creates a new attachment through the Autotask Web Services API.
      .DESCRIPTION
      This function creates a new attachment connected to either an Account, an Opportunity,
      a Project or a Ticket. The attachment can be passed through the pipeline or provided as
      en URL or a file or folder path.
      .INPUTS
      Nothing
      .OUTPUTS
      Autotask attachments
      .EXAMPLE
      New-AtwsAttachment -TicketId 0 -Path C:\Document.docx
      Uploads C:\Document.docx as an attachment to the Ticket with id 0 and sets the attachment title to 'Document.docx'.
      .EXAMPLE
      New-AtwsAttachment -TicketId 0 -Path C:\Document.docx -Title 'A title'
      Uploads C:\Document.docx as an attachment to the Ticket with id 0 and sets the attachment title to 'A title'.
      .EXAMPLE
      New-AtwsAttachment -TicketId 0 -Path C:\Document.docx -FileLink
      Adds an file link attachment to the Ticket with id 0, title 'Document.docx' and C:\Document.docx as full path.
      .EXAMPLE
      $Attachment = Get-AtwsAttachment -TicketID 0 | Select-Object -First 1
      New-AtwsAttachment -Data $Attachment.Data -TicketId 1 -Title $Attachment.Info.Title -Extension $([IO.Path]::GetExtension($Attachment.Info.FullPath))
      Gets the first attachment from Ticket with id 0 and attaches it to Ticket with id 1
      .NOTE
      Strongly related to Get-AtwsAttachmentInfo
  #>


  [CmdLetBinding(SupportsShouldProcess = $True, DefaultParameterSetName = 'Ticket', ConfirmImpact = 'None')]
  Param
  (

    # An object as a byte array that will be attached to an Autotask object
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Opportunity_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Project_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Ticket_as_byte'
    )]
    [ValidateNotNullOrEmpty()]
    [Byte[]]
    $Data,
    
    # An object as a byte array that will be attached to an Autotask object
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Opportunity_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Project_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Ticket_as_byte'
    )]
    [ValidateNotNullOrEmpty()]
    [ValidatePattern('^\.?\w+$')]
    [String]
    $Extension,

    # A is required for Data
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Opportunity_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Project_as_byte'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Ticket_as_byte'
    )]
    [Parameter(
        ParameterSetName = 'Account'
    )]
    [Parameter(
        ParameterSetName = 'Opportunity'
    )]
    [Parameter(
        ParameterSetName = 'Project'
    )]
    [Parameter(
        ParameterSetName = 'Ticket'
    )]
    [Parameter(
        ParameterSetName = 'Account_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Opportunity_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Project_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Ticket_as_url'
    )]
    [ValidateNotNullOrEmpty()]
    [String]
    $Title,
    
    # A file path that will be attached to an Autotask object
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Account'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Opportunity'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Project'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Ticket'
    )]
    [ValidateNotNullOrEmpty()]
    [ValidateScript({
          if( -Not ($_ | Test-Path) ){
            throw "File or folder does not exist"
          }
          return $true
    })]
    [IO.FileInfo]
    $Path,

    # URL to attach
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Account_as_url'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Opportunity_as_url'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Project_as_url'
    )]
    [Parameter(
        Mandatory = $True, 
        ParameterSetName = 'Ticket_as_url'
    )]
    [URI]
    $URI,
    
    # Attach as a file link, not an attachment
    [Parameter(
        ParameterSetName = 'Account_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Opportunity_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Project_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Ticket_as_url'
    )]
    [Alias('Link')]
    [Switch]
    $FileLink,
    
    # Attach as a folder link, not an attachment
    [Parameter(
        ParameterSetName = 'Account_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Opportunity_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Project_as_url'
    )]
    [Parameter(
        ParameterSetName = 'Ticket_as_url'
    )]
    [Alias('Folder')]
    [Switch]
    $FolderLink,

    # Account ID
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account_as_byte'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Account_as_url'
    )]
    [ValidateScript({
          if( -Not (Get-AtwsAccount -id $_) ){
            throw "Account does not exist"
          }
          return $true
    })]    
    [long]
    $AccountID,

    # Opportunity ID
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Opportunity'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Opportunity_as_byte'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Opportunity_as_url'
    )]
    [ValidateScript({
          if( -Not (Get-AtwsOpportunity -id $_) ){
            throw "Opportunity does not exist"
          }
          return $true
    })]
    [long]
    $OpportunityID,

    # Project ID
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Project'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Project_as_byte'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Project_as_url'
    )]
    [ValidateScript({
          if( -Not (Get-AtwsProject -id $_) ){
            throw "Project does not exist"
          }
          return $true
    })]
    [long]
    $ProjectID,

    # Ticket ID
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Ticket'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Ticket_as_byte'
    )]
    [Parameter(
        Mandatory = $True,
        ParameterSetName = 'Ticket_as_url'
    )]
    [ValidateScript({
          if( -Not (Get-AtwsTicket -id $_) ){
            throw "Ticket does not exist"
          }
          return $true
    })]
    [long]
    $TicketID,

    [ValidateSet('All Autotask Users','Internal Users Only')]
    [String]
    $Publish = 'All Autotask Users'
   
  )

  Begin { 
   
    # Enable modern -Debug behavior
    If ($PSCmdlet.MyInvocation.BoundParameters['Debug'].IsPresent) { $DebugPreference = 'Continue' }
    
    Write-Debug ('{0}: Begin of function' -F $MyInvocation.MyCommand.Name)
    
    # Dynamic field info
    $Fields = Get-AtwsFieldInfo -Entity AttachmentInfo
    $DateTimeParams = $Fields.Where({$_.Type -eq 'datetime'}).Name
    $Picklists = $Fields.Where{$_.IsPickList}
    
    # Set up TimeZone offset handling
    If (-not($script:ESTzone)) {
      $script:ESTzone = [System.TimeZoneInfo]::FindSystemTimeZoneById("Eastern Standard Time")
    }
    
    If (-not($script:ESToffset)) {
      $Now = Get-Date
      $ESTtime = [System.TimeZoneInfo]::ConvertTimeFromUtc($Now.ToUniversalTime(), $ESTzone)

      $script:ESToffset = (New-TimeSpan -Start $ESTtime -End $Now).TotalHours
    }
    
    # Publish dictionary
    $PublishToIndex = @{
      'All Autotask Users' = '1'
      'Internal Users Only' = '2'
    }
  }


  Process {
    
    # A new Attachment object
    $Attachment = New-object "Autotask.Attachment"
    
    # A new AttachmentInfo object
    $AttachmentInfo = New-object "Autotask.AttachmentInfo"
    
    # Attach info object to attachment object
    $Attachment.Info = $AttachmentInfo

    # Publishsettings
    $AttachmentInfo.Publish = $PublishToIndex[$Publish]
    
    # Attachment type
    If ($Data) {
      $Attachment.Data = $Data
      $AttachmentInfo.Type = 'FILE_ATTACHMENT'
      $AttachmentInfo.FullPath = '{0}.{1}' -F $Title, $Extension.TrimStart('.')
    }
    # Is it an URL?
    ElseIf ($URI) {
      If ($FolderLink.IsPresent) {
        $AttachmentInfo.Type = 'FOLDER_LINK'
      }
      ElseIf ($FileLink.IsPresent) {
        $AttachmentInfo.Type = 'FILE_LINK'
      }
      Else { 
        $AttachmentInfo.Type = 'URL'
      }
      $ATtachmentInfo.FullPath = $URI.AbsoluteUri
      $AttachmentInfo.Title = $AttachmentInfo.FullPath
    }
    # It is a file and it is going to be attached.
    Else {
      [Byte[]]$Data = Get-Content -Path $Path.FullName -Encoding Byte -ReadCount 0
      $Attachment.Data = $Data

      # Type is attachment
      $AttachmentInfo.Type = 'FILE_ATTACHMENT'
      $AttachmentInfo.Title = $Path.BaseName
      $AttachmentInfo.FullPath = $Path.FullName
      $AttachmentInfo.ContentType = 'image/png'

    }
    
    # Overwrite title with $Title if it exists
    If ($Title) {
      $AttachmentInfo.Title = $Title
    }
    
    # What are we attaching to?
    $ObjectType = ($PSCmdlet.ParameterSetName -split '_')[0]

    If ($OpportunityID) {
       $AttachmentInfo.OpportunityId = $OpportunityId
    }
    Else {
      $AttachmentInfo.ParentId = $TicketId + $AccountID + $ProjectID
      $AttachmentInfo.ParentType = $Picklists.Where{$_.name -eq 'ParentType'}.PickListValues.Where{$_.Label -eq $ObjectType}.Value
    }

    $Caption = $MyInvocation.MyCommand.Name
    $VerboseDescrition = '{0}: About to create an attachment of type {1} with title {2}.' -F $Caption, $AttachmentInfo.Type, $AttachmentInfo.Title
    $VerboseWarning = '{0}: About to create an attachment of type {1} with title {2}. Do you want to continue?' -F $Caption, $AttachmentInfo.Type, $AttachmentInfo.Title
    
    If ($PSCmdlet.ShouldProcess($VerboseDescrition, $VerboseWarning, $Caption)) { 
      $AttachmentId = $Script:Atws.CreateAttachment($Attachment)
      
      $Result = Get-AtwsAttachmentInfo -id $AttachmentId
      
      Write-Verbose ('{0}: Created attachment with id {1} and title {2}' -F $MyInvocation.MyCommand.Name, $AttachmentId, $Result.Title)
    

      # Expand UDFs by default
      # Normalize dates (convert to local time). EVery datetime field ever returned
      # By the API is in CEST.
      Foreach ($Item in $Result)
      {
        # Any userdefined fields?
        If ($Item.UserDefinedFields.Count -gt 0)
        { 
          # Expand User defined fields for easy filtering of collections and readability
          Foreach ($UDF in $Item.UserDefinedFields)
          {
            # Make names you HAVE TO escape...
            $UDFName = '#{0}' -F $UDF.Name
            Add-Member -InputObject $Item -MemberType NoteProperty -Name $UDFName -Value $UDF.Value -Force
          }  
        }
      
        # Adjust TimeZone on all DateTime properties
        # Dates RETURNED by the API are always in CEST. Add timezone difference
        # to get local time
        Foreach ($DateTimeParam in $DateTimeParams) {
      
          # Get the datetime value
          $Value = $Item.$DateTimeParam
                
          # Skip if parameter is empty
          If (-not ($Value)) {
            Continue
          }
          # Yes, you really have to ADD the difference
          $Item.$DateTimeParam  = $Value.AddHours($script:ESToffset)
        }

        If ($Script:UsePickListLabels) { 
          # Restore picklist labels
          Foreach ($Field in $Picklists)
          {
            If ($Item.$($Field.Name) -in $Field.PicklistValues.Value) {
              $Item.$($Field.Name) = ($Field.PickListValues.Where{$_.Value -eq $Item.$($Field.Name)}).Label
            }
          }
        }
      }
    }
  }

  End {
    Write-Debug ('{0}: End of function' -F $MyInvocation.MyCommand.Name)
    If ($Result) {
      Return $Result
    }
  }


}