PSGet.psm1

Microsoft.PowerShell.Core\Set-StrictMode -Version Latest

$script:ProgramFilesModulesPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell\Modules"
$script:MyDocumentsModulesPath = Microsoft.PowerShell.Management\Join-Path -Path ([Environment]::GetFolderPath("MyDocuments")) -ChildPath "WindowsPowerShell\Modules"
$script:PSGetItemInfoFileName = "_PSGetItemInfo.xml"

Microsoft.PowerShell.Utility\Import-LocalizedData  LocalizedData -filename PSGet.Resource.psd1

function Find-Module
{
    [CmdletBinding()]
    [outputtype("PSCustomObject[]")]
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true,
                   Position=0)]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNull()]
        [Alias("Version")]
        [Version]
        $MinimumVersion,
        
        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNull()]
        [Version]
        $RequiredVersion
    )

    Process
    {
        $ErrorActionPreference="continue"

        if($RequiredVersion -and $MinimumVersion)
        {
            ThrowError -ExceptionName "System.ArgumentException" `
                       -ExceptionMessage $LocalizedData.MinimumVersionAndRequiredVersionCannotBeSpecifiedTogether `
                       -ErrorId "MinimumVersionAndRequiredVersionCannotBeSpecifiedTogether" `
                       -ErrorCategory InvalidArgument
        }

        if($RequiredVersion -or $MinimumVersion)
        {
            if(-not $Name -or $Name.Count -ne 1 -or (Test-WildcardPattern -Name $Name[0]))
            {
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage $LocalizedData.VersionParametersAreAllowedOnlyWithSingleModule `
                           -ErrorId "VersionParametersAreAllowedOnlyWithSingleModule" `
                           -ErrorCategory InvalidArgument
            }

            if($RequiredVersion)
            {
                $fullQualifiedName = @{ModuleName=$Name[0]; RequiredVersion=$RequiredVersion;}
            }
            else
            {
                $fullQualifiedName = @{ModuleName=$Name[0]; ModuleVersion=$MinimumVersion;}
            }

            Find-PSGetExtModule -FullyQualifiedName $fullQualifiedName `
                                -Verbose:$VerbosePreference `
                                -ErrorAction $ErrorActionPreference `
                                -WarningAction $WarningPreference `
                                -Debug:$DebugPreference
        }
        else
        {
            Find-PSGetExtModule -Name $Name `
                                -Verbose:$VerbosePreference `
                                -ErrorAction $ErrorActionPreference `
                                -WarningAction $WarningPreference `
                                -Debug:$DebugPreference
        }
    }
}

function Install-Module
{
    [CmdletBinding(DefaultParameterSetName='NameParameterSet',
                   SupportsShouldProcess=$true)]
    Param
    (
        [Parameter(Mandatory=$true, 
                   ValueFromPipelineByPropertyName=$true,
                   Position=0,
                   ParameterSetName='NameParameterSet')]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Name,

        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   Position=0,
                   ParameterSetName='PSGetItemInfo')]
        [ValidateNotNull()]
        [PSCustomObject[]]
        $PSGetItemInfo,

        [Parameter(ValueFromPipelineByPropertyName=$true,
                   ParameterSetName='NameParameterSet')]
        [Alias("Version")]
        [ValidateNotNull()]
        [Version]
        $MinimumVersion,
        
        [Parameter(ValueFromPipelineByPropertyName=$true,
                   ParameterSetName='NameParameterSet')]
        [ValidateNotNull()]
        [Version]
        $RequiredVersion,

        [Parameter()] 
        [ValidateSet("CurrentUser","AllUsers")]
        [string]
        $Scope = "AllUsers",

        [Parameter()]
        [switch]
        $Force
    )

    Begin
    {
        $ErrorActionPreference="continue"

        if ($Scope -eq "CurrentUser")
        {
            $destination = $script:MyDocumentsModulesPath
        }
        else
        {
            $destination = $script:programFilesModulesPath
        }
    }

    Process
    {
        # Throw an error when Install-Module is used on a non-admin console and '-Scope CurrentUser' is not specified
        if(-not (Test-RunningAsElevated) -and ($Scope -ne "CurrentUser"))
        {
            $message = $LocalizedData.InstallModuleNeedsCurrentUserScopeParameterForNonAdminUser -f @($script:programFilesModulesPath, $script:MyDocumentsModulesPath)

            ThrowError -ExceptionName "System.ArgumentException" `
                       -ExceptionMessage $message `
                       -ErrorId "InstallModuleNeedsCurrentUserScopeParameterForNonAdminUser" `
                       -ErrorCategory InvalidArgument
        }

        $message = $LocalizedData.ModuleDestination -f @($destination)        
        Write-Verbose -Message $message

        if($PSCmdlet.ParameterSetName -eq "NameParameterSet")
        {
            if($RequiredVersion -and $MinimumVersion)
            {
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage $LocalizedData.MinimumVersionAndRequiredVersionCannotBeSpecifiedTogether `
                           -ErrorId "MinimumVersionAndRequiredVersionCannotBeSpecifiedTogether" `
                           -ErrorCategory InvalidArgument
            }

            if(($RequiredVersion -or $MinimumVersion) -and (-not $Name -or $Name.Count -ne 1 -or (Test-WildcardPattern -Name $Name[0])))
            {
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage $LocalizedData.VersionParametersAreAllowedOnlyWithSingleModule `
                           -ErrorId "VersionParametersAreAllowedOnlyWithSingleModule" `
                           -ErrorCategory InvalidArgument
            }

            $null = $PSBoundParameters.Remove("Scope")
            $null = $PSBoundParameters.Remove("Force")
            $null = $PSBoundParameters.Remove("WhatIf")
            $null = $PSBoundParameters.Remove("Confirm")

            $items = Find-Module @PSBoundParameters

            if($items)
            {
                $PSGetItemInfo = $items
            }
        }

        foreach($psgItemInfo in $PSGetItemInfo)
        {
            # Test if module is already installed
            $InstalledModuleInfo = Test-ModuleInstalled -Name $psgItemInfo.Name
            if(-not $Force -and $InstalledModuleInfo)
            {
                if( ($RequiredVersion -and ($RequiredVersion -ne $InstalledModuleInfo.Version)) -or
                    ($MinimumVersion -and ($MinimumVersion -gt $InstalledModuleInfo.Version)))
                {
                    $message = $LocalizedData.ModuleAlreadyInstalled -f ($InstalledModuleInfo.Version, $InstalledModuleInfo.Name, $InstalledModuleInfo.ModuleBase, $InstalledModuleInfo.Version, $psgItemInfo.Version)
                    Write-Error -Message $message -ErrorId "ModuleAlreadyInstalled" -Category InvalidOperation
                }
                else
                {
                    $message = $LocalizedData.ModuleAlreadyInstalledVerbose -f ($InstalledModuleInfo.Version, $InstalledModuleInfo.Name, $InstalledModuleInfo.ModuleBase)
                    Write-Verbose -Message $message
                }
                continue
            }

            # create a temp folder and download the module
            $tempDestination = "$env:TEMP\$(Microsoft.PowerShell.Utility\Get-Random)"
            $null = Microsoft.PowerShell.Management\New-Item -Path $tempDestination -ItemType Directory -Force -Confirm:$false -WhatIf:$false

            try
            {
                $activity = $LocalizedData.DownloadingModuleFromGallery -f ($psgItemInfo.Name, $psgItemInfo.Version)

                $Success = Get-PSGetExtModule -PSGetItemInfo $psgItemInfo `
                                              -OutputDirectory $tempDestination `
                                              -Verbose:$VerbosePreference `
                                              -WarningAction $WarningPreference `
                                              -ErrorAction $ErrorActionPreference `
                                              -Debug:$DebugPreference

                if($Success)
                {
                    $sourceModulePath = Microsoft.PowerShell.Management\Join-Path $tempDestination $psgItemInfo.Name
                    $destinationModulePath = Microsoft.PowerShell.Management\Join-Path $destination $psgItemInfo.Name

                    # Validate the module
                    if(-not $Force -and -not (Test-ValidManifestModule -ModuleBasePath $sourceModulePath))
                    {                        
                        $message = $LocalizedData.InvalidPSModule -f ($psgItemInfo.Name)
                        Write-Error -Message $message -ErrorId "InvalidManifestModule" -Category InvalidOperation
                        continue
                    }

                    # check if module is in use
                    if($InstalledModuleInfo)
                    {
                       $moduleInUse = Test-ModuleInUse -ModuleBasePath $InstalledModuleInfo.ModuleBase `
                                                       -Verbose:$VerbosePreference `
                                                       -WarningAction $WarningPreference `
                                                       -ErrorAction $ErrorActionPreference `
                                                       -Debug:$DebugPreference
 
                       if($moduleInUse)
                       {
                           $message = $LocalizedData.ModuleIsInUse -f ($psgItemInfo.Name)
                           Write-Verbose -Message $message
                           continue
                       }
                    }

                    $shouldprocessmessage = $LocalizedData.InstallModulewhatIfMessage -f ($psgItemInfo.Version, $psgItemInfo.Name)
                    if($Force -or $PSCmdlet.ShouldProcess($shouldprocessmessage, "Install-Module"))
                    {
                        Copy-Module -SourcePath $sourceModulePath -DestinationPath $destinationModulePath -PSGetItemInfo $psgItemInfo

                        $message = $LocalizedData.ModuleInstalledSuccessfully -f ($psgItemInfo.Name)
                        Write-Verbose -Message $message
                    }
                }
                else
                {
                    $message = $LocalizedData.InvalidPSModule -f ($psgItemInfo.Name)
                    Write-Error -Message $message -ErrorId "InvalidManifestModule" -Category InvalidOperation
                }
            }
            finally
            {
                Microsoft.PowerShell.Management\Remove-Item $tempDestination -Force -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
            }
        }
    }
}

function Update-Module
{
    [CmdletBinding(SupportsShouldProcess=$true)]
    Param
    (
        [Parameter(ValueFromPipelineByPropertyName=$true, 
                   Position=0)]
        [ValidateNotNullOrEmpty()]
        [String[]]
        $Name, 

        [Parameter(ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNull()]
        [Version]
        $RequiredVersion,

        [Parameter()]
        [Switch]
        $Force
    )

    Process
    {
        $ErrorActionPreference="continue"
        $moduleBasesToUpdate = @()

        if($RequiredVersion -and (-not $Name -or $Name.Count -ne 1 -or (Test-WildcardPattern -Name $Name[0])))
        {
            ThrowError -ExceptionName "System.ArgumentException" `
                       -ExceptionMessage $LocalizedData.RequiredVersionAllowedOnlyWithSingleModuleName `
                       -ErrorId "RequiredVersionAllowedOnlyWithSingleModuleName" `
                       -ErrorCategory InvalidArgument
        }

        if($Name)
        {
            foreach($moduleName in $Name)
            {
                $availableModules = Get-Module -ListAvailable $moduleName -Verbose:$false
        
                if(-not $availableModules -and -not (Test-WildcardPattern -Name $moduleName))
                {                    
                    $message = $LocalizedData.ModuleNotInstalledOnThiseMachine -f ($moduleName)
                    Write-Error -Message $message -ErrorId "ModuleNotInstalledOnThisMachine" -Category InvalidOperation
                    continue
                }

                foreach($mod in $availableModules)
                {
                    # Check if this module got installed with PSGet and user has required permissions
                    $PSGetItemInfoPath = Microsoft.PowerShell.Management\Join-Path $mod.ModuleBase $script:PSGetItemInfoFileName
                    if (Microsoft.PowerShell.Management\Test-path $PSGetItemInfoPath)
                    {
                        if(-not (Test-RunningAsElevated) -and $mod.ModuleBase.StartsWith($script:programFilesModulesPath, [System.StringComparison]::OrdinalIgnoreCase))
                        {                            
                            if(-not (Test-WildcardPattern -Name $moduleName))
                            {
                                $message = $LocalizedData.AdminPrivilegesRequiredForUpdate -f ($mod.Name, $mod.ModuleBase)
                                Write-Error -Message $message -ErrorId "AdminPrivilegesAreRequiredForUpdate" -Category InvalidOperation
                            }
                            continue
                        }

                        $moduleBasesToUpdate += $mod.ModuleBase
                    }
                    else
                    {
                        if(-not (Test-WildcardPattern -Name $moduleName))
                        {
                            $message = $LocalizedData.ModuleNotInstalledUsingPowerShellGet -f ($mod.Name)
                            Write-Error -Message $message -ErrorId "ModuleNotInstalledUsingInstallModuleCmdlet" -Category InvalidOperation
                        }
                        continue
                    }
                }
            }
        }
        else
        {            
            $modulePaths = @()
            $modulePaths += $script:MyDocumentsModulesPath

            if((Test-RunningAsElevated))
            {
                $modulePaths += $script:programFilesModulesPath
            }
        
            foreach ($location in $modulePaths)
            {
                # find all modules installed using PSGet
                $moduleBases = Microsoft.PowerShell.Management\Get-ChildItem $location -Recurse `
                                                                             -Attributes Hidden -Filter $script:PSGetItemInfoFileName `
                                                                             -ErrorAction SilentlyContinue `
                                                                             -WarningAction SilentlyContinue `
                                                                             | Microsoft.PowerShell.Core\Foreach-Object { $_.Directory }
                foreach ($moduleBase in $moduleBases)
                {
                    $PSGetItemInfoPath = Microsoft.PowerShell.Management\Join-Path $moduleBase.FullName $script:PSGetItemInfoFileName

                    # Check if this module got installed using PSGet, read its contents and compare with current version
                    if (Microsoft.PowerShell.Management\Test-Path $PSGetItemInfoPath)
                    {
                        $moduleBasesToUpdate += $moduleBase
                    }
                }
            }
        }

        foreach($moduleBase in $moduleBasesToUpdate)
        {
            $PSGetItemInfoPath = Microsoft.PowerShell.Management\Join-Path $moduleBase $script:PSGetItemInfoFileName

            $psgetItemInfo = Microsoft.PowerShell.Utility\Import-Clixml -Path $PSGetItemInfoPath
                        
            $message = $LocalizedData.CheckingForModuleUpdate -f ($psgetItemInfo.Name)
            Write-Verbose -Message $message

            if($RequiredVersion)
            {
                $newPsgetItemInfo = Find-Module -Name $psgetItemInfo.Name `
                                                -RequiredVersion $RequiredVersion `
                                                -Verbose:$VerbosePreference `
                                                -WarningAction $WarningPreference `
                                                -ErrorAction $ErrorActionPreference `
                                                -Debug:$DebugPreference
            }
            else
            {
                $newPsgetItemInfo = Find-Module -Name $psgetItemInfo.Name `
                                                -Verbose:$VerbosePreference `
                                                -WarningAction $WarningPreference `
                                                -ErrorAction $ErrorActionPreference `
                                                -Debug:$DebugPreference
            }

            if(-not $newPsgetItemInfo)
            {                
                $message = $LocalizedData.NoUpdateAvailable -f ($psgetItemInfo.Name)
                Write-Verbose -Message $message
                continue
            }

            if($Force -or $psgetItemInfo.Version -lt $newPsgetItemInfo.Version)
            {
                $message = $LocalizedData.FoundModuleUpdate -f ($newPsgetItemInfo.Name, $newPsgetItemInfo.Version)
                Write-Verbose -Message $message                
                
                # create a temp folder and download it
                $tempDestination = "$env:TEMP\$(Microsoft.PowerShell.Utility\Get-Random)"
                $null = Microsoft.PowerShell.Management\New-Item -Path $tempDestination -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false

                try
                {
                    $Success = Get-PSGetExtModule -PSGetItemInfo $newPsgetItemInfo `
                                                  -OutputDirectory $tempDestination `
                                                  -Verbose:$VerbosePreference `
                                                  -WarningAction $WarningPreference `
                                                  -ErrorAction $ErrorActionPreference `
                                                  -Debug:$DebugPreference

                    if($Success)
                    {
                        $sourceModulePath = Microsoft.PowerShell.Management\Join-Path $tempDestination $newPsgetItemInfo.Name
                        $destinationModulePath = $moduleBase

                        # Validate the module
                        if(-not (Test-ValidManifestModule -ModuleBasePath $sourceModulePath))
                        {
                            $message = $LocalizedData.InvalidPSModuleDuringUpdate -f ($psgetItemInfo.Name)
                            Write-Verbose -Message $message
                            continue
                        }

                        # check if module is in use
                        $moduleInUse = Test-ModuleInUse -ModuleBasePath $destinationModulePath `
                                                        -Verbose:$VerbosePreference `
                                                        -WarningAction $WarningPreference `
                                                        -ErrorAction $ErrorActionPreference `
                                                        -Debug:$DebugPreference
                        if($moduleInUse)
                        {
                            $message = $LocalizedData.ModuleIsInUse -f ($psgetItemInfo.Name)
                            Write-Verbose -Message $message

                            continue
                        }

                        $shouldprocessmessage = $LocalizedData.UpdateModulewhatIfMessage -f ($psgetItemInfo.Version, $psgetItemInfo.Name, $newPsgetItemInfo.Version)

                        if($Force -or $PSCmdlet.ShouldProcess($shouldprocessmessage, "Update-Module"))
                        {
                            Copy-Module -SourcePath $sourceModulePath -DestinationPath $destinationModulePath -PSGetItemInfo $newPsgetItemInfo

                            $message = $LocalizedData.ModuleGotUpdated -f ($psgetItemInfo.Name)
                            Write-Verbose -Message $message
                        }
                    }
                    else
                    {
                        $message = $LocalizedData.InvalidPSModuleDuringUpdate -f ($psgetItemInfo.Name)
                        Write-Verbose -Message $message
                    }
                }
                finally
                {
                    Microsoft.PowerShell.Management\Remove-Item $tempDestination -Force -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
                }
            }
            else
            {
                $message = $LocalizedData.NoUpdateAvailable -f ($psgetItemInfo.Name)
                Write-Verbose -Message $message
            }
        }
    }
}

