function Load-SharePoint-Powershell
{
 	If ((Get-PsSnapin |?{$_.Name -eq "Microsoft.SharePoint.PowerShell"})-eq $null)
	{
       	Write-Host "Loading SharePoint PowerShell Snap-in ..." -NoNewline;
   		$PSSnapin = Add-PsSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
        Write-Host -f Green " success";
	}
}

function Get-InstalledVersion
{
    $version = $null;
    try
    {
        $assembly = [System.Reflection.Assembly]::Load("SharePointProducts.CopyMove, Version=6.0.0.0, Culture=neutral, PublicKeyToken=9e95445247029289");
        $assemblyVersionInfo = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($assembly.Location);
        $version = New-Object System.Version($assemblyVersionInfo.FileVersion);
    }
    catch [System.Exception]
    {
    }
    return $version
}

function Get-AdminWebApplicationUrls
{
	$websvc = [Microsoft.SharePoint.Administration.SPWebService]::AdministrationService
	$adminurls = @()
	foreach ($webapp in $websvc.WebApplications)
	{
		$adminurls = $adminurls + $webapp.GetResponseUri(([Microsoft.SharePoint.Administration.SPUrlZone]::Default)).ToString()
	}
	return $adminurls
}

function Install-PowerShellModule([string] $psRepoPath) {
    Write-Host "Installing CopyMove PowerShell module ..." -NoNewline;
    $psRepoParameters = @{
        Name               = "CopyMoveRepo"
        SourceLocation     = $psRepoPath
        PublishLocation    = $psRepoPath
        InstallationPolicy = "Trusted"
    }
    Register-PSRepository @psRepoParameters;
    Install-Module "SharePointProducts.CopyMove.PowerShell" -Repository "CopyMoveRepo";
    Unregister-PSRepository "CopyMoveRepo";
    Write-Host -f Green " success";
}

function Uninstall-PowerShellModule {
	Write-Host "Uninstalling CopyMove PowerShell module ..." -NoNewline;
    Uninstall-Module "SharePointProducts.CopyMove.PowerShell" -Force;
    Write-Host -f Green " success";
}

function Add-FarmSolution([string] $SolutionPath) {
    $solutionNameIndex = $SolutionPath.LastIndexOf('\');
    $solutionName = $SolutionPath.Substring($solutionNameIndex+1);

    Write-Host "Adding farm solution $solutionName ..." -NoNewline;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
        Write-Host -f Yellow " skipped - solution already added";
	} else
	{
		$solution = Add-SPSolution -LiteralPath $solutionPath | Out-Null;
        Write-Host -f Green " success";
	}
}

function Remove-FarmSolution([string] $SolutionName) {
    Write-Host "Removing farm solution $solutionName ..." -NoNewline;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
		Remove-SPSolution -Identity $solutionName -Confirm:$false;
        Write-Host -f Green " success";
	} else
	{
        Write-Host -f Yellow " skipped - solution not found";
	}
}

function Install-FarmSolution([string] $solutionName) {
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
	    foreach ($adminurl in Get-AdminWebApplicationUrls)
	    {
		    Write-Host "Deploying $solutionName to $adminurl ..." -NoNewLine;
		    Install-SPSolution -Identity $solutionName -WebApplication $adminurl -GACDeployment -Force;
		    WaitForTimer($solutionName);
	    }

	    Write-Host "Deploying $solutionName to all content Web applications ..." -NoNewLine;
	    Install-SPSolution -Identity $solutionName -AllWebApplications -GACDeployment -Force;
	    WaitForTimer($solutionName);
    } else
    {
	    Write-Host "Deploying $solutionName to all Web applications ..." -NoNewLine;
        Write-Host -f Yellow " skipped - solution not found";
    }
}

function Install-FarmSolutionGlobal([string] $solutionName) {
	Write-Host "Deploying $solutionName globally ..." -NoNewLine;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
	    Install-SPSolution -Identity $solutionName -GACDeployment -Force;
	    WaitForTimer($solutionName);
    } else
    {
        Write-Host -f Yellow " skipped - solution not found";
    }
}

function Upgrade-FarmSolution([string] $solutionName, [string] $solutionPath) {
	Write-Host "Upgrading $solutionName ..." -NoNewLine;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
	    Update-SPSolution -Identity $solutionName -LiteralPath $solutionPath -GACDeployment;
	    WaitForTimer($solutionName);
    } else
    {
        Write-Host -f Yellow " skipped - solution not found";
    }
}

function Uninstall-FarmSolution([string] $solutionName) {
	Write-Host "Retracting $solutionName from all content Web applications ..." -NoNewLine;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
	    Uninstall-SPSolution -Identity $solutionName -AllWebApplications -Confirm:$false;
	    WaitForTimer($solutionName);

	    foreach ($adminurl in Get-AdminWebApplicationUrls)
	    {
		    Write-Host "Retracting $solutionName from $adminurl ..." -NoNewLine;
		    Uninstall-SPSolution -Identity $solutionName -WebApplication $adminurl -Confirm:$false;
		    WaitForTimer($solutionName);
	    }
    } else
    {
        Write-Host -f Yellow " skipped - solution not found";
    }
}

