PSNavigation.psm1

Class PSNavigationLocation {
    [string]$Id
    [string]$Location
    [string]$DateCreated
    [int]$Invoked

    PSNavigationLocation ([string]$Id, [string]$Location, [string]$DateCreated, [int]$Invoked) {
        $this.Id = $Id
        $this.Location = $Location
        $this.DateCreated = $DateCreated
        $this.Invoked = $Invoked
    }

    [string]ToString() {
        return ("[{0}]{1}({2},{3})" -f $this.Id, $this.Location, $this.DateCreated, $this.Invoked)
    }
}
Function ExportXMLData {
    Param(
        [Parameter(Mandatory=$True)][PSObject]$InputObject,
        [Parameter(Mandatory=$True)][string]$Path
    )
    $DataFileCreatedDate = $InputObject.DataFileCreatedDate
    $Locations = $InputObject.Locations
    $RemovedLocations = $InputObject.RemovedLocations

    $XMLWriter = New-Object System.XML.XmlTextWriter($Path,$Null)
    $XMLWriter.Formatting = 'Indented'
    $XMLWriter.Indentation = 1
    $XMLWriter.IndentChar = "`t"
    $XMLWriter.WriteStartDocument()
    $XMLWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl;")
    $XMLWriter.WriteStartElement('navigation')
        $XMLWriter.WriteAttributeString('datecreated',$DataFileCreatedDate)
        $XMLWriter.WriteStartElement('locations')
        ForEach ($Location in $Locations) {
            $XMLWriter.WriteStartElement('location')
                $XMLWriter.WriteAttributeString('id',$($Location.Id))
                $XMLWriter.WriteAttributeString('location',$($Location.Location))
                $XMLWriter.WriteAttributeString('datecreated',$($Location.DateCreated))
                $XMLWriter.WriteAttributeString('invoked',$($Location.Invoked))
            $XMLWriter.WriteEndElement() # End of 'location' element
        }
        $XMLWriter.WriteEndElement() # End of 'locations' element
        $XMLWriter.WriteStartElement('removedlocations')
        ForEach ($RemovedLocation in $RemovedLocations) {
            $XMLWriter.WriteStartElement('location')
                $XMLWriter.WriteAttributeString('id',$($RemovedLocation.Id))
                $XMLWriter.WriteAttributeString('location',$($RemovedLocation.Location))
                $XMLWriter.WriteAttributeString('datecreated',$($RemovedLocation.DateCreated))
                $XMLWriter.WriteAttributeString('invoked',$($RemovedLocation.Invoked))
                $XMLWriter.WriteAttributeString('datedeleted',$($RemovedLocation.DateDeleted))
            $XMLWriter.WriteEndElement() # End of 'location' element
        }
        $XMLWriter.WriteEndElement() # End of 'removedlocations' element
    $XMLWriter.WriteEndElement() # End of 'navigation' element
    $XMLWriter.WriteEndDocument()
    $XMLWriter.Flush()
    $XMLWriter.Close()
}
Function ImportXMLData {
    Param(
        [Parameter(Mandatory=$True)][string]$Path
    )
    $XML = New-Object -TypeName XML
    $XML.Load($Path)
    $DataFileCreatedDate = $XML.navigation.datecreated
    $Locations = @()
    ForEach ($Item in $($XML.navigation.locations.location)) {
        $Props = @{
            'Id'=$($Item.id);
            'Location'=$($Item.location);
            'DateCreated'=$($Item.datecreated);
            'Invoked'=[int]$($Item.invoked);
        }
        $Obj = New-Object -TypeName PSObject -Property $Props
        $Locations += $Obj
    }
    $RemovedLocations = @()
    ForEach ($Item in $($XML.navigation.removedlocations.location)) {
        $Props = @{
            'Id'=$($Item.id);
            'Location'=$($Item.location);
            'DateCreated'=$($Item.datecreated);
            'Invoked'=[int]$($Item.invoked);
            'datedeleted'=$($Item.datedeleted);
        }
        $Obj = New-Object -TypeName PSObject -Property $Props
        $RemovedLocations += $Obj
    }
    $Props = @{
        'DataFileCreatedDate'=$DataFileCreatedDate;
        'Locations'=$Locations;
        'RemovedLocations'=$RemovedLocations;
    }
    $Output = New-Object -TypeName PSObject -Property $Props
    Write-Output $Output
}
Function Initialize-XMLDataFile {
    Param(
        [Parameter(Mandatory=$True)][string]$Path
    )
    $ParentPath = Split-Path -Path $Path -Parent
    If (!(Test-Path -Path $ParentPath)) {
        New-Item -Path $ParentPath -ItemType Directory -Force
    }
    $DateTimeStamp = (Get-Date -Format "MM/dd/yyy-THH:mm:ss")
    $XMLWriter = New-Object System.XML.XmlTextWriter($Path,$Null)
    $XMLWriter.Formatting = 'Indented'
    $XMLWriter.Indentation = 1
    $XMLWriter.IndentChar = "`t"
    $XMLWriter.WriteStartDocument()
    $XMLWriter.WriteProcessingInstruction("xml-stylesheet", "type='text/xsl' href='style.xsl;")
    $XMLWriter.WriteStartElement('navigation')
        $XMLWriter.WriteAttributeString('datecreated',$($DateTimeStamp))
        $XMLWriter.WriteStartElement('locations')
        $XMLWriter.WriteEndElement() # End of locations element
        $XMLWriter.WriteStartElement('removedlocations')
        $XMLWriter.WriteEndElement() # End of 'removedlocations' element
    $XMLWriter.WriteEndElement() # End of navigation element
    $XMLWriter.WriteEndDocument()
    $XMLWriter.Flush()
    $XMLWriter.Close()
}
Function Add-NavLocation {
    <#
    .EXTERNALHELP PSNavigation-help.xml
    #>

    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [Parameter(Position=0,Mandatory=$True)]
        [ValidateLength(1,15)]
        [string]$Id,

        [Parameter(Position=1)]
        [string]$Location = ''
    )

    $DateTimeStamp = (Get-Date).ToString()
    $DataSaveFile = "$HOME\AppData\Local\THBIV\Powershell\Navigation\data.xml"
    If (!(Test-Path $DataSaveFile)) {
        Initialize-XMLDataFile -Path $DataSaveFile
    }

    Write-Verbose '[AddNavLocation] Importing data file'
    Try {
        $NavData = ImportXMLData -Path $DataSaveFile
    }
    Catch {
        $ErrorMessage = $_.Exception.Message
        Throw $ErrorMessage
    }
    $DataFileCreatedDate = $NavData.DataFileCreatedDate
    $Locations = $NavData.Locations
    $RemovedLocations = $NavData.RemovedLocations
    Write-Verbose '[AddNavLocation] Adding members to temp object'
    If ($Location -eq '') {
        Write-Verbose '[AddNavLocation] Using the consoles current location'
        $NewLocation = (Get-Location).Path
    } Else {
        Write-Verbose "[AddNavLocation] Using $Location"
        $NewLocation = $Location
    }
    Write-Verbose "[AddNavLocation] Check if $Id is a duplicate"
    ForEach ($Item in $Locations) {
        If ($Item.Id -eq $New.Id) {
            Throw "Duplicate ID: $Id"
        }
    }
    $Props = @{
        'Id'=$Id;
        'Location'=$NewLocation;
        'DateCreated'=$DateTimeStamp;
        'Invoked'=[int]0;
    }
    $Obj = New-Object -TypeName PSObject -Property $Props

    $Locations += $Obj
    Write-Verbose '[AddNavLocation] Updating the data file'
    If ($pscmdlet.ShouldProcess("Adding '$Id' to Location Database")) {
        $Props = @{
            'DataFileCreatedDate'=$DataFileCreatedDate;
            'Locations'=$Locations;
            'RemovedLocations'=$RemovedLocations;
        }
        $NavObject = New-Object -TypeName PSObject -Property $Props
        ExportXMLData -InputObject $NavObject -Path $DataSaveFile
    }
}
Function Get-NavLocation {
    <#
    .EXTERNALHELP PSNavigation-help.xml
    #>

    [CmdletBinding(SupportsPaging)]
    [OutputType([PSNavigationLocation])]
    Param()

    $DataSaveFile = "$HOME\AppData\Local\THBIV\Powershell\Navigation\data.xml"
    If (!(Test-Path $DataSaveFile)) {
        Write-Verbose "[GetNavLocation] No data file exists. Nothing to output."
    } Else {
        Try {
            Write-Verbose "[GetNavLocation] Data file exists. Importing data."
            $NavData = ImportXMLData -Path $DataSaveFile
        }
        Catch {
            $ErrorMessage = $_.Exception.Message
            Throw $ErrorMessage
        }
        Write-Verbose "[GetNavLocation] Output PSNavigationLocation Objects"
        ForEach ($Location in $NavData.Locations) {
            $Obj = New-Object -TypeName PSNavigationLocation -ArgumentList $($Location.Id),
                                                                           $($Location.Location),
                                                                           $($Location.DateCreated),
                                                                           $($Location.Invoked)
            Write-Output $Obj
        }
    }
}
Function Invoke-GoLocation {
    <#
    .EXTERNALHELP PSNavigation-help.xml
    #>

    [CmdletBinding(SupportsShouldProcess)]
    Param(
        [Parameter(Position=0,Mandatory=$True)]
        [string]$Id
    )

    $DataSaveFile = "$HOME\AppData\Local\THBIV\Powershell\Navigation\data.xml"
    If (!(Test-Path $DataSaveFile)) {
        Write-Warning "[InvokeGoLocation] No data file exists. Cannot Invoke a location."
    } Else {
        Write-Verbose '[InvokeGoLocation] Importing data file'
        Try {
            $NavData = ImportXMLData -Path $DataSaveFile
        }
        Catch {
            $ErrorMessage = $_.Exception.Message
            Throw $ErrorMessage
        }
        $DataFileCreatedDate = $NavData.DataFileCreatedDate
        $Locations = $NavData.Locations
        $RemovedLocations = $NavData.RemovedLocations
        Write-Verbose '[InvokeGoLocation] Get object to invoke'
        $Invoked = $Locations | Where-Object {$_.Id -eq $Id}
        Write-Verbose '[InvokeGoLocation] Setting console location'
        If ($Invoked) {
            If ($pscmdlet.ShouldProcess("Invoking the Go Location '$Id'")) {
                Set-Location $Invoked.Location
                $Locations | Where-Object {$_.Id -eq $Id} | ForEach-Object {$_.Invoked ++}
                $Props = @{
                    'DataFileCreatedDate'=$DataFileCreatedDate;
                    'Locations'=$Locations;
                    'RemovedLocations'=$RemovedLocations;
                }
                $NavObject = New-Object -TypeName PSObject -Property $Props
                ExportXMLData -InputObject $NavObject -Path $DataSaveFile
            }
        }
    }
}
Set-Alias -Name Go -Value Invoke-GoLocation
Function Invoke-OpenLocation {
    <#
    .EXTERNALHELP PSNavigation-help.xml
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Position=0)]
        [string]$Id = '',

        [Alias('p')]
        [string]$Path
    )

    $DataSaveFile = "$HOME\AppData\Local\THBIV\Powershell\Navigation\data.xml"
    If (!(Test-Path $DataSaveFile)) {
        Write-Warning "[InvokeOpenLocation] No data file exists. Cannot Invoke a location."
    } Else {
        Write-Verbose '[InvokeOpenLocation] Importing data file'
        Try {
            $NavData = ImportXMLData -Path $DataSaveFile
        }
        Catch {
            $ErrorMessage = $_.Exception.Message
            Throw $ErrorMessage
        }
        $DataFileCreatedDate = $NavData.DataFileCreatedDate
        $Locations = $NavData.Locations
        $RemovedLocations = $NavData.RemovedLocations
        Write-Verbose '[InvokeOpenLocation] Get object to invoke'
        If ($Path) {
            If (Test-Path -Path $Path) {
                explorer.exe $Path
            } Else {
                Throw "$Path does not exist"
            }
        } Else {
            If ($Id -eq '') {
                $OpenItem = (Get-Location).Path
            } Else {
                $OpenItem = $Locations | Where-Object {$_.Id -eq $Id}
                If (!($OpenItem)) {

                    Throw "$Id does not exist in the database"
                }
            }
            If ($OpenItem) {
                Write-Verbose '[InvokeOpenLocation] Opening explorer window'
                Push-Location $OpenItem.Location
                explorer.exe .
                Pop-Location
                If ($Id) {
                    $Locations | Where-Object {$_.Id -eq $Id} | ForEach-Object {$_.Invoked ++}
                    $Props = @{
                        'DataFileCreatedDate'=$DataFileCreatedDate;
                        'Locations'=$Locations;
                        'RemovedLocations'=$RemovedLocations;
                    }
                    $NavObject = New-Object -TypeName PSObject -Property $Props
                    ExportXMLData -InputObject $NavObject -Path $DataSaveFile
                }
            }
        }
    }
}
Set-Alias -Name Open -Value Invoke-OpenLocation
Function Remove-NavLocation {
    <#
    .EXTERNALHELP PSNavigation-help.xml
    #>

    [CmdletBinding(SupportsShouldProcess=$True)]
    Param(
        [Parameter(Position=0,Mandatory=$True,ParameterSetName='Id')]
        [string]$Id,

        [Parameter(Mandatory=$True,ParameterSetName='All')]
        [switch]$All
    )

    $DateTimeStamp = (Get-Date).ToString()
    $DataSaveFile = "$HOME\AppData\Local\THBIV\Powershell\Navigation\data.xml"
    If (!(Test-Path $DataSaveFile)) {
        Write-Verbose "[GetNavLocation] No data file exists. Nothing to remove."
    } Else {
        Write-Verbose 'Importing data file'
        Try {
            $NavData = ImportXMLData -Path $DataSaveFile
        }
        Catch {
            $ErrorMessage = $_.Exception.Message
            Throw $ErrorMessage
        }
        $DataFileCreatedDate = $NavData.DataFileCreatedDate
        $Locations = $NavData.Locations
        $RemovedLocations = $NavData.RemovedLocations
        $Locations | Add-Member -MemberType NoteProperty -Name tag -Value 0
        If ($All) {
            Write-Verbose 'Deleting All Go data'
            Write-Verbose 'Updating data file. Omitting objects with tag equal to 0.'
            $DelItems = $Locations
            $DelItems | Add-Member -MemberType NoteProperty -Name DateDeleted -Value $DateTimeStamp
            $DelItems | ForEach-Object {$RemovedLocations += $_} | Select-Object Id,Location,DateCreated,Invoked,DateDeleted
            $Locations = $Locations | Where-Object {$_.tag -ne 0} | Select-Object Id,Location,DateCreated,Invoked
        }
        Else {
            Write-Verbose 'Selecting object to remove and setting its tag to 1'
            $DelItem = $Locations | Where-Object {$_.Id -eq $Id}
            ForEach ($Item in $DelItem) {
                $Item.tag = 1
            }
            $DelItem | Add-Member -MemberType NoteProperty -Name DateDeleted -Value $DateTimeStamp
            ForEach ($Item in $DelItem) {
                $Obj = $Item | Select-Object Id,Location,DateCreated,Invoked,DateDeleted
                $RemovedLocations += $Obj
            }
            Write-Verbose 'Updating data file. Omitting the object with tag equal to 1.'
        }
        If ($pscmdlet.ShouldProcess($Id)) {
            $Locations = $Locations | Where-Object {$_.tag -ne 1} | Select-Object Id,Location,DateCreated,Invoked
            $Props = @{
                'DataFileCreatedDate'=$DataFileCreatedDate;
                'Locations'=$Locations;
                'RemovedLocations'=$RemovedLocations;
            }
            $NavObject = New-Object -TypeName PSObject -Property $Props
            ExportXMLData -InputObject $NavObject -Path $DataSaveFile
        }
    }
}