function Publish-Module
{
    [CmdletBinding(SupportsShouldProcess=$true,
                   PositionalBinding=$false,
                   DefaultParameterSetName="ModuleNameParameterSet")]
    Param
    (
        [Parameter(Mandatory=$true, 
                   ParameterSetName="ModuleNameParameterSet",
                   ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name,

        [Parameter(Mandatory=$true, 
                   ParameterSetName="ModulePathParameterSet",
                   ValueFromPipelineByPropertyName=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Path,

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $NuGetApiKey,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $ReleaseNotes,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string[]]
        $Tag,
        
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $LicenseUri,

        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $IconUri,
        
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]
        $ProjectUri
    )

    Begin
    {
        $ErrorActionPreference="continue"

        if($LicenseUri -and -not (Test-WebUri -address $LicenseUri))
        {
            $message = $LocalizedData.InvalidWebUri -f ($LicenseUri, "LicenseUri")
            ThrowError -ExceptionName "System.ArgumentException" `
                        -ExceptionMessage $message `
                        -ErrorId "InvalidWebUri" `
                        -ErrorCategory InvalidArgument
        }

        if($IconUri -and -not (Test-WebUri -address $IconUri))
        {
            $message = $LocalizedData.InvalidWebUri -f ($IconUri, "IconUri")
            ThrowError -ExceptionName "System.ArgumentException" `
                        -ExceptionMessage $message `
                        -ErrorId "InvalidWebUri" `
                        -ErrorCategory InvalidArgument
        }

        if($ProjectUri -and -not (Test-WebUri -address $ProjectUri))
        {
            $message = $LocalizedData.InvalidWebUri -f ($ProjectUri, "ProjectUri")
            ThrowError -ExceptionName "System.ArgumentException" `
                        -ExceptionMessage $message `
                        -ErrorId "InvalidWebUri" `
                        -ErrorCategory InvalidArgument
        }
    }

    Process
    {
        if($Name)
        {
            $module = Get-Module -ListAvailable -Name $Name -Verbose:$false

            if(-not $module)
            {
                $message = $LocalizedData.ModuleNotAvailableLocally -f ($Name)
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage $message `
                           -ErrorId "ModuleNotAvailableLocallyToPublish" `
                           -ErrorCategory InvalidArgument

            }
            elseif($module.GetType().ToString() -ne "System.Management.Automation.PSModuleInfo")
            {
                $message = $LocalizedData.AmbiguousModuleName -f ($Name)
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage $message `
                           -ErrorId "AmbiguousModuleNameToPublish" `
                           -ErrorCategory InvalidArgument
            }

            $Path = $module.ModuleBase
        }
        else
        {
            if(-not (Microsoft.PowerShell.Management\Test-Path -path $Path -PathType Container))
            {                
                ThrowError -ExceptionName "System.ArgumentException" `
                           -ExceptionMessage ($LocalizedData.PathIsNotADirectory -f ($Path)) `
                           -ErrorId "PathIsNotADirectory" `
                           -ErrorCategory InvalidArgument
            }
        }

        $moduleName = Microsoft.PowerShell.Management\Split-Path $Path -Leaf

        $message = $LocalizedData.PublishModuleLocation -f ($moduleName, $Path)
        Write-Verbose -Message $message

        # Copy the source module to temp location to publish
        $tempModulePath = "$env:TEMP\$(Microsoft.PowerShell.Utility\Get-Random)"
        $null = Microsoft.PowerShell.Management\New-Item -Path $tempModulePath -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
        Microsoft.PowerShell.Management\Copy-Item -Path $Path -Destination $tempModulePath -Force -Recurse -Confirm:$false -WhatIf:$false

        try
        {
            $manifestPath = Microsoft.PowerShell.Management\Join-Path (Microsoft.PowerShell.Management\Join-Path $tempModulePath $moduleName) "$moduleName.psd1"
        
            if(-not (Microsoft.PowerShell.Management\Test-Path $manifestPath))
            {
                $message = $LocalizedData.InvalidModuleToPublish -f ($moduleName)
                ThrowError -ExceptionName "System.InvalidOperationException" `
                           -ExceptionMessage $message `
                           -ErrorId "InvalidModuleToPublish" `
                           -ErrorCategory InvalidOperation
            }

            $moduleInfo = Microsoft.PowerShell.Core\Test-ModuleManifest -Path $manifestPath `
                                              -ErrorAction SilentlyContinue `
                                              -WarningAction SilentlyContinue `
                                              -Verbose:$VerbosePreference

            if(-not $moduleInfo -or 
               -not $moduleInfo.Author -or 
               -not $moduleInfo.Description)
            {
                $message = $LocalizedData.MissingRequiredManifestKeys -f ($moduleName)
                ThrowError -ExceptionName "System.InvalidOperationException" `
                           -ExceptionMessage $message `
                           -ErrorId "MissingRequiredModuleManifestKeys" `
                           -ErrorCategory InvalidOperation
            }

            $currentPSGetItemInfo = Find-PSGetExtModule -Name $moduleInfo.Name `
                                                        -Verbose:$VerbosePreference `
                                                        -ErrorAction "SilentlyContinue" `
                                                        -WarningAction $WarningPreference `
                                                        -Debug:$DebugPreference

            if($currentPSGetItemInfo -and $currentPSGetItemInfo.Version -ge $moduleInfo.Version)
            {
                $message = $LocalizedData.ModuleVersionShouldBeGreaterThanGalleryVersion -f ($moduleInfo.Name, $moduleInfo.Version, $currentPSGetItemInfo.Version)
                ThrowError -ExceptionName "System.InvalidOperationException" `
                           -ExceptionMessage $message `
                           -ErrorId "ModuleVersionShouldBeGreaterThanGalleryVersion" `
                           -ErrorCategory InvalidOperation
            }

            $shouldProcessMessage = $LocalizedData.PublishModulewhatIfMessage -f ($moduleInfo.Version, $moduleInfo.Name)
            if($PSCmdlet.ShouldProcess($shouldProcessMessage, "Publish-Module"))
            {
                Publish-PSGetExtModule -PSModuleInfo $moduleInfo `
                                       -NugetApiKey $NuGetApiKey `
                                       -ReleaseNotes $ReleaseNotes `
                                       -Tag $Tag `
                                       -LicenseUri $LicenseUri `
                                       -IconUri $IconUri `
                                       -ProjectUri $ProjectUri `
                                       -Verbose:$VerbosePreference `
                                       -WarningAction $WarningPreference `
                                       -ErrorAction $ErrorActionPreference `
                                       -Debug:$DebugPreference
            }
        }
        finally
        {
            Microsoft.PowerShell.Management\Remove-Item $tempModulePath -Force -Recurse -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
        }
    }
}

function Test-ModuleInstalled
{
    [CmdletBinding(PositionalBinding=$false)]
    [OutputType("PSModuleInfo")]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $Name
    )

    # Check if module is already installed
    $availableModule = Get-Module -ListAvailable $Name -Verbose:$false
    if($availableModule)
    {
        return $availableModule
    }
    else
    {
        return $null
    }
}

function Copy-Module
{
    [CmdletBinding(PositionalBinding=$false)]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $SourcePath,

        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $DestinationPath,

        [Parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [PSCustomObject]
        $PSGetItemInfo
    )
    
    if(Microsoft.PowerShell.Management\Test-Path $DestinationPath)
    {
        Microsoft.PowerShell.Management\Remove-Item -Path $DestinationPath -Recurse -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
    }

    # Copy the module to destination
    $null = Microsoft.PowerShell.Management\New-Item -Path $DestinationPath -ItemType Directory -Force -ErrorAction SilentlyContinue -WarningAction SilentlyContinue -Confirm:$false -WhatIf:$false
    Microsoft.PowerShell.Management\Copy-Item -Path "$SourcePath\*" -Destination $DestinationPath -Force -Recurse -Confirm:$false -WhatIf:$false
                    
    # Create _PSGetItemInfo.xml
    $psgetItemInfopath = Microsoft.PowerShell.Management\Join-Path $DestinationPath $script:PSGetItemInfoFileName
    Microsoft.PowerShell.Utility\Export-Clixml -InputObject $PSGetItemInfo -Path $psgetItemInfopath -Force -Confirm:$false -WhatIf:$false
    [System.IO.File]::SetAttributes($psgetItemInfopath, [System.IO.FileAttributes]::Hidden)
}

function Test-ModuleInUse
{
    [CmdletBinding()]
    [OutputType([bool])]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $ModuleBasePath
    )
    $dllsInModule = Get-ChildItem -Path $ModuleBasePath `
                                  -Filter *.dll `
                                  -Recurse `
                                  -ErrorAction SilentlyContinue `
                                  -WarningAction SilentlyContinue | Microsoft.PowerShell.Core\Foreach-Object{$_.FullName}
    if($dllsInModule)
    {
        $currentProcesses = Get-Process
        $processesDlls = $currentProcesses | Microsoft.PowerShell.Core\Foreach-Object{$_.Modules} | Sort-Object -Unique
        
        $moduleDllsInUse = $processesDlls | Where-Object {$_ -and ($dllsInModule -contains $_.FileName)}
    
        if($moduleDllsInUse)
        {
            $processes = $moduleDllsInUse | Microsoft.PowerShell.Core\Foreach-Object{$dllName = $_.ModuleName; $currentProcesses | Where-Object {$_ -and $_.Modules -and $_.Modules.ModuleName -eq $dllName} }
        
            if($processes)
            {
                $moduleName = Microsoft.PowerShell.Management\Split-Path $ModuleBasePath -Leaf

                $message = $LocalizedData.ModuleInUseWithProcessDetails -f ($moduleName, $($processes | Microsoft.PowerShell.Core\Foreach-Object{"$($_.ProcessName):$($_.Id) "}))
                Write-Error -Message $message -ErrorId "ModuleToBeUpdatedIsInUse" -Category InvalidOperation

                return $true
            }
        }
    }

    return $false
}