function Uninstall-FarmSolutionGlobal([string] $solutionName) {
	Write-Host "Retracting $solutionName ..." -NoNewLine;
    $solution = Get-FarmSolution $solutionName;
	if ($solution -ne $null)
	{
	    Uninstall-SPSolution -Identity $solutionName -Confirm:$false;
	    WaitForTimer($solutionName);
    } else
    {
        Write-Host -f Yellow " skipped - solution not found";
    }
}

function Get-FarmSolution([string] $solutionName) {
    $solution = Get-SPSolution | Where {
        ($solutionName -eq $_.Name)
    }
    return $solution;
}

function Restart-SharePointServices
{
    Write-Host "About to reset the following services on all servers:";
    Write-Host "- IIS (W3SVC)";
    Write-Host "- Admin Service (SPAdminV4)";
    Write-Host "- Timer Service (SPTimerV4)";
    Write-Host "- Search Service (OSearch15)";
    Write-Host "Press <ESC> to skip reset or press <ENTER> to reset before un-install. A reset will ensure that no services have taken a lock on the CopyMove binaries which can prevent SharePoint from properly retracting the CopyMove WSP solution packages.";

    $waitCount = 0;
    $resetServices = $false;
    while ($waitCount -lt 15) {
        if ($Host.UI.RawUI.KeyAvailable)
        {
            $key = $Host.UI.RawUI.ReadKey("IncludeKeyUp,NoEcho").Character;
            if ([convert]::ToChar(27) -eq $key) {
                break;
            } elseif ([convert]::ToChar(13) -eq $key)
            {
                $resetServices = $true;
                break;
            }
        }

        Start-Sleep 1;
        $waitCount++;
    }

    if ($resetServices -eq $true)
    {
        $ServiceNames=@("SPTimerV4","SPAdminV4","W3SVC", "OSearch15")
        Restart-SharePointServicesOnAllServers $ServiceNames;
    }
}

function Restart-SharePointServicesOnLocalServer([Array] $ServiceNames)
{
    foreach($serviceName in $ServiceNames)
    {
        $serviceInstance = Get-Service -Name $serviceName -ErrorAction SilentlyContinue;
        if(($serviceInstance -ne $null) -and ($serviceInstance.Status -eq "Running"))
        {
            Write-Host "Restarting service $serviceName ..." -NoNewline;
            try
            {
                Restart-Service -InputObject $serviceInstance -Force -ea Stop;
                Write-Host " success" -f Green;
            }
            catch
            {
                Write-Host " error" -f Red;
                Write-Host $error[0] -f Red;
            }
        }
    }
}

function Restart-SharePointServicesOnAllServers([Array] $ServiceNames)
{
    Write-Host "Querying servers in the SharePoint Farm";
    $farm = Get-SPFarm;
    $servers = $farm.Servers;
    Write-Host "Found" $servers.Count "servers in Farm (including database servers)";
    foreach($server in $servers)
    {
        $serverName = $server.Name;
        if($server.Role -eq [Microsoft.SharePoint.Administration.SPServerRole]::Invalid)
        {
            continue;
        }

        foreach($serviceName in $ServiceNames)
        {
    		Start-Sleep 10;

            $serviceInstance = Get-Service -ComputerName $server.Name -Name $serviceName -ErrorAction SilentlyContinue;
            if(($serviceInstance -ne $null) -and ($serviceInstance.Status -eq "Running"))
            {
                Write-Host "Restarting service $serverName::$serviceName ..." -NoNewline;
                try
                {
                    Restart-Service -InputObject $serviceInstance -Force -ea Stop;
                    Write-Host -f Green " success";
                }
                catch
                {
                    Write-Host -f Red " error";
                    Write-Host -f Red $error[0];
                }
            }
        }
    }
}

$scriptCreateEventSource =
{
    param([string] $source)

    $serverName = $env:COMPUTERNAME;
    Write-Host "Creating Windows Event Log source on [$serverName] ..." -NoNewline;
    if ([System.Diagnostics.EventLog]::SourceExists($source) -eq $false)
    {
        [System.Diagnostics.EventLog]::CreateEventSource($source, "Application");
        Write-Host -f Green " success";
    } else
    {
        Write-Host -f Yellow " skipped - event source exists";
    }
}

function Create-EventSource([string] $source)
{
    $farm = Get-SPFarm;
    $servers = $farm.Servers;
    foreach($server in $servers)
    {
        $serverName = $server.Name;
        if($server.Role -eq [Microsoft.SharePoint.Administration.SPServerRole]::Invalid)
        {
            continue;
        }
	    Invoke-Command -ComputerName $serverName -ScriptBlock $scriptCreateEventSource -ArgumentList $source;
    }
}

function WaitForTimer([string] $solutionName)
{
	do
	{
		Start-Sleep 5;
		$retryCount++
		Write-Host "." -NoNewLine;
		if ($retryCount -gt 60)
		{
            Write-Host -f Red " error - timeout";
			return;
		}
	} while ((Get-SPSolution $solutionName).JobExists)

	$solution = (Get-SPSolution $solutionName)
	if ($solution.LastOperationResult -gt 2)
	{
        Write-Host -f Red " error";
		Write-Error $solution.LastOperationDetails;
	} else
    {
        Write-Host -f Green " success";
    }
}

function Pause
{
	Write-Host "Press any key to continue ..."
	$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
}