function Test-ValidManifestModule
{
    [CmdletBinding()]
    [OutputType([bool])]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $ModuleBasePath
    )

    $moduleName = Microsoft.PowerShell.Management\Split-Path $ModuleBasePath -Leaf
    $manifestPath = Microsoft.PowerShell.Management\Join-Path $ModuleBasePath "$moduleName.psd1"
        
    if((Microsoft.PowerShell.Management\Test-Path $manifestPath) -and 
       (Microsoft.PowerShell.Core\Test-ModuleManifest -Path $manifestPath -ErrorAction SilentlyContinue -WarningAction SilentlyContinue))
    {
        return $true
    }

    return $false
}

function Test-WebUri
{
    [CmdletBinding()]
    [OutputType([bool])]
    Param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $address
    )

    $uri = $address -as [System.URI]
    return ($uri -ne $null) -and ($uri.AbsoluteURI -ne $null) -and ($uri.Scheme -match '[http|https]')
}

Set-Alias -Name fimo -Value Find-Module
Set-Alias -Name inmo -Value Install-Module
Set-Alias -Name upmo -Value Update-Module
Set-Alias -Name pumo -Value Publish-Module
Export-ModuleMember -Function Find-Module, Install-Module, Update-Module, Publish-Module -Alias fimo, inmo, upmo, pumo -Variable PSGallerySourceUri,PSGalleryPublishUri