diff --git a/Add-AzureLocalResource.ps1 b/Add-AzureLocalResource.ps1 new file mode 100644 index 0000000..3e379a1 --- /dev/null +++ b/Add-AzureLocalResource.ps1 @@ -0,0 +1,116 @@ +function Add-AzureLocalResource +{ + <# + .Synopsis + Adds an Azure local storage resource to a service definition + .Description + Adds an Azure local storage resource to a service definition. + + Azure local storage can create well-known directories on the host machine + .Example + New-AzureServiceDefinition -ServiceName "foo" | + Add-AzureLocalResource -ServiceDefinition + .Link + New-AzureServiceDefinition + #> + [OutputType([xml],[string])] + param( + # The ServiceDefinition XML. This should be created with New-AzureServiceDefinition or retreived with Import-AzureServiceDefinition + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # If set, the local resource will only apply to the role named ToRole. If ToRole is not found, or doesn't + # exist, the last role will be used. + [string] + $ToRole, + + # The name of the local storage. This will be the path of the name storage element, relative to the root drive. + [Parameter(Mandatory=$true)] + [string] + $Name, + + # The size of the storage. Sizes will be rounded up to the nearest megabyte. + [Long] + $Size = 1mb, + + # If set, a role will not be cleaned on recycle + [switch] + $DoNotcleanOnRoleRecycle, + + # If set, will output results as string rather than XML + [switch] + $AsString + ) + + process { + + #region Resolve the role if it set, create the role if it doesn't exist, and track it if they assume the last item. + $roles = @($ServiceDefinition.ServiceDefinition.WebRole), @($ServiceDefinition.ServiceDefinition.WorkerRole) + @($ServiceDefinition.ServiceDefinition.VirtualMachineRole) + $xmlNamespace = @{'ServiceDefinition'='http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition'} + $selectXmlParams = @{ + XPath = '//ServiceDefinition:WebRole|//ServiceDefinition:WorkerRole|//ServiceDefinition:VirtualMachineRole' + Namespace = $xmlNamespace + } + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + if (-not $roles) { + $ServiceDefinition = $ServiceDefinition | + Add-AzureRole -RoleName "WebRole1" + + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + } + #endregion Resolve the role if it set, create the role if it doesn't exist, and track it if they assume the last item. + + if ($roles.Count -gt 1) { + if ($ToRole) { + } else { + $role = $roles[-1] + } + } else { + if ($ToRole) { + if ($roles[0].Name -eq $ToRole) { + $role = $roles[0] + } else { + $role = $null + } + } else { + $role = $roles[0] + } + } + + if (-not $role) { return } + + $realSize = [Math]::Ceiling($size / 1mb) + + if (-not $role.LocalResources) { + $role.InnerXml += "" + } + + $localResourcesNode = Select-Xml -Xml $role -Namespace $xmlNamespace -XPath '//ServiceDefinition:LocalResources' | + Select-Object -ExpandProperty Node + + $localResourcesNode.InnerXml += "" + + } + + end { + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $serviceDefinition.Save($strWrite) + return "$strWrite" + } else { + $serviceDefinition + } + } +} diff --git a/Add-AzureRole.ps1 b/Add-AzureRole.ps1 new file mode 100644 index 0000000..46dab35 --- /dev/null +++ b/Add-AzureRole.ps1 @@ -0,0 +1,108 @@ +function Add-AzureRole +{ + <# + .Synopsis + Adds and azure role to a service definition + .Description + Adds an azure role to a service definition + .Example + New-AzureServiceDefinition -ServiceName AService | + Add-AzureRole -RoleName MyWebRole -VMSize Large -RoleType Web -AsString + .Link + New-AzureServiceDefinition + #> + [OutputType([xml],[string])] + param( + # The Service Definition + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # The name of the role + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true)] + [string] + $RoleName, + + # The VMSize + [ValidateSet('ExtraSmall','Small','Medium', 'Large', 'Extra-Large', 'XS', 'XL', 'S', 'M', 'L')] + [string] + $VMSize, + + # If set, will disable native code execution on the role. This will prevent PHP or other CGI from working + [Switch] + $DisableNativeCodeExecution, + + # If set, will output as a string + [switch] + $AsString, + + # The type of the role. + [ValidateSet('Web','Worker','VirtualMachine', 'VM')] + [string] + $RoleType = 'Web' + ) + + process { + #region Correct Parameters + $enableNativeCodeExecution = (-not $DisableNativeCodeExecution).ToString().ToLower() + $vmSize = if ('XS' -eq $VmSize) { + "ExtraSmall" + } elseif ('XL' -eq $VmSize) { + "ExtraLarge" + } elseif ('M' -eq $VmSize) { + "Medium" + } elseif ('S' -eq $VmSize) { + "Small" + } elseif ('L' -eq $VmSize) { + "Large" + } elseif ($vmSize) { + $vmSize + } else { + $null + } + + if ($vmSize) { + # Force every instance of a subword into camel case + foreach ($subWord in 'Extra','Small', 'Medium', 'Large') { + $vmSize= $vmSize -ireplace $subWord, $subWord + } + } + #endregion Correct Parameters + + $roleElement = if ($roleType -eq 'Web') { + "WebRole" + } elseif ($roleType -eq 'Worker') { + "WorkerRole" + } elseif ('VirtualMachine', 'VM' -contains $roleType) { + "VirtualMachineRole" + } + + if ($vmSize) { + @($serviceDefinition.ChildNodes)[-1].InnerXml += "<$roleElement name='$RoleName' vmsize='$VMSize' enableNativeCodeExecution='$enableNativeCodeExecution' />" + } else { + @($serviceDefinition.ChildNodes)[-1].InnerXml += "<$roleElement name='$RoleName' enableNativeCodeExecution='$enableNativeCodeExecution' />" + } + + + } + + end { + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $serviceDefinition.Save($strWrite) + return "$strWrite" + } else { + $serviceDefinition + } + } +} diff --git a/Add-AzureSetting.ps1 b/Add-AzureSetting.ps1 new file mode 100644 index 0000000..51a6b7f --- /dev/null +++ b/Add-AzureSetting.ps1 @@ -0,0 +1,122 @@ +function Add-AzureSetting +{ + <# + .Synopsis + Adds an Azure local storage resource to a service definition + .Description + Adds an Azure local storage resource to a service definition. + + Azure local storage can create well-known directories on the host machine + .Link + New-AzureServiceDefinition + .Example + New-AzureServiceDefinition -ServiceName MyService | + Add-AzureSetting -Name MySetting -Value MyValue -AsString + #> + [CmdletBinding(DefaultParameterSetName='NameAndValue')] + [OutputType([xml],[string])] + param( + # The ServiceDefinition XML. This should be created with New-AzureServiceDefinition or retreived with Import-AzureServiceDefinition + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # The name of the setting to configure + [Parameter(Mandatory=$true, ParameterSetName='NameAndValue')] + [string] + $Name, + + # The value to us for the setting + [Parameter(Mandatory=$true, ParameterSetName='NameAndValue')] + [string] + $Value, + + # A table of names and values for Azure settings + [Parameter(Mandatory=$true, ParameterSetName='SettingTable')] + [Hashtable] + $Setting, + + # If set, will output results as string rather than XML + [switch] + $AsString + ) + + process { + if ($psCmdlet.ParameterSetName -eq 'NameAndValue') { + # Resolve the role if it set, create the role if it doesn't exist, and track it if they assume the last item. + $roles = @($ServiceDefinition.ServiceDefinition.WebRole), + @($ServiceDefinition.ServiceDefinition.WorkerRole) + + @($ServiceDefinition.ServiceDefinition.VirtualMachineRole) + $xmlNamespace = @{'ServiceDefinition'='http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition'} + $selectXmlParams = @{ + XPath = '//ServiceDefinition:WebRole|//ServiceDefinition:WorkerRole|//ServiceDefinition:VirtualMachineRole' + Namespace = $xmlNamespace + } + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + if (-not $roles) { + $ServiceDefinition = $ServiceDefinition | + Add-AzureRole -RoleName "WebRole1" + + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + } + + if ($roles.Count -gt 1) { + if ($ToRole) { + } else { + $role = $roles[-1] + } + } else { + if ($ToRole) { + if ($roles[0].Name -eq $ToRole) { + $role = $roles[0] + } else { + $role = $null + } + } else { + $role = $roles[0] + } + } + + if (-not $role) { return } + + if (-not $role.ConfigurationSettings) { + $role.InnerXml += "" + } + $ConfigurationSettingsNode = + $(Select-Xml -Xml $role -Namespace $xmlNamespace -XPath '//ServiceDefinition:ConfigurationSettings' | + Select-Object -ExpandProperty Node -First 1) + + $ConfigurationSettingsNode.InnerXml += "" + } elseif ($psCmdlet.ParameterSetName -eq 'SettingTable') { + $null = $psboundParameters.Remove('asString') + $null = $psboundParameters.Remove('setting') + foreach ($kv in $setting.GetEnumerator()) { + $psboundParameters.Name = $kv.Key + $psboundParameters.Value = $kv.Value + $psboundParameters.ServiceDefinition = $ServiceDefinition + $ServiceDefinition = & $myInvocation.MyCommand @psBoundParameters + } + } + } + + end { + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $serviceDefinition.Save($strWrite) + return "$strWrite" + } else { + $serviceDefinition + } + } +} diff --git a/Add-AzureStartupTask.ps1 b/Add-AzureStartupTask.ps1 new file mode 100644 index 0000000..5ce9ca4 --- /dev/null +++ b/Add-AzureStartupTask.ps1 @@ -0,0 +1,159 @@ +function Add-AzureStartupTask +{ + <# + .Synopsis + Adds a startup task to Azure + .Description + Adds a startup task to an azure service configuration, and packs some extra information into the XML to allow + using ScriptBlock as startup tasks + .Example + New-AzureServiceDefinition -ServiceName "MyService" | + Add-AzureStartupTask -ScriptBlock { "Hello World" } -Elevated -asString + + .Link + Out-AzureService + #> + [OutputType([xml],[string])] + [CmdletBinding(DefaultParameterSetName='CommandLine')] + param( + # The Service Definition XML + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # The role + [string] + $ToRole, + + # The command line to run + [Parameter(Mandatory=$true,ParameterSetName='CommandLine')] + [string] + $CommandLine, + + + # The batch script to run + [Parameter(Mandatory=$true,ParameterSetName='BatchScript')] + [string] + $BatchScript, + + # The ScriptBlock to run. + [Parameter(Mandatory=$true,ParameterSetName='ScriptBlock')] + [ScriptBlock] + $ScriptBlock, + + # The parameter to be passed to the script block + [Parameter(ParameterSetName='ScriptBlock')] + [Hashtable] + $Parameter, + + # The task type. + [ValidateSet('Simple', 'Background', 'Foreground')] + [string] + $TaskType = 'Simple', + + # If set, the task will be run elevated + [switch] + $Elevated, + + # If set, returns the service definition XML up to this point as a string + [switch] + $AsString + ) + + process { + $taskType = $taskType.ToLower() + + # Resolve the role if it set, create the role if it doesn't exist, and track it if they assume the last item. + $roles = @($ServiceDefinition.ServiceDefinition.WebRole), @($ServiceDefinition.ServiceDefinition.WorkerRole) + @($ServiceDefinition.ServiceDefinition.VirtualMachineRole) + $xmlNamespace = @{'ServiceDefinition'='http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition'} + $selectXmlParams = @{ + XPath = '//ServiceDefinition:WebRole|//ServiceDefinition:WorkerRole|//ServiceDefinition:VirtualMachineRole' + Namespace = $xmlNamespace + } + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + if (-not $roles) { + $ServiceDefinition = $ServiceDefinition | + Add-AzureRole -RoleName "WebRole1" + + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + } + + if ($roles.Count -gt 1) { + if ($ToRole) { + } else { + $role = $roles[-1] + } + } else { + if ($ToRole) { + if ($roles[0].Name -eq $ToRole) { + $role = $roles[0] + } else { + $role = $null + } + } else { + $role = $roles[0] + } + } + + if (-not $role) { return } + + if (-not $role.Startup) { + $role.InnerXml += "" + } + + $startupNode = Select-Xml -Xml $role -Namespace $xmlNamespace -XPath '//ServiceDefinition:Startup' | + Select-Object -ExpandProperty Node -First 1 + + $execContext= if ($elevated) { 'elevated' } else { 'limited' } + if ($psCmdlet.ParameterSetName -eq 'CommandLine') { + $startupNode.InnerXml += "" + } elseif ($psCmdlet.ParameterSetName -eq 'ScriptBlock') { + $parameterChunk = if ($parameter) { + $parameterChunk = "" + foreach ($kv in $parameter.GetEnumerator()) { + if ($kv.Value) { + $parameterChunk += "" + } else { + $parameterChunk += "" + } + } + $parameterChunk += "" + } else { ""} + $startupNode.InnerXml += " + + $([Security.SecurityElement]::Escape($ScriptBlock)) + + $parameterChunk + " + } elseif ($psCmdlet.ParameterSetName -eq 'BatchScript') { + $startupNode.InnerXml += " + + $([Security.SecurityElement]::Escape($BatchScript)) + + " + } + + } + + end { + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $serviceDefinition.Save($strWrite) + return "$strWrite" + } else { + $serviceDefinition + } + + } +} diff --git a/Add-AzureWebSite.ps1 b/Add-AzureWebSite.ps1 new file mode 100644 index 0000000..170bc39 --- /dev/null +++ b/Add-AzureWebSite.ps1 @@ -0,0 +1,261 @@ +function Add-AzureWebSite +{ + <# + .Synopsis + Adds an Azure web site to a service definition. + .Description + Adds an Azure web site to a service definition. + + The site can bind to multiple host + + Creates a web role if one does not exist. + .Example + New-AzureServiceDefinition -ServiceName "AService" | + Add-AzureWebSite -SiteName "ASite" -PhysicalDirectory "C:\inetpub\wwwroot\asite" -HostHeader a.subdomain.com, nakeddomain.com, www.fulldomain.com -asString + .Link + New-AzureServiceDefinition + #> + [OutputType([xml],[string])] + param( + # The ServiceDefinition XML. This should be created with New-AzureServiceDefinition or retreived with Import-AzureServiceDefinition + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # If set, the local resource will only apply to the role named ToRole. If ToRole is not found, or doesn't + # exist, the last role will be used. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $ToRole, + + # The name of the site to create. If Sitename is not set, sites will be named Web1, Web2, Web3, etc + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $SiteName, + + # The physical directory of the website. This is where the web site files are located. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $PhysicalDirectory, + + # One or more host headers to use for the site + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $HostHeader, + + # Additional bindings. Each hashtable can contain an EndpointName, Name, and HostHeader + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable[]] + [ValidateScript({ + Test-SafeDictionary -Dictionary $_ -ThrowOnUnsafe + })] + $Binding, + + # Additional virtual directories. + # The keys will be the name of the virtual directories, and the values will be the physical directory on + # the local machine. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable] + [ValidateScript({ + Test-SafeDictionary -Dictionary $_ -ThrowOnUnsafe + })] + $VirtualDirectory, + + # Additional virtual applications. + # The keys will be the name of the virtual applications, and the values will be the physical directory on + # the local machine. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable] + [ValidateScript({ + Test-SafeDictionary -Dictionary $_ -ThrowOnUnsafe + })] + $VirtualApplication, + + # The VMSize + [ValidateSet('ExtraSmall','Small','Medium', 'Large', 'Extra-Large', 'XS', 'XL', 'S', 'M', 'L')] + $VMSize, + + # If set, will return values as a string + [switch] + $AsString + ) + + begin { + $xmlNamespace = @{'ServiceDefinition'='http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition'} + if (-not $script:siteCount) {$script:SiteCount = 0} + } + + process { + + $script:siteCount++ + # Resolve the role if it set, create the role if it doesn't exist, and track it if they assume the last item. + $roles = @($ServiceDefinition.ServiceDefinition.WebRole), @($ServiceDefinition.ServiceDefinition.WorkerRole) + @($ServiceDefinition.ServiceDefinition.VirtualMachineRole) + + $selectXmlParams = @{ + XPath = '//ServiceDefinition:WebRole' + Namespace = $xmlNamespace + } + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + if (-not $roles) { + $params = @{} + if ($vmSize) { $params['vmSize']= $vmSize} + $ServiceDefinition = $ServiceDefinition | + Add-AzureRole -RoleName "WebRole1" @params + + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + } + + if ($roles.Count -gt 1) { + if ($ToRole) { + } else { + $role = $roles[-1] + } + } else { + if ($ToRole) { + if ($roles[0].Name -eq $ToRole) { + $role = $roles[0] + } else { + $role = $null + } + } else { + $role = $roles[0] + } + } + + if (-not $role) { return } + + $realSize = [Math]::Ceiling($size / 1mb) + + if (-not $role.Sites) { + $role.InnerXml += "" + } + + $sitesNode = Select-Xml -Xml $role -Namespace $xmlNamespace -XPath '//ServiceDefinition:Sites' | + Select-Object -ExpandProperty Node + if (-not $siteName) { + if ($physicalPath) { + $SiteName = "WebSite${siteCount}" + } else { + $SiteName = "Web${siteCount}" + } + + } + + if ($PhysicalDirectory) { + $translatedPhysicalPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($PhysicalDirectory) + $sitesNode.InnerXml += "" + } else { + $sitesNode.InnerXml += "" + } + + $siteNode = Select-Xml -Xml $sitesNode -Namespace $xmlNamespace -XPath "//ServiceDefinition:Site"| + Where-Object { $_.Node.Name -eq $siteName } | + Select-Object -ExpandProperty Node -Last 1 + + + if ($psBoundParameters.VirtualDirectory) + { + foreach ($kv in $psBoundParameters.VirtualDirectory.GetEnumerator()) { + $siteNode.InnerXml += " " + $role.Sites.InnerXml = $sitesNode.InnerXml.Replace('xmlns=""', '') + } + } + + if ($psBoundParameters.VirtualApplication) { + foreach ($kv in $psBoundParameters.VirtualApplication.GetEnumerator()) { + $siteNode.InnerXml += " " + $role.Sites.InnerXml = $sitesNode.InnerXml.Replace('xmlns=""', '') + } + } + + $usedDefaultEndPoint = $false + if (-not $role.Endpoints) { + $usedDefaultEndPoint = $true + $role.InnerXml += " + " + } + + $specifiesBindingEndPoint = $false + + if ((-not $psBoundParameters.Binding) -and (-not $psBoundParameters.HostHeader)) { + $endpointsNode = Select-Xml -Xml $role -Namespace $xmlNamespace -XPath "//ServiceDefinition:Endpoints"| + Select-Object -ExpandProperty Node + + $endPointName = @($endPointsNode.InputEndPoint)[-1].Name + + $siteNode.InnerXml += "" + $role.Sites.InnerXml = $sitesNode.InnerXml.Replace('xmlns=""', '') + } + + if ($psBoundParameters.Binding) { + $bindings = foreach ($ht in $psBoundParameters.Binding) { + $bindingXmlText = "" + } + + $ofs = [Environment]::NewLine + $siteNode.InnerXml += "$bindings" + $role.Sites.InnerXml = $sitesNode.InnerXml.Replace('xmlns=""', '') + + } + + if ($psBoundParameters.HostHeader) { + $endpointsNode = Select-Xml -Xml $role -Namespace $xmlNamespace -XPath "//ServiceDefinition:Endpoints"| + Select-Object -ExpandProperty Node + + $endPointName = @($endPointsNode.InputEndPoint)[-1].Name + + $bindingCount = 1 + $bindings = foreach ($header in $psBoundParameters.HostHeader) { + "" + } + $ofs = [Environment]::NewLine + if ($siteNode.InnerXml) { + $siteNode.InnerXml += "$bindings" + } else { + $siteNode.InnerXml = "$bindings" + } + $role.Sites.InnerXml = $sitesNode.InnerXml.Replace('xmlns=""', '') + + } + } + + end { + $webRole= Select-Xml -Xml $role -Namespace $xmlNamespace -XPath '//ServiceDefinition:WebRole' | + Select-Object -ExpandProperty Node + + + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $serviceDefinition.Save($strWrite) + return "$strWrite" + } else { + $serviceDefinition + } + + } +} diff --git a/Add-Deployment.ps1 b/Add-Deployment.ps1 new file mode 100644 index 0000000..02f1fa4 Binary files /dev/null and b/Add-Deployment.ps1 differ diff --git a/Add-EC2.ps1 b/Add-EC2.ps1 new file mode 100644 index 0000000..df8b81e Binary files /dev/null and b/Add-EC2.ps1 differ diff --git a/Add-Interaction.ps1 b/Add-Interaction.ps1 new file mode 100644 index 0000000..62962d6 Binary files /dev/null and b/Add-Interaction.ps1 differ diff --git a/Add-SQLTable.ps1 b/Add-SQLTable.ps1 new file mode 100644 index 0000000..c6ee4ca Binary files /dev/null and b/Add-SQLTable.ps1 differ diff --git a/Add-SecureSetting.ps1 b/Add-SecureSetting.ps1 new file mode 100644 index 0000000..9e77ed0 Binary files /dev/null and b/Add-SecureSetting.ps1 differ diff --git a/Assets/Pipeworks_48.png b/Assets/Pipeworks_48.png new file mode 100644 index 0000000..b8faaab Binary files /dev/null and b/Assets/Pipeworks_48.png differ diff --git a/Assets/PowerShellPipeworks_150.png b/Assets/PowerShellPipeworks_150.png new file mode 100644 index 0000000..7d1bfcf Binary files /dev/null and b/Assets/PowerShellPipeworks_150.png differ diff --git a/Assets/PowerShellPipeworks_30.png b/Assets/PowerShellPipeworks_30.png new file mode 100644 index 0000000..12911d1 Binary files /dev/null and b/Assets/PowerShellPipeworks_30.png differ diff --git a/Assets/PowerShellPipeworks_310_150.png b/Assets/PowerShellPipeworks_310_150.png new file mode 100644 index 0000000..ee6d512 Binary files /dev/null and b/Assets/PowerShellPipeworks_310_150.png differ diff --git a/Assets/PowerShellPipeworks_50.png b/Assets/PowerShellPipeworks_50.png new file mode 100644 index 0000000..6b93154 Binary files /dev/null and b/Assets/PowerShellPipeworks_50.png differ diff --git a/Assets/PowerShellPipeworks_620_300.png b/Assets/PowerShellPipeworks_620_300.png new file mode 100644 index 0000000..6874563 Binary files /dev/null and b/Assets/PowerShellPipeworks_620_300.png differ diff --git a/CSharp/AddAzureTableCommand.cs b/CSharp/AddAzureTableCommand.cs new file mode 100644 index 0000000..5051171 --- /dev/null +++ b/CSharp/AddAzureTableCommand.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Management.Automation; +using System.Net; +using System.IO; +using System.Linq; +using System.Xml.Linq; +using System.Security; + +namespace AzureStorageCmdlets +{ + [Cmdlet(VerbsCommon.Add, "AzureTable")] + public class AddAzureTableCommand : AzureTableCmdletBase + { + [Parameter(Mandatory = true, Position=0, ValueFromPipelineByPropertyName = true)] + [Alias("Name")] + public string TableName + { + get; + set; + } + + [Parameter()] + public SwitchParameter PassThru + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName = true)] + public string Author + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName = true)] + public string Email + { + get; + set; + } + + private List CreateTable(string tableName) + { + return Retry>(delegate() + { + HttpWebResponse response; + List tables = new List(); + + try + { + string now = DateTime.UtcNow.ToString("o"); + + string requestBody = String.Format("" + + " " + + " " + + " <updated>" + now + "</updated> " + + " <author>" + + " <name/> " + + " </author> " + + " <id/> " + + " <content type=\"application/xml\">" + + " <m:properties>" + + " <d:TableName>{0}</d:TableName>" + + " </m:properties>" + + " </content> " + + "</entry>", + tableName); + + if (! String.IsNullOrEmpty(Author)) { + if (!String.IsNullOrEmpty(Email)) { + requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(Author) + "</name><email>" + SecurityElement.Escape(Email) + "</email>")); + } else { + requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(Author) + "</name>")); + } + + } + response = CreateRESTRequest("POST", "Tables", requestBody, null, null, null).GetResponse() as HttpWebResponse; + if (response.StatusCode == HttpStatusCode.Created) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string result = reader.ReadToEnd(); + + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + + XElement x = XElement.Parse(result, LoadOptions.SetBaseUri); + foreach (XElement table in x.Descendants(ns + "entry")) + { + AzureTable tableOutput = new AzureTable(); + tableOutput.TableId = new Uri(table.Descendants(ns + "id").First().Value); + tableOutput.TableName = table.Descendants(d + "TableName").First().Value; + tableOutput.Updated = (DateTime)LanguagePrimitives.ConvertTo((table.Descendants(ns + "updated").First().Value), DateTime.Now.GetType()); + tables.Add(tableOutput); + } + } + } + response.Close(); + + return tables; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null && + (int)(ex.Response as HttpWebResponse).StatusCode == 409) + return null; + + throw; + } + }); + } + + protected override void ProcessRecord() + { + // Calling the base processrecord + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + + if (PassThru) + { + this.WriteObject(CreateTable(TableName), true); + } + else + { + CreateTable(TableName); + } + + } + } +} diff --git a/CSharp/AzureStorageCmdletBase.cs b/CSharp/AzureStorageCmdletBase.cs new file mode 100644 index 0000000..7394792 --- /dev/null +++ b/CSharp/AzureStorageCmdletBase.cs @@ -0,0 +1,366 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Linq; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Collections.Specialized; + using System.Net; + using System.Collections; + using System.Web; + using System.Threading; + + public class AzureStorageAccountData + { + public string AccountName + { + get; + set; + } + + public string Key + { + get; + set; + } + } + public abstract class AzureStorageCmdletBase: PSCmdlet + { + public abstract string Endpoint + { + get; + } + + [Parameter()] + public string StorageAccount + { + get; set; + } + + [Parameter()] + public string StorageKey + { + get; set; + } + + protected virtual bool IsTableStorage + { + get + { + return false; + } + } + + #region REST HTTP Request Helper Methods + + // Construct and issue a REST request and return the response. + + public virtual HttpWebRequest CreateRESTRequest(string method, string resource, string requestBody, SortedList<string, string> headers, string ifMatch, string md5) + { + byte[] byteArray = null; + DateTime now = DateTime.UtcNow; + string uri = Endpoint + resource; + + HttpWebRequest request = HttpWebRequest.Create(uri) as HttpWebRequest; + request.Method = method; + request.ContentLength = 0; + request.Headers.Add("x-ms-date", now.ToString("R", System.Globalization.CultureInfo.InvariantCulture)); + request.Headers.Add("x-ms-version", "2011-08-18"); + + if (IsTableStorage) + { + request.ContentType = "application/atom+xml"; + + request.Headers.Add("DataServiceVersion", "2.0;NetFx"); + request.Headers.Add("MaxDataServiceVersion", "2.0;NetFx"); + } + + if (headers != null) + { + foreach (KeyValuePair<string, string> header in headers) + { + request.Headers.Add(header.Key, header.Value); + } + } + + if (!String.IsNullOrEmpty(requestBody)) + { + request.Headers.Add("Accept-Charset", "UTF-8"); + + byteArray = Encoding.UTF8.GetBytes(requestBody); + request.ContentLength = byteArray.Length; + } + + request.Headers.Add("Authorization", AuthorizationHeader(method, now, request, ifMatch, md5)); + + if (!String.IsNullOrEmpty(requestBody)) + { + request.GetRequestStream().Write(byteArray, 0, byteArray.Length); + } + + return request; + } + + + // Generate an authorization header. + + public virtual string AuthorizationHeader(string method, DateTime now, HttpWebRequest request, string ifMatch, string md5) + { + string MessageSignature; + + if (IsTableStorage) + { + MessageSignature = String.Format("{0}\n\n{1}\n{2}\n{3}", + method, + "application/atom+xml", + now.ToString("R", System.Globalization.CultureInfo.InvariantCulture), + GetCanonicalizedResource(request.RequestUri, StorageAccount) + ); + } + else + { + MessageSignature = String.Format("{0}\n\n\n{1}\n{5}\n\n\n\n{2}\n\n\n\n{3}{4}", + method, + (method == "GET" || method == "HEAD") ? String.Empty : request.ContentLength.ToString(), + ifMatch, + GetCanonicalizedHeaders(request), + GetCanonicalizedResource(request.RequestUri, StorageAccount), + md5 + ); + } + byte[] SignatureBytes = System.Text.Encoding.UTF8.GetBytes(MessageSignature); + System.Security.Cryptography.HMACSHA256 SHA256 = new System.Security.Cryptography.HMACSHA256(Convert.FromBase64String(StorageKey)); + String AuthorizationHeader = "SharedKey " + StorageAccount + ":" + Convert.ToBase64String(SHA256.ComputeHash(SignatureBytes)); + return AuthorizationHeader; + } + + // Get canonicalized headers. + + public static string GetCanonicalizedHeaders(HttpWebRequest request) + { + ArrayList headerNameList = new ArrayList(); + StringBuilder sb = new StringBuilder(); + foreach (string headerName in request.Headers.Keys) + { + if (headerName.ToLowerInvariant().StartsWith("x-ms-", StringComparison.Ordinal)) + { + headerNameList.Add(headerName.ToLowerInvariant()); + } + } + headerNameList.Sort(); + foreach (string headerName in headerNameList) + { + StringBuilder builder = new StringBuilder(headerName); + string separator = ":"; + foreach (string headerValue in GetHeaderValues(request.Headers, headerName)) + { + string trimmedValue = headerValue.Replace("\r\n", String.Empty); + builder.Append(separator); + builder.Append(trimmedValue); + separator = ","; + } + sb.Append(builder.ToString()); + sb.Append("\n"); + } + return sb.ToString(); + } + + // Get header values. + + public static ArrayList GetHeaderValues(NameValueCollection headers, string headerName) + { + ArrayList list = new ArrayList(); + string[] values = headers.GetValues(headerName); + if (values != null) + { + foreach (string str in values) + { + list.Add(str.TrimStart(null)); + } + } + return list; + } + + // Get canonicalized resource. + + public virtual string GetCanonicalizedResource(Uri address, string accountName) + { + StringBuilder str = new StringBuilder(); + StringBuilder builder = new StringBuilder("/"); + builder.Append(accountName); + builder.Append(address.AbsolutePath); + str.Append(builder.ToString()); + NameValueCollection values2 = new NameValueCollection(); + if (!IsTableStorage) + { + NameValueCollection values = HttpUtility.ParseQueryString(address.Query); + foreach (string str2 in values.Keys) + { + ArrayList list = new ArrayList(values.GetValues(str2)); + list.Sort(); + StringBuilder builder2 = new StringBuilder(); + foreach (object obj2 in list) + { + if (builder2.Length > 0) + { + builder2.Append(","); + } + builder2.Append(obj2.ToString()); + } + values2.Add((str2 == null) ? str2 : str2.ToLowerInvariant(), builder2.ToString()); + } + } + ArrayList list2 = new ArrayList(values2.AllKeys); + list2.Sort(); + foreach (string str3 in list2) + { + StringBuilder builder3 = new StringBuilder(string.Empty); + builder3.Append(str3); + builder3.Append(":"); + builder3.Append(values2[str3]); + str.Append("\n"); + str.Append(builder3.ToString()); + } + return str.ToString(); + } + + #endregion + + #region Retry Delegate + + public delegate T RetryDelegate<T>(); + public delegate void RetryDelegate(); + + const int retryCount = 3; + const int retryIntervalMS = 200; + + // Retry delegate with default retry settings. + + public static T Retry<T>(RetryDelegate<T> del) + { + return Retry<T>(del, retryCount, retryIntervalMS); + } + + // Retry delegate. + + public static T Retry<T>(RetryDelegate<T> del, int numberOfRetries, int msPause) + { + int counter = 0; + RetryLabel: + + try + { + counter++; + return del.Invoke(); + } + catch (Exception ex) + { + if (counter > numberOfRetries) + { + throw ex; + } + else + { + if (msPause > 0) + { + Thread.Sleep(msPause); + } + goto RetryLabel; + } + } + } + + + // Retry delegate with default retry settings. + + public static bool Retry(RetryDelegate del) + { + return Retry(del, retryCount, retryIntervalMS); + } + + + public static bool Retry(RetryDelegate del, int numberOfRetries, int msPause) + { + int counter = 0; + + RetryLabel: + try + { + counter++; + del.Invoke(); + return true; + } + catch (Exception ex) + { + if (counter > numberOfRetries) + { + throw ex; + } + else + { + if (msPause > 0) + { + Thread.Sleep(msPause); + } + goto RetryLabel; + } + } + } + + protected void WriteWebError(WebException ex, string extraString) + { + WriteError( + new ErrorRecord( + new InvalidOperationException( + ((ex.Response as HttpWebResponse).StatusCode.ToString()) + ' ' + extraString), + "SetAzureTableCommand.WebError." + ((int)(ex.Response as HttpWebResponse).StatusCode).ToString(), + ErrorCategory.InvalidOperation, + this) + ); + } + + protected override void ProcessRecord() + { + if (! (this.MyInvocation.MyCommand.Module.PrivateData is AzureStorageAccountData)) { + this.MyInvocation.MyCommand.Module.PrivateData = new AzureStorageAccountData(); + AzureStorageAccountData accountData = new AzureStorageAccountData(); + accountData.AccountName = StorageAccount; + accountData.Key = StorageKey; + } + + if (!this.MyInvocation.BoundParameters.ContainsKey("StorageKey")) + { + StorageKey = (this.MyInvocation.MyCommand.Module.PrivateData as AzureStorageAccountData).Key; + } + else + { + (this.MyInvocation.MyCommand.Module.PrivateData as AzureStorageAccountData).Key = StorageKey; + } + + if (!this.MyInvocation.BoundParameters.ContainsKey("StorageAccount")) + { + StorageAccount = (this.MyInvocation.MyCommand.Module.PrivateData as AzureStorageAccountData).AccountName; + } + else + { + (this.MyInvocation.MyCommand.Module.PrivateData as AzureStorageAccountData).AccountName = StorageAccount; + } + + + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) + { + WriteError( + new ErrorRecord( + new Exception("Must provide a StorageAccount and StorageKey"), + "MissingAzureStorageAccountOrKey", + ErrorCategory.InvalidOperation, + null) + ); + } + } + + + #endregion + } +} diff --git a/CSharp/AzureTableCmdletBase.cs b/CSharp/AzureTableCmdletBase.cs new file mode 100644 index 0000000..dfbdf75 --- /dev/null +++ b/CSharp/AzureTableCmdletBase.cs @@ -0,0 +1,436 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Net; + using System.IO; + using System.Linq; + using System.Xml.Linq; + using System.Security; + using System.Collections; + using System.Collections.ObjectModel; + + public class AzureTableCmdletBase: AzureStorageCmdletBase + { + public override string Endpoint + { + get { + return "http://" + StorageAccount + ".table.core.windows.net/"; + } + } + + // Query entities. Returned entity list XML matching query filter. + // Return true on success, false if not found, throw exception on error. + protected string QueryEntities(string tableName, string partition, string row, string filter, string sort, string select, uint first) + { + return Retry<string>(delegate() + { + HttpWebRequest request; + HttpWebResponse response; + + string entityXml = null; + + try + { + string resource = tableName; + if ( + !String.IsNullOrEmpty(row) + && + !String.IsNullOrEmpty(partition) + ) + { + resource += @"(PartitionKey=""" + partition + @""",RowKey=""" + row + @""")?"; + } + else + { + resource += "()?"; + } + if (!String.IsNullOrEmpty(sort)) + { + resource += "$OrderBy=" + sort + "&"; + } + + if (!String.IsNullOrEmpty(filter)) + { + resource += "$filter=" + Uri.EscapeDataString(filter) + "&"; + } + if (!String.IsNullOrEmpty(select)) + { + resource += "$select=" + select + "&"; + } + + if (first > 0) + { + resource += "$top=" + first + "&"; + } + + resource = resource.TrimEnd('&'); + this.WriteVerbose("Creating Request for " + (Endpoint + resource)); + SortedList<string, string> headers = new SortedList<string, string>(); + if (! String.IsNullOrEmpty(nextTable)) { + + resource += ("&NextTableName=" + nextTable); + WriteVerbose("NextTable" + nextTable); + } + if (! String.IsNullOrEmpty(nextPart)) { + + resource += ("&NextPartitionKey=" + nextPart); + WriteVerbose("NextPart" + nextPart); + } + if (! String.IsNullOrEmpty(nextRow)) { + + resource += ("&NextRowKey=" + nextRow); + WriteVerbose("NextRow" + nextRow); + } + if (headers.Count == 0) { + headers = null; + } + request = CreateRESTRequest("GET", resource, null, headers, null, null); + nextRow = String.Empty; + nextPart = String.Empty; + request.Accept = "application/atom+xml,application/xml"; + + response = request.GetResponse() as HttpWebResponse; + + if ((int)response.StatusCode == 200) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string result = reader.ReadToEnd(); + + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + + XElement entry = XElement.Parse(result); + + entityXml = entry.ToString(); + } + } + + response.Close(); + string nextRowHeader = response.Headers["x-ms-continuation-NextRowKey"]; + if (! (String.IsNullOrEmpty(nextRowHeader))) { + nextRow = nextRowHeader; + } + string nextPartHeader = response.Headers["x-ms-continuation-NextPartitionKey"]; + if (! (String.IsNullOrEmpty(nextPartHeader))) { + nextPart = nextPartHeader; + } + string nextTableName = response.Headers["x-ms-continuation-NextTableName"]; + if (! (String.IsNullOrEmpty(nextTableName))) { + nextTable = nextPartHeader; + } + return entityXml; + } + catch (WebException ex) + { + WriteWebError(ex, "Table: " + tableName + " Filter: " + filter); + return string.Empty; + } + }); + } + + protected string nextRow; + protected string nextPart; + protected string nextTable; + + protected PSObject InsertEntity(string tableName, string partitionKey, string rowKey, PSObject obj, string author, string email, bool update, bool merge, bool excludeTableInfo) + { + return Retry<PSObject>(delegate() + { + HttpWebResponse response; + + try + { + // Create properties list. Use reflection to retrieve properties from the object. + + StringBuilder properties = new StringBuilder(); + properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", "PartitionKey", partitionKey)); + properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", "RowKey", rowKey)); + + string lastTypeName = obj.TypeNames.Last(); + if (lastTypeName != "System.Object" && lastTypeName != "System.Management.Automation.PSObject") + { + + properties.Append(string.Format("<d:psTypeName>{0}</d:psTypeName>\n", SecurityElement.Escape(lastTypeName))); + } + foreach (PSPropertyInfo p in obj.Properties) + { + try + { + + string valueToInsert = (string)LanguagePrimitives.ConvertTo(p.Value, typeof(string)); + + properties.Append(string.Format("<d:{0}>{1}</d:{0}>\n", p.Name, SecurityElement.Escape(valueToInsert))); + } + catch + { + + } + } + + string now = DateTime.UtcNow.ToString("o", System.Globalization.CultureInfo.InvariantCulture); + string id = String.Empty; + if (update || merge) + { + id = String.Format("http://{0}.table.core.windows.net/{1}(PartitionKey='{2}',RowKey='{3}')", StorageAccount, tableName, partitionKey, rowKey); + } + string requestBody = String.Format("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>" + + "<entry xmlns:d=\"http://schemas.microsoft.com/ado/2007/08/dataservices\"" + + " xmlns:m=\"http://schemas.microsoft.com/ado/2007/08/dataservices/metadata\"" + + " xmlns=\"http://www.w3.org/2005/Atom\"> " + + " <title /> " + + " <updated>{0}</updated> " + + " <author>" + + " <name/> " + + " </author> " + + " <id>{1}</id> " + + " <content type=\"application/xml\">" + + " <m:properties>" + + "{2}" + + " </m:properties>" + + " </content> " + + "</entry>", + now, + id, + properties); + + if (!String.IsNullOrEmpty(author)) + { + if (!String.IsNullOrEmpty(email)) + { + requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(author) + "</name><email>" + SecurityElement.Escape(email) + "</email>")); + } + else + { + requestBody.Replace("<name/>", ("<name>" + SecurityElement.Escape(author) + "</name>")); + } + + } + + if (merge) + { + string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey); + SortedList<string, string> headers = new SortedList<string, string>(); + headers.Add("If-Match", "*"); + response = CreateRESTRequest("MERGE", resource, requestBody, headers, String.Empty, String.Empty).GetResponse() as HttpWebResponse; + } + else if (update) + { + string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey); + SortedList<string, string> headers = new SortedList<string, string>(); + headers.Add("If-Match", "*"); + + response = CreateRESTRequest("PUT", resource, requestBody, headers, String.Empty, String.Empty).GetResponse() as HttpWebResponse; + } + else + { + response = CreateRESTRequest("POST", tableName, requestBody, null, String.Empty, String.Empty).GetResponse() as HttpWebResponse; + } + + if (response.StatusCode == HttpStatusCode.Created) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string result = reader.ReadToEnd(); + + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; + return RecreateObject(result, ! excludeTableInfo, tableName).First(); + } + } + response.Close(); + + return null; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null) + + WriteError( + new ErrorRecord( + new InvalidOperationException( + ((ex.Response as HttpWebResponse).StatusCode.ToString()) + "-- Table: " + tableName + "Partition: " + partitionKey + "Row: " + rowKey), + "SetAzureTableCommand.WebError." + ((int)(ex.Response as HttpWebResponse).StatusCode).ToString(), + ErrorCategory.InvalidOperation, + this) + ); + return null; + } + }); + } + + protected IEnumerable ExpandObject(string atomXml, bool includeTableInfo, string fromTable) + { + if (String.IsNullOrEmpty(atomXml)) { + yield break; + } + XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; + this.WriteDebug(atomXml); + + XElement entry = XElement.Parse(atomXml); + + + + foreach (XElement propertyGroup in entry.Descendants(m + "properties")) + { + PSObject returnObject = new PSObject(); + foreach (XElement element in propertyGroup.Descendants()) + { + if (element.Name.LocalName == "psTypeName") + { + returnObject.TypeNames.Clear(); + foreach (string typename in element.Value.Split(',')) { + returnObject.TypeNames.Add(typename); + } + + } else if (element.Name.LocalName == "PartitionKey") { + if (!includeTableInfo) { continue;} + PSNoteProperty noteProperty = new PSNoteProperty("PartitionKey", element.Value); + try { + returnObject.Properties.Add(noteProperty); + } catch { + try { + // Remove the old and replace with the new + returnObject.Properties.Remove(noteProperty.Name); + returnObject.Properties.Add(noteProperty); + } catch { + + } + } + } else if (element.Name.LocalName == "RowKey") { + if (!includeTableInfo) { continue;} + PSNoteProperty noteProperty = new PSNoteProperty("RowKey", element.Value); + try { + returnObject.Properties.Add(noteProperty); + } catch { + try { + // Remove the old and replace with the new + returnObject.Properties.Remove(noteProperty.Name); + returnObject.Properties.Add(noteProperty); + } catch { + + } + } + } else if (element.Name.LocalName == "Timestamp") { + if (!includeTableInfo) { continue;} + try { + DateTime lastUpdated = DateTime.Parse(element.Value); + PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", lastUpdated); + returnObject.Properties.Add(noteProperty); + } catch { + try { + // Remove the old and replace with the new + returnObject.Properties.Remove("Timestamp"); + PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", element.Value); + returnObject.Properties.Add(noteProperty); + } catch { + + } + } + + } else { + PSNoteProperty noteProperty = new PSNoteProperty(element.Name.LocalName, element.Value); + returnObject.Properties.Add(noteProperty); + } + } + if (includeTableInfo) + { + try { + PSNoteProperty noteProperty = new PSNoteProperty("TableName", fromTable); + returnObject.Properties.Add(noteProperty); + } catch { + + } + } + yield return returnObject; + } + + } + + protected Collection<PSObject> RecreateObject(string atomXml, bool includeTableInfo, string fromTable) + { + if (String.IsNullOrEmpty(atomXml)) { + return null; + } + Collection<PSObject> psObjects = new Collection<PSObject>(); + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + XNamespace m = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"; + this.WriteVerbose("Recreating Object From ATOM:" + Environment.NewLine + atomXml); + XElement entry = XElement.Parse(atomXml); + + + + foreach (XElement propertyGroup in entry.Descendants(m + "properties")) + { + PSObject returnObject = new PSObject(); + foreach (XElement element in propertyGroup.Descendants()) + { + if (element.Name.LocalName == "psTypeName") + { + returnObject.TypeNames.Clear(); + foreach (string typename in element.Value.Split(',')) { + returnObject.TypeNames.Add(typename); + } + + } + else if (element.Name.LocalName == "PartitionKey") + { + if (includeTableInfo) + { + returnObject.Properties.Remove("PartitionKey"); + PSNoteProperty noteProperty = new PSNoteProperty("PartitionKey", element.Value); + returnObject.Properties.Add(noteProperty); + } + } + else if (element.Name.LocalName == "RowKey") + { + if (includeTableInfo) + { + returnObject.Properties.Remove("RowKey"); + PSNoteProperty noteProperty = new PSNoteProperty("RowKey", element.Value); + returnObject.Properties.Add(noteProperty); + } + } + else if (element.Name.LocalName == "Timestamp") + { + if (includeTableInfo) + { + returnObject.Properties.Remove("Timestamp"); + DateTime lastUpdated = (DateTime)LanguagePrimitives.ConvertTo(element.Value, typeof(DateTime)); + PSNoteProperty noteProperty = new PSNoteProperty("Timestamp", element.Value); + returnObject.Properties.Add(noteProperty); + } + } + else + { + PSNoteProperty noteProperty = new PSNoteProperty(element.Name.LocalName, element.Value); + returnObject.Properties.Add(noteProperty); + } + } + if (includeTableInfo && !String.IsNullOrEmpty(fromTable)) + { + returnObject.Properties.Remove("TableName"); + PSNoteProperty noteProperty = new PSNoteProperty("TableName", fromTable); + returnObject.Properties.Add(noteProperty); + } + psObjects.Add(returnObject); + } + + return psObjects; + } + + protected override bool IsTableStorage + { + get + { + return true; + } + } + } +} diff --git a/CSharp/GetAzureTableCommand.cs b/CSharp/GetAzureTableCommand.cs new file mode 100644 index 0000000..25eae40 --- /dev/null +++ b/CSharp/GetAzureTableCommand.cs @@ -0,0 +1,210 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Net; + using System.IO; + using System.Management.Automation; + using System.Linq; + using System.Xml.Linq; + + public class AzureTable + { + public Uri TableId + { + get; + set; + } + + public DateTime Updated + { + get; + set; + } + + public string TableName + { + get; + set; + } + } + + + [Cmdlet(VerbsCommon.Get, "AzureTable", DefaultParameterSetName="GetATable")] + public class GetAzureTableCommand : AzureTableCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position=0,ParameterSetName = "GetSpecificItem")] + [Parameter(ValueFromPipelineByPropertyName = true, Position=0,ParameterSetName = "GetATable")] + [Alias(new string[] { "Name", "Table" })] + public string TableName + { + get; + set; + } + + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position=1,ParameterSetName = "GetSpecificItem")] + [Alias(new string[] { "TablePart", "TablePartition", "PartitionKey" })] + public string Partition + { + get; + set; + } + + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, Position=2,ParameterSetName = "GetSpecificItem")] + [Alias(new string[] { "TableRow", "RowKey" })] + public string Row + { + get; + set; + } + + [Parameter()] + public SwitchParameter ExcludeTableInfo + { + get; + set; + } + + private List<AzureTable> ListTables() + { + return Retry<List<AzureTable>>(delegate() + { + HttpWebResponse response; + List<AzureTable> tables = new List<AzureTable>(); + + tables = new List<AzureTable>(); + + try + { + response = CreateRESTRequest("GET", "Tables", String.Empty, null, String.Empty, String.Empty).GetResponse() as HttpWebResponse; + + if ((int)response.StatusCode == 200) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string result = reader.ReadToEnd(); + + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + + XElement x = XElement.Parse(result, LoadOptions.SetBaseUri); + + foreach (XElement table in x.Descendants(ns + "entry")) + { + AzureTable tableOutput = new AzureTable(); + tableOutput.TableId = new Uri(table.Descendants(ns + "id").First().Value); + tableOutput.TableName = table.Descendants(d + "TableName").First().Value; + tableOutput.Updated = (DateTime)LanguagePrimitives.ConvertTo((table.Descendants(ns + "updated").First().Value), DateTime.Now.GetType()); + tables.Add(tableOutput); + } + } + } + + response.Close(); + + return tables; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null && + (int)(ex.Response as HttpWebResponse).StatusCode == 404) + return null; + + throw; + } + }); + } + + // Retrieve an entity. Returns entity XML. + // Return true on success, false if not found, throw exception on error. + + private string GetEntity(string tableName, string partitionKey, string rowKey) + { + return Retry<string>(delegate() + { + HttpWebRequest request; + HttpWebResponse response; + + string entityXml = null; + + try + { + string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey); + + SortedList<string, string> headers = new SortedList<string, string>(); + headers.Add("If-Match", "*"); + + request = CreateRESTRequest("GET", resource, null, headers, String.Empty, String.Empty); + + request.Accept = "application/atom+xml"; + + response = request.GetResponse() as HttpWebResponse; + + if ((int)response.StatusCode == 200) + { + using (StreamReader reader = new StreamReader(response.GetResponseStream())) + { + string result = reader.ReadToEnd(); + if (! String.IsNullOrEmpty(result)) { + XNamespace ns = "http://www.w3.org/2005/Atom"; + XNamespace d = "http://schemas.microsoft.com/ado/2007/08/dataservices"; + + XElement entry = XElement.Parse(result); + + entityXml = entry.ToString(); + } + + } + } + + response.Close(); + + return entityXml; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null) + { + WriteWebError(ex, tableName + ":" + partitionKey + ":" + rowKey); + return String.Empty; + } + return String.Empty; + } + }); + } + + + + protected override void ProcessRecord() + { + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + if (this.ParameterSetName == "GetATable") + { + if (String.IsNullOrEmpty(this.TableName)) { + WriteObject(ListTables(), true); + } else { + foreach (AzureTable at in ListTables()) { + if (this.TableName.Contains('?') || this.TableName.Contains('*')) { + WildcardPattern wp = new WildcardPattern(this.TableName); + if (wp.IsMatch(at.TableName)) { + WriteObject(at); + } + } else { + if (String.Compare(at.TableName, this.TableName, StringComparison.InvariantCultureIgnoreCase) == 0) { + WriteObject(at); + } + } + + } + } + } else if (this.ParameterSetName == "GetSpecificItem") { + string itemXml = GetEntity(this.TableName, this.Partition, this.Row); + WriteObject(ExpandObject(itemXml, (!ExcludeTableInfo), this.TableName), true); + } + } + } +} diff --git a/CSharp/RemoveAzureTableCommand.cs b/CSharp/RemoveAzureTableCommand.cs new file mode 100644 index 0000000..fbe4987 --- /dev/null +++ b/CSharp/RemoveAzureTableCommand.cs @@ -0,0 +1,122 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Net; + using System.IO; + using System.Linq; + using System.Xml.Linq; + + [Cmdlet(VerbsCommon.Remove, "AzureTable", ConfirmImpact=ConfirmImpact.High, SupportsShouldProcess=true)] + public class RemoveAzureTableCommand : AzureTableCmdletBase + { + #region Parameters + + [Parameter(Mandatory = true, Position=0,ValueFromPipelineByPropertyName = true)] + [Alias("Name")] + public string TableName + { + get; + set; + } + + [Parameter(Position=1,ValueFromPipelineByPropertyName = true)] + public string PartitionKey + { + get; + set; + } + + [Parameter(Position=2,ValueFromPipelineByPropertyName = true)] + public string RowKey + { + get; + set; + } + + #endregion + + private bool DeleteTable(string tableName) + { + return Retry<bool>(delegate() + { + HttpWebResponse response; + List<AzureTable> tables = new List<AzureTable>(); + try + { + response = CreateRESTRequest("DELETE", "Tables('" + tableName + "')", String.Empty, null, String.Empty, String.Empty).GetResponse() as HttpWebResponse; + response.Close(); + + return true; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null && + (int)(ex.Response as HttpWebResponse).StatusCode == 409) + return false; + + throw; + } + }); + } + + public bool DeleteEntity(string tableName, string partitionKey, string rowKey) + { + return Retry<bool>(delegate() + { + HttpWebRequest request; + HttpWebResponse response; + + try + { + string resource = String.Format(tableName + "(PartitionKey='{0}',RowKey='{1}')", partitionKey, rowKey); + + SortedList<string, string> headers = new SortedList<string, string>(); + headers.Add("If-Match", "*"); + + request = CreateRESTRequest("DELETE", resource, null, headers, String.Empty, String.Empty); + + response = request.GetResponse() as HttpWebResponse; + response.Close(); + + return true; + } + catch (WebException ex) + { + if (ex.Status == WebExceptionStatus.ProtocolError && + ex.Response != null && + (int)(ex.Response as HttpWebResponse).StatusCode == 409) + return false; + + throw; + } + }); + } + + protected override void ProcessRecord() + { + + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + + if (this.MyInvocation.BoundParameters.ContainsKey("TableName") && + this.MyInvocation.BoundParameters.ContainsKey("PartitionKey") && this.MyInvocation.BoundParameters.ContainsKey("RowKey")) { + if (this.ShouldProcess(TableName + "/" + PartitionKey + "/" + RowKey)) + { + DeleteEntity(TableName, PartitionKey, RowKey); + } + } else if (this.MyInvocation.BoundParameters.ContainsKey("TableName") && + this.MyInvocation.BoundParameters.ContainsKey("PartitionKey")) { + // Name and Partition if (this.ShouldProcess(TableName + "/" + PartitionKey)) { } + } else { // Just Name + if (this.ShouldProcess(TableName)) + { + DeleteTable(TableName); + } + } + } + } +} diff --git a/CSharp/SearchAzureTableCommand.cs b/CSharp/SearchAzureTableCommand.cs new file mode 100644 index 0000000..dce0cac --- /dev/null +++ b/CSharp/SearchAzureTableCommand.cs @@ -0,0 +1,410 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Net; + using System.IO; + using System.Linq; + using System.Xml.Linq; + using System.Security; + using System.Collections.ObjectModel; + using System.Collections.Specialized; + + [Cmdlet(VerbsCommon.Search, "AzureTable", DefaultParameterSetName="WholeTable")] + public class SearchAzureTableCommand : AzureTableCmdletBase + { + [Parameter(Mandatory = true, + Position=0, + ValueFromPipelineByPropertyName=true )] + [Alias(new string[] { "Name", "Table" })] + public string TableName + { + get; + set; + } + + [Parameter(Mandatory = true,ParameterSetName="FilterString")] + public string Filter + { + get; + set; + } + + [Parameter(Mandatory = true,ValueFromPipelineByPropertyName=true,ParameterSetName="ContinueSearch")] + public string NextRowKey + { + get; + set; + } + + [Parameter(Mandatory = true,ValueFromPipelineByPropertyName=true,ParameterSetName="ContinueSearch")] + public string NextPartition + { + get; + set; + } + + [Parameter(ParameterSetName="ContinueSearch")] + public uint Next + { + get; + set; + } + + + + [Parameter(Mandatory = true, + Position=1, + ParameterSetName = "WhereBlock")] + public ScriptBlock[] Where + { + get; + set; + } + + [Parameter(ParameterSetName = "WhereBlock")] + public SwitchParameter Or + { + get; + set; + } + + + + [Parameter()] + public string[] Select + { + get; + set; + } + + + public string[] Sort + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + public uint BatchSize + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + public uint First + { + get; + set; + } + + + public uint Skip + { + get; + set; + } + + [Parameter()] + public SwitchParameter ExcludeTableInfo + { + get; + set; + } + + protected override void ProcessRecord() + { + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + #region WhereBlockTranslation + this.WriteVerbose(this.ParameterSetName); + if (this.ParameterSetName == "WhereBlock") + { + + StringCollection filterList = new StringCollection(); + + foreach (ScriptBlock whereBlock in Where) + { + this.WriteVerbose(String.Format("Processing Where Clause {0}", whereBlock.ToString())); + string whereString = whereBlock.ToString(); + if (whereString.Length > 512) + { + WriteError( + new ErrorRecord(new Exception("Will not tokenize filters longer than 512 characters"), + "SearchAzureTable.WhereBlockTooLong", ErrorCategory.InvalidArgument, whereBlock)); + continue; + } + + Collection<PSParseError> error = new Collection<PSParseError>(); ; + Collection<PSToken> tokens = PSParser.Tokenize(whereString, out error); + this.WriteVerbose(String.Format("Tokens Count {0}", tokens.Count.ToString())); + bool ok = true; + string adoFilter = String.Empty; + IEnumerator<PSToken> enumerator = tokens.GetEnumerator(); + enumerator.MoveNext(); + while (enumerator.Current != null) + { + this.WriteVerbose(String.Format("Processing {0}", enumerator.Current.ToString())); + if (enumerator.Current.Type != PSTokenType.Variable || enumerator.Current.Content != "_") + { + WriteError( + new ErrorRecord(new Exception("The first item in the filter script must $_"), + "SearchAzureTable.FilterScriptMustStartWithDollarUnderbar", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + if (!enumerator.MoveNext()) + { + ok = false; + break; + } + if (enumerator.Current.Type != PSTokenType.Operator && enumerator.Current.Content != ".") { + WriteError( + new ErrorRecord(new Exception("$_ must be followed by the . operator"), + "SearchAzureTable.FilterScriptDollarUnderBarMustBeFollowedByDot", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + if (!enumerator.MoveNext()) + { + ok = false; + break; + } + + if (enumerator.Current.Type != PSTokenType.Member) + { + WriteError( + new ErrorRecord(new Exception("The . operator must be followed by a property name"), + "SearchAzureTable.FilterScriptDotMustBeFollowedByPropertyName", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + adoFilter += enumerator.Current.Content; + + + if (!enumerator.MoveNext()) + { + ok = false; + break; + } + + + if (enumerator.Current.Type != PSTokenType.Operator) + { + WriteError( + new ErrorRecord(new Exception("The filter item must be followed by an operator"), + "SearchAzureTable.FilterScriptItemMustBeFollowedByOperator", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + string[] validOperators = new string[] { "-gt", "-lt", "-ge", "-le", "-ne", "-eq" }; + bool isValidOperator = false; + foreach (string validOp in validOperators) { + if (enumerator.Current.Content == validOp) + { + isValidOperator = true; + break; + } + } + + if (!isValidOperator) + { + WriteError( + new ErrorRecord(new Exception(enumerator.Current.Content + @" is not a valid operator. Please use ""-gt"", ""-lt"", ""-ge"", ""-le"", ""-ne"", ""-eq"""), + "SearchAzureTable.FilterScriptUsesInvalidOperator", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + adoFilter += enumerator.Current.Content.Replace("-", " "); + + if (!enumerator.MoveNext()) + { + ok = false; + break; + } + + this.WriteVerbose(String.Format("Comparing Tokens {0}", enumerator.Current.Type.ToString())); + if (! (enumerator.Current.Type == PSTokenType.Number || enumerator.Current.Type == PSTokenType.String)) + { + WriteError( + new ErrorRecord(new Exception("The operator must be followed by a string or a number"), + "SearchAzureTable.FilterScriptOperatorMustBeFollowedByStringOrNumber", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + if (enumerator.Current.Type == PSTokenType.String && enumerator.Current.Content.Contains("$(")) + { + WriteError( + new ErrorRecord(new Exception("Variables expansion not allowed in filter script"), + "SearchAzureTable.FilterScriptCannotContainVariables", + ErrorCategory.InvalidArgument, + enumerator.Current)); + ok = false; + break; + } + + adoFilter += " '" + this.SessionState.InvokeCommand.ExpandString(enumerator.Current.Content) + "'"; + enumerator.MoveNext(); + } + if (ok) { filterList.Add(adoFilter); } else { + return; + } + } + + if (filterList.Count >= 1) + { + if (filterList.Count > 1) + { + StringBuilder filterBuilder = new StringBuilder(); + foreach (string f in filterList) + { + filterBuilder.Append("("); + filterBuilder.Append(f); + filterBuilder.Append(")"); + if (Or) + { + filterBuilder.Append("or"); + } + else + { + filterBuilder.Append("and"); + } + } + } + else + { + Filter = filterList[0]; + } + } + } + #endregion + + + string selectString = String.Empty; + if (this.MyInvocation.BoundParameters.ContainsKey("Select")) + { + + for (int i =0 ;i < Select.Length; i++) { + selectString+=Select[i]; + if (i != (Select.Length - 1)) { + selectString += ","; + } + } + } + + string sortString = String.Empty; + if (this.MyInvocation.BoundParameters.ContainsKey("Sort")) + { + + for (int i = 0; i < Sort.Length; i++) + { + sortString += Sort[i]; + if (i != (Sort.Length - 1)) + { + sortString += ","; + } + } + } + + if (! this.MyInvocation.BoundParameters.ContainsKey("BatchSize")) { + BatchSize =640; + } + + + + + bool thereIsMore =false; + nextRow = String.Empty; + if (this.MyInvocation.BoundParameters.ContainsKey("NextRowKey")) { + nextRow = NextRowKey; + } + nextPart = String.Empty; + if (this.MyInvocation.BoundParameters.ContainsKey("NextPartition")) { + nextPart = NextPartition; + } + int collectedSoFar = 0; + if (this.MyInvocation.BoundParameters.ContainsKey("Next")) { + First = Next; + } + + if (this.MyInvocation.BoundParameters.ContainsKey("First") && First < BatchSize) { + BatchSize = First; + } + + + + do { + if (this.MyInvocation.BoundParameters.ContainsKey("First") || + this.MyInvocation.BoundParameters.ContainsKey("Next")) { + if (collectedSoFar >= First) { + break; + } + } + string result = QueryEntities( + this.TableName, + null, + null, + this.Filter, + sortString, + selectString, + BatchSize); + + if (!String.IsNullOrEmpty(result)) + { + if (this.MyInvocation.BoundParameters.ContainsKey("First") || + this.MyInvocation.BoundParameters.ContainsKey("Next")) { + foreach (PSObject resultObj in ExpandObject(result, !ExcludeTableInfo, this.TableName)) { + + + collectedSoFar++; + + if (collectedSoFar >= First) { + if (! (String.IsNullOrEmpty(nextRow) && String.IsNullOrEmpty(nextPart))) { + PSNoteProperty nextRowKey = new PSNoteProperty("NextRowKey", nextRow); + resultObj.Properties.Add(nextRowKey); + PSNoteProperty nextPartition= new PSNoteProperty("NextPartition", nextPart); + resultObj.Properties.Add(nextPartition); + + } + WriteObject(resultObj); + break; + } else { + WriteObject(resultObj); + } + } + } else { + WriteObject( + ExpandObject(result, !ExcludeTableInfo, this.TableName), true); + } + } + + if (! (String.IsNullOrEmpty(nextRow) && String.IsNullOrEmpty(nextPart))) { + thereIsMore = true; + } else { + thereIsMore = false; + } + } while (thereIsMore); + } + } +} diff --git a/CSharp/SetAzureTableCommand.cs b/CSharp/SetAzureTableCommand.cs new file mode 100644 index 0000000..7dbaa04 --- /dev/null +++ b/CSharp/SetAzureTableCommand.cs @@ -0,0 +1,113 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Net; + using System.IO; + using System.Linq; + using System.Xml.Linq; + using System.Security; + using System.Reflection; + + [Cmdlet(VerbsCommon.Set, "AzureTable", SupportsShouldProcess=true)] + public class SetAzureTableCommand : AzureTableCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName=true)] + [Alias("Name")] + public string TableName + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + [Alias(new string[] { "TablePart", "TablePartition", "Partition" })] + public string PartitionKey + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + [Alias(new string[] { "TableRow", "Row"})] + public string RowKey + { + get; + set; + } + + [Parameter(Mandatory=true,Position=0,ValueFromPipeline=true)] + public PSObject InputObject + { + get; + set; + } + + + [Parameter()] + public SwitchParameter PassThru + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + public string Author + { + get; + set; + } + + [Parameter(ValueFromPipelineByPropertyName=true)] + public string Email + { + get; + set; + } + + [Parameter()] + public SwitchParameter ExcludeTableInfo + { + get; + set; + } + + + [Parameter()] + public int StartAtRow + { + get { return rowNumber; } + set { rowNumber = value; } + } + + int rowNumber = 0; + protected override void ProcessRecord() + { + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + if (! (this.MyInvocation.BoundParameters.ContainsKey("RowKey"))) + { + RowKey = this.rowNumber.ToString(); + rowNumber++; + } + if (!(this.MyInvocation.BoundParameters.ContainsKey("PartitionKey"))) + { + PartitionKey = "Default"; + } + if (this.ShouldProcess(this.TableName + "/" + this.PartitionKey + "/" + this.RowKey)) { + if (PassThru) + { + WriteObject( + InsertEntity(this.TableName, this.PartitionKey, this.RowKey, this.InputObject, this.Author, this.Email, false, false, ExcludeTableInfo), + true); + } + else + { + InsertEntity(this.TableName, this.PartitionKey, this.RowKey, this.InputObject, this.Author, this.Email, false, false, true); + } + } + } + } +} diff --git a/CSharp/UpdateAzureTableCommand.cs b/CSharp/UpdateAzureTableCommand.cs new file mode 100644 index 0000000..1e05421 --- /dev/null +++ b/CSharp/UpdateAzureTableCommand.cs @@ -0,0 +1,84 @@ +namespace AzureStorageCmdlets +{ + using System; + using System.Collections.Generic; + using System.Text; + using System.Management.Automation; + using System.Net; + using System.IO; + using System.Linq; + using System.Xml.Linq; + using System.Security; + using System.Reflection; + + [Cmdlet(VerbsData.Update, "AzureTable")] + public class UpdateAzureTableCommand: AzureTableCmdletBase + { + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Alias("Name")] + public string TableName + { + get; + set; + } + + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Alias(new string[] { "TablePart", "TablePartition"})] + public string PartitionKey + { + get; + set; + } + + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Alias(new string[] { "TableRow"})] + public string RowKey + { + get; + set; + } + + [Parameter(Mandatory = true, Position = 0, ValueFromPipeline=true, ValueFromPipelineByPropertyName = true)] + public PSObject Value + { + get; + set; + } + + + [Parameter()] + public SwitchParameter PassThru + { + get; + set; + } + + [Parameter()] + public string Author + { + get; + set; + } + + [Parameter()] + public string Email + { + get; + set; + } + + [Parameter()] + public SwitchParameter Merge + { + get; + set; + } + + protected override void ProcessRecord() + { + base.ProcessRecord(); + if (String.IsNullOrEmpty(StorageAccount) || String.IsNullOrEmpty(StorageKey)) { return; } + InsertEntity(this.TableName, this.PartitionKey, this.RowKey, this.Value, this.Author, this.Email, true, Merge, true); + } + } +} diff --git a/Close-Port.ps1 b/Close-Port.ps1 new file mode 100644 index 0000000..ada1f49 Binary files /dev/null and b/Close-Port.ps1 differ diff --git a/Compress-Data.ps1 b/Compress-Data.ps1 new file mode 100644 index 0000000..6a76472 Binary files /dev/null and b/Compress-Data.ps1 differ diff --git a/Confirm-Person.ps1 b/Confirm-Person.ps1 new file mode 100644 index 0000000..9f95fb3 Binary files /dev/null and b/Confirm-Person.ps1 differ diff --git a/Connect-EC2.ps1 b/Connect-EC2.ps1 new file mode 100644 index 0000000..4cafeb6 Binary files /dev/null and b/Connect-EC2.ps1 differ diff --git a/ConvertFrom-InlinePowerShell.ps1 b/ConvertFrom-InlinePowerShell.ps1 new file mode 100644 index 0000000..3059d31 Binary files /dev/null and b/ConvertFrom-InlinePowerShell.ps1 differ diff --git a/ConvertFrom-Markdown.ps1 b/ConvertFrom-Markdown.ps1 new file mode 100644 index 0000000..955d2b9 Binary files /dev/null and b/ConvertFrom-Markdown.ps1 differ diff --git a/ConvertTo-DataTable.ps1 b/ConvertTo-DataTable.ps1 new file mode 100644 index 0000000..95171e6 Binary files /dev/null and b/ConvertTo-DataTable.ps1 differ diff --git a/ConvertTo-ModuleService.ps1 b/ConvertTo-ModuleService.ps1 new file mode 100644 index 0000000..82a9e58 Binary files /dev/null and b/ConvertTo-ModuleService.ps1 differ diff --git a/ConvertTo-ServiceUrl.ps1 b/ConvertTo-ServiceUrl.ps1 new file mode 100644 index 0000000..4e7f30a --- /dev/null +++ b/ConvertTo-ServiceUrl.ps1 @@ -0,0 +1,63 @@ +function ConvertTo-ServiceUrl +{ + <# + .Synopsis + Converts parameters into a full URL for a Pipeworks Service + .Description + Converts parameters into a full URL for a Pipeworks web service. + + This allows you to easily call another a Pipeworks web service with a uniform URL. + + This can work because all Pipeworks services have a very uniform URL format: + + ServiceUrl/Command/?Command_Parameter1=Value&Command_Parameter2=Value + .Example + ConvertTo-ServiceUrl -ServiceUrl "http://powershellpipeworks.com/" -CommandName "Write-ScriptHTML" -Parameter @{ + Text = "'hello world'" + } + .Link + ConvertTo-ModuleService + #> + [OutputType([string])] + param( + # The root URL of the web service + [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] + [Uri] + $ServiceUrl, + + # The name of the command in the Pipeworks module. + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true)] + [string] + $CommandName, + + # The name of the command in the Pipeworks module. + [Parameter(Mandatory=$true,Position=2,ValueFromPipelineByPropertyName=$true)] + [Alias('Parameters')] + [Hashtable] + $Parameter, + + # If set, will get a URL to return the XML + [switch] + $AsXml + ) + + process { + #region Create a GET string + # Carry over the parameters + $actionUrl = foreach ($kv in $Parameter.GetEnumerator()) { + "${CommandName}_$($kv.Key)=$([Web.HttpUtility]::UrlEncode($kv.Value))" + } + + $actionUrl = "/${CommandName}/?" + ($actionUrl -join '&') + #endregion Create a GET string + + if ($AsXml) { + $actionUrl += "&AsXml=true" + } + if ($ServiceUrl) { + $actionUrl = "$ServiceUrl".TrimEnd("/") + $actionUrl + } + + $actionUrl + } +} \ No newline at end of file diff --git a/Enable-EC2Remoting.ps1 b/Enable-EC2Remoting.ps1 new file mode 100644 index 0000000..29ced66 Binary files /dev/null and b/Enable-EC2Remoting.ps1 differ diff --git a/Expand-Data.ps1 b/Expand-Data.ps1 new file mode 100644 index 0000000..6e72565 Binary files /dev/null and b/Expand-Data.ps1 differ diff --git a/Expand-Zip.ps1 b/Expand-Zip.ps1 new file mode 100644 index 0000000..f69cf4d Binary files /dev/null and b/Expand-Zip.ps1 differ diff --git a/Export-Blob.ps1 b/Export-Blob.ps1 new file mode 100644 index 0000000..c8c8d90 --- /dev/null +++ b/Export-Blob.ps1 @@ -0,0 +1,413 @@ +function Export-Blob +{ + <# + .Synopsis + Exports data to a cloud blob + .Description + Exports data to a blob in Azure + .Example + Get-ChildItem -Filter *.ps1 | Export-Blob -Container scripts -StorageAccount astorageAccount -StorageKey (Get-SecureSetting aStorageKey -ValueOnly) + .Link + Get-Blob + .Link + Import-Blob + .Link + Remove-Blob + #> + [OutputType([Nullable])] + param( + # The input object for the blob. + [Parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + + # The name of the container + [Parameter(Mandatory=$true,Position=0, ValueFromPipelineByPropertyName=$true)] + [string]$Container, + + # The name of the blob + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true)] + [string]$Name, + + # The content type. If a file is provided as input, this will be provided automatically. If not, it will be text/plain + [string]$ContentType = "text/plain", + + # The storage account + [string]$StorageAccount, + + # The storage key + [string]$StorageKey, + + # If set, the container the blob is put into will be made public + [Switch] + $Public + ) + + + begin { + + #region Create a lookup table of mime types + if (-not $script:cachedContentTypes) { + $script:cachedContentTypes = @{} + $ctKey = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey("MIME\Database\Content Type") + $ctKey.GetSubKeyNames() | + ForEach-Object { + $extension= $ctKey.OpenSubKey($_).GetValue("Extension") + if ($extension) { + $script:cachedContentTypes["${extension}"] = $_ + } + } + + } + #endregion Create a lookup table of mime types + + # Create a lambda to sign messages +$signMessage = { + param( + [Hashtable]$Header, + [Uri]$Url, + [Uint32]$ContentLength, + [string]$IfMatch ="", + [string]$Md5OrContentType = "", + [string]$NowString = [DateTime]::now.ToString("R", [Globalization.CultureInfo]::InvariantCulture), + [Switch]$IsTableStorage, + [string]$method = "GET", + [string]$Storageaccount, + [string]$StorageKey + ) + + $method = $method.ToUpper() + $MessageSignature = + if ($IsTableStorage) { + [String]::Format("{0}`n`n{1}`n{2}`n{3}",@( + $method, + "application/atom+xml", + $NowString, + ( & $GetCanonicalizedResource $Url $StorageAccount))) + + } else { + if ($md5OrCOntentType) { + [String]::Format("{0}`n`n`n{1}`n`n{5}`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } else { + [String]::Format("{0}`n`n`n{1}`n{5}`n`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } + + } + + $SignatureBytes = [Text.Encoding]::UTF8.GetBytes($MessageSignature) + + [byte[]]$b64Arr = [Convert]::FromBase64String($StorageKey) + $SHA256 = new-object Security.Cryptography.HMACSHA256 + $sha256.Key = $b64Arr + $AuthorizationHeader = "SharedKey " + $StorageAccount + ":" + [Convert]::ToBase64String($SHA256.ComputeHash($SignatureBytes)) + $AuthorizationHeader +} + +$GetCanonicalizedHeader = { + param( + [Hashtable]$Header + ) + + $headerNameList = new-OBject Collections.ArrayList; + $sb = new-object Text.StringBuilder; + foreach ($headerName in $Header.Keys) { + if ($headerName.ToLowerInvariant().StartsWith("x-ms-", [StringComparison]::Ordinal)) { + $null = $headerNameList.Add($headerName.ToLowerInvariant()); + } + } + $null = $headerNameList.Sort(); + [Collections.Specialized.NameValueCollection]$headers =NEw-OBject Collections.Specialized.NameValueCollection + foreach ($h in $header.Keys) { + $null = $headers.Add($h, $header[$h]) + } + + + foreach ($headerName in $headerNameList) + { + $builder = new-Object Text.StringBuilder $headerName + $separator = ":"; + foreach ($headerValue in (& $GetHeaderValues $headers $headerName)) + { + $trimmedValue = $headerValue.Replace("`r`n", [String]::Empty) + $null = $builder.Append($separator) + $null = $builder.Append($trimmedValue) + $separator = "," + } + $null = $sb.Append($builder.ToString()) + $null = $sb.Append("`n") + } + return $sb.ToString() +} + + +$GetHeaderValues = { + param([Collections.Specialized.NameValueCollection]$headers, $headerName) + $list = new-OBject Collections.ArrayList + + $values = $headers.GetValues($headerName) + if ($values -ne $null) + { + foreach ($str in $values) { + $null = $list.Add($str.TrimStart($null)) + } + } + return $list; +} + +$GetCanonicalizedResource = { + param([uri]$address, [string]$accountName) + + $str = New-object Text.StringBuilder + $builder = New-object Text.StringBuilder "/" + $null = $builder.Append($accountName) + $null = $builder.Append($address.AbsolutePath) + $null = $str.Append($builder.ToString()) + $values2 = New-Object Collections.Specialized.NameValueCollection + if (!$IsTableStorage) { + $values = [Web.HttpUtility]::ParseQueryString($address.Query) + foreach ($str2 in $values.Keys) { + $list = New-Object Collections.ArrayList + foreach ($v in $values.GetValues($str2)) { + $null = $list.add($v) + } + $null = $list.Sort(); + $builder2 = New-Object Text.StringBuilder + foreach ($obj2 in $list) + { + if ($builder2.Length -gt 0) + { + $null = $builder2.Append(","); + } + $null = $builder2.Append($obj2.ToString()); + } + $valueName = if ($str2 -eq $null) { + $str2 + } else { + $str2.ToLowerInvariant() + } + $values2.Add($valueName , $builder2.ToString()) + } + } + $list2 = New-Object Collections.ArrayList + foreach ($k in $values2.AllKeys) { + $null = $list2.Add($k) + } + $null = $list2.Sort() + foreach ($str3 in $list2) + { + $builder3 = New-Object Text.StringBuilder([string]::Empty); + $null = $builder3.Append($str3); + $null = $builder3.Append(":"); + $null = $builder3.Append($values2[$str3]); + $null = $str.Append("`n"); + $null = $str.Append($builder3.ToString()); + } + return $str.ToString(); + +} + + #$inputList = New-Object Collections.ArrayList + $inputData = New-Object Collections.ArrayList + + if (-not $script:alreadyPublicContainers) { + $script:alreadyPublicContainers = @{} + } + + if (-not $script:knownContainers) { + $script:knownContainers= @{} + } + } + + + process { + #$null = $inputList.Add($inputObject) + $null = $inputData.Add((@{} + $psBoundParameters)) + } + + end { + #region check for and cache the storage account + if (-not $StorageAccount) { + $storageAccount = $script:CachedStorageAccount + } + + if (-not $StorageKey) { + $StorageKey = $script:CachedStorageKey + } + + if (-not $StorageAccount) { + Write-Error "No storage account provided" + return + } + + if (-not $StorageKey) { + Write-Error "No storage key provided" + return + } + + $script:CachedStorageAccount = $StorageAccount + $script:CachedStorageKey = $StorageKey + #endregion check for and cache the storage account + foreach ($inputInfo in $inputData) { + if ($inputInfo.Name) { + $Name = $inputInfo.Name + } + + if ($inputInfo.Container) { + $Container = $inputInfo.Container + } + + $InputObject = $inputInfo.InputObject + + $containerBlobList = $null + $Container = "$Container".ToLower() + + if (-not $knownContainers[$Container]) { + $method = 'GET' + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container&comp=list&include=metadata" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method GET + + + $containerBlobList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method GET -ErrorAction SilentlyContinue -ErrorVariable err -HideProgress + + if ($containerBlobList) { + $knownContainers[$Container] = $knownContainers[$Container] + } + } + + if (-not $containerBlobList) { + # Tries to create the container if it's not found + $method = 'PUT' + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container" + + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method PUT + $Putresult = + try { + Get-Web -UseWebRequest -Header $header -Url $Uri -Method PUT -HideProgress + } catch { + $_ + } + $null = $Putresult + + } + + + if ($Public -and -not $script:alreadyPublicContainers[$Container]){ + + # Enables public access to the container + $acl =@" +<?xml version="1.0" encoding="utf-8"?> + <SignedIdentifiers> + <SignedIdentifier> + <Id>Policy1</Id> + <AccessPolicy> + <Start>2011-01-01T09:38:05Z</Start> + <Expiry>2011-12-31T09:38:05Z</Expiry> + <Permission>r</Permission> + </AccessPolicy> + </SignedIdentifier> + <SignedIdentifier> + <Id>Policy2</Id> + <AccessPolicy> + <Start>2010-01-01T09:38:05Z</Start> + <Expiry>2012-12-31T09:38:05Z</Expiry> + <Permission>r</Permission> + </AccessPolicy> + </SignedIdentifier> +</SignedIdentifiers> +"@ + + $aclBytes= [Text.Encoding]::UTF8.GetBytes("$acl") + $method = 'PUT' + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container&comp=acl" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "x-ms-blob-public-access" = "container" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + 'content-type' = $ct + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $ct ='application/x-www-form-urlencoded' + $header.authorization = & $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength $aclBytes.Length -method PUT -md5OrContentType $ct + $Created = Get-Web -UseWebRequest -Header $header -Url $Uri -Method PUT -RequestBody $aclBytes -ErrorAction SilentlyContinue + $null = $Created + $script:alreadyPublicContainers[$Container] = $Container + + + } + + + $uri = "http://$StorageAccount.blob.core.windows.net/$Container/$Name" + + + # Turn our input into bytes + if ($InputObject -is [IO.FileInfo]) { + $bytes = [io.fIle]::ReadAllBytes($InputObject.Fullname) + $extension = [IO.Path]::GetExtension($InputObject.Fullname) + $mimeType = $script:CachedContentTypes[$extension] + if (-not $mimeType) { + $mimetype = "unknown/unknown" + } + } elseif ($InputObject -as [byte[]]) { + $bytes = $InputObject -as [byte[]] + } else { + $bytes = [Text.Encoding]::UTF8.GetBytes("$InputObject") + } + + if (-not $mimetype -or $psBoundParameters.ContentType) { + $mimeType = $ContentType + } + + $method = 'PUT' + $header = @{ + 'x-ms-blob-type' = 'BlockBlob' + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + 'content-type' = $mimeType + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength $bytes.Length -method PUT -md5OrContentType $mimeType + + + $blobData= Get-Web -UseWebRequest -Header $header -Url $Uri -Method PUT -RequestBody $bytes -ContentType $mimeType + $null = $blobData + } + } +} \ No newline at end of file diff --git a/Export-DataTable.ps1 b/Export-DataTable.ps1 new file mode 100644 index 0000000..efa509e Binary files /dev/null and b/Export-DataTable.ps1 differ diff --git a/Export-PSData.ps1 b/Export-PSData.ps1 new file mode 100644 index 0000000..26e25ac Binary files /dev/null and b/Export-PSData.ps1 differ diff --git a/Find-Factual.ps1 b/Find-Factual.ps1 new file mode 100644 index 0000000..d373276 --- /dev/null +++ b/Find-Factual.ps1 @@ -0,0 +1,308 @@ +function Find-Factual { + <# + .Synopsis + Finds content on Factual + .Description + Finds content on Factual's global places API + .Example + Find-Factual Starbucks in Seattle + .Example + $l = Resolve-Location -Address 'Redmond, WA' + Find-Factual -GeoPulse -TypeOfFilter Point -Filter "$($l.longitude),$($l.Latitude)" -Verbose + .Example + Find-Factual -InTable vYrq7F -Filter 'Washington' -TypeOfFilter State -Limit 50 + .Example + # Wineries + Find-Factual -InTable cQUvfi + .Link + Get-Web + #> + [OutputType([PSObject])] + param( + # The factual query + [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] + [string] + $Query, + + # The type of the filter + [Parameter(Position=1)] + [ValidateSet("In","Near","Category","Country", "UPC", "EAN", "Brand", "Point", "Name", "Brewery", "Beer", "Style", "State", "PostCode")] + [string[]] + $TypeOfFilter, + + # The filter + [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] + [string[]] + $Filter, + + # Within. This is only used when 'near' is used + [Parameter(Position=3,ValueFromPipelineByPropertyName=$true)] + [Uint32] + $Within = 1000, + + # Your Factual API Key + [Parameter(Position=4,ValueFromPipelineByPropertyName=$true)] + [string] + $FactualKey, + + # A secure setting containing your factual key + [Parameter(Position=5)] + [string] + $FactualKeySetting = "FactualKey", + + # If set, will only find US resturaunts + [Switch] + $Restaurants, + + # If set, will only find health care providers + [Switch] + $HeathCare, + + # If set, will only find products + [Switch] + $Product, + + # If set, searches the places data set + [switch] + $Place, + + # If set, gets the GeoPulse of an area + [Switch] + $GeoPulse, + + # If set, will get data from a table + [string] + $InTable, + + # If set, will limit the number of responses returned + [ValidateRange(1,50)] + [Uint32] + $Limit, + + # If set, will start returning results at a point + [Uint32] + $Offset, + + # If set, will query all records that match a filter. This will result in multiple queries. + [Switch] + $All + + + ) + + process { + $filters = "" + + if ($TypeOfFilter.Count -ne $Filter.Count) { + throw "Must be an equal number of filters and types of filters" + } + + + $geoString = "" + + + $filterString = + for ($i = 0; $i -lt $TypeOfFilter.Count; $i++) { + if ($TypeOfFilter[$i] -eq 'Category') { + "{`"category`":{`"`$bw`":$('"' + ($Filter[$i] -join '","') + '"')}}" + } elseif ($TypeOfFilter[$i] -eq 'In') { + + "{`"locality`":{`"`$in`":[$('"' + ($Filter[$i] -join '","') + '"')]}}" + + + } elseif ($TypeOfFilter[$i] -eq 'Upc') { + + "{`"upc`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Ean13') { + + "{`"ean13`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'ProductName') { + + "{`"product_name`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Name') { + + "{`"name`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Brewery') { + + "{`"brewery`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Beer') { + + "{`"beer`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'State') { + + "{`"state`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Country') { + + "{`"country`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Style') { + + "{`"style`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Brand') { + + "{`"brand`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'PostCode') { + + "{`"postcode`":`"$($Filter[$i])`"}" + + + } elseif ($TypeOfFilter[$i] -eq 'Near') { + + "" + $geoString = "&geo={`"`$circle`":{`"`$center`":[$(($Filter[$i] -split ",")[0]),$(($Filter[$i] -split ",")[1])],`"`$meters`":$Within }}" + } elseif ($TypeOfFilter[$i] -eq 'Point') { + "" + $lat = [Math]::Round((($Filter[$i] -split ",")[0]), 5) + $long = [Math]::Round((($Filter[$i] -split ",")[1]), 5) + $geoString = "&geo={`"`$point`":[$lat,$long], `"`$meters`":$within}" + } + } + + + + + + + $factualUrl = "http://api.v3.factual.com/t/global?" + if ($Restaurants) { + $factualUrl = "http://api.v3.factual.com/t/restaurants-us?" + } elseif ($HeathCare) { + $factualUrl = "http://api.v3.factual.com/t/health-care-providers-us?" + } elseif ($Place) { + $factualUrl = "http://api.v3.factual.com/t/world-geographies?" + } elseif ($product) { + $factualUrl = "http://api.v3.factual.com/t/products-cpg?" + } elseif ($GeoPulse) { + $factualUrl = "http://api.v3.factual.com/places/geopulse?" + } elseif ($InTable) { + $factualUrl = "http://api.v3.factual.com/t/${InTable}?" + + } + + if ($Query) { + $factualUrl += "q=$Query&" + } else { + } + + if ($filterString) { + + $factualUrl += + if ($filterstring -is [Array]) { + # ands + "filters={`"`$and`":[$($filterString -join ',')]}" + } else { + # simple $filter + "filters=$($filterString)" + } + } else { + $geoString= $geoString.TrimStart("&") + } + if ($geoString) { + $factualUrl += $geostring + } + + if (-not $GeoPulse) { + $factualUrl +="&include_count=true" + + if ($limit) { + $factualUrl +="&limit=$limit" + } + + if ($Offset) { + $factualUrl +="&offset=$offset" + } + } + + + + + Write-Verbose "Querying From Factual $factualUrl&Key=******" + + if (-not $FactualKey) { + $FactualKey = Get-SecureSetting -Name $FactualKeySetting -ValueOnly + } + + + $factualUrl += + if($FactualKey ){ + "&KEY=$FACTUALKey" + } + + $factualResult = Get-Web -Url $factualUrl -AsJson -UseWebRequest + + while ($factualResult) { + + $rowCount = $factualResult.response.total_row_count + if ($rowCount) { + Write-Verbose "$RowCount total records to return" + } + + + + + + + $factualResult= $factualResult.response.data + if (-not $factualResult) { break } + + $factualResult = foreach ($f in $factualResult) { + if (-not $f){ continue } + + if ($geoPulse) { + $null = Update-List -InputObject $f -remove "System.Management.Automation.PSCustomObject", "System.Object" -add "Factual.GeoPulse" -Property pstypenames + } elseif ($f.Beer) { + $null = Update-List -InputObject $f -remove "System.Management.Automation.PSCustomObject", "System.Object" -add "Factual.Beer" -Property pstypenames + } elseif ($f.Operating_Name -and $f.permit_number) { + $null = Update-List -InputObject $f -remove "System.Management.Automation.PSCustomObject", "System.Object" -add "Factual.Winery" -Property pstypenames + } elseif (-not $Product) { + $f = $f | + Add-Member AliasProperty telephone tel -Force -PassThru | + Add-Member AliasProperty url website -Force -PassThru + $null = Update-List -InputObject $f -remove "System.Management.Automation.PSCustomObject", "System.Object" -add "http://schema.org/Place" -Property pstypenames + } else { + + $null = Update-List -InputObject $f -remove "System.Management.Automation.PSCustomObject", "System.Object" -add "http://schema.org/Product" -Property pstypenames + } + $f + } + $factualResult + + if ($all) { + if ($factualUrl -like "*offset=*") { + $factualUrl = $factualUrl -replace '\&offset=\d{1,}', '' + $Offset += 20 + } else { + $Offset = 20 + } + $factualUrl+="&offset=$Offset" + $factualResult = Get-Web -Url $factualUrl -AsJson -UseWebRequest + } else { + $factualResult = $null + + } + } +# + } +} + + + diff --git a/Get-Ami.ps1 b/Get-Ami.ps1 new file mode 100644 index 0000000..d05236b Binary files /dev/null and b/Get-Ami.ps1 differ diff --git a/Get-Bootstrapped.ps1 b/Get-Bootstrapped.ps1 new file mode 100644 index 0000000..28e187e Binary files /dev/null and b/Get-Bootstrapped.ps1 differ diff --git a/Get-Deployment.ps1 b/Get-Deployment.ps1 new file mode 100644 index 0000000..acede63 Binary files /dev/null and b/Get-Deployment.ps1 differ diff --git a/Get-EC2.ps1 b/Get-EC2.ps1 new file mode 100644 index 0000000..a3cb9c0 Binary files /dev/null and b/Get-EC2.ps1 differ diff --git a/Get-EC2AvailabilityZone.ps1 b/Get-EC2AvailabilityZone.ps1 new file mode 100644 index 0000000..04c4a74 Binary files /dev/null and b/Get-EC2AvailabilityZone.ps1 differ diff --git a/Get-EC2InstancePassword.ps1 b/Get-EC2InstancePassword.ps1 new file mode 100644 index 0000000..90f0957 Binary files /dev/null and b/Get-EC2InstancePassword.ps1 differ diff --git a/Get-EC2KeyPair.ps1 b/Get-EC2KeyPair.ps1 new file mode 100644 index 0000000..38e3d42 Binary files /dev/null and b/Get-EC2KeyPair.ps1 differ diff --git a/Get-EC2SecurityGroup.ps1 b/Get-EC2SecurityGroup.ps1 new file mode 100644 index 0000000..2c834df Binary files /dev/null and b/Get-EC2SecurityGroup.ps1 differ diff --git a/Get-Email.old.ps1 b/Get-Email.old.ps1 new file mode 100644 index 0000000..83f76ae Binary files /dev/null and b/Get-Email.old.ps1 differ diff --git a/Get-Email.ps1 b/Get-Email.ps1 new file mode 100644 index 0000000..cccb2f8 --- /dev/null +++ b/Get-Email.ps1 @@ -0,0 +1,194 @@ +function Get-Email +{ + <# + .Synopsis + Gets email from exchange + .Description + Gets email from an exchange server + .Link + Invoke-Office365 + .Example + Get-Email + #> + [OutputType([PSObject])] + [CmdletBinding(DefaultParameterSetName='UserNameAndPasswordSetting')] + param( + # The account + [Parameter(Mandatory=$true,ParameterSetName='SpecificAccount')] + [Management.Automation.PSCredential] + $Account, + + # The setting containing the username + [Parameter(ParameterSetName='UserNameAndPasswordSetting')] + [string] + $UserNameSetting = 'Office365Username', + + # The setting containing the password + [Parameter(ParameterSetName='UserNameAndPasswordSetting')] + [string] + $PasswordSetting = 'Office365Password', + + # The email account to connect to retreive data from. If not specified, email will be retreived for the account used to connect. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $Email, + + # If set, will only return unread messages + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $Unread, + + # The name of the contact the email was sent to. This the displayed name, not a full email address + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $To, + + # The email that sent the message + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $From, + + # If set, will download the email content, not just the headers + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $Download + ) + + begin { +$wsPath = $MyInvocation.MyCommand.ScriptBlock.File | + Split-Path | + Get-ChildItem -Filter bin | + Get-ChildItem -Filter Microsoft.Exchange.WebServices.dll + +$ra = Add-Type -Path $wspath.FullName -PassThru | Select-Object -ExpandProperty Assembly -Unique | Select-Object -ExpandProperty Location + +Add-Type -ReferencedAssemblies $ra -TypeDefinition @' +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Exchange.WebServices.Data; +using System.Net.Security; +using System.Net; +using Microsoft.Exchange.WebServices.Autodiscover; +using System.Configuration; + +public class Office365EWSHelper2 +{ + /// <summary> + /// Bind to Mailbox via AutoDiscovery + /// </summary> + /// <returns>Exchange Service object</returns> + public static ExchangeService GetBinding(WebCredentials credentials, string lookupEmail) + { + // Create the binding. + ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP1); + + // Define credentials. + service.Credentials = credentials; + + // Use the AutodiscoverUrl method to locate the service endpoint. + service.AutodiscoverUrl(lookupEmail, RedirectionUrlValidationCallback); + return service; + } + + + // Create the callback to validate the redirection URL. + static bool RedirectionUrlValidationCallback(String redirectionUrl) + { + // Perform validation. + return true; // (redirectionUrl == "https://autodiscover-s.outlook.com/autodiscover/autodiscover.xml"); + } + +} + +'@ + + + + } + process { + if ($Account) { + $Cred = $Account + } elseif ($UserNameSetting -and $PasswordSetting) { + $cred = New-Object Management.Automation.PSCredential (Get-SecureSetting $UserNameSetting -ValueOnly), + (ConvertTo-SecureString -AsPlainText -Force (Get-SecureSetting $PasswordSetting -ValueOnly)) + } + + if (-not $script:ewsForUser) { + $script:ewsForUser = @{} + } + $ForEmail = if ($Email) { + $Email + } else { + $cred.UserName + } + if (-not $ewsForUser["${ForEmail}_AS_$($Cred.UserName)"]) { + + $ews = [Office365EwsHelper2]::GetBinding($cred.GetNetworkCredential(), $ForEmail) + $script:ewsForUser["${ForEmail}_AS_$($Cred.UserName)"] = $ews + } else { + $ews = $script:ewsForUser["${ForEmail}_AS_$($Cred.UserName)"] + } + + $coll =New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection + + if ($Unread) { + $unreadFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo -Property @{PropertyDefinition=[Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::IsRead;Value='false'} + $coll.add($unreadFilter) + } + + if ($To) { + if ($to -notlike "*@.*") { + $toEmail = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring -Property @{PropertyDefinition=[Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::DisplayTo;Value=$To} + $coll.add($toEmail) + + + } else { + $toEmail = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+ContainsSubstring -Property @{PropertyDefinition=[Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::DisplayTo;Value=$To} + $coll.add($toEmail) + } + + } + + if ($From) { + $fromEmail = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo -Property @{PropertyDefinition=[Microsoft.Exchange.WebServices.Data.EmailMessageSchema]::From;Value=$From} + $coll.add($fromEmail ) + + } + + + + $fid = New-Object Microsoft.Exchange.WebServices.Data.FolderId "Inbox", $ForEmail + $iv = New-Object Microsoft.Exchange.WebServices.Data.ItemView 1000 + $fiItems = $null + do{ + + if ($coll.Count) { + $fiItems = $ews.FindItems($fid , $coll, $iv) + } else { + $fiItems = $ews.FindItems($fid , "", $iv) + } + + foreach ($Item in $fiItems) { + if ($Download) { + $item.load() + } + + if ($item.From.RoutingType -eq 'EX') { + $_ = $_ + $emails = $ews.ResolveName($item.From.Name, "DirectoryOnly", $true) | ForEach-Object { $_.Mailbox } | ForEach-Object{ $_.GetSearchString() } + $emails = $emails -join ',' + $item | + Add-Member NoteProperty FromEmail $emails + } else { + $item | + Add-Member NoteProperty FromEmail $item.From.Address + } + $Item + } + $iv.offset += $fiItems.Items.Count + }while($fiItems.MoreAvailable -eq $true) + + } +} \ No newline at end of file diff --git a/Get-FunctionFromScript.ps1 b/Get-FunctionFromScript.ps1 new file mode 100644 index 0000000..c1a6618 --- /dev/null +++ b/Get-FunctionFromScript.ps1 @@ -0,0 +1,109 @@ +function Get-FunctionFromScript { + <# + .Synopsis + Gets the functions declared within a script block or a file + .Description + Gets the functions exactly as they are written within a script or file + .Example + Get-FunctionFromScript { + function foo() { + "foo" + } + function bar() { + "bar" + } + } + .Link + http://powershellpipeworks.com/ + #> + [CmdletBinding(DefaultParameterSetName='File')] + [OutputType([ScriptBlock], [PSObject])] + param( + # The script block containing functions + [Parameter(Mandatory=$true, + Position=0, + ParameterSetName="ScriptBlock", + ValueFromPipelineByPropertyName=$true)] + [ScriptBlock] + $ScriptBlock, + + # A file containing functions + [Parameter(Mandatory=$true, + ParameterSetName="File", + ValueFromPipelineByPropertyName=$true)] + [Alias('FullName')] + [String] + $File, + + # If set, outputs the command metadatas + [switch] + $OutputMetaData + ) + + process { + if ($psCmdlet.ParameterSetName -eq "File") { + #region Resolve the file, create a script block, and pass the data down + $realFile = Get-Item $File + if (-not $realFile) { + $realFile = Get-Item -LiteralPath $File -ErrorAction SilentlyContinue + if (-not $realFile) { + return + } + } + $text = [IO.File]::ReadAllText($realFile.Fullname) + $scriptBlock = [ScriptBlock]::Create($text) + if ($scriptBlock) { + $functionsInScript = + Get-FunctionFromScript -ScriptBlock $scriptBlock -OutputMetaData:$OutputMetaData + if ($OutputMetaData) + { + $functionsInScript | + Add-Member NoteProperty File $realFile.FullName -PassThru + } + } + #endregion Resolve the file, create a script block, and pass the data down + } elseif ($psCmdlet.ParameterSetName -eq "ScriptBlock") { + #region Extract out core functions from a Script Block + $text = $scriptBlock.ToString() + $tokens = [Management.Automation.PSParser]::Tokenize($scriptBlock, [ref]$null) + for ($i = 0; $i -lt $tokens.Count; $i++) { + if ($tokens[$i].Content -eq "function" -and + $tokens[$i].Type -eq "Keyword") { + $groupDepth = 0 + $functionName = $tokens[$i + 1].Content + $ii = $i + $done = $false + while (-not $done) { + while ($tokens[$ii] -and $tokens[$ii].Type -ne 'GroupStart') { $ii++ } + $groupDepth++ + while ($groupDepth -and $tokens[$ii]) { + $ii++ + if ($tokens[$ii].Type -eq 'GroupStart') { $groupDepth++ } + if ($tokens[$ii].Type -eq 'GroupEnd') { $groupDepth-- } + } + if (-not $tokens[$ii]) { break } + if ($tokens[$ii].Content -eq "}") { + $done = $true + } + } + if (-not $tokens[$ii] -or + ($tokens[$ii].Start + $tokens[$ii].Length) -ge $Text.Length) { + $chunk = $text.Substring($tokens[$i].Start) + } else { + $chunk = $text.Substring($tokens[$i].Start, + $tokens[$ii].Start + $tokens[$ii].Length - $tokens[$i].Start) + } + if ($OutputMetaData) { + New-Object PSObject -Property @{ + Name = $functionName + Definition = [ScriptBlock]::Create($chunk) + } + } else { + [ScriptBlock]::Create($chunk) + } + } + } + #endregion Extract out core functions from a Script Block + } + } +} diff --git a/Get-Hash.ps1 b/Get-Hash.ps1 new file mode 100644 index 0000000..f745a83 Binary files /dev/null and b/Get-Hash.ps1 differ diff --git a/Get-Paid.ps1 b/Get-Paid.ps1 new file mode 100644 index 0000000..ad4461c Binary files /dev/null and b/Get-Paid.ps1 differ diff --git a/Get-PhoneCall.ps1 b/Get-PhoneCall.ps1 new file mode 100644 index 0000000..e5f0693 Binary files /dev/null and b/Get-PhoneCall.ps1 differ diff --git a/Get-PipeworksManifest.ps1 b/Get-PipeworksManifest.ps1 new file mode 100644 index 0000000..fe424e3 Binary files /dev/null and b/Get-PipeworksManifest.ps1 differ diff --git a/Get-SQLTable.ps1 b/Get-SQLTable.ps1 new file mode 100644 index 0000000..53ddd2b Binary files /dev/null and b/Get-SQLTable.ps1 differ diff --git a/Get-SecureSetting.ps1 b/Get-SecureSetting.ps1 new file mode 100644 index 0000000..c1864dc Binary files /dev/null and b/Get-SecureSetting.ps1 differ diff --git a/Get-TextMessage.ps1 b/Get-TextMessage.ps1 new file mode 100644 index 0000000..be9475b Binary files /dev/null and b/Get-TextMessage.ps1 differ diff --git a/Get-Translation.ps1 b/Get-Translation.ps1 new file mode 100644 index 0000000..8324dd8 Binary files /dev/null and b/Get-Translation.ps1 differ diff --git a/Get-Walkthru.ps1 b/Get-Walkthru.ps1 new file mode 100644 index 0000000..b3c0318 --- /dev/null +++ b/Get-Walkthru.ps1 @@ -0,0 +1,211 @@ +function Get-Walkthru { + <# + .Synopsis + Gets information from a file as a walkthru + .Description + Parses walkthru steps from a walkthru file. + Walkthru files contain step-by-step examples for using PowerShell. + .Link + Write-WalkthruHTML + .Example + Get-Walkthru -Text { +# Walkthrus are just scripts with comments that start at column 0. + + +# Step 1: +Get-Process + +#Step 2: +Get-Command + } + #> + [CmdletBinding(DefaultParameterSetName="File")] + [OutputType([PSObject])] + param( + # The command used to generate walkthrus + [Parameter(Mandatory=$true, + ParameterSetName="Command", + ValueFromPipeline=$true)] + [Management.Automation.CommandInfo] + $Command, + + # The module containing walkthrus + [Parameter(Mandatory=$true, + ParameterSetName="Module", + ValueFromPipeline=$true)] + [Management.Automation.PSModuleInfo] + $Module, + + # The file used to generate walkthrus + [Parameter(Mandatory=$true, + ParameterSetName="File", + ValueFromPipelineByPropertyName=$true)] + [Alias('Fullname')] + [string]$File, + + # The text used to generate walkthrus + [Parameter(Mandatory=$true, + ParameterSetName="Text")] + [String]$Text, + + # The script block used to generate a walkthru + [Parameter(Mandatory=$true, + ParameterSetName="ScriptBlock")] + [ScriptBlock]$ScriptBlock + ) + + begin { + $err = $null + #region Create walkthru type if it doesn't exist + if (-not ('PSWalkthru.WalkthruData' -as [Type])) { + Add-Type -UsingNamespace System.Management.Automation -Namespace PSWalkthru -Name WalkthruData -MemberDefinition ' +public string SourceFile = String.Empty;',' +public string Command = String.Empty;',' +public string Explanation = String.Empty;',' +public string AudioFile = String.Empty;',' +public string VideoFile = String.Empty;',' +public string Question = String.Empty;',' +public string Answer = String.Empty;',' +public string Link = String.Empty;',' +public string Screenshot = String.Empty;',' +public string[] Hint;',' +public ScriptBlock Script; +public ScriptBlock Silent;',' +public DateTime LastWriteTime; +' + } + #endregion Create walkthru type if it doesn't exist + } + process { + if ($psCmdlet.ParameterSetName -eq "File") { + # If the walkthru's in a file, open it and send it to Get-Walthru -Text + $realItem = Get-Item $file -ErrorAction SilentlyContinue + if (-not $realItem) { return } + $text = [IO.File]::ReadAllText($realItem.FullName) + $Result = Get-Walkthru -Text $text + if ($result) { + # If there was in fact walkthru information, add on the file name and the last write time. + foreach ($r in $result) { + $r.Sourcefile = $realItem.Fullname + $r.LastWriteTime = $realItem.LastWriteTime + $r + } + } + return + } elseif ($psCmdlet.ParameterSetName -eq "Command") { + # If they want to see a command's examples a a walkthru, then pass each example to Get-Walkthru -Text + $help = $command | Get-Help + + $c= 1 + $help.Examples.Example | + ForEach-Object { + $text = $_.code + ($_.remarks | Out-String) + Get-Walkthru -Text $text | + ForEach-Object { + $_.Command = "$command Walkthru $c" + $_ + } + $c++ + } + return + } elseif ($psCmdlet.ParameterSetName -eq 'Module') { + # For modules, enumerate all files for the current culture, then pass them down to Get-Walkthru -File + $moduleRoot = Split-Path $module.Path + Get-ChildItem -Path (Join-Path $moduleRoot "$(Get-Culture)") -Filter *.walkthru.help.txt | + Get-Walkthru + return + } + + if ($psCmdlet.ParameterSetName -eq 'ScriptBlock') { + $text = "$ScriptBlock" + } + + # Tokenize the script + $tokens = [Management.Automation.PSParser]::Tokenize($text, [ref]$err) + if ($err.Count) { return } + + $lastToken = $null + $isInContent = $false + $lastResult = New-Object PSWalkthru.WalkthruData + + foreach ($token in $tokens) { + if ($token.Type -eq "Newline") { continue } + if ($token.Type -ne "Comment" -or $token.StartColumn -gt 1) { + $isInContent = $true + if (-not $lastToken) { $lastToken = $token } + } else { + if ($lastToken.Type -ne "Comment" -and $lastToken.StartColumn -eq 1) { + $chunk = $text.Substring($lastToken.Start, + $token.Start - 1 - $lastToken.Start) + $lastResult.Script = [ScriptBlock]::Create($chunk) + # mutliparagraph, split up the results if multiparagraph + + $paragraphs = @() + $lastResult + $null = $paragraphs + $lastToken = $null + $lastResult = New-Object PSWalkthru.WalkthruData + $isInContent = $false + } + } + + if ($isInContent) { + if ($token.Type -eq 'Comment' -and $token.StartColumn -eq 1) { + $chunk = $text.Substring($lastToken.Start, + $token.Start - 1 - $lastToken.Start) + $lastResult.Script = [ScriptBlock]::Create($chunk) + # mutliparagraph, split up the results if multiparagraph + + $paragraphs = @() + $lastResult + $null = $paragraphs + $lastToken = $null + $lastResult = New-Object PSWalkthru.WalkthruData + $isInContent = $false + } + } + if (-not $isInContent) { + $lines = $token.Content.Trim("<>#") + $lines = $lines.Split([Environment]::NewLine, + [StringSplitOptions]"RemoveEmptyEntries") + # Handle specialized return data + foreach ($_ in $lines) { + if ($_ -like ".Audio *" ) { + $lastResult.AudioFile = ($_ -ireplace "\.Audio","").Trim() + } elseif ($_ -like ".Video *" ) { + $lastResult.VideoFile = ($_ -ireplace "\.Video","").Trim() + } elseif ($_ -like ".Question *"){ + $lastResult.Question = ($_ -ireplace "\.Question","").Trim() + } elseif ($_ -like ".Answer *" ) { + $lastResult.Answer = ($_ -ireplace "\.Answer","").Trim() + } elseif ($_ -like ".Hint *") { + $lastResult.Hint = $_.Substring(".Hint ".Length) -split ',' + } elseif ($_ -like ".Link *") { + $lastResult.Link = ($_ -ireplace "\.link","").Trim() + } elseif ($_ -like ".Screenshot *") { + $lastResult.Screenshot = ($_ -ireplace "\.Screenshot","").Trim() + } elseif ($_ -like "*.Silent *") { + $lastResult.Silent = [ScriptBlock]::Create(($_ -ireplace "\.Silent","").Trim()) + } else { + if ($_.TrimEnd().EndsWith(".")) { + $lastResult.Explanation += ($_ + [Environment]::NewLine + [Environment]::NewLine + [Environment]::NewLine ) + } else { + $lastResult.Explanation += ($_ + [Environment]::NewLine) + } + + } + + } + } + } + + + if ($lastToken -and $lastResult) { + $chunk = $text.Substring($lastToken.Start) + $lastResult.Script = [ScriptBlock]::Create($chunk) + $lastResult + } elseif ($lastResult) { + $lastResult + } + } +} diff --git a/Get-Web.ps1 b/Get-Web.ps1 new file mode 100644 index 0000000..b54a6d5 --- /dev/null +++ b/Get-Web.ps1 @@ -0,0 +1,1481 @@ +function Get-Web { + <# + .Synopsis + Gets content from the web, or parses web content. + .Description + Gets content from the web. + + If -Tag is passed, extracts out tags from within the document. + + If -AsByte is passed, returns the response bytes + .Example + # Download the Microsoft front page and extract out links + Get-Web -Url http://microsoft.com/ -Tag a + .Example + # Extract the rows from ConvertTo-HTML + $text = Get-ChildItem | Select Name, LastWriteTime | ConvertTo-HTML | Out-String + Get-Web "tr" $text + .Example + # Extract all PHP elements from a directory of .php scripts + Get-ChildItem -Recurse -Filter *.php | + Get-Web -Tag .\?php, \? + .Example + # Extract all asp tags from .asp files + Get-ChildItem -Recurse | + Where-Object { '.aspx', '.asp'. '.ashx' -contains $_.Extension } | + Get-Web -Tag .\% + .Example + # Get a list of all schemas from schema.org + $schemasList = Get-Web -Url http://schema.org/docs/full.html -Tag a | + Where-Object { $_.Xml.href -like '/*' } | + ForEach-Object { "http://schema.org" + $_.xml.Href } + + .Example + # Extract out the example of a schema from schema.org + + $schema = 'http://schema.org/Event' + Get-Web -Url $schema -Tag pre | + Where-Object { $_.Xml.Class -like '*prettyprint*' } | + ForEach-Object { + Get-Web -Html $_.Xml.InnerText -AsMicrodata -ItemType $schema + } + .Example + # List the top 1000 sites on the web: + Get-Web "http://www.google.com/adplanner/static/top1000/" -Tag 'a' | + where-Object {$_.Tag -like "*_blank*" } | + ForEach-Object { + ([xml]$_.StartTag.Replace('"t', '" t')).a.href + } + .Link + http://schema.org + #> + + [CmdletBinding(DefaultParameterSetName='HTML')] + [OutputType([PSObject],[string])] + param( + # The tags to extract. + [Parameter( + ValueFromPipelineByPropertyName=$true)] + [string[]]$Tag, + + # If used with -Tag, -RequireAttribute will only match tags with a given keyword in the tag + [string[]]$TextInTag, + + # The source HTML. + [Parameter(Mandatory=$true, + ParameterSetName='HTML', + ValueFromPipelineByPropertyName=$true)] + [string]$Html, + + # The Url + [Parameter(Mandatory=$true, + Position=0, + ParameterSetName='Url', + ValueFromPipelineByPropertyName=$true)] + [Alias('Uri')] + [string]$Url, + + # The root of the website. + # All images, css, javascript, related links, and pages beneath this root will be downloaded into a hashtable + [Parameter(Mandatory=$true, + ParameterSetName='WGet', + ValueFromPipelineByPropertyName=$true)] + [string]$Root, + + # Any parameters to the URL + [Parameter(ParameterSetName='Url', + Position=1, + ValueFromPipelineByPropertyName=$true)] + [Hashtable]$Parameter, + + # Filename + [Parameter(Mandatory=$true, + ParameterSetName='FileName', + ValueFromPipelineByPropertyName=$true)] + [Alias('Fullname')] + [ValidateScript({$ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($_)})] + [string]$FileName, + + # The User Agent + [Parameter(ParameterSetName='Url', + ValueFromPipelineByPropertyName=$true)] + [string]$UserAgent = "PowerShellPipeworks/Get-Web (1.0 powershellpipeworks.com)", + + # If set, will not show progress for long-running operations + [Switch]$HideProgress, + + # If set, returns resutls as bytes + [Alias('Byte', 'Bytes')] + [Switch]$AsByte, + + # If set, returns results as XML + [Alias('Xml')] + [Switch]$AsXml, + + # If set, returns results as json + [Switch]$AsJson, + + # If set, extracts Microdata out of a page + [Alias('Microdata')] + [Switch]$AsMicrodata, + + # If set, will get back microdata from the page that matches an itemtype + [string[]]$ItemType, + + # If set, extracts OpenGraph information out of a page + [Switch]$OpenGraph, + + # If set, will extract all meta tags from a page + [Switch]$MetaData, + + # The MIME content type you're requesting from the web site + [string]$ContentType, + + # The credential used to connect to the web site + [Parameter(ParameterSetName='Url', + ValueFromPipelineByPropertyName=$true)] + [Management.Automation.PSCredential] + $WebCredential, + + # If set, will use the default user credential to connect to the web site + [Parameter(ParameterSetName='Url', + ValueFromPipelineByPropertyName=$true)] + [switch] + $UseDefaultCredential, + + + # The HTTP method to use + [Parameter(ParameterSetName='Url', + ValueFromPipelineByPropertyName=$true)] + [ValidateSet('GET','POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD', 'TRACE', 'CONNECT', 'MERGE')] + [string]$Method = "GET", + + # a hashtable of headers to send with the request. + [Hashtable]$Header, + + # The Request Body. This can be either a string, or bytes + $RequestBody, + + # Any request ascii data. Data will be joined together with &, and will be sent in the request body. + [string[]] + $Data, + + # If set, will use a the Net.WebRequest class to download. Otherwise, will use the xmlhttprequest. + # Xmlhttprequest adds some extra headers and caches GET requests, so, if you wish to avoid this, -UseWebRequest. + [Switch] + $UseWebRequest, + + # A Progress Identifier. This is used to show progress inside of an existing layer of progress bars. + [int] + $ProgressIdentifier, + + # If set, the server error will be turned into a result. + # This is useful for servers that provide complex error information inside of XML or JSON. + [Switch] + $UseErrorAsResult, + + # If set, then a note property will be added to the result containing the response headers + [Switch] + $OutputResponseHeader, + + # The amount of time before a web request times out. + [Timespan] + $Timeout, + + # If set, will request the web site asynchronously, and return the results + [Switch] + $Async + ) + + begin { + #region Escape Special Characters + $replacements = @{ + "<BR>" = "<BR />" + "<HR>" = "<HR />" + " " = " " + '¯'='¯' + 'Ð'='Ð' + '¶'='¶' + '¥'='¥' + 'º'='º' + '¹'='¹' + 'ª'='ª' + '­'='­' + '²'='²' + 'Ç'='Ç' + 'Î'='Î' + '¤'='¤' + '½'='½' + '§'='§' + 'Â'='â' + 'Û'='Û' + '±'='±' + '®'='®' + '´'='´' + 'Õ'='Õ' + '¦'='¦' + '£'='£' + 'Í'='Í' + '·'='·' + 'Ô'='Ô' + '¼'='¼' + '¨'='¨' + 'Ó'='Ó' + '°'='°' + 'Ý'='Ý' + 'À'='À' + 'Ö'='Ö' + '"'='"' + 'Ã'='Ã' + 'Þ'='Þ' + '¾'='¾' + '¿'='¿' + '×'='×' + 'Ø'='Ø' + '÷'='÷' + '¡'='¡' + '³'='³' + 'Ï'='Ï' + '¢'='¢' + '©'='©' + 'Ä'='Ä' + 'Ò'='Ò' + 'Å'='Å' + 'È'='È' + 'Ü'='Ü' + 'Á'='Á' + 'Ì'='Ì' + 'Ñ'='Ñ' + 'Ê'='Ê' + '¸'='¸' + 'Ù'='Ù' + 'ß'='ß' + '»'='»' + 'ë'='ë' + 'É'='É' + 'µ'='µ' + '¬'='¬' + 'Ú'='Ú' + 'Æ'='Æ' + '€'= "€" + '—' = '—' + } + #endregion Escape Special Characters + $quotes = '"', "'" + function Convert-Json + { + <# + .Synopsis + Inline JSON converter + .Description + Converts JSON into PowerShell hashtables using regular expressions + #> + param( + # The JSON + [Parameter(ValueFromPipeline=$true)] + [string]$Json, + + # If set, will use full language mode when parsing the data. + # If not set, the data will be parsed in "data-language" mode, which allows for the declaration of hashtables but prevents the execution of code + [switch]$FullLanguage) + + begin { + function ConvertFrom-Hashtable + { + param($results) + $psObject = New-Object PSObject + foreach ($key in $results.Keys) { + $result = $null + if ($results[$key] -is [Hashtable]) { + $result = ConvertFrom-Hashtable $results[$key] + } elseif ($results[$key] -is [Array]) { + $result = foreach ($result in $results[$key]){ + if ($result -is [Hashtable]) { + ConvertFrom-Hashtable $result + } else { + $result + } + } + } else { + $result = $results[$key] + } + + if ($key) { + $psObject.psObject.Properties.Add( + (New-Object Management.Automation.PSNoteProperty $key, $result) + ) + } + + + + } + $psobject + } + } + process { + $json = [Regex]::Replace($Json, + "\\u([\dabcdefABCDEF]{4,4})", { + ("0x" + $args[0].Groups[1].Value) -as [Uint32] -as [Char] + }) + + $json = $Json.Replace('$', '$ ') + + $script = + $json -replace + '“|”', '`"' -replace + '"\s{0,}:', '"=' -replace + "\\{2,2}", "\" -replace + "\[", "$([Environment]::NewLine)@(" -replace + "\]", ")" -replace + ',\[', ", $([Environment]::NewLine)@(" -replace + "\],",")," -replace + '\{"', "@{$([Environment]::NewLine)`"" -replace + "\[\]", "@()" -replace + "=(\w)*(\[)", '=@(' -replace + "=(\d{1,}),",'=$1;' -replace + "=(\d{1,}.\d{1,}),",'=$1;' -replace + "=-(\d{1,}.\d{1,}),",'=-$1;' -replace + "true", "`$true" -replace + "false", "`$false" -replace + "null", '$null' -replace + "\]}", ")}" -replace + "{", "@{" -replace + '\\"', '`"' -replace + "@@", "@" -replace + '(["})]),', "`$1$([Environment]::NewLine)" -replace + '(\$true),', "`$1$([Environment]::NewLine)" -replace + '(\$false),', "`$1$([Environment]::NewLine)" -replace + '(\$null),', "`$1$([Environment]::NewLine)" -replace + "(-{0,1})(\d{1,}),", "`$1`$2$([Environment]::NewLine)" -replace + "\\/","/" -replace + '\$true(\w{1,})', 'true$1' -replace + '\$false(\w{1,})', 'false$1' -replace + '\$null(\w{1,})', 'null$1' + + + $replacements = @(@{ + Find = '}\s{1,}@{' + Replace = '},@{' + }) + foreach ($r in $replacements) { + foreach ($f in $r.find) { + $regex =New-Object Regex $f, "Multiline, IgnoreCase" + $script = $regex.Replace($script , $r.Replace) + } + } + + if ($script.Startswith("[")) + { + $script = "@(" + $script.Substring(1).TrimEnd("]") + ")" + } + $results = $null + Write-Verbose $script + if ($FullLanguage) { + $results = Invoke-Expression "$script" + } else { + $results = Invoke-Expression "data { $script }" + } + + if ($results) { + foreach ($result in $results) {ConvertFrom-Hashtable $result } + } + } + } + + # Add system.web, in case it's not loaded + Add-Type -AssemblyName System.Web + + + if ($ProgressIdentifier) { + $script:CachedProgressId = $ProgressIdentifier + } + + if (-not $script:CachedProgressId) { + $script:CachedProgressId = Get-Random + + } + $progressId = $script:CachedProgressId + } + + process { + if ($psCmdlet.ParameterSetName -eq 'WGet') { + if (-not $script:cachedContentTypes) { + $script:cachedContentTypes = @{} + $ctKey = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey("MIME\Database\Content Type") + $ctKey.GetSubKeyNames() | + ForEach-Object { + $extension= $ctKey.OpenSubKey($_).GetValue("Extension") + if ($extension) { + $script:cachedContentTypes["${extension}"] = $_ + } + } + + } + $currentRoot = "$Root" + + if ($currentRoot -like "http*//*" -and $currentRoot -notlike "http*//*/") { + $currentRoot+= '/' + } + $hostname = ([uri]$currentRoot).DnsSafeHost + $followMeDown = New-OBject Collections.Queue + $null = $followMeDown.Enqueue($currentRoot) + + $pages = @{} + $pagedata = @{} + while ($followMeDown.Count -gt 0) { + $pageRoot = $followMeDown.Dequeue() + + $pageHost = ([uri]$pageRoot).DnsSafeHost + + if ($pageHost -ne $hostname) { + continue + } + + + + $relativeRoot = $pageRoot.Substring(0, $pageRoot.LastIndexOf("/")) + + + + $pageMimetype= + if ($pageRoot -like "http*//*/*.*") { + $extension = $pageRoot.Substring($pageRoot.LastIndexOf(".")) + if ($script:cachedContentTypes[$extension]) { + $script:cachedContentTypes[$extension] + } else { + "unknown/unknown" + } + } elseif ($pageRoot -like "http*//*/") { + "text/html" + } else { + "unknown/unknown" + } + + + $pageHtml = "" + if ($pageMimetype -like "text/*") { + $pageHtml = Get-Web -Url $pageRoot -UseWebRequest + $pagedata[$pageRoot] = $pageHtml + } else { + $pagedata[$pageRoot] = Get-Web -Url $pageRoot -UseWebRequest -AsByte + + } + + + if (-not $pageHtml) { + continue + } + + $linksCssAndImagesAndScripts = Get-Web -Html $pageHtml -Tag a, link, img, script + + + + # Enqueue relative links + $relativeLinks = $linksCssAndImagesAndScripts | + Where-Object { + $_.Xml.Name -eq 'a' + } | + Where-Object { + $x = $_.Xml + + + $startTag = $x.SelectSingleNode("/*") + $startTag.Href -and ( + ($startTag.Href -like "/*" -or $startTag.Href -notlike "*://*") -or + (([uri]$startTag.Href).DnsSafeHost -eq "$hostname") + ) -and ($startTag.Href -notlike "javascript:*") + + } + + <# + $requiredScripts = $linksCssAndImagesAndScripts | + Where-Object { + $_.Xml.Name -eq 'Script' -and $_.Xml.src + }#> + + $links = $linksCssAndImagesAndScripts | + Where-Object { + $_.Xml.Name -eq 'link' + } + + + $images = $linksCssAndImagesAndScripts | + Where-Object { + ($_.StartTag -like "*img*" -or $_.StartTag -like "*script*") -and + $_.StartTag -match "src=['`"]{0,1}([\w\:/\.-]{1,})" + } |ForEach-Object { + $Matches.'1' + } + + + + + $potentialHrefs = @() + $potentialHrefs += + foreach ($img in $images) { + $img + } + + foreach ($r in $relativeLinks) { + $potentialHrefs += $r.Xml.Href + } + + foreach ($href in $potentialHrefs) { + if (-not $href) { continue } + if ($href -like "$relativeRoot*") { + if (-not $followMeDown.Contains($href) -and -not $pagedata.Contains($href)) { + $null = $followMeDown.Enqueue($href) + } + } if (-not ([uri]$href).DnsSafeHost) { + if (-not $followMeDown.Contains($href) -and -not $pagedata.Contains($href)) { + + if ($href -like "/*") { + $null = $followMeDown.Enqueue(([uri]$currentRoot).Scheme+ "://" + $hostname + $href) + } else { + $null = $followMeDown.Enqueue($relativeRoot + '/' + $href) + } + + } + } else { + + $null = $null + } + } + } + + + if ($GetStory) { + $story = @{} + foreach ($pd in $pagedata.GetEnumerator()) { + if ($pd.value -is [string]) { + $partsOfStory = @( + Get-Web -Tag 'div', 'p' -Html $pd.Value | + ForEach-Object { + $firsttagEnd = $_.StartTag.IndexOfAny(' >') + $tagName = $_.StartTag.Substring(1, $firsttagEnd - 1) + + $newTag= $_.Tag.Substring($_.StartTag.Length) + + $changeindex = $newTag.IndexOf("*</$tagName>", [stringcomparison]::OrdinalIgnoreCase) + if ($changeindex -ne -1) { + + + + $newTag = $newTag.Substring(0, $changeindex) + } + $strippedTags = [Regex]::Replace($newTag, "<[^>]*>", [Environment]::NewLine); + $strippedTags + + }) + + if ($partsOfStory -ne '') { + $segments = ([uri]$pd.Key).Segments + if ($segments.Count -le 1) { + $newPath = '/' + } else { + $newPath = (([uri]$pd.Key).Segments -join '' -replace '/', '_').Trim('_') + } + + $story[$newPath] = $partsOfStory -ne '' -join ([Environment]::NewLine * 4) + } + } + } + + + $pagedata += $story + } + $pagedata + } elseif ($psCmdlet.ParameterSetName -eq 'URL') { + #Region Download URL + $fullUrl = "$url" + + + if ($Data -and -not $RequestBody) { + $RequestBody = $data -join '&' + $UseWebRequest = $true + if (-not $psBoundParameters.Method) { + $Method = 'POST' + } + } + + $xmlHttp = New-Object -ComObject Microsoft.xmlhttp + + if ($useWebRequest) { + + if ($Parameter -and ('PUT', 'POST' -notcontains $method)) { + $fullUrl += "?" + foreach ($param in $parameter.GetEnumerator()) { + $fullUrl += "$($param.key)=$([Web.HttpUtility]::UrlEncode($param.Value.ToString()))&" + } + } + + $req = [Net.WebRequest]::Create("$fullUrl") + $req.UserAgent = $UserAgent + + + $req.Method = $Method; + if ($psBoundParameters.ContentType) { + $req.ContentType = $ContentType + } + + + + if ($psBoundParameters.WebCredential) { + $req.Credentials = $WebCredential.GetNetworkCredential() + } elseif ($psBoundParameters.UseDefaultCredential) { + $req.Credentials = [net.credentialcache]::DefaultNetworkCredentials + } + + if ($header) { + foreach ($kv in $header.GetEnumerator()) { + if ($kv.Key -eq 'Accept') { + $req.Accept = $kv.Value + } elseif ($kv.Key -eq 'content-type') { + $req.ContentType = $kv.Value + } else { + $null = $req.Headers.add("$($kv.Key)", "$($kv.Value)") + } + + } + } + + if ($timeout) { + $req.Timeout = $timeout.TotalMilliseconds + + } + + + + + $RequestTime = [DateTime]::Now + if (-not $HideProgress) { + Write-Progress "Sending Web Request" $url -Id $progressId + } + $requestStream = try { + + + if ($Parameter -and ('PUT', 'POST' -contains $method)) { + if (-not $RequestBody) { + $RequestBody = "" + } + + $RequestBody += + (@(foreach ($param in $parameter.GetEnumerator()) { + "$($param.key)=$([Uri]::EscapeDataString($param.Value.ToString()))" + }) -join '&') + + } else { + $paramStr = "" + } + if ($ContentType) { + $req.ContentType = $ContentType + } + + + + if ($requestBody) { + if ($RequestBody -is [string]) { + if (-not $ContentType) { + $req.ContentType = 'application/x-www-form-urlencoded' + } + $bytes = [Text.Encoding]::UTF8.GetBytes($RequestBody) + $postDataBytes = $bytes -as [Byte[]] + $req.ContentLength = $postDataBytes.Length + $requestStream = $req.GetRequestStream() + $requestStream.Write($postDataBytes, 0, $postDataBytes.Count) + $requestStream.Close() + } elseif ($RequestBody -as [byte[]]) { + if (-not $ContentType) { + $req.ContentType = 'application/x-www-form-urlencoded' + } + $postDataBytes = $RequestBody -as [Byte[]] + $req.ContentLength = $postDataBytes.Length + + $requestStream = $req.GetRequestStream() + if ($req.ContentLength -gt 256kb) { + if (-not $HideProgress) { + Write-Progress "Uploading" $url -Id $progressId + } + #$requestStream.Write($postDataBytes, 0, $postDataBytes.Count) + + + $tLen = 0 + $chunkTotal = [Math]::Ceiling($postDataBytes.Count / 256kb) + for ($chunkCount = 0; $chunkCount -lt $chunkTotal; $chunkCount++) { + if ($chunkCount -ne ($chunkTotal -1 )) { + + $arr = $postDataBytes[($chunkCount * 256kb)..(([uint32]($chunkCount + 1) * 256kb) - 1)] + $tLen+=$arr.Length + } else { + $arr = $postDataBytes[($chunkCount * 256kb)..($postDataBytes.Length - 1)] + $tLen+=$arr.Length + } + $requestStream.Write($arr, 0 , $arr.Length) + + if (-not $HideProgress) { + $perc = $chunkCount * 100 / $chunkTotal + Write-Progress "Uploading" $url -Id $progressId -PercentComplete $perc + } + } + + if (-not $HideProgress) { + Write-Progress "Uploading" $url -Id $progressId -Completed + } + } else { + + $requestStream.Write($postDataBytes, 0, $postDataBytes.Count) + + } + $requestStream.Close() + } + + } elseif ($paramStr) { + $postData = "$($paramStr -join '&')" + $postDataBytes = [Text.Encoding]::UTF8.GetBytes($postData) + $req.ContentLength = $postDataBytes.Length + $requestStream = $req.GetRequestStream() + $requestStream.Write($postDataBytes, 0, $postDataBytes.Count) + + $requestStream.Close() + } elseif ($method -ne 'GET' -and $method -ne 'HEAD') { + $req.ContentLength = 0 + } + } catch { + + if (-not ($_.Exception.HResult -eq -2146233087)) { + $_ | Write-Error + return + } + + + } + + Write-Verbose "Getting $fullUrl" + + $responseIsError = $false + + + if ($Async) { + return New-Object PSObject -Property @{ + WebRequest = $req + AsyncOperation = $req.BeginGetResponse({}, $null) + } + } + $webresponse = + try { + $req.GetResponse() + } catch { + $ex = $_ + if ($ex.Exception.InnerException.Response) { + $streamIn = New-Object IO.StreamReader $ex.Exception.InnerException.Response.GetResponseStream() + $strResponse = $streamIn.ReadToEnd(); + $streamIn.Close(); + if (-not $UseErrorAsResult) { + Write-Error $strResponse + return + } else { + $html = $strResponse + } + + } else { + $ex | Write-Error + return + } + # + } + if ($webResponse) { + + $rs = $webresponse.GetResponseStream() + $responseHeaders = $webresponse.Headers + $responseHeaders = if ($responseHeaders -and $responseHeaders.GetEnumerator()) { + $reHead = @{} + foreach ($r in $responseHeaders.GetEnumerator()) { + $reHead[$r] = $responseHeaders[$r] + } + $reHead + } else { + $null + } + $unexpectedResponseType = $false + + if ($psBoundParameters.ContentType -and + $webresponse.ContentType -and + $webResponse.ContentType -ne $ContentType) { + + if ($webresponse.ContentType -notlike "text/*" -and $webresponse.ContentType -notlike "*xml*") { + $pageRoot = "$($WebResponse.ResponseUri)" + $relativeRoot = $pageRoot.Substring($pageRoot.LastIndexOf("/") + 1) + + $unexpectedResponseType = $true + $AsByte = $true + } + + } + + + + + if ($AsByte) { + + + $byteBuffer = new-object byte[] $webresponse.ContentLength; + + + [int]$ToRead = $webresponse.ContentLength + [int]$TotalRead = 0 + [Int]$bytesRead = 0 + while ($toRead -gt 0 -and ($toRead -ge $TotalRead)) { + try { + $amountToRead = + if (($ToRead - $TotalRead) -gt .25kb) { + .25kb + } else { + $ToRead - $TotalRead + } + + $bytesRead = $rs.Read($byteBuffer, $TotalRead, $amountToRead ) + } catch { + $global:LastStreamReadError = $_ + } + + if ($bytesRead -eq 0) { + break + } + $TotalRead += $bytesRead + if (($byteBuffer.Length -gt 256kb) -and -not $hideProgress) { + + $perc = ($totalRead / $byteBuffer.Length) * 100 + Write-Progress "Downloading" $url -Id $progressId -PercentComplete $perc + + + + } + } + + if (-not $HideProgress) { + $perc = $totalRead / $byteBuffer.Length + Write-Progress "Download Completed" $url -Id $progressId -Complete + } + #$null = $rs.CopyTo($ms) + + $outBytes = $byteBuffer + #New-Object byte[] $ms.Length + #$null = $ms.Write($outBytes, 0, $ms.Length); + } else { + $streamIn = New-Object IO.StreamReader($rs); + $strResponse = $streamIn.ReadToEnd(); + $html = $strResponse + $streamIn.Close(); + } + + $rs.close() + $rs.Dispose() + + if ($AsByte) { + if ($unexpectedResponseType) { + + return @{$relativeRoot= $outBytes} + } else { + return $outBytes + } + } + + if ($unexpectedResponseType -and $Html) { + return @{$relativeRoot= $Html} + } + } + + } + # $req.CookieContainer + + if (! $html -and -not $UseWebRequest) { + if ($WebCredential) { + $xmlHttp.open("$Method", + $fullUrl, + $false, + $WebCredential.GetNetworkCredential().Username, + $WebCredential.GetNetworkCredential().Password) + } else { + $xmlHttp.open("$Method", $fullUrl, $false) + } + $xmlHttp.setRequestHeader("UserAgent", $userAgent) + if ($header) { + foreach ($kv in $header.GetEnumerator()) { + $xmlHttp.setRequestHeader("$($kv.Key)", $kv.Value) + } + } + + if (-not $HideProgress) { + Write-Progress "Sending Web Request" $url -Id $progressId + } + + if ($parameter -and ('PUT', 'POST' -contains $method)) { + $paramStr = foreach ($param in $parameter.GetEnumerator()) { + "$($param.key)=$([Web.HttpUtility]::UrlEncode($param.Value.ToString()))" + } + + if ($header -and $Header.ContainsKey('ContentType')) { + $ContentType = $Header['ContentType'] + } elseif ($header -and$Header.ContainsKey('Content-Type')) { + $ContentType = $Header['Content-Type'] + } + if ($ContentType) { + $xmlHttp.SetRequestHeader("Content-Type","$ContentType") + } else { + $xmlHttp.SetRequestHeader("Content-Type","application/x-www-form-urlencoded") + + } + + + + + + + if ($requestBody) { + $xmlHttp.Send("$requestBody") + + } else { + $xmlHttp.Send("$($paramStr -join '&')") + } + + } else { + + $xmlHttp.Send($RequestBody) + } + $requestTime = [Datetime]::Now + while ($xmlHttp.ReadyState -ne 4) { + if (-not $hideProgress) { + Write-Progress "Waiting for response" $url -id $progressId + } + Start-Sleep -Milliseconds 10 + } + } + + + $ResponseTime = [Datetime]::Now - $RequestTime + + if (-not $hideProgress) { + Write-Progress "Response received" $url -id $progressId + } + if ($xmlHttp.Status -like "2*") { + Write-Verbose "Server Responded with Success $($xmlHttp.Status)" + } elseif ($xmlHttp.Status -like "1*") { + Write-Debug "Server Responded with Information $($xmlHttp.Status)" + } elseif ($xmlHttp.Status -like "3*") { + Write-Warning "Server wishes to redirect: $($xmlHttp.Status)" + } elseif ($xmlHttp.Status -like "4*") { + + $errorWithinPage = + Get-Web -Html $xmlHttp.responseText -Tag span | + Where-Object { $_.Tag -like '*ui-state-error*' } | + ForEach-Object { + $short = $_.Tag.Substring($_.Tag.IndexOf(">") + 1); + $short.Substring(0, $short.LastIndexOf("</")) + } + + $errorText = if ($errorWithinPage) { + $errorWithinPage + } else { + $xmlHttp.MessageText + } + Write-Error "Server Responded with Error: $($xmlHttp.Status) - $($errorText)" + + return + } + #endregion Download URL + + if ($AsByte) { + return $xmlHttp.ResponseBody + } elseif (-not $UseWebRequest) { + $html = $xmlHttp.ResponseText + } + } elseif ($psCmdlet.ParameterSetName -eq 'FileName') { + if ($AsByte) { + [IO.File]::ReadAllBytes($ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($FileName)) + return + } + $html = [IO.File]::ReadAllText($ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($FileName)) + } + + if (-not $html) { return } + + if ($AsXml) { + $xHtml = [xml]$html + + + + if ($OutputResponseHeader) { + + $xHtml | + Add-Member NoteProperty Headers $responseHeaders -Force -PassThru + } else { + $xHtml + } + return + } + + if ($AsJson) { + <#$msJsonConvert = Get-Command ConvertFrom-Json -Module Microsoft* -ErrorAction SilentlyContinue + + if (-not $msJsonConvert) { + + }#> + $jsResult = Convert-Json -json $html #-FullLanguage + + if ($OutputResponseHeader) { + + $jsResult | + Add-Member NoteProperty Headers $responseHeaders -Force -PassThru + } else { + $jsResult + } + return + } + + if (-not $Tag -or $AsMicrodata) { + if ($AsByte) { + return [Text.Encoding]::Unicode.GetBytes($html) + } + } + + foreach ($r in $replacements.GetEnumerator()) { + $l = 0 + do { + $l = $html.IndexOf($r.Key, $l, [StringComparison]"CurrentCultureIgnoreCase") + if ($l -ne -1) { + $html = $html.Remove($l, $r.Key.Length) + $html = $html.Insert($l, $r.Value) + } + } while ($l -ne -1) + } + + if ($tag -and -not ($AsMicrodata -or $OpenGraph -or $MetaData -or $ItemType)) { + $tryToBalance = $true + + if ($openGraph -or $metaData) { + $tryToBalance = $false + } + + foreach ($htmlTag in $tag) { + if (-not $htmlTag) { continue } + $r = New-Object Text.RegularExpressions.Regex ('</' + $htmlTag + '>'), ("Singleline", "IgnoreCase") + $endTags = @($r.Matches($html)) + if ($textInTag) { + + $r = New-Object Text.RegularExpressions.Regex ('<' + $htmlTag + '[^>]*' + ($textInTag -join '[^>]*') + '[^>]*>'), ("Singleline", "IgnoreCase") + } else { + $r = New-Object Text.RegularExpressions.Regex ('<' + $htmlTag + '[^>]*>'), ("Singleline", "IgnoreCase") + } + + $startTags = @($r.Matches($html)) + $tagText = New-Object Collections.ArrayList + $tagStarts = New-Object Collections.ArrayList + if ($tryToBalance -and ($startTags.Count -eq $endTags.Count)) { + $allTags = $startTags + $endTags | Sort-Object Index + $startTags = New-Object Collections.Stack + + foreach($t in $allTags) { + if (-not $t) { continue } + + if ($t.Value -like "<$htmlTag*") { + $startTags.Push($t) + } else { + $start = try { $startTags.Pop() } catch {} + $null = $tagStarts.Add($start.Index) + $null = $tagText.Add($html.Substring($start.Index, $t.Index + $t.Length - $start.Index)) + } + } + } else { + # Unbalanced document, use start tags only and make sure that the tag is self-enclosed + foreach ($_ in $startTags) { + if (-not $_) { continue } + $t = "$($_.Value)" + if ($t -notlike "*/>") { + $t = $t.Insert($t.Length - 1, "/") + } + $null = $tagStarts.Add($t.Index) + $null = $tagText.Add($t) + + } + } + + $tagCount = 0 + foreach ($t in $tagText) { + if (-not $t) {continue } + $tagStartIndex = $tagStarts[$tagCount] + $tagCount++ + # Correct HTML which doesn't quote the attributes so it can be coerced into XML + $inTag = $false + for ($i = 0; $i -lt $t.Length; $i++) { + + if ($t[$i] -eq "<") { + $inTag = $true + } else { + if ($t[$i] -eq ">") { + $inTag = $false + } + } + if ($inTag -and ($t[$i] -eq "=")) { + if ($quotes -notcontains $t[$i + 1]) { + $endQuoteSpot = $t.IndexOfAny(" >", $i + 1) + # Find the end of the attribute, then quote + $t = $t.Insert($i + 1, "'") + if ($endQuoteSpot -ne -1) { + if ($t[$endQuoteSpot] -eq ' ') { + $t = $t.Insert($endQuoteSpot + 2, "'") + } else { + $t = $t.Insert($endQuoteSpot + 1, "'") + } + } + + $i = $endQuoteSpot + if ($i -eq -1) { + break + } + } else { + # Make sure the quotes are correctly formatted, otherwise, + # end the quotes manually + $whichQuote = $t[$i + 1] + $endQuoteSpot = $t.IndexOf($whichQuote, $i + 2) + $i = $endQuoteSpot + if ($i -eq -1) { + break + } + } + } + } + + $startTag = $t.Substring(0, $t.IndexOf(">") + 1) + if ($pscmdlet.ParameterSetName -eq 'Url') { + if ($OutputResponseHeader) { + New-Object PsObject -Property @{ + Tag= $t + StartTag = $startTag + StartsAt = $tagStartIndex + Xml = ($t -as [xml]).$htmlTag + Source = $url + Headers = $responseHeaders + } + } else { + New-Object PsObject -Property @{ + Tag= $t + StartTag = $startTag + StartsAt = $tagStartIndex + Xml = ($t -as [xml]).$htmlTag + Source = $url + } + } + + } else { + if ($OutputResponseHeader) { + New-Object PsObject -Property @{ + Tag= $t + StartTag = $startTag + StartsAt = $tagStartIndex + Xml = ($t -as [xml]).$htmlTag + Headers = $responseHeaders + } + } else { + New-Object PsObject -Property @{ + Tag= $t + StartTag = $startTag + StartsAt = $tagStartIndex + Xml = ($t -as [xml]).$htmlTag + } + } + + } + + } + } + } elseif ($OpenGraph) { + $metaTags = Get-Web -Html $html -Tag 'meta' + $outputObject = New-Object PSObject + foreach ($mt in $metaTags) { + if ($mt.Xml.Property -like "og:*") { + $propName = $mt.Xml.Property.Substring(3) + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $mt.Xml.Content + if ($outputObject.psobject.properties[$propName]) { + $outputObject.psobject.properties[$propName].Value = + @($outputObject.psobject.properties[$propName].Value) + $noteProperty.Value | Select-Object -Unique + } else { + try { + $null = Add-Member -InputObject $outputObject NoteProperty $noteProperty.name $noteProperty.Value -Force + } catch { + + Write-Error $_ + } + } + } + + } + $null = $OutputObject.pstypenames.add('OpenGraph') + if ($OutputResponseHeader) { + $outputObject | Add-Member NoteProperty Headers $responseHeaders -Force -PassThru + } else { + $outputObject + } + + } elseif ($MetaData) { + $titleTag= Get-Web -Html $html -Tag 'title' + $titleText = $titleTag.xml.Trim() + $metaTags = Get-Web -Html $html -Tag 'meta' + $outputObject = New-Object PSObject + Add-Member NoteProperty Title $titleText -InputObject $outputObject + foreach ($mt in $metaTags) { + $propName = if ($mt.Xml.Property) { + $mt.Xml.Property + } elseif ($mt.Xml.name -and $mt.Xml.Name -ne 'meta') { + $mt.Xml.name + } + + if (-not $PropName) { continue } + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $mt.Xml.Content + if ($outputObject.psobject.properties[$propName]) { + $outputObject.psobject.properties[$propName].Value = + @($outputObject.psobject.properties[$propName].Value) + $noteProperty.Value | Select-Object -Unique + } else { + try { + $null = Add-Member -InputObject $outputObject NoteProperty $noteProperty.name $noteProperty.Value -Force + } catch { + + Write-Error $_ + } + } + } + $null = $OutputObject.pstypenames.add('HTMLMetaData') + if ($psBoundParameters.Url -and -not $outputObject.psobject.properties['Url']) { + Add-Member -InputObject $outputObject NoteProperty Url $psboundParameters.Url + } + if ($OutputResponseHeader) { + $outputObject | Add-Member NoteProperty Headers $responseHeaders -Force -PassThru + } else { + $outputObject + } + } elseif ($AsMicrodata -or $ItemType) { + $getInnerScope = { + if (-not $knownTags[$htmlTag]) { + $r = New-Object Text.RegularExpressions.Regex ('<[/]*' + $htmlTag + '[^>]*>'), ("Singleline", "IgnoreCase") + $Tags = @($r.Matches($html)) + $knownTags[$htmlTag] = $tags + } + + $i = 0 + $myTagIndex = foreach ($_ in $knownTags[$htmlTag]) { + if ($_.Value -eq $targetValue -and $_.Index -eq $targetIndex) { + $i + } + $i++ + } + + # Once the tag index is known, we start there and wait until the tags are balanced again + $balance = 1 + for ($i = $myTagIndex + 1; $i -lt $knownTags[$htmlTag].Count; $i++) { + if ($knownTags[$htmlTag][$i].Value -like "<$htmlTag*"){ + $balance++ + } else { + $balance-- + } + if ($balance -eq 0) { + break + } + } + + if ($balance -eq 0 -and ($i -ne $knownTags[$htmlTag].Count)) { + $start = $knownTags[$htmlTag][$MyTagIndex].Index + $end = $knownTags[$htmlTag][$i].Index + $knownTags[$htmlTag][$i].Length + $innerScope = $html.Substring($start, $end-$start) + } else { + $innerScope = "" + } + + $myTagAsXml = $knownTags[$htmlTag][$MyTagIndex].Value + if ($myTagASXml -notlike "*itemscope=*") { + $myTagASXml = $myTagASXml -ireplace 'itemscope', 'itemscope=""' + } + try { + $myTagAsXml = [xml]($myTagAsXml.TrimEnd("/>") + "/>") + } catch { + + } + + + } + + + + $itemScopeFinder = New-Object Text.RegularExpressions.Regex ('<(?<t>\w*)[^>]*itemscope[^>]*>'), ("Singleline", "IgnoreCase") + $knownTags = @{} + foreach ($matchInfo in $itemScopeFinder.Matches($html)) { + if (-not $matchInfo) { continue } + $htmlTag = $matchInfo.Groups[1].Value + $targetValue = $matchInfo.Groups[0].Value + $targetIndex = $matchInfo.Groups[0].Index + + . $getInnerScope + + + $itemPropFinder = New-Object Text.RegularExpressions.Regex ('<(?<t>\w*)[^>]*itemprop[^>]*>'), ("Singleline", "IgnoreCase") + $outputObject = New-Object PSObject + $outputObject.pstypenames.clear() + foreach ($itemTypeName in $myTagAsXml.firstchild.itemtype -split " ") { + if (-not $itemTypeName) { continue } + $null = $outputObject.pstypenames.add($itemTypeName) + } + + # If we've asked for a specific item type, and this isn't it, continue + if ($ItemType) { + $found = foreach ($tn in $outputObject.pstypenames) { + if ($ItemType -contains $tn) { + $true + } + } + if (-not $found) { + continue + } + } + + + + if ($myTagAsXml.firstChild.itemId) { + $itemID = New-Object Management.Automation.PSNoteProperty "ItemId", $myTagAsXml.firstChild.itemId + $null = $outputObject.psobject.properties.add($itemID) + } + + $avoidRange = @() + + foreach ($itemPropMatch in $itemPropFinder.Matches($innerScope)) { + $propName = "" + $propValue = "" + $htmlTag = $itemPropMatch.Groups[1].Value + $targetValue = $itemPropMatch.Groups[0].Value + if ($itemPropMatch.Groups[0].Value -eq $matchInfo.Groups[0].Value) { + # skip relf references so we don't infinitely recurse + continue + } + $targetIndex = $matchInfo.Groups[0].Index + $itemPropMatch.Groups[0].Index + if ($avoidRange -contains $itemPropMatch.Groups[0].Index) { + continue + } + . $getInnerScope + $propName = $myTagAsXml.firstchild.itemprop + if (-not $propName) { + Write-Debug "No Property Name, Skipping" + continue + } + + if (-not $innerScope) { + + # get the data from one of a few properties. href, src, or content + $fixedXml = try { [xml]($itemPropMatch.Groups[0].Value.TrimEnd("/>") + "/>") } catch { } + $propName = $fixedxml.firstchild.itemprop + $propValue = if ($fixedXml.firstchild.href) { + $fixedXml.firstchild.href + } elseif ($fixedXml.firstchild.src) { + $fixedXml.firstchild.src + } elseif ($fixedXml.firstchild.content) { + $fixedXml.firstchild.content + } elseif ('p', 'span', 'h1','h2','h3','h4','h5','h6' -contains $htmlTag) { + $innerTextWithoutspaces = ([xml]$innerScope).innertext -replace "\s{1,}", " " + $innerTextWithoutSpaces.TrimStart() + } + if ($propName) { + try { + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $propValue + } catch { + Write-Debug "Could not create note property" + } + } + + } else { + if ($innerScope -notlike '*itemscope*') { + $innerScopeXml = try { [xml]$innerScope } catch { } + + if ($innerScopeXml.firstChild.InnerXml -like "*<*>") { + $propValue = if ($myTagAsXml.firstchild.href) { + $myTagAsXml.firstchild.href + } elseif ($myTagAsXml.firstchild.src) { + $myTagAsXml.firstchild.src + } elseif ($myTagAsXml.firstchild.content) { + $myTagAsXml.firstchild.content + } elseif ('p', 'span', 'h1','h2','h3','h4','h5','h6' -contains $htmlTag) { + $innerTextWithoutspaces = ([xml]$innerScope).innertext -replace "\s{1,}", " " + $innerTextWithoutSpaces.TrimStart() + } else { + $innerScope + } + try { + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $propValue + } catch { + Write-Debug "Could not create note property" + } + } else { + $innerText = $innerScope.Substring($itemPropMatch.Groups[0].Value.Length) + $innerText = $innerText.Substring(0, $innerText.Length - "</$htmlTag>".Length) + $innerTextWithoutspaces = $innertext -replace "\s{1,}", " " + $innerTextWithoutSpaces = $innerTextWithoutSpaces.TrimStart() + try { + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $innerTextWithoutSpaces + } catch { + Write-Debug "Could not create note property" + } + } + + } else { + # Keep track of where this item was seen, so everything else can skip nested data + $avoidRange += $itemPropMatch.Groups[0].Index..($itemPropMatch.Groups[0].Index + $innerScope.Length) + + $propValue = Get-Web -Html $innerScope -Microdata + $noteProperty = New-Object Management.Automation.PSNoteProperty $propName, $propValue + } + + $innerItemHtml = $innerScope + } + if ($outputObject.psobject.properties[$propName]) { + if ($noteProperty.Value -is [string]) { + $outputObject.psobject.properties[$propName].Value = + @($outputObject.psobject.properties[$propName].Value) + $noteProperty.Value | Select-Object -Unique + } else { + $outputObject.psobject.properties[$propName].Value = + @($outputObject.psobject.properties[$propName].Value) + $noteProperty.Value + } + } else { + try { + $null = Add-Member -InputObject $outputObject NoteProperty $noteProperty.name $noteProperty.Value -Force + } catch { + + Write-Error $_ + } + } + + + #$propName, $propValue + } + if ($psBoundParameters.Url -and -not $outputObject.psobject.properties['Url']) { + Add-Member -InputObject $outputObject NoteProperty Url $psboundParameters.Url + } + + if ($OutputResponseHeader) { + $outputObject | Add-Member NoteProperty Headers $responseHeaders -Force -PassThru + } else { + $outputObject + } + } + # In this case, construct a regular expression that finds all itemscopes + # Then create another regular expression to find all itemprops + # Walk thru the combined list + } else { + if ($OutputResponseHeader) { + $Html | + Add-Member NoteProperty Headers $responseHeaders -Force -PassThru | + Add-Member NoteProperty ResponseTime $responseTime -PassThru | + Add-Member NoteProperty RequestTime $requestTime -PassThru + } else { + $Html | + Add-Member NoteProperty ResponseTime $responseTime -PassThru | + Add-Member NoteProperty RequestTime $requestTime -PassThru + } + + } + } +} diff --git a/Get-WebConfigurationSetting.ps1 b/Get-WebConfigurationSetting.ps1 new file mode 100644 index 0000000..b31a6f6 Binary files /dev/null and b/Get-WebConfigurationSetting.ps1 differ diff --git a/Get-WebInput.ps1 b/Get-WebInput.ps1 new file mode 100644 index 0000000..785fb1a --- /dev/null +++ b/Get-WebInput.ps1 @@ -0,0 +1,368 @@ +function Get-WebInput +{ + <# + .Synopsis + Get the Web Request parameters for a PowerShell command + .Description + Get the Web Request parameters for a PowerShell command. + + Script Blocks parameters will automatically be run, and text values will be converted + to their native types. + .Example + Get-WebInput -CommandMetaData (Get-Command Get-Command) -DenyParameter ArgumentList + .Link + Request-CommandInput + #> + [OutputType([Hashtable])] + param( + # The metadata of the command that is being wrapped + [Parameter(Mandatory=$true,ValueFromPipeline=$true)] + [Management.Automation.CommandMetaData] + $CommandMetaData, + + # The parameter set within the command + [string] + $ParameterSet, + + # Explicitly allowed parameters (by default, all are allowed unless they are explictly denied) + [string[]] + $AllowedParameter, + + # Explicitly denied parameters. + [string[]] + $DenyParameter, + + # Any aliases for parameter names. + [Hashtable]$ParameterAlias, + + # A UI element containing that will contain all of the values. If this option is used, the module ShowUI should also be loaded. + $Control + ) + + process { + + + $webParameters = @{} + $safecommandName = $commandMetaData.Name.Replace("-", "") + $webParameterNames = $commandMetaData.Parameters.Keys + $webParameterNames = $webParameterNames | + Where-Object { $DenyParameter -notcontains $_ } | + ForEach-Object -Begin { + if ($ParameterAlias) { + foreach ($k in $ParameterAlias.Keys) { $k } + } + } -Process { + if ($_ -ne "URL") { + } else { + } + "$($CommandMetaData.Name)_$_" + } + + + $help = Get-Help -Name $CommandMetaData.Name -ErrorAction SilentlyContinue + if ($request.Params -is [Hashtable]) { + + $paramNames = $request.Params.Keys + + } else { + $paramNames = @($request.Params) + ($request.Files) + @($request.Headers) + + + } + + + if ($Control) { + + if (-not $ExecutionContext.SessionState.InvokeCommand.GetCommand("Get-ChildControl", "All")) { + + return + } + + $uiValue = @{} + $uivalue = Get-ChildControl -Control $control -OutputNamedControl + + foreach ($kv in @($uivalue.GetEnumerator())) { + if (($kv.Key -notlike "${SafeCommandName}_*")) { + $uiValue.Remove($kv.Key) + } + } + + + + foreach ($kv in @($uiValue.GetEnumerator())) { + if ($kv.Value.Text) { + $uiValue[$kv.Key] = $kv.Value.Text + } elseif ($kv.Value.SelectedItems) { + $uiValue[$kv.Key] = $kv.Value.SelectedItems + } elseif ($kv.Value -is [Windows.Controls.Checkbox] -and $kv.Value.IsChecked) { + $uiValue[$kv.Key] = $kv.Value.IsChecked + } elseif ($kv.Value.Password) { + $uiValue[$kv.Key] = $kv.Value.Password + } else { + $uiValue.Remove($kv.Key) + } + } + $webParameterNames = $webParameterNames | + ForEach-Object { + $_.Replace("-","") + } + $paramNames = $uiValue.Keys | + ForEach-Object { $_.Trim() } + + } + + + + + foreach ($param in $paramNames) { + + if ($webParameterNames -notcontains $param) { + continue + } + + + $parameterHelp = + foreach ($p in $help.Parameters.Parameter) { + if ($p.Name -eq $parameter) { + $p.Description | Select-Object -ExpandProperty Text + } + } + + + #$parameterVisibleHelp = $parameterHelp -split ("[`n`r]") |? { $_ -notlike "|*" } + + $pipeworksDirectives = @{} + foreach ($line in $parameterHelp -split ("[`n`r]")) { + if ($line -like "|*") { + $directiveEnd= $line.IndexofAny(": `n`r".ToCharArray()) + if ($directiveEnd -ne -1) { + $name, $rest = $line.Substring(1, $directiveEnd -1).Trim(), $line.Substring($directiveEnd +1).Trim() + $pipeworksDirectives.$Name = $rest + } else { + $name = $line.Substring(1).Trim() + $pipeworksDirectives.$Name = $true + } + + + } + } + + + + + + if ($request.Params -is [Hashtable]) { + $value = $request.Params[$param] + } elseif ($request) { + $value = $request[$param] + if ((-not $value) -and $request.Files) { + $value = $request.Files[$param] + } + if ((-not $value) -and $request.Headers) { + $value = $request.Headers[$param] + + } + } elseif ($uiValue) { + $value = $uiValue[$param] + } + + if (-not $value) { + if ([string]::IsNullOrEmpty($value)) { + continue + } + if ($value -ne 0){ + # Do not skip the the value is really 0 + continue + } + + } + + if ($value -and $value.Trim()[-1] -eq '=') { + # Make everything handle base64 input (as long as it's not to short to be an accident) + $valueFromBase64 = try { + [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($value)) + } catch { + + } + } + + # If the value was passed in base64, convert it + if ($valueFromBase64) { + $value = $valueFromBase64 + } + + # If there was no value, quit + if (-not $value) { + $expectedparameterType = $commandMetaData.Parameters[$realParamName].ParameterType + if (-not ([int], [uint32], [double] -contains $expectedparameterType)) { + continue + } + + } + + + if ($parameterAlias -and $parameterAlias[$param]) { + $realParamName = $parameterAlias[$param] + + } else { + $realParamName= $param -iReplace + "$($CommandMetaData.Name)_", "" -ireplace + "$($commandMetaData.Name.Replace('-',''))_", "" + + } + + #region Coerce Type + + $expectedparameterType = $commandMetaData.Parameters[$realParamName].ParameterType + + if ($pipeworksDirectives.FileName) { + if ($request.Files) { + $value = $request.Files.AllKeys -as $expectedparameterType + continue + } + continue + } + + + if ($expectedParameterType -eq [ScriptBlock]) { + # Script Blocks are converted after being trimmed. + $valueAsType = [ScriptBlock]::Create($value.Trim()) + + if ($valueAsType -and ($valueAsType.ToString().Length -gt 1)) { + $webParameters[$realParamName] = $valueAsType + } + + } elseif ($expectedParameterType -eq [Security.SecureString]) { + $trimmedValue = $value.Trim() + + if ($trimmedValue) { + $webParameters[$realParamName] = ConvertTo-SecureString -AsPlainText -Force $trimmedValue + } + } elseif ([switch], [bool] -contains $expectedParameterType) { + # Switches and bools do a check for false, otherwise true + if ($value -ilike "false") { + $webParameters[$realParamName] = $false + } else { + $webParameters[$realParamName] = $true + } + } elseif ($ExpectedParameterType -eq [Hashtable] -or + $expectedparameterType -eq [Hashtable[]] -or + $expectedparameterType -eq [PSObject] -or + $expectedparameterType -eq [PSObject[]]) { + + $trimmedValue = $value.Trim().Trim([Environment]::newline).Trim() + if (($TrimmedValue.StartsWith("[") -or $trimmedValue.StartsWith("{")) -and $PSVersionTable.PSVersion -ge '3.0') { + # JSON + $webParameters[$realParamName] = ConvertFrom-Json -InputObject $trimmedValue + } elseif ($trimmedValue -like "*@{*") { + # PowerShell Hashtable + $asScriptBlock = try { [ScriptBlock]::Create($trimmedValue) } catch { } + if (-not $asScriptBlock) { + continue + } + + # If it's a script block, make a data language block around it, and catch + $asDataLanguage= try { [ScriptBlock]::Create("data { + $asScriptBlock + }") } catch { } + if (-not $asDataLanguage) { + continue + } + + # Run the data language block + + if ($expectedparameterType -eq [PSObject] -or + $expectedparameterType -eq [PSObject[]]) { + $webParameters[$realParamName] = foreach ($d in & $asDataLanguage) { + $typeName = if ($d.PSTypeName) { + $d.PSTypeName + $d.Remove("PSTypeName") + } else { + "" + } + $o = New-Object PSObject -Property $d + if ($typename) { + $o.pstypenames.clear() + $o.pstypenames.add($typeName) + } + $o + } + } else { + $webParameters[$realParamName] = & $asDataLanguage + } + } elseif ($trimmedValue) { + $multivalue = $value -split "-{3,}" + $webParameters[$realParamName] = + foreach ($mv in $multivalue) { + $fromStringData = ConvertFrom-StringData -StringData $mv + if ($fromStringData -and ($expectedparameterType -eq [PSObject] -or $expectedparameterType -eq [PSObject[]])) { + $d = New-Object PSObject -Property $fromStringData + $typeName = if ($d.PSTypeName) { + $d.PSTypeName + $d.Remove("PSTypeName") + } else { + "" + } + $o = New-Object PSObject -Property $d + if ($typename) { + $o.pstypenames.clear() + $o.pstypenames.add($typeName) + } + $o + } else { + $fromStringData + } + } + } + + } elseif ($ExpectedParameterType.IsArray) { + # If it's an array, split each line and coerce the line into the correct type + if ($expectedparameterType -eq [string[]]) { + # String arrays are split on | or newlines + $valueAsType = @($value -split "[$([Environment]::NewLine)|]" -ne '' | ForEach-Object { $_.Trim() }) -as $expectedParameterType + } elseif ($expectedParameterType -eq [Byte[]]) { + # If it's a byte array, try to read the input stream for the value + $is = $value.InputStream + if (-not $is) { continue } + $buffer = New-Object Byte[] $is.Length + $read = $is.Read($buffer, 0, $is.Length) + $null = $read + $valueAsType = $buffer + + } else { + # Everything else is split on |, newlines, or commas + $valueAsType = @($value -split "[$([Environment]::NewLine)|,]" | ForEach-Object { $_.Trim() }) -as $expectedParameterType + } + + if ($valueAsType) { + $webParameters[$realParamName] = $valueAsType + } + } else { + # In the default case, we can just coerce the value. + # PowerShell's casting magic will handle the rest. + if ($expectedParameterType) { + $valueAsType = $value -as $expectedparameterType + if ($valueAsType) { + $webParameters[$realParamName] = $valueAsType + } + } else { + $webParameters[$realParamName] = $value + } + + } + + #endregion Coerce Type + + } + + $finalParams = @{} + + foreach ($wp in $webParameters.GetEnumerator()) { + if (-not $wp) {continue } + if (-not $wp.Value) { continue } + $finalParams[$wp.Key] = $wp.Value + } + + $finalParams + } +} diff --git a/Get-WebTemplateEditableRegion.ps1 b/Get-WebTemplateEditableRegion.ps1 new file mode 100644 index 0000000..dea627a Binary files /dev/null and b/Get-WebTemplateEditableRegion.ps1 differ diff --git a/Import-Blob.ps1 b/Import-Blob.ps1 new file mode 100644 index 0000000..94aa6ee --- /dev/null +++ b/Import-Blob.ps1 @@ -0,0 +1,274 @@ +function Import-Blob +{ + <# + .Synopsis + Imports from blob storage + .Description + Imports content from azure blob storage + .Example + "hello world" | Export-Blob acontainer ablob.txt -StorageAccount myStorageAccount -StorageKey myStorageKey + Import-Blob acontainer ablob.txt + .Link + Export-Blob + .Link + Get-Blob + .Link + Remove-Blob + #> + [OutputType([string], [byte[]])] + param( + # The name of the container + [Parameter(Mandatory=$true, Position=0,ValueFromPipelineByPropertyName=$true)] + [string]$Container, + + # The name of the item + [Parameter(Mandatory=$true, Position=1,ValueFromPipelineByPropertyName=$true)] + [string]$Name, + + # The storage account + [string]$StorageAccount, + + # The storage key + [string]$StorageKey + ) + + + begin { +#region Azure Signature code +$signMessage = { + param( + [Hashtable]$Header, + [Uri]$Url, + [Uint32]$ContentLength, + [string]$IfMatch ="", + [string]$Md5 = "", + [string]$NowString = [DateTime]::now.ToString("R", [Globalization.CultureInfo]::InvariantCulture), + [Switch]$IsTableStorage, + [string]$method = "GET", + [string]$Storageaccount, + [string]$StorageKey + ) + + $method = $method.ToUpper() + $MessageSignature = + if ($IsTableStorage) { + [String]::Format("{0}`n`n{1}`n{2}`n{3}",@( + $method, + "application/atom+xml", + $NowString, + ( & $GetCanonicalizedResource $Url $StorageAccount))) + + } else { + [String]::Format("{0}`n`n`n{1}`n{5}`n`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $ifMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $md5 + )); + } + + $SignatureBytes = [Text.Encoding]::UTF8.GetBytes($MessageSignature) + + [byte[]]$b64Arr = [Convert]::FromBase64String($StorageKey) + $SHA256 = new-object Security.Cryptography.HMACSHA256 + $sha256.Key = $b64Arr + $AuthorizationHeader = "SharedKey " + $StorageAccount + ":" + [Convert]::ToBase64String($SHA256.ComputeHash($SignatureBytes)) + $AuthorizationHeader +} + +$GetCanonicalizedHeader = { + param( + [Hashtable]$Header + ) + + $headerNameList = new-OBject Collections.ArrayList; + $sb = new-object Text.StringBuilder; + foreach ($headerName in $Header.Keys) { + if ($headerName.ToLowerInvariant().StartsWith("x-ms-", [StringComparison]::Ordinal)) { + $null = $headerNameList.Add($headerName.ToLowerInvariant()); + } + } + $null = $headerNameList.Sort(); + [Collections.Specialized.NameValueCollection]$headers =NEw-OBject Collections.Specialized.NameValueCollection + foreach ($h in $header.Keys) { + $null = $headers.Add($h, $header[$h]) + } + + + foreach ($headerName in $headerNameList) + { + $builder = new-Object Text.StringBuilder $headerName + $separator = ":"; + foreach ($headerValue in (& $GetHeaderValues $headers $headerName)) + { + $trimmedValue = $headerValue.Replace("`r`n", [String]::Empty) + $null = $builder.Append($separator) + $null = $builder.Append($trimmedValue) + $separator = "," + } + $null = $sb.Append($builder.ToString()) + $null = $sb.Append("`n") + } + return $sb.ToString() +} + + +$GetHeaderValues = { + param([Collections.Specialized.NameValueCollection]$headers, $headerName) + $list = new-OBject Collections.ArrayList + + $values = $headers.GetValues($headerName) + if ($values -ne $null) + { + foreach ($str in $values) { + $null = $list.Add($str.TrimStart($null)) + } + } + return $list; +} + +$GetCanonicalizedResource = { + param([uri]$address, [string]$accountName) + + $str = New-object Text.StringBuilder + $builder = New-object Text.StringBuilder "/" + $null = $builder.Append($accountName) + $null = $builder.Append($address.AbsolutePath) + $null = $str.Append($builder.ToString()) + $values2 = New-Object Collections.Specialized.NameValueCollection + if (!$IsTableStorage) { + $values = [Web.HttpUtility]::ParseQueryString($address.Query) + foreach ($str2 in $values.Keys) { + $list = New-Object Collections.ArrayList + foreach ($v in $values.GetValues($str2)) { + $null = $list.add($v) + } + $null = $list.Sort(); + $builder2 = New-Object Text.StringBuilder + foreach ($obj2 in $list) + { + if ($builder2.Length -gt 0) + { + $null = $builder2.Append(","); + } + $null = $builder2.Append($obj2.ToString()); + } + $valueName = if ($str2 -eq $null) { + $str2 + } else { + $str2.ToLowerInvariant() + } + $values2.Add($valueName , $builder2.ToString()) + } + } + $list2 = New-Object Collections.ArrayList + foreach ($k in $values2.AllKeys) { + $null = $list2.Add($k) + } + $null = $list2.Sort() + foreach ($str3 in $list2) + { + $builder3 = New-Object Text.StringBuilder([string]::Empty); + $null = $builder3.Append($str3); + $null = $builder3.Append(":"); + $null = $builder3.Append($values2[$str3]); + $null = $str.Append("`n"); + $null = $str.Append($builder3.ToString()); + } + return $str.ToString(); + +} +#endregion Azure Signature code + + } + + + process { + #region check for and cache the storage account + if (-not $StorageAccount) { + $storageAccount = $script:CachedStorageAccount + } + + if (-not $StorageKey) { + $StorageKey = $script:CachedStorageKey + } + + if (-not $StorageAccount) { + Write-Error "No storage account provided" + return + } + + if (-not $StorageKey) { + Write-Error "No storage key provided" + return + } + + $script:CachedStorageAccount = $StorageAccount + $script:CachedStorageKey = $StorageKey + #endregion check for and cache the storage account + + $method = 'GET' + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container&comp=list&include=metadata" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = & $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method GET + + + $containerBlobList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method GET -AsXml + # $err + + if (-not $containerBlobList ) { return } + $theBlob = $containerBlobList.EnumerationResults.Blobs.Blob | + Where-Object { + $_.Name -eq $Name + } + + + + + $uri = "http://$StorageAccount.blob.core.windows.net/$Container/$Name" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $theBlob.Url -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -method GET + + + + if ($theBlob.Properties.'Content-Type' -ne 'application/x-www-form-urlencoded' -and + $theBlob.Properties.'Content-Type' -notlike "*text*" -and + $theBlob.Properties.'Content-Type' -notlike "*Message*" -and + $theBlob.Properties.'Content-Type' -notlike "*xml*") { + $blobData= Get-Web -UseWebRequest -Header $header -Url $theBlob.Url -Method GET -ErrorAction SilentlyContinue -ErrorVariable Err -AsByte + } else { + $blobData= Get-Web -UseWebRequest -Header $header -Url $theBlob.Url -Method GET -ErrorAction SilentlyContinue -ErrorVariable Err + } + + + + + + $blobData + + + + + + + + } +} \ No newline at end of file diff --git a/Import-DataTable.ps1 b/Import-DataTable.ps1 new file mode 100644 index 0000000..56bebfc Binary files /dev/null and b/Import-DataTable.ps1 differ diff --git a/Import-Deployment.ps1 b/Import-Deployment.ps1 new file mode 100644 index 0000000..84536cc Binary files /dev/null and b/Import-Deployment.ps1 differ diff --git a/Import-PSData.ps1 b/Import-PSData.ps1 new file mode 100644 index 0000000..cb490af Binary files /dev/null and b/Import-PSData.ps1 differ diff --git a/Import-StoredProcedure.ps1 b/Import-StoredProcedure.ps1 new file mode 100644 index 0000000..43c0dea Binary files /dev/null and b/Import-StoredProcedure.ps1 differ diff --git a/Install-PSNode.ps1 b/Install-PSNode.ps1 new file mode 100644 index 0000000..c84711a Binary files /dev/null and b/Install-PSNode.ps1 differ diff --git a/Invoke-EC2.ps1 b/Invoke-EC2.ps1 new file mode 100644 index 0000000..922ca27 --- /dev/null +++ b/Invoke-EC2.ps1 @@ -0,0 +1,127 @@ +function Invoke-EC2 { + <# + .Synopsis + Invokes commands on EC2 instances + .Description + Invokes PowerShell commands on EC2 instances + .Example + Get-EC2 | + Invoke-EC2 -ScriptBlock { Get-Process } + .Link + Get-EC2 + #> + [CmdletBinding(DefaultParameterSetName='InProcess')] + param( + # The EC2 instance ID + [Parameter(ParameterSetName='ComputerName',Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)] + [string] + $InstanceId, + + # An existing PS Session + [Parameter(ParameterSetName='Session', Position=0, ValueFromPipelineByPropertyName=$true)] + [ValidateNotNullOrEmpty()] + [System.Management.Automation.Runspaces.PSSession[]] + ${Session}, + + # The port used to invoke the command + [Parameter(ParameterSetName='ComputerName')] + [ValidateRange(1, 65535)] + [System.Int32] + ${Port}, + + # If set, will use SSL + [Parameter(ParameterSetName='ComputerName')] + [Switch] + ${UseSSL}, + + # The configuration name + [Parameter(ParameterSetName='ComputerName', ValueFromPipelineByPropertyName=$true)] + [System.String] + ${ConfigurationName}, + + # The application name + [Parameter(ParameterSetName='ComputerName', ValueFromPipelineByPropertyName=$true)] + [System.String] + ${ApplicationName}, + + # The throttle limit + [Parameter(ParameterSetName='ComputerName')] + [System.Int32] + ${ThrottleLimit}, + + # If set, will run the command as a job + [Parameter(ParameterSetName='ComputerName')] + [Parameter(ParameterSetName='Session')] + [Switch] + ${AsJob}, + + # If set, will hide the computername property from returned objects + [Parameter(ParameterSetName='ComputerName')] + [Parameter(ParameterSetName='Session')] + [Alias('HCN')] + [Switch] + ${HideComputerName}, + + # The name of the job + [Parameter(ParameterSetName='Session')] + [Parameter(ParameterSetName='ComputerName')] + [System.String] + ${JobName}, + + # The command to run on the EC2 instance + [Parameter(ParameterSetName='ComputerName', Mandatory=$true, Position=1)] + [Parameter(ParameterSetName='Session', Mandatory=$true, Position=1)] + [Alias('Command')] + [ValidateNotNull()] + [System.Management.Automation.ScriptBlock] + ${ScriptBlock}, + + # Remoting session options + [Parameter(ParameterSetName='ComputerName')] + [System.Management.Automation.Remoting.PSSessionOption] + ${SessionOption}, + + # Remoting authentication options + [Parameter(ParameterSetName='ComputerName')] + [System.Management.Automation.Runspaces.AuthenticationMechanism] + ${Authentication}, + + # An input object + [Parameter(ValueFromPipeline=$true)] + [System.Management.Automation.PSObject] + ${InputObject}, + + # Any arguments to the remote script + [Alias('Args')] + [System.Object[]] + ${ArgumentList}, + + # The certificate thumbprint + [Parameter(ParameterSetName='ComputerName')] + [System.String] + ${CertificateThumbprint} + ) + + begin { + + } + process { + $ec2 = Get-EC2 -InstanceId $InstanceID + + if ($psCmdlet.ParameterSetNAme -eq 'ComputerName') { + $ec2Cred = $ec2 | Get-EC2InstancePassword -AsCredential + $ec2 | Enable-EC2Remoting -PowerShell -ErrorAction SilentlyContinue + $icmParams = @{} + $psBoundParameters + $icmParams.Remove('InstanceId') + + Invoke-Command -ComputerName $ec2.PublicDnsName -Credential $ec2Cred @icmParams + } else { + Invoke-Command @psboundParameters + } + + + } + end { + + } +} diff --git a/Invoke-Office365.ps1 b/Invoke-Office365.ps1 new file mode 100644 index 0000000..bcc0a17 --- /dev/null +++ b/Invoke-Office365.ps1 @@ -0,0 +1,211 @@ +function Invoke-Office365 +{ + <# + .Synopsis + Invokes commands within Office365 + .Description + Invokes PowerShell commands within Office365 + .Example + Invoke-Office365 -ScriptBlock { Get-Mailbox -Identity james.brundage@start-automating.com } + .LINK + http://help.outlook.com/en-us/140/cc952755.aspx + #> + [CmdletBinding(DefaultParameterSetName='Office365')] + [OutputType([PSObject])] + param( + # The credential for the Office365 account + [Parameter(Position=1,ParameterSetName='ExchangeServer', ValueFromPipelineByPropertyName=$true)] + [Parameter(Position=1,ParameterSetName='Office365', ValueFromPipelineByPropertyName=$true)] + [Management.Automation.PSCredential] + $Account, + + # A list of account settings to use. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $AccountSetting = @("Office365UserName", "Office365Password"), + + # The exchange server name. Only required if you're not invoking against Office365 + [Parameter(Mandatory=$true,Position=2,ParameterSetName='ExchangeServer', ValueFromPipelineByPropertyName=$true)] + [string] + $ServerName, + + # The script block to run in Office365 + [Parameter(Position=0)] + [string[]] + $ScriptBlock, + + # Any arguments to the script + [Parameter(ValueFromPipeline=$true, ValueFromRemainingArguments=$true)] + [PSObject[]] + $ArgumentList, + + # The name of the session. If omitted, the name will contain the email used to connect to Office365. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string]$Name, + + + # If set, will run the command in a background job + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $AsJob, + + # If set, will create a fresh connection and destroy the connection when the command is complete. + # This is slower, but less likely to make the exchange server experience a session bottleneck. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $FreshConnection + ) + + begin { + if (-not $script:JobCounter) { + $script:JobCounter = 1 + } + } + + process { + #region Copy Credential for Office365 + if (-not $Account) { + if ($AccountSetting -and $accountSetting.Count -eq 2) { + $username = Get-SecureSetting $accountSetting[0] -ValueOnly + $password = Get-SecureSetting $accountSetting[1] -ValueOnly + if ($username -and $password) { + $account = New-Object Management.Automation.PSCredential $username,(ConvertTo-SecureString -AsPlainText -Force $password ) + $psBoundParameters.Account = $account + } + } + } + + if (-not $account) { + Write-Error "Must provide an Account or AccountSetting to connect to Office365" + return + } + #endregion Copy Credential for Office365 + + + + #region Launch Background Job if Needed + if ($AsJob) { + $myDefinition = [ScriptBLock]::Create("function Invoke-Office365 { +$(Get-Command Invoke-Office365 | Select-Object -ExpandProperty Definition) +} +") + $null = $psBoundParameters.Remove('AsJob') + + $myJob= [ScriptBLock]::Create("" + { + param([Hashtable]$parameter) + + } + $myDefinition + { + + Invoke-Office365 @parameter + }) + if (-not $name) { + $name = "Office365Job${script:JobCounter}" + $script:JobCounter++ + } + Start-Job -Name "$name " -ScriptBlock $myJob -ArgumentList $psBoundParameters + return + } + #endregion Launch Background Job if Needed + + + + #region Prepare Session Parameters + if ($psCmdlet.ParameterSetName -eq 'Office365') { + if ($script:ExchangeWebService -and $script:CachedCredential.Username -eq $script:CachedCredential) { + return + } + $ExchangeServer = "https://ps.outlook.com/" + Write-Progress "Connecting to Office365" "$exchangeServer" + $script:CachedCredential = $Account + + $newSessionParameters = @{ + ConnectionUri='https://ps.outlook.com/powershell' + ConfigurationName='Microsoft.Exchange' + Authentication='Basic' + Credential=$Account + AllowRedirection=$true + WarningAction = "silentlycontinue" + SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"}) + Name = "https://$($Account.UserName)@ps.outlook.com/powershell" + + } + + $ExchangeServer = "https://ps.outlook.com/" + } else { + $ExchangeServer = $ServerName + $newSessionParameters = @{ + ConnectionUri="https://$ServerName/powershell" + ConfigurationName='Microsoft.Exchange' + Authentication='Basic' + Credential=$Account + AllowRedirection=$true + WarningAction = "silentlycontinue" + Name = "https://$ServerName/powershell" + SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"}) + + } + } + + if ($psBoundParameters.Name) { + $newSessionParameters.Name = $psBoundParameters.Name + } + #endregion Prepare Session Parameters + + + #region Find or Create Session + $existingSession = (Get-PSSession -Name $newSessionParameters.Name -ErrorAction SilentlyContinue) + if ($FreshConnection -or (-not $existingSession ) -or ($existingSession.State -ne 'Opened')) { + if ($existingSession) { + $existingSession | Remove-PSSession + } + if (-not $FreshConnection) { + $Session = New-PSSession @newSessionParameters -WarningVariable warning + } + } else { + $Session = $existingSession + } + #endregion Find or Create Session + + + #region Invoke on Office365 + if (-not $Session -and -not $FreshConnection) { return } + + foreach ($s in $scriptBlock) { + $realScriptBlock =[ScriptBlock]::Create($s) + if (-not $realScriptBlock) { continue } + + if (-not $FreshConnection) { + Invoke-Command -Session $session -ArgumentList $Arguments -ScriptBlock $realScriptBlock + } else { + $null = $newSessionParameters.Remove("Name") + Start-Job -ArgumentList $Account, $realScriptBlock,$Arguments -ScriptBlock { + param([Management.Automation.PSCredential]$account, $realScriptBlock, $Arguments) + + $realScriptBlock = [ScriptBlock]::Create($realScriptBlock) + $newSessionParameters = @{ + ConnectionUri='https://ps.outlook.com/powershell' + ConfigurationName='Microsoft.Exchange' + Authentication='Basic' + Credential=$Account + AllowRedirection=$true + WarningAction = "silentlycontinue" + SessionOption=(New-Object Management.Automation.Remoting.PSSessionOption -Property @{OpenTimeout="00:30:00"}) + + + } + + Invoke-Command @newsessionParameters -ArgumentList $Arguments -ScriptBlock $realScriptBlock + } | Wait-Job | Receive-Job + + } + + } + + if ($session -and $FreshConnection) { + Remove-PSSession -Session $session + } + #endregion Invoke on Office365 + } +} + + diff --git a/Invoke-Parallel.ps1 b/Invoke-Parallel.ps1 new file mode 100644 index 0000000..ed9ed69 Binary files /dev/null and b/Invoke-Parallel.ps1 differ diff --git a/Invoke-WebCommand.ps1 b/Invoke-WebCommand.ps1 new file mode 100644 index 0000000..a2ec218 Binary files /dev/null and b/Invoke-WebCommand.ps1 differ diff --git a/Join-Website.ps1 b/Join-Website.ps1 new file mode 100644 index 0000000..3bc8d59 Binary files /dev/null and b/Join-Website.ps1 differ diff --git a/License.md b/License.md new file mode 100644 index 0000000..ded6bba Binary files /dev/null and b/License.md differ diff --git a/New-AzureServiceDefinition.ps1 b/New-AzureServiceDefinition.ps1 new file mode 100644 index 0000000..646ce58 --- /dev/null +++ b/New-AzureServiceDefinition.ps1 @@ -0,0 +1,68 @@ +function New-AzureServiceDefinition +{ + <# + .Synopsis + Creates a new Azure Service Definition XML + .Description + Creates a new Azure Service Definition XML. + Additional commands are used to modify the XML's settings + .Example + New-AzureServiceDefinition -ServiceName TestService | + Add-AzureWebSite -HostHeader www.testsite.com -PhysicalDirectory 'C:\inetpub\wwwroot\testsite' + .Link + Add-AzureRole + .Link + Add-AzureStartupTask + .Link + Add-AzureWebSite + .Link + Add-AzureLocalResource + #> + [OutputType([xml],[string])] + param( + # Required. The name of the service. The name must be unique within the service account. + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [Alias('Name')] + [String] + $ServiceName, + + <# + Optional. Specifies the number of upgrade domains across which roles in this service are allocated. + Role instances are allocated to an upgrade domain when the service is deployed. For more information, + see "How to Perform In-Place Upgrades" on MSDN. + + You can specify up to 5 upgrade domains. If not specified, the default number of upgrade domains is 5. + #> + [int] + $UpgradeDomainCount, + + # If set, will output the XML as text. If this is not set, an XmlElement is returned. + [switch] + $AsString + ) + + process { + #region Declare the root XML + if ($psBoundParameters.ContainsKey('UpgradeDomainCount')) { + $def = @" +<ServiceDefinition name="$ServiceName" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" upgradeDomainCount="$UpgradeDomainCount" /> +"@ + } else { + $def = @" +<ServiceDefinition name="$ServiceName" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition" /> +"@ + } + #endregion Declare the root XML + + #region Output the configuration + $xml = [xml]$def + if ($AsString) { + $strWrite = New-Object IO.StringWriter + $xml.Save($strWrite) + return "$strWrite" + } else { + $Xml + } + #endregion Output the configuration + } +} diff --git a/New-Datatable.ps1 b/New-Datatable.ps1 new file mode 100644 index 0000000..2bd06bf Binary files /dev/null and b/New-Datatable.ps1 differ diff --git a/New-PipeworksManifest.ps1 b/New-PipeworksManifest.ps1 new file mode 100644 index 0000000..3680739 --- /dev/null +++ b/New-PipeworksManifest.ps1 @@ -0,0 +1,524 @@ +function New-PipeworksManifest +{ + <# + .Synopsis + Creates a Pipeworks manifest for a module, so it can become a site. + .Description + Creates a Pipeworks manifest for a module, so that it can become a pipeworks site. + + + The Pipeworks manifest is at the heart of how you publish your PowerShell as a web site or software service. + + + New-PipeworksManifest is designed to help you create Pipeworks manifests for most common cases. + .Example + # Creates a quick site to download the ScriptCoverage module + New-PipeworksManifest -Name ScriptCoverage -Domain ScriptCoverage.Start-Automating.com, ScriptCoverasge.StartAutomating.com -AllowDownload + .Link + Get-PipeworksManifest + #> + [OutputType([string])] + param( + # The name of the module + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true,Position=0)] + [string] + $Name, + + # A list of domains where the site will be published + [Parameter(ValueFromPipelineByPropertyName=$true,Position=1)] + [Uri[]] + $Domain, + + # The names of secure settings that will be used within the website. You should have already configured these settings locally with Add-SecureSetting. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=2)] + [string[]] + $SecureSetting, + + + # A list of Keywords that will be used for all pages in the website + [Parameter(ValueFromPipelineByPropertyName=$true,Position=2)] + [string[]] + $Keyword, + + <# + + Commands used within the site. + + + #> + [Parameter(ValueFromPipelineByPropertyName=$true,Position=3)] + [Hashtable] + $WebCommand, + + # The logo of the website. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=4)] + [string] + $Logo, + + # If set, the module will be downloadable. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=5)] + [Switch] + $AllowDownload, + + + # The table for the website. + # This is used to store public information + [Parameter(ValueFromPipelineByPropertyName=$true,Position=6)] + [string] + $Table, + + # The usertable for the website. + # This is used to enable logging into the site, and to store private information + [Parameter(ValueFromPipelineByPropertyName=$true,Position=7)] + [string] + $UserTable, + + # The partition in the usertable where information will be stored. By default, "Users". + # This is used to enable logging into the site, and to store private information + [Parameter(ValueFromPipelineByPropertyName=$true,Position=8)] + [string] + $UserPartition = "Users", + + + # The name of the secure setting containing the table storage account name. By default, AzureStorageAccountName + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $TableAccountNameSetting = "AzureStorageAccountName", + + # The name of the secure setting containing the table storage account key. By default, AzureStorageAccountKey + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $TableAccountKeySetting = "AzureStorageAccountKey", + + <# + The LiveConnect ID. + + + This is used to enable Single Sign On using a Microsoft Account. + + + You must also provide a LiveConnectSecretSetting, and a SecureSetting containing the LiveConnect App Secret. + #> + [Parameter(ValueFromPipelineByPropertyName=$true, Position=9)] + [string] + $LiveConnectID, + + <# + + The name of the SecureSetting that contains the LiveConnect client secret. + + + This is used to enable Single Sign On using a Microsoft Account. + #> + [Parameter(ValueFromPipelineByPropertyName=$true,Position=10)] + [string] + $LiveConnectSecretSetting, + + # The LiveConnect Scopes to use. If not provided, wl.basic, wl.signin, wl.birthday, and wl.emails will be requested + [Parameter(ValueFromPipelineByPropertyName=$true, Position=11)] + [string[]] + $LiveConnectScope, + + # The facebook AppID to use. If provided, then like buttons will be added to each page and users will be able to login with Facebook + [string] + $FacebookAppId, + + # The facebook login scope to use. + [string] + $FacebookScope, + + # The schematics used to publish the website. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=12)] + [string[]] + $Schematic = "Default", + + + # A group describes how commands and topics should be grouped together. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=13)] + [Hashtable[]] + $Group, + + # A paypal email to use for payment processing. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=14)] + [string] + $PaypalEmail, + + # The in which the commands will be shown. If not provided, commands are sorted alphabetically. + # If a Group is provided instead, the Group will be used + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $CommandOrder, + + # Settings related to the main region. + # If you need to change the default look and feel of the main region on a pipeworks site, supply a hashtable containing parameters you would use for New-Region. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable] + $MainRegion, + + + # Settings related to the inner region. + # If you need to change the default look and feel of the inner regions in a pipeworks site, supply a hashtable containing parameters you would use for New-Region. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable] + $InnerRegion, + + # Any addtional settings + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable[]] + $AdditionalSetting, + + # A Google Analytics ID. This will be added to each page for tracking purposes + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $AnalyticsID, + + # A google site verification. This will validate the site for Google Webmaster Tools + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $GoogleSiteVerification, + + # A Bing Validation Key. This will validate the site for Bing Webmaster Tools + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $BingValidationKey, + + # A style sheet to use + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable] + $Style, + + # If set, will use Bootstrap when creating the page + [Switch] + [Alias('Bootstrap')] + $UseBootstrap, + + + # The foreground color + #|Color + [string] + $ForegroundColor, + + # The background color + #|Color + [string] + $BackgroundColor, + + + # The link color + #|Color + [string] + $LinkColor, + + # A list of CSS files to use + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $Css, + + # A list slides in a slideshow. Slides can either be a URL, or HTML content + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $SlideShow, + + # GitIt - Git projects to include + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Hashtable[]] + $GitIt, + + + # The JQueryUI Theme to use. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $JQueryUITheme, + + # Trusted walkthrus will run their sample code. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $TrustedWalkthru, + + # Web walkthrus will output HTML + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]] + $WebWalkthru, + + # An AdSense ID + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $AdSenseID, + + # An AdSense AdSlot + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $AdSlot, + + # If set, will add a plusone to each page + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $AddPlusOne, + + # If set, will add a tweet button to each page + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $Tweet, + + + # If set, will use the Raphael.js library in the site + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $UseRaphael, + + # If set, will use the g.Raphael.js library in the site + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $UseGraphael, + + # If set, will use the tablesorter JQuery plugin in the site + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $UseTablesorter, + + # If set, will change the default branding. By default, pages will display "Powered By PowerShell Pipeworks" + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $Branding, + + # Provides the identity of a Win8 App + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $Win8Identity, + + # Provides the publisher of a Win8 App + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string] + $Win8Publisher, + + # Provides the version of a Win8 App + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Version] + $Win8Version = "1.0.0.0", + + # Provides logos for use in a Win8 App + [Parameter(ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $requiredKeys = "splash","small","wide","store","square" + + $missingKeys = @() + $ht = $_ + foreach ($r in $requiredKeys) { + if (-not $ht.Contains($r)) { + $missingKeys +=$r + } + } + if ($missingKeys) { + throw "Missing $missingKeys" + } else { + return $true + } + })] + [Hashtable] + $Win8Logo + ) + + + process { + $params = @{} + $PSBoundParameters + $params.Remove("AdditionalSetting") + $params.Remove("Name") + $params.Remove("CSS") + $params.Remove("Domain") + $params.Remove("Schematic") + $params.Remove("AdditionalSetting") + $params.Remove("UserTable") + $params.Remove("Table") + $params.Remove("LiveConnectId") + $params.Remove("LiveConnectSecretSetting") + $params.Remove("LiveConnectScope") + $params.Remove("PayPalEmail") + $params.Remove("Win8Logo") + $params.Remove("Win8Version") + $params.Remove("Win8Identity") + $params.Remove("Win8Publisher") + $params.Remove("ForegroundColor") + $params.Remove("BackgroundColor") + $params.Remove("LinkColor") + $params.Remove("SlideShow") + + if ($Win8Logo -and $Win8Identity -and $Win8Publisher) { + $params += @{ + Win8 = @{ + Identity = @{ + Name = $Win8Identity + Publisher = $Win8Publisher + Version = $Win8Version + } + Assets = @{ + "splash.png" = $Win8Logo.Splash + "smallTile.png" = $Win8Logo.Small + "wideTile.png" = $Win8Logo.Wide + "storeLogo.png" = $Win8Logo.Store + "squareTile.png" = $Win8Logo.Square + } + + ServiceUrl = "http://$Domain/" + Name = $Name + + + } + } + } + + + if ($SlideShow) { + $params += @{ + SlideShow = @{ + Slides = $SlideShow + } + } + } + + + if ($PSBoundParameters.PayPalEmail) { + $params+= @{ + PaymentProcessing = @{ + "PayPalEmail" = $PaypalEmail + } + } + } + + if ($PSBoundParameters.Domain) { + $params+= @{ + DomainSchematics = @{ + "$($domain -join ' | ')" = ($Schematic -join "','") + } + } + } + + if ($PSBoundParameters.Css) { + $c = 0 + $cssDict = @{} + foreach ($cs in $css) { + $cssDict["Css$c"] = $cs + $c++ + } + $params+= @{ + Css = $cssDict + } + } + + if ($PSBoundParameters.AdditionalSetting) { + foreach ($a in $AdditionalSetting) { + $params+= $a + } + } + + if ($PSBoundParameters.UserTable) { + $params += @{ + UserTable = @{ + Name = $UserTable + Partition = $UserPartition + StorageAccountSetting = $TableAccountNameSetting + StorageKeySetting = $TableAccountKeySetting + } + } + } + + if ($PSBoundParameters.Table) { + $params += @{ + Table = @{ + Name = $Table + StorageAccountSetting = $TableAccountNameSetting + StorageKeySetting = $TableAccountKeySetting + } + } + } + + if ($PSBoundParameters.LiveConnectID -and $psBoundParameters.LiveConnectSecretSetting) { + $params += @{ + LiveConnect = @{ + ClientID = $LiveConnectID + ClientSecretSetting = $LiveConnectSecretSetting + } + } + + if ($liveconnectScope) { + $params.LiveConnect.Scope = $LiveConnectScope + } + } + + if ($PSBoundParameters.FacebookAppId) { + $params += @{ + Facebook = @{ + AppId = $FacebookAppId + } + } + + if ($FacebookScope) { + $params.Facebook.Scope = $FacebookScope + } + } + + if ($ForegroundColor -or $BackgroundColor -or $LinkColor) { + if (-not $params.Style) { + $params.Style = @{} + } + + if (-not $params.Style.Body) { + $params.Style.Body = @{} + } + + if (-not $params.Style.A) { + $params.Style.A = @{} + } + + $invert = { + param($color) + $color = '#' + $color.TrimStart('#') + $redPart = [int]::Parse($color[1..2]-join'', + [Globalization.NumberStyles]::HexNumber) + $greenPart = [int]::Parse($color[3..4]-join '', + [Globalization.NumberStyles]::HexNumber) + $bluePart = [int]::Parse($color[5..6] -join'', + [Globalization.NumberStyles]::HexNumber) + + $newr = $redPart + $newB = $bluePart + $newg = $greenPart + + $newr = (255 - $redPart) + $newg = (255 - $greenPart) + $newb = (255 - $bluePart) + + "#" + ("{0:x}" -f ([int]$newr)).PadLeft(2, "0") + ("{0:x}" -f ([int]$newg)).PadLeft(2, "0") + ("{0:x}" -f ([int]$newb)).PadLeft(2, "0") + } + + + + + + if ($foregroundColor -and -not $BackgroundColor) { + $BackgroundColor = & $invert $ForegroundColor + } elseif ($BackgroundColor -and -not $ForegroundColor) { + $ForegroundColor = & $invert $BackgroundColor + } + + if ($ForegroundColor) { + $params.Style.Body.color = "#" + "$ForegroundColor".TrimStart('#') + } + + if ($BackgroundColor) { + $params.Style.Body.'background-color' = "#" + "$BackgroundColor".TrimStart('#') + } + + + if ($LinkColor) { + $params.Style.a.color = "#" + "$LinkColor".TrimStart('#') + } else { + $params.Style.a.color = "#" + "$foregroundColor".TrimStart('#') + } + + } + + + + Write-PowerShellHashtable $params + } +} \ No newline at end of file diff --git a/New-RDPFile.ps1 b/New-RDPFile.ps1 new file mode 100644 index 0000000..739fac0 Binary files /dev/null and b/New-RDPFile.ps1 differ diff --git a/New-Region.ps1 b/New-Region.ps1 new file mode 100644 index 0000000..cdae74a --- /dev/null +++ b/New-Region.ps1 @@ -0,0 +1,2563 @@ +function New-Region +{ + <# + .Synopsis + Creates a new web region. + .Description + Creates a new web region. Web regions are lightweight HTML controls that help you create web pages. + .Link + New-Webpage + + .Example + # Makes a JQueryUI tab + New-Region -Layer @{ + "Tab1" = "Content In Tab One" + "Tab2" = "Content in Tab Two" + } -AsTab + .Example + # Makes a JQueryUI accordian + New-Region -Layer @{ + "Accordian1" = "Content In The first Accordian" + "Accordian1" = "Content in the second Accordian" + } -AsAccordian + .Example + # Makes an empty region + New-Region -Style @{} -Content -LayerID MyId + .Example + # A Centered Region containing Microdata + New-Region -ItemType http://schema.org/Event -Style @{ + 'margin-left' = '7.5%' + 'margin-right' = '7.5%' + } -Content ' +<a itemprop="url" href="nba-miami-philidelphia-game3.html"> +NBA Eastern Conference First Round Playoff Tickets: +<span itemprop="name"> Miami Heat at Philadelphia 76ers - Game 3 (Home Game 1) </span> </a> +<meta itemprop="startDate" content="2016-04-21T20:00"> Thu, 04/21/16 8:00 p.m. +<div itemprop="location" itemscope itemtype="http://schema.org/Place"> + <a itemprop="url" href="wells-fargo-center.html"> Wells Fargo Center </a> + <div itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> + <span itemprop="addressLocality">Philadelphia</span>, <span itemprop="addressRegion">PA</span> + </div> +</div> +<div itemprop="offers" itemscope itemtype="http://schema.org/AggregateOffer"> + Priced from: <span itemprop="lowPrice">$35</span> <span itemprop="offerCount">1938</span> tickets left +</div>' + .Notes + The Parameter set design on New-Region is a little complex. + + There are two base parameter sets, raw layer content (-Content) and structured layer content (-Layer) + + In each case, parameters named -As* determine how the actual layer will be rendered. + + To make matters more complex, different -As* parameters require different JavaScript frameworks + #> + [CmdletBinding(DefaultParameterSetName='Content')] + [OutputType([string])] + param( + # The content within the region. This content will be placed on an unnamed layer. + [Parameter(ParameterSetName='Content',Position=0,ValueFromPipeline=$true)] + [string[]]$Content, + # A set of layer names and layer content + [Parameter(ParameterSetName='Layer',Position=0)] + [Alias('Item')] + [Hashtable]$Layer, + # A set of layer names and layer URLs. Any time the layer is brought up, the content will be loaded via AJAX + [Parameter(ParameterSetName='Layer')] + [Hashtable]$LayerUrl = @{}, + + # A set of layer direct links + [Parameter(ParameterSetName='Layer')] + [Hashtable]$LayerLink = @{}, + + + + # The order the layers should appear. If this is not set, the order will + # be the alphabetized list of layer names. + [Parameter(ParameterSetName='Layer')] + [Alias('LayerOrder')] + [string[]]$Order, + # The default layer. If this is not set and if -DefaultToFirst is not set, a layer + # will be randomly chosen. + [Parameter(ParameterSetName='Layer')] + [string]$Default, + # The default layer. If this is not set and if -DefaultToFirst is not set, a layer + # will be randomly chosen. + [Parameter(ParameterSetName='Layer')] + [switch]$DefaultToFirst, + # The Name of the the container. The names becomes the HTML element ID of the root container. + [Alias('Container')] + [Alias('Id')] + [string]$LayerID = 'Layer', + # The percentage margin on the left. The region will appear this % distance from the side of the screen, regardless of resolution + [Parameter(ValueFromPipelineByPropertyName=$true,Position=1)] + [ValidateRange(0,100)] + [Double]$LeftMargin = 2.5, + # The percentage margin on the right. The region will appear this % distance from the side of the screen, regardless of resolution + [Parameter(ValueFromPipelineByPropertyName=$true,Position=2)] + [ValidateRange(0,100)] + [Double]$RightMargin = 2.5, + # The percentage margin on the top. The region will appear this % distance from the top of the screen, regardless of resolution + [Parameter(ValueFromPipelineByPropertyName=$true,Position=3)] + [ValidateRange(0,100)] + [Double]$TopMargin = 10, + # The percentage margin on the bottom. The region will appear this % distance from the bottom of the screen, regardless of resolution + [Parameter(ValueFromPipelineByPropertyName=$true,Position=4)] + [ValidateRange(0,100)] + [Double]$BottomMargin = 10, + # The border for the region. Becomes the CSS border attribute of the main container + [string]$Border = "1px solid black", + + # If set, hides the forward and back buttons + [switch]$HideDirectionButton, + # If set, hides the more button + [switch]$HideMoreButton, + # If set, hides the title area + [switch]$HideTitleArea, + # If set, shows a horizontal rule under the title + [switch]$HorizontalRuleUnderTitle, + + # If set, places the toolbar above + [switch]$ToolbarAbove, + # The margin of the toolbar + [int]$ToolbarMargin, + # URL for a logo to go on the title of each page + [uri]$Logo, + + # If set, the control will not be aware of the web request string. + # Otherwise, a URL can provide which layer of a region to show. + [switch]$NotRequestAware, + + # If set, the region will have any commands to change its content, + # and will only have one layer + [switch]$IsStaticRegion, + + # If set, turns off fade effects + [switch]$NotCool, + + # The transition time for all fade effects. Defaults to 200ms + [Timespan]$TransitionTime = "0:0:0.2", + + # The layer heading size + [ValidateRange(1, 6)] + [Uint32]$LayerHeadingSize = 2, + + + # The number of keyframes in all transitions + [ValidateRange(10, 100)] + [Uint32]$KeyFrameCount = 10, + + # If set, enables a pan effect within the layers + [Switch]$CanPan, + + # If set, the entire container can be dragged + [Switch]$CanDrag, + + # The scroll speed (when on iOs or webkit) + [int]$ScrollSpeed = 25, + + # The CSS class to use + [string[]]$CssClass, + + # A custom CSS style. + [Hashtable]$Style, + + # If set, the layer will not be automatically resized, and percentage based margins will be ignored + [switch]$FixedSize, + + # If set, will not allow the contents of the layer to be switched + [switch]$DisableLayerSwitcher, + + # If set, will automatically switch the contents on an interval + [Parameter(ParameterSetName='Layer')] + [Timespan]$AutoSwitch = "0:0:4", + + # If set, will create the region as an JQueryUI Accordion. + [Parameter(ParameterSetName='Layer')] + [Switch]$AsAccordian, + + # If set, will create the region as an JQueryUI Tab. + [Parameter(ParameterSetName='Layer')] + [Switch]$AsTab, + + # If set, the JQueryUI tab will appear below the content, instead of above + [Parameter(ParameterSetName='Layer')] + [Switch]$TabBelow, + + # If set, will open the tabs on a mouseover event + [Switch]$OpenOnMouseOver, + + # If set, will create a set of popout regions. When the tile of each layer is clicked, the layer will be shown. + [Parameter(ParameterSetName='Layer')] + [Switch]$AsPopout, + + # If set, will create a set of popdown regions. As region is clicked, the underlying content will be shown below. + [Parameter(ParameterSetName='Layer')] + [Switch]$AsPopdown, + + # If set, will create a set of popdown regions. As region is clicked, the underlying content will be shown below. + [Parameter(ParameterSetName='Layer')] + [Switch]$AsPopIn, + + # If set, will create a slide of the layers. If a layer title is clicked, the slideshow will stop + [Parameter(ParameterSetName='Layer')] + [Switch]$AsSlideShow, + + # If set, the layer will be created as a portlet + [Parameter(ParameterSetName='Layer')] + [Switch]$AsPortlet, + + # If set, the layer will be created as a newspaper + [Parameter(ParameterSetName='Layer')] + [Switch]$AsNewspaper, + + # If set, the newspaper headline buttons will be underlined + [Switch]$UnderlineNewspaperHeadline, + + # If set, newspaper headlines will be displayed as buttons + [Switch]$UseButtonForNewspaperHeadline, + + # The alignment used for the heading in a newspaper + [ValidateSet("left", "center", "right")] + [string]$NewspaperHeadingAlignment = "left", + + # The size of the headings in a newspaper + [ValidateRange(1,6)] + [Uint32] + $NewspaperHeadingSize = 2, + + # The width of the columns in the newspaper + [Float[]]$NewspaperColumn = @(.67, .33, .5, .5, 1), + + # The columns that will be expanded + [string[]]$ExpandNewspaperColumn = @(0, 1, 2, 3), + + # The number of columns in a Portlet + [Uint32]$ColumnCount = 2, + + # The width of a column within a Portlet + [string]$ColumnWidth, + + # If set, will create a set of JQueryUI buttons which popup JQueryUI dialogs + [Switch]$AsPopup, + + + # If set, will create a set of JQueryUI simple widgets + [Switch]$AsWidget, + + # If set, will create the layer as a series of resizable items + [Switch]$AsResizable, + + # If set, will create the layer as a series of draggable items + [Switch]$AsDraggable, + + # If set, the layer will be created as a left sidebar menu + [switch]$AsLeftSidebarMenu, + + # If set, the layer will be created as a right sidebar menu + [Switch]$AsRightSidebarMenu, + + # If set, the layer will be created as a grid + [Switch]$AsGrid, + + # If set, the layer will be created as a menu + [Switch]$AsMenu, + + # If set, the layer will be created as a Bootstrap navbar menu + [Switch]$AsNavbar, + + # If set, the layer will be created as a Bootstrap carousel + [Switch]$AsCarousel, + + # If set, the layer will be created as set of Bootstrap headlines + [Switch]$AsHeadline, + + # If set, the layer will be created as set of Bootstrap buttons + [Switch]$AsButton, + + # If set, the content will be displayed as a tree + [Switch]$AsTree, + + # If set, the content will be displayed as a series of columns + [Switch]$AsColumn, + + # The color of the branch in a tree + [string]$BranchColor = '#000000', + + + # If set, will show large item titles for items in the carousel + [switch]$ShowLayerTitle, + + # If set, the layer will be created as a Bootstrap featurette + [Switch]$AsFeaturette, + + # If set, the layer will be created as a Bootstrap row + [Switch]$AsRow, + + # If set, the layer will be created as pair of Bootstrap rows, with content expanding down in the right half of the grid. + [Switch]$AsHangingSpan, + + # The size (in Bootstrap spans) of the buttons used in -AsHangingSpan. + # The size of the hanging area will be the remainder of the 12 span grid, with one span reserved for offset + [Uint32]$SpanButtonSize = 2, + + # The Bootstrap RowSpan + [string[]]$RowSpan = 'span4', + + # The width of items within a grid + [Uint32]$GridItemWidth = 175, + + # The height of items within a grid + [Uint32]$GridItemHeight = 112, + + # The width of a sidebar in a left or right sidebar menu + [ValidateRange(1,100)] + [Uint32]$SidebarWidth = 20, + + # One or more item types to apply to the region. If set, an itemscope and itemtype will be added to the region + [string[]] + $ItemType, + + # If set, will use a vector (%percentage based layout) for the region. + [Switch] + $AsVectorLayout, + + # If set, will hide the slide name buttons (effectively creating an endless slideshow) + [switch] + $HideSlideNameButton, + + # If set, will use a middot instead of a slide name for a slideshow button. + [switch] + $UseDotInsteadOfName, + + # The background color in a popin + [string] + $MenuBackgroundColor, + + # The inner order within a + [Hashtable] + $LayerInnerOrder + ) + + begin { + # Creates an ID out of any input string + $getSafeLayerName = { + param($layerName) + + $layerName.Replace(" ", "_space_").Replace("-", "").Replace("+", "").Replace("!", "_bang_").Replace(",", "").Replace("<", "").Replace(">","").Replace("'", "").Replace('"','').Replace('=', '').Replace('&', '').Replace('?', '').Replace("/", "").Replace(",", "_").Replace("|", "_").Replace(":","_").Replace(".", "_").Replace("@", "_at_").Replace("(", "-__").Replace(")","__-").Replace("#", "_Pound_").Replace('%', '_Percent_') + } + + + # Gets ajax script for a url + $getAjaxAndPutIntoContainer = { + param($url, $container, $UrlType) + + +if ($url -like "*.zip" -or + $url -like "*.exe" -or + $UrlType -eq 'Button') { +@" +window.location = '$url'; +"@ + +} else { +$xmlHttpVar = "xmlRequest${Container}" +@" + var $xmlHttpVar; + if (window.XMLHttpRequest) + { + $xmlHttpVar = new XMLHttpRequest(); // code for IE7+, FireFox, Chrome, Opera, Safari + } else + { + $xmlHttpVar = new ActiveXObject("Microsoft.XMLHTTP"); // code for IE5, IE6 + } + + $xmlHttpVar.onreadystatechange = function() { + + if (${xmlHttpVar}.readyState == 4) { + if (${xmlHttpVar}.status == 200) { + document.getElementById("${Container}").innerHTML = $xmlHttpVar.responseText; + var innerScripts = document.getElementById("${Container}").getElementsByTagName("script"); + for(var i=0;i<innerScripts.length;i++) + { + try { + eval(innerScripts[i].text); + } catch (e) { + } + } + window.${Container}_Response = $xmlHttpVar.responseText; + } else { + document.getElementById("${Container}").innerHTML = $xmlHttpVar.Status; + } + + + } else { + window.show${Container}loading = function () { + var ih = document.getElementById("${Container}").innerHTML; + if (ih.length < 40) { + ih += "·" + } else { + ih = "·" + } + if (window.${Container}_Response == null) { + document.getElementById("${Container}").innerHTML= ih; + + setTimeout('show${Container}loading()', 75); + } + } + setTimeout('show${Container}loading()', 75); + } + } + + if (window.${Container}_Response == null) { + $xmlHttpVar.open('POST', "$Url", true); + + $xmlHttpVar.send(); + + document.getElementById("${Container}").innerHTML = "·" + } +"@ +} + + + } + + $ShowLayerContent = { + param($showButtonId, + $popoutLayerId, + [string[]]$AdditionalElements, + [Switch]$Exclusive, + [Switch]$StopSlideShow, + [Switch]$IsCurrentSlide, + [string]$ShownState = 'inline') + +@" + <script> + var ${LayerId}_currentSlide = $(if ($isCurrentSlide) { "'$popoutLayerId'"} else {'null'}); + window.${ShowButtonId}_click = function () { + $( + if ($layerUrl -and $layerUrl[$layerName]) { + . $getAjaxAndPutIntoContainer $layerUrl[$layerName] $popoutLayerId + } elseif ($LayerLink -and $LayerLink[$layerName]) { + . $getAjaxAndPutIntoContainer $LayerLink[$layerName] $popoutLayerId "Button" + } else { + "" + }) + $(if ($Exclusive) { + " + + if (${LayerId}_currentSlide != null) { + var documentElement = document.getElementById(${LayerId}_currentSlide); + documentElement.style.display = 'none'; + + } + " + }) + $( + $AdditionalElements = @($popoutLayerId) + $AdditionalElements + foreach ($el in $AdditionalElements) { + if (-not $el) { continue } +" + var documentElement = document.getElementById('$el'); + if (documentElement != null && documentElement.style.display == 'none') { + documentElement.style.display = '$ShownState'; + } else { + if (documentElement != null) { + documentElement.style.display = 'none'; + } + } +" + } + ) + + ${LayerId}_currentSlide = '$popoutLayerId'; + $(if ($StopSlideShow) { + "clearInterval(${LayerId}_SlideshowTimer); + for (i =0; i < ${LayerId}_SlideNames.length; i++) { + if (${LayerId}_SlideNames[i] != '$popoutLayerId') { + document.getElementById(${LayerId}_SlideNames[i]).style.display = 'none'; + } + } + " + }) + } + </script> +"@ + } + + $GetLayerContent = { + param($originalParameters) + +@" +$( +if ($layer[$layerName] -is [Hashtable]) { + $parameterCopy = @{} + $originalParameters + + + $parameterCopy.Layer = $layer[$layerName] + $parameterCopy.Order = if ($layer[$layerName].InnerOrder) { + $layer[$layerName].InnerOrder + } else { + $layer[$layerName].Keys | Sort-Object + } + $parameterCopy.LayerId = $LayerID + "_" + $layerName + "_items" + + $innerLayerOrder = $layer[$layerName].Keys | Sort-Object + + New-Region @parameterCopy +} elseif ($layer[$layername] -is [string]) { + $layer[$layerName] +} elseif ($layer[$layerName]) { + $layer[$layerName] | Out-HTML +}) +"@ + + } + + + + + $layerCount = 0 + $originalLayerId = $layerId + } + + process { + + if ($layerCount -gt 0) { + $layerId = $originalLayerId + $layerCount + } + $layerCount++ + #region Internal changing of parameters of parameter set overlaps + if ($psCmdlet.ParameterSetName -eq 'Content') { + if (-not $Layer) { + $Layer = @{} + } + $Layer.'Default' = $Content + + $hideTitleArea = $true + $hideDirectionButton = $true + $hideMoreButton = $true + + if (-not $asVectorLayout) { + $DisableLayerSwitcher = $true + } + } + + + $layerId= . $getSafeLayerName $layerId + if ($psBoundParameters.AutoSwitch -and (-not $AsSlideshow)) { + $DisableLayerSwitcher = $false + $NotRequestAware = $true + } + + + if ($AsAccordian -or $AsTab -or $AsPopout -or $asPopin -or $AsPopdown -or $asPopup -or + $asSlideshow -or $AsWidget -or $AsPortlet -or $AsLeftSidebarMEnu -or $ASRightSidebarMenu -or + $AsResizable -or $AsDraggable -or $AsGrid -or $AsNavbar -or $AsCarousel -or $asHeadline -or + $Asbutton -or $asTree -or $AsFeaturette -or $AsRow -or $AsColumn -or $AsHangingSpan -or $AsMenu -or $AsNewspaper) { + $NotRequestAware = $true + $IsStaticRegion = $true + $DisableLayerSwitcher = $true + $FixedSize = $true + + } elseif (-not $AsVectorLayout -and -not $psboundParameters.Style -and -not $psBoundParameters.Keys -like "*as*") { + $NotRequestAware = $true + $IsStaticRegion = $true + $DisableLayerSwitcher = $true + $FixedSize = $true + + if ($layer.Count -eq 1) { + $AsWidget = $true + $AsResizable = $true + } elseif ($layer.Count -le 5) { + $AsTab = $true + } else { + $AsAccordian = $true + } + } + + if ($Style) { + $FixedSize = $true + } + + + + if ($FixedSize) { + $HideDirectionButton = $true + $HideMoreButton = $true + # DCR : Make -*Margin turn into styles + if (-not $style) { + $style= @{} + } + if ($psBoundParameters.LeftMargin) { + $style["margin-left"] = "${LeftMargin}%" + } + if ($psBoundParameters.RightMargin) { + $style["margin-right"] = "${RightMargin}%" + } + if ($psBoundParameters.TopMargin) { + $style["margin-top"] = "${TopMargin}%" + } + + if ($psBoundParameters.BottomMargin) { + $style["margin-bottom"] = "${BottomMargin}%" + } + if ($psBoundParameters.Border) { + $style["border"] = "$border" + } + } + + if ($isStaticRegion) { + $hideTitleArea = $true + $HideMoreButton = $true + $hideDirectionButton = $true + } + #endregion Internal changing of parameters of parameter set overlaps + + + + if (-not $psBoundParameters.Default) { + $randomOnLoad = "" + + } + + + if (-not $psBoundParameters.Order) { + $order = $layer.Keys |Sort-Object + } + + + + + $layerTitleAreaText =if (-not $HideTitleArea) { " + <div style='margin-top:5px;margin-right:5px;z-index:-1' id='${LayerID}_TitleArea' NotALayer='true'> + </div> + " + + } else { + "" + } + + $fadeJavaScriptFunctions = if (-not $NotCool) { +@" +function set${LayerID}LayerOpacity(id, opacityLevel) +{ + var eStyle = document.getElementById(id).style; + eStyle.opacity = opacityLevel / 100; + eStyle.filter = 'alpha(opacity='+opacityLevel+')'; + if (eStyle.filter != 'alpha(opacity=0)') { + eStyle.visibility = 'visible' + } + if (opacityLevel > 0) { + eStyle.visibility = 'visible' + } else { + eStyle.visibility = 'hidden' + } +} + + +function fade${LayerID}Layer(eID, startOpacity, stopOpacity, duration, steps) { + + if (steps == null) { steps = duration } + var opacityStep = (stopOpacity - startOpacity) / steps; + var timerStep = duration / steps; + var timeStamp = 10 + var opacity = startOpacity + for (var i=0; i < steps;i++) { + opacity += opacityStep + timeStamp += timerStep + setTimeout("set${LayerID}LayerOpacity('"+eID+"',"+opacity+")", timeStamp); + } + + return +} + +"@ + } else { ""} + + $dragChunk= if ($CanDrag) { @" + if (document.getElementById('$LayerID').addEventListener) { + document.getElementById('$LayerID').addEventListener('touchmove', function(e) { + e.preventDefault(); + curX = e.targetTouches[0].pageX; + curY = e.targetTouches[0].pageY; + document.getElementById('$LayerID').style.webkitTransform = 'translate(' + curX + 'px, ' + curY + 'px)' + }) + } +"@ } else { ""} + + $enablePanChunk = if ($CanPan) { @" + var last${LayerID}touchX = null; + var last${LayerID}touchY = null; + var last${LayerID}scrollX = 0; + var last${LayerID}scrollY = 0; + var eventHandler = function(e) { + e.preventDefault(); + container = document.getElementById('$LayerID') + layer = getCurrent${LayerID}Layer() + if (last${LayerID}touchX && last${LayerID}touchX > e.targetTouches[0].pageX) { + // Moving right + } + if (last${LayerID}touchX && last${LayerID}touchX < e.targetTouches[0].pageX) { + // Moving left + } + if (last${LayerID}touchY && last${LayerID}touchY < e.targetTouches[0].pageY) { + // Moving up + last${LayerID}scrollY+= $ScrollSpeed + + if (last${LayerID}scrollY > 0) { + last${LayerID}scrollY = 0 + } + + } + if (last${LayerID}touchY && last${LayerID}touchY > e.targetTouches[0].pageY) { + // Moving down + last${LayerID}scrollY-= $ScrollSpeed + + // if less than zero, set a timeout to bounce the content back + if (last${LayerID}scrollY < -layer.scrollHeight) { + last${LayerID}scrollY = -layer.scrollHeight + + } + + } + last${LayerID}touchX = e.targetTouches[0].pageX + last${LayerID}touchY = e.targetTouches[0].pageY + + layer.style.webkitTransform = 'translate(' + last${LayerID}scrollX +"px," + last${LayerID}scrollY +"px)"; + + + + } + if (document.getElementById('$LayerID').addEventListener) { + document.getElementById('$LayerID').addEventListener('touchmove', eventHandler) + } + + var layers = get${LayerID}Layer(); + while (layers[layerCount]) { + if (layers[layerCount].addEventListener) { + layers[layerCount].addEventListener('touchmove', eventHandler) + } + + layerCount++; + } + + +"@ } else { ""} + $layerSwitcherScripts = if (-not $DisableLayerSwitcher) { + + + $moreButtonText = if (-not $HideMoreButton) { + " + <span style='padding:5px;'> + <input type='button' id='${$LayerID}_MoreButton' onclick='show${LayerID}Morebar();' style='border:1px solid black;padding:5;font-size:medium' value='...' /> + </span> + " + } else { + "" + } + + $directionButtonText = if (-not $HideDirectionButton) { + " + <span style='padding:5px;'> + <input type='button' id='${LayerID}_LastButton' onclick='moveToLast${LayerID}Item();' style='border:1px solid black;padding:5;font-size:medium' value='<' /> + <input type='button' id='${LayerID}_NextButton' onclick='moveToNext${LayerID}Item();' style='border:1px solid black;padding:5;font-size:medium' value='>' /> + </span> + " + + } else { + "" + } + + + +@" + + $fadeJavaScriptFunctions + + function moveToNext${LayerID}Item() { + var layers = get${LayerID}Layer(); + var layerCount = 0; + var lastLayerWasVisible = false; + while (layers[layerCount]) { + if (lastLayerWasVisible == true) { + select${LayerID}Layer(layers[layerCount].id); + lastLayerWasVisible = false; + break + } + + + if (layers[layerCount].style.opacity == 1) { + // This layer is visible. Hide it, and make sure we know to show the next one. + lastLayerWasVisible = true; + } + layerCount++; + } + + if (lastLayerWasVisible == true) { + select${LayerID}Layer(layers[0].id); + } + } + + function moveToLast${LayerID}Item() { + var layers = get${LayerID}Layer(); + + var layerCount = 0; + var lastLayer = null; + var lastLayerWasVisible = false; + var showLastLayer =false; + while (layers[layerCount]) { + if (layers[layerCount].style.visibility == 'visible') { + if (lastLayer == null) { + showLastLayer = true; + } else { + select${LayerID}Layer(lastLayer.id); + } + } + + lastLayer = layers[layerCount]; + layerCount++; + } + + if (showLastLayer == true) { + select${LayerID}Layer(lastLayer.id); + } + } + + function select${LayerID}Layer(name, hideMoreBar) { + var layers = get${LayerID}Layer(); + var layerCount = 0; + var found = false; + while (layers[layerCount]) { + + containerName = '${LayerID}_' + name + if (layers[layerCount].id == name || + layers[layerCount].id == containerName || + layers[layerCount].id == containerName.replace("-", "_").replace(" ", "")) { + if (typeof fade${LayerID}Layer == "function") { + layers[layerCount].style.zIndex = 1 + fade${LayerID}Layer(layers[layerCount].id, 0, 100, $($TransitionTime.TotalMilliseconds), $KeyFrameCount) + } else { + layers[layerCount].style.visibility = 'visible'; + layers[layerCount].style.opacity = 1; + } + if (document.getElementById('${LayerID}_TitleArea') != null) { + document.getElementById('${LayerID}_TitleArea').innerHTML = get${LayerID}LayerTitleHTML(layers[layerCount]); + } + } else { + if (typeof fade${LayerID}Layer == "function") { + if (layers[layerCount].style.opacity != 0) { + fade${LayerID}Layer(layers[layerCount].id, 100, 0, $($TransitionTime.TotalMilliseconds), $KeyFrameCount) + } + } else { + layers[layerCount].style.visibility = 'hidden'; + } + } + layerCount++; + } + + if (hideMoreBar == true) { + hide${LayerID}Morebar(); + } + } + + function add${LayerID}Layer(name, content, layerUrl, refreshInterval) + { + var layers = get${LayerID}Layer(); + var safeLayerName = name.replace(' ', '').replace('-', '_'); + newHtml = "<div id='${LayerID}_" + safeLayerName +"' style='$(if ($AsTab -or -not $DisableLayerSwitcher) {'visibility:hidden;'})position:absolute;margin-top:0px;margin-left:0px;opacity:0;overflow:auto;-webkit-overflow-scrolling: touch;'>" + newHtml += content + newHtml += "</div><script>" + newHtml += ("document.getElementById('${LayerID}_" + safeLayerName + "').setAttribute('friendlyName', '" + name + "');") + if (layerUrl) { + + newHtml += ("document.getElementById('${LayerID}_" + safeLayerName + "').setAttribute('layerUrl', '" + layerUrl + "');") + } + newHtml += ("<" + "/script>") + + document.getElementById("${LayerID}").innerHTML += newHtml; + layers = get${LayerID}Layer() + layerCount =0 + while (layers[layerCount]) { + if (layers[layerCount].id == "${LayerID}_" + safeLayerName) { + layers[layerCount].setAttribute('friendlyName', name); + if (layerUrl) { + layers[layerCount].setAttribute('layerUrl', layerUrl); + } + if (refreshInterval) { + layers[layerCount].setAttribute('refreshInterval', refreshInterval); + } + if (layerCount == 0) { + // first layer, show it + select${LayerID}Layer(layers[layerCount].id, true); + } + } + layerCount++ + } + } + + function set${LayerID}Layer(name, newHTML) + { + var safeLayerName = name.replace(' ', '').replace('-', '_'); + layerId ="${LayerID}_" + safeLayerName + + document.getElementById(layerId).innerHTML = newHtml; + select${LayerID}Layer(layerId); + } + + + function new${LayerID}CrossLink(containerName, sectionName, displayName) + { + if (! displayName) { + displayName = sectionName + } + "<a href='javascript:void(0)' onclick='" + "select" +containerName + "layer(\"" + sectionName+ "\")'>" + displayName + "</a>'" + } + + function show${LayerID}Morebar() { + var morebar = document.getElementById('${LayerID}_Toolbar') + var layers = get${LayerID}Layer(); + var layerCount = layers.length; + + newHtml = "<span><select id='${LayerID}_ToolbarJumplist' style='font-size:large;padding:5'>" + + for (i =0 ;i < layerCount;i++) { + newHtml += "<option style='font-size:large;' value='"; + newHtml += layers[i].id; + newHtml += "'>"; + newHtml += layers[i].attributes['friendlyName'].value; + newHtml += "</option>" + } + newHtml += "</select> \ + <input type='button' style='border:1px solid black;padding:5;font-size:medium' value='Go' onclick='select${LayerID}Layer(document.getElementById(\"${LayerID}_ToolbarJumplist\").value, true);'>\ + </span>" + morebar.innerHTML = newHtml; + + // morebar.style.visibility = 'visible'; + } + + function hide${LayerID}Morebar() { + document.getElementById('${LayerID}_Toolbar').innerHTML = "$($directionButtonText -split ([Environment]::NewLine) -join ('\' + [Environment]::NewLine) + $moreButtonText -split ([Environment]::NewLine) -join ('\' + [Environment]::NewLine))"; + } +"@ + } else { + "" + } + + $cssStyleAttr = if ($psBoundParameters.Style) { + . Write-CSS -Style $style -OutputAttribute + } else { + "" + } + $cssFontAttr = if ($psBoundParameters.Style.Keys -like "font*"){ + $2ndStyle = @{} + foreach ($k in $psBoundParameters.sTyle.keys) { + if ($k -like "font*"){ + $2ndStyle[$k] = $style[$k] + } + } + . Write-CSS -Style $2ndStyle -outputAttribute + } else { + "" + } + $cssStyleChunk = if ($psBoundParameters.Style) { + "style='" +$cssStyleAttr + "'" + } else { + "" + } + + + if ($AsCarousel) { + if (-not $CssClass) { + $CssClass = @() + + } + if ($CssClass -notcontains "carousel") { + $CssClass += "carousel" + } + if ($CssClass -notcontains "slide") { + $CssClass += "slide" + } + + } + + if ($AsRow) { + if (-not $CssClass) { + $CssClass = @() + + } + if ($CssClass -notcontains "container") { + $CssClass += "container" + } + + } + + if ($Asnavbar) { + if (-not $CssClass) { + $CssClass = @() + + } + if ($CssClass -notcontains "navbar-wrapper") { + $CssClass += "navbar-wrapper" + } + } + + if ($AsFeaturette) { + if (-not $CssClass) { + $CssClass = @() + + } + if ($CssClass -notcontains "navbar-wrapper") { + $CssClass += "navbar-wrapper" + } + if ($CssClass -notcontains "featurette") { + $CssClass += "featurette" + } + } + + + $classChunk = if ($CssClass) { + "class='$($cssClass -join ' ')'" + } else { + "" + } + + $itemTypeChunk = if ($itemType) { + "itemscope='' $(if ($itemId) {'itemid="' + $itemId + '"' }) itemtype='$($itemType -join ' ')'" + } else { + "" + } + + $outSb = New-Object Text.StringBuilder + if (-not $DisableLayerSwitcher) { + $null = $outSb.Append(@" +<div id='$LayerID' $classChunk $cssStyleChunk $itemTypeChunk> +$layerTitleAreaText +<script type='text/javascript'> + function get${LayerID}LayerTitleHTML(layer) { + var logoUrl = '$Logo' + var fullTitle = "" + if (logoUrl != '') { + fullTitle = "<img style='align:left' src='" + logoUrl + "' border='0'/><span style='font-size:x-large'>" + layer.attributes['friendlyName'].value +'</span>' $(if ($HorizontalRuleUnderTitle) { '+ "<HR/>"'}); + } else { + fullTitle= "<span style='font-size:x-large'>" + layer.attributes['friendlyName'].value +'</span>' $(if ($HorizontalRuleUnderTitle) { '+ "<HR/>"'}); + } + + if (layer.attributes['layerUrl']) { + fullTitle = "<a href='" + layer.attributes['layerUrl'].value + "'>" + fullTitle + "</a>" + } + + $socialChunk + + return fullTitle + } + + function get${LayerID}Layer() { + var element = document.getElementById('$LayerID'); + var layers = element.getElementsByTagName('div'); + var layersOut = new Array(); + var layerCount = 0; + var layersOutCount = 0; + while (layers[layerCount]) { + + if (layers[layerCount].parentNode == element && layers[layerCount].attributes["NotALayer"] == null) { + layersOut[layersOutCount] = layers[layerCount]; + layersOutCount++ + } + layerCount++; + } + return layersOut; + } + + function getCurrent${LayerID}Layer() { + var element = document.getElementById('$LayerID'); + var layers = element.getElementsByTagName('div'); + var layersOut = new Array(); + var layerCount = 0; + var layersOutCount = 0; + while (layers[layerCount]) { + + if (layers[layerCount].parentNode == element && layers[layerCount].attributes["NotALayer"] == null) { + if (layers[layerCount].style.visibility == 'visible') { + return layers[layerCount] + } + layersOutCount++ + } + layerCount++; + } + } + + + + $layerSwitcherScripts +</script> +"@) + } elseif ($AsResizable -or $AsWidget -or $AsDraggable) { + $null = $outSb.Append(@" +"@) + } else { + $null = $outSb.Append(@" +<div id='$LayerID' $classChunk $cssStyleChunk $itemTypeChunk> +$layerTitleAreaText +"@) + } + + if ($psCmdlet.ParameterSetName -eq 'Content' -and + -not ($AsDraggable -or $AsWidget -or $AsResizable)) { + $null = $outSb.Append("$content") + $null = $outSb.Append("</div>") + } else { + if ($AsTab) { + $null = $outSb.Append("<ul>") + + + $content= + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $n = $layerName.Replace(" ", "").Replace("-", "").Replace(",", "").Replace("<", "").Replace(">","").Replace("'", "").Replace('"','').Replace('=', '').Replace('&', '').Replace('?', '').Replace("/", "") + + # If there's a layer URL, make use of the nifty ajax loading + if ($LayerUrl[$LayerName]) { + "<li><a $cssStyleChunk href='ajax/$($LayerUrl[$LayerName])'>${LayerName}</a></li>" + } else { + "<li><a $cssStyleChunk href='#${LayerID}_$safeLayerName'>${LayerName}</a></li>" + } + + } + $null = $outSb.Append("$content") + $null = $outSb.Append("</ul>") + + + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + if ($layerUrl[$layerName]) { continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + + "<div id='${LayerID}_$safeLayerName' $cssStyleChunk> + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div>" + } + $null = $outSb.Append("$content") + } elseif ($AsMenu) { + $null = $outSb.Append(" + <div class='menu' $cssStyleChunk> + <ul class='nav' id='${LayerId}_Menu'> + ") + $underContent = "" + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $popoutLayerId = "${LayerID}_$safeLayerName" + + # If there's a layer URL, make use of the nifty ajax loading + if ($LayerUrl[$LayerName]) { + "<li><a $cssStyleChunk id='$showButtonId ' href='$($LayerUrl[$LayerName])'>${LayerName}</a></li>" + } elseif ($layer[$layerName] -as [hashtable[]]) { + "<li><a $cssStyleChunk id='$showButtonId' href='javascript:void(0)'>${LayerName}</a>" + + "<ul>" + $innerLayers = @($Layer[$layerName]) + + + foreach ($ht in $Layer[$layerName]) { + + + $innerOrder= $ht.Keys | Sort-Object + $innerOrder = if ($LayerInnerOrder -and $layerInnerOrder[$layerName]) { + $layerInnerOrder[$layerName] + } else { + $innerOrder + } + foreach ($k in $innerOrder) { + if (-not $k) { continue} + "<li><a href='$($ht[$k])'>$k</a></li>" + } + } + "</ul></li>" + + } else { + " + <script type='text/javascript'> + function ${ShowButtonId}_click() { + $ajaxLoad + var popout = document.getElementById('$popoutLayerId'); + if (popout.style.display == 'none') { + popout.style.display = 'inline'; + separator.style.display = 'inline'; + bottomseparator.style.display = 'inline'; + } else { + popout.style.display = 'none' + separator.style.display = 'none'; + bottomseparator.style.display = 'none'; + } + } + </script> + <li><a $cssStyleChunk id='$showButtonId' href='#${LayerID}_$safeLayerName' onclick='${ShowButtonId}_click();'>${LayerName}</a></li> + " + $underContent += "<div id='$popoutLayerId' style='display:none'>$($layer[$layerName])</div>" + } + + } + $null = $outSb.Append(@" +$content +</ul> +$underContent +<script> +var csssubmenuoffset=-1 //Offset of submenus from main menu. Default is 0 pixels. +var ultags=document.getElementById('${LayerId}_Menu').getElementsByTagName("ul") +for (var t=0; t<ultags.length; t++){ + ultags[t].style.top=ultags[t].parentNode.offsetHeight+csssubmenuoffset+"px" + ultags[t].parentNode.onmouseover= + function(){ + this.style.zIndex=100 + this.getElementsByTagName("ul")[0].style.visibility="visible" + this.getElementsByTagName("ul")[0].style.zIndex=0 + } + ultags[t].parentNode.onmouseout= + function(){ + this.style.zIndex=0 + this.getElementsByTagName("ul")[0].style.visibility="hidden" + this.getElementsByTagName("ul")[0].style.zIndex=100 + } +} + + + + </script> +</div> +"@) + + } elseif ($AsNavbar) { + $null = $outSb.Append(" +<script> +var ${LayerId}_CurrentSlide = '$defaultSlide' +</script> + <div class='container' $cssStyleChunk><div class='navbar$(if ($AsFeaturette) { ' featurette' })' $cssStyleChunk><div class='nav-collapse collapse' $cssStyleChunk><ul class='nav'>") + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $popoutLayerId = "${LayerID}_$safeLayerName" + $n = $layerName.Replace(" ", "").Replace("-", "").Replace(",", "").Replace("<", "").Replace(">","").Replace("'", "").Replace('"','').Replace('=', '').Replace('&', '').Replace('?', '').Replace("/", "") + + # If there's a layer URL, make use of the nifty ajax loading + if ($LayerUrl[$LayerName]) { + "<li><a $cssStyleChunk id='$showButtonId ' href='$($LayerUrl[$LayerName])'>${LayerName}</a></li>" + } elseif ($layer[$layerName] -as [hashtable[]]) { + "<li class='dropdown'><a $cssStyleChunk id='$showButtonId' href='javascript:void(0)' data-toggle='dropdown'>${LayerName} <b class='caret'></b></a>" + + "<ul class='dropdown-menu'>" + $innerLayers = @($Layer[$layerName]) + + + $c = 0 + foreach ($ht in $Layer[$layerName]) { + + + + + $innerOrder = if ($LayerInnerOrder -and $layerInnerOrder[$layerName]) { + $layerInnerOrder[$layerName] + } else { + $innerOrder= $ht.Keys | Sort-Object + } + foreach ($k in $innerOrder) { + if (-not $k) { continue} + "<li><a href='$($ht[$k])'>$k</a></li>" + } + if ($c -lt ($innerLayers.Count - 1)) { + "<li class='divider'></li>" + } + $c++ + + } + "</ul></li>" + + } else { + " + <li><a $cssStyleChunk id='$showButtonId' href='#${LayerID}_$safeLayerName'>${LayerName}</a></li> + <script> + `$( '#$ShowButtonId' ).click(function(){ + if (${LayerId}_CurrentSlide != null && ${LayerId}_CurrentSlide != '$popOutLayerId') { + if (document.getElementById(${LayerId}_CurrentSlide)) { + `$( (`"#`" + ${LayerId}_CurrentSlide) ).hide(200); + document.getElementById(${LayerId}_CurrentSlide).style.visibility = 'hidden'; + document.getElementById(${LayerId}_CurrentSlide).style.display = 'none'; + + } + + } + + + `$( `"#$popoutLayerId`" ).show(200); + document.getElementById(`"$popoutLayerId`").style.visibility = 'visible'; + document.getElementById(`"$popoutLayerId`").style.display = 'inline' + ${LayerId}_CurrentSlide = '$popoutLayerId'; + + + + }); + </script> + + " + } + + } + $null = $outSb.Append("$content") + + $null = $outSb.Append("</ul></div></div></div>") + + $Content = foreach ($layerName in $Order) { + if (-not $layerName) {continue } + if ($layerUrl[$layerName]) { continue } + if ($layer[$layerName] -as [hashtable[]]) { continue } + + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + + " + + <div id='${LayerID}_$safeLayerName' $cssStyleChunk style='display:none:visibility:hidden'> + $($(if ($AsFeaturette) {'<h1>'}) + $layerName + $(if ($AsFeaturette) {'</h1><hr/>'}) ) + $($layer[$layerName]) + </div> + + " + } + $null = $outSb.Append("$content") + + } elseif ($AsAccordian) { + + $content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + "<h3><a href='javascript:void(0)'>${LayerName}</a></h3> + <div> + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div> + " + } + $null = $outSb.Append("$content") + } elseif ($AsCarousel) { + $isFirst = $true + if (-not $psBoundParameters.LayerHeadingSize) { + $LayerHeadingSize = 2 + } + $null = $outSb.Append("<div class='carousel-inner' style='margin-left:10%;margin-right:10%'>") + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + if ($isFirst) { + $isFirst = $false + $itemActive = 'active' + } else { + $itemActive = '' + } + " + <div class='item $itemActive'> + <a name='${LayerName}'> </a>$(if ($ShowLayerTitle) { "<h$LayerHeadingSize>$layerName</h$LayerHeadingSize>"}) + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div> + " + } + $null = $outSb.Append("$Content") + } elseif ($AsFeaturette) { + $null = $outSb.Append(" +<style> +.featurette-divider { + margin: 80px 0; +} +.featurette { + overflow: hidden; + line-height: 1.3; +} +.featurette-image { + margin-top: -120px; +} + + +.featurette-image.pull-left { + margin-right: 40px; +} +.featurette-image.pull-right { + margin-left: 40px; +} + + .featurette-heading { + font-size: 3em; +} + +.featurette { + font-size: 1.15em; + +} + +.featurette li { + font-size: 1.2em; + line-height: 1.3; +} + + +/* Thin out the marketing headings */ +.featurette h1 { + font-size: 2.1em; + font-weight: 300; + line-height: 2.2; + letter-spacing: -1px; +} + +.featurette h2 { + font-size: 1.7em; + font-weight: 200; + line-height: 1.8; + letter-spacing: -1px; +} + + +.featurette h3 { + font-size: 1.5em; + font-weight: 100; + line-height: 1.6; + letter-spacing: -1px; +} + + +.featurette h4 { + font-size: 1.4em; + font-weight: 100; + line-height: 1.45; + letter-spacing: -1px; +} +</style> +<div class='container'><div class='featurette'> +") + $c = 0 + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + $span = if ($c -lt $RowSpan.Length) { + $RowSpan[$c] + } else { + $RowSpan[$RowSpan.Length - 1] + } + $c++ + " + <div class='lead'> + <a name='${LayerName}'> </a>$(if ($ShowLayerTitle) { "<h$LayerHeadingSize>$layerName</h$LayerHeadingSize>"}) + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div> + " + } + $null = $outSb.Append("$Content") + $null = $outSb.Append("</div></div>") + + } elseif ($Asrow) { + + $null = $outSb.Append(" +<div class='container'> +<div class='row'> + ") + $c = 0 + $rowContent = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + $span = if ($c -lt $RowSpan.Length) { + $RowSpan[$c] + } else { + $RowSpan[$RowSpan.Length - 1] + } + $c++ + " + <div class='$span'> + <a name='${LayerName}'> </a>$(if ($ShowLayerTitle) { "<h$LayerHeadingSize>$layerName</h$LayerHeadingSize>"}) + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div> + " + } + $null = $outSb.Append("$rowContent</div></div>") + + } elseif ($AsWidget -or $AsResizable -or $AsDraggable) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + " + <div id='${LayerID}_$safeLayerName' class='ui-widget-content' $cssStyleChunk $itemTypeChunk> + $(if ($LayerName -ne 'Default') { "<h3 class='ui-widget-header'>${LayerName}</h3>" }) + $($(if ($putInParagraph) {'<p>'}) + $layer[$layerName] + $(if ($putInParagraph) {'</p>'}) ) + </div> + " + + if ($AsResizable) { +@" + <script type='text/javascript'> + `$(function() { + `$('#${LayerID}_$safeLayerName').resizable(); + }); + </script> +"@ + } + if ($AsDraggable) { +@" + <script type='text/javascript'> + `$(function() { + `$('#${LayerID}_$safeLayerName').draggable(); + }); + </script> +"@ + } + } + $null = $outSb.Append("$Content") + } elseif ($AsPopUp) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $ajaxLoad = + if ($layerUrl -and $layerUrl[$layerName]) { + . $getAjaxAndPutIntoContainer $layerUrl[$layerName] $popoutLayerId + } elseif ($LayerLink -and $LayerLink[$layerName]) { + . $getAjaxAndPutIntoContainer $LayerLink[$layerName] $popoutLayerId "Button" + } else { + "" + } + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch +@" +<div> +<script> + `$(function() { + `$( "#${ShowButtonId}").button() + `$( "#$ShowButtonId" ).click(function(){ + var options = {}; + $ajaxLoad + `$( "#$popoutLayerId" ).dialog({modal:true, title:"$($layerName -replace '"','\"')"}); + }); + + }); +</script> +<a id='$ShowButtonId' $cssStyleChunk class='ui-widget-header ui-corner-all' href='javascript:void(0)'>${LayerName}</a> +<div id='$popoutLayerId' class='ui-widget-content ui-corner-all' style="display:none;"> + $($layer[$layerName]) +</div> +</div> +"@ + + } + + $null = $outSb.Append("$Content") + } elseif ($AsPopout) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $ajaxLoad = + if ($layerUrl -and $layerUrl[$layerName]) { + . $getAjaxAndPutIntoContainer $layerUrl[$layerName] $popoutLayerId + } elseif ($LayerLink -and $LayerLink[$layerName]) { + . $getAjaxAndPutIntoContainer $LayerLink[$layerName] $popoutLayerId "Button" + } else { + "" + } + + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch +@" +<div> +<script> + `$(function() { + `$( "#${ShowButtonId}").button(); + `$( "#$ShowButtonId" ).click(function(){ + `$( "#$popoutLayerId" ).toggle( "fold", {}, 200); + }); + }); +</script> +<a id='$ShowButtonId' class='ui-widget-header ui-corner-all btn btn-primary btn-large' href='javascript:void(0)' style='min-width:100%;$cssStyleAttr'>${LayerName}</a> +<div id='$popoutLayerId' class='ui-widget-content ui-corner-all' style="display:none;visibility:hidden;$cssStyleAttr"> + $($layer[$layerName]) +</div> +</div> +"@ + + } + + $null = $outSb.Append("$Content") + } elseif ($ascolumn) { + $columnSize = 100 / $layer.Count + $null = $outsb.Append("<div style='clear:both'></div>") + $Content = + foreach ($LayerName in $Order) { +" +<div style='float:left;width:${ColumnSize}%'> + <h$LayerHeadingSize> + $($layerName) + </h$LayerHeadingSize> + $($layer[$LayerName]) +</div> +" + } + $null = $outSb.Append("$Content") + $null = $outsb.Append("<div style='clear:both'></div>") + } elseif ($AsTree) { + $null = $outSb.Append("<table style='border:none;margin:0;padding:0;border-collapse:collapse'>") + + + if (-not $psBoundParameters.BranchColor -and + $pipeworksManifest.Style.Body.color) { + $BranchColor = $pipeworksManifest.Style.Body.color + } + + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + + +@" +<tr> + <td style='text-indent: -21px;padding-left: 21px;$cssStyleAttr' colspan='4'> + $(. $ShowLayerContent $showButtonId $popoutLayerId) + <a id='$ShowButtonId' class='btn-primary' href='javascript:void(0)' style='$cssStyleAttr' onclick='${ShowButtonId}_click();'>${LayerName}</a> + <br style='line-height:200%' /> + + </td> +</tr> +<tr id='$popoutLayerId' class='' style="display:none;visibility:hidden;$cssStyleAttr"> + <td style="width:20px"></td> + <td style="background-color:$('#' + $BranchColor.TrimStart('#'));width: 1px"></td> + <td style="width:10px"></td> + <td> + $( + + if ($layer[$layerName] -as [Hashtable]) { + $innerLayerId = $LayerID + "_ " + $layerNAme + $innerLayerOrder = ($layer[$layerName] -as [Hashtable]).Keys | Sort-Object + New-Region -AsTree -Layer $layer[$layerName] -LayerId $innerLayerId -Order $innerLayerOrder -Style @{ + "font-size" = ".99em" + "padding" = '20px' + 'margin-top' = '25px' + 'margin-bottom' = '25px' + } -BranchColor $BranchColor + } elseif ($layer[$layername] -is [string]) { + $layer[$layerName] + } elseif ($layer[$layerName]) { + $layer[$layerName] | Out-HTML + } + + + + ) + </td> +</tr> +"@ + } + $null =$outSb.Append("$Content") + $null =$outSb.Append("</table>") + } elseif ($AsButton) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $ajaxLoad = + if ($layerUrl -and $layerUrl[$layerName]) { + . $getAjaxAndPutIntoContainer $layerUrl[$layerName] $popoutLayerId + } elseif ($LayerLink -and $LayerLink[$layerName]) { + . $getAjaxAndPutIntoContainer $LayerLink[$layerName] $popoutLayerId "Button" + } else { + "" + } + + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch +@" + +<script> + `$(function() { + + `$( "#$ShowButtonId" ).click( + function(){ + if (`$( "#$popoutLayerId" )[0].style.visibility == 'hidden') { + `$( "#$popoutLayerId" )[0].style.visibility = 'visible' + $ajaxLoad + } else { + `$( "#$popoutLayerId" )[0].style.visibility = 'hidden' + } + `$( "#$popoutLayerId" ).toggle(200); + }) + }); +</script> +<a id='$ShowButtonId' href='javascript:void(0)' style='padding:15px;$cssStyleAttr' class='btn-primary'>${LayerName}</a> +<br style='line-height:175%' /> +<div id='$popoutLayerId' class='' style="display:none;visibility:hidden;$cssStyleAttr"> + $($layer[$layerName]) +</div> +<br style='line-height:175%' /> +"@ + + } + + $null = $outSb.Append("$Content") + } elseif ($AsNewspaper) { + $counter = 0 + $runningWidthTracker = 0 + $content = + foreach ($layerName in $order) { + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $columnContainer = "${LayerID}_${safeLayerName}_Column" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $showScript = . $showLayerContent $showButtonId $popoutLayerId + + $newspapercolumnWidth = + if ($counter -ge $NewspaperColumn.Count) { + $NewspaperColumn[$NewspaperColumn.Count - 1] + } else { + $NewspaperColumn[$counter] + } + $runningWidthTracker+= $newspapercolumnWidth + + $lineBreaks = $false + if ($runningWidthTracker -ge 1) { + $runningWidthTracker = 0 + $lineBreaks = $true + } + $isexpanded = + $ExpandNewspaperColumn -contains $layerName -or + $ExpandNewspaperColumn -contains "$counter" + + + $counter++ +@" + $showScript + + <div id='$ColumnContainer' style='float:left;width:$($newspapercolumnWidth * 100)%;margin-left:auto;margin-right:auto;'> + <div style='padding-left:.5%;padding-right:.5%'> + <h$NewsPaperHeadingSize style='text-align:$NewspaperHeadingAlignment'> + <a id='$ShowButtonId' href='javascript:void(0)' style='$cssStyleAttr;text-align:$NewspaperHeadingAlignment;' $(if ($UseButtonForNewspaperHeadline) { "class='btn-primary rounded' "}) onclick='${ShowButtonId}_click();'> + ${LayerName} + </a> + </h$NewsPaperHeadingSize> + $(if ($UnderlineNewspaperHeadline) { "<hr style='width:100%;height:2px;' class='solidForeground' />" }) + <div id='$popoutLayerId' style="display:none;$cssStyleAttr;"> + <div style='margin:3%'> + $(. $GetLayerContent $psBoundParameters) + </div> + </div> + </div> + </div> + $(if ($lineBreaks) { "<br style='clear:both' />" }) + $(if ($isexpanded) { "<script>${ShowButtonId}_click();</script>" }) + + +"@ + + + } + $null = $outSb.Append("$Content") + + } elseif ($AsHangingSpan) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $showScript = . $showLayerContent $showButtonId $popoutLayerId "${PopOutLayerId}_separator","${PopOutLayerId}_bottomseparator" + + + +@" + $showScript + <br style='clear:both;display:none' id='${popOutLayerId}_separator' /> + + <a id='$ShowButtonId' href='javascript:void(0)' style='padding-left:15px;padding-right:15px;padding-top:4%;padding-bottom:4%;margin-top:5px;margin-bottom:5px;$cssStyleAttr;text-align:center;display:block;float:left;' class='btn-primary span$SpanButtonSize' onclick='${ShowButtonId}_click();'> + ${LayerName} + </a> + + + <div id='$popoutLayerId' style="display:none;$cssStyleAttr;" class='span$(12 -([Math]::Floor($SpanButtonSize /2))) offset$([Math]::Floor($SpanButtonSize / 2))'> + $( + if ($layer[$layerName] -as [Hashtable]) { + $innerLayerId = $LayerID + "_ " + $layerNAme + $innerLayerOrder = ($layer[$layerName] -as [Hashtable]).Keys | Sort-Object + "<div class='row'>" + ( + New-Region -AsHangingSpan -Layer $layer[$layerName] -LayerId $innerLayerId -Order $innerLayerOrder -Style @{ + "font-size" = ".99em" + }) + "</div>" + } elseif ($layer[$layername] -is [string]) { + $layer[$layerName] + } elseif ($layer[$layerName]) { + $layer[$layerName] | Out-HTML + }) + </div> + + <br style='clear:both;display:none' id='${popOutLayerId}_bottomseparator' /> +"@ + + } + + $null = $outSb.Append("$Content") + } elseif ($AsHeadline) { + + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $showScript = . $showLayerContent $showButtonId $popoutLayerId + + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch +@" +<div class='hero-unit'> + $showScript + <a id='$ShowButtonId' href='javascript:void(0)' style='min-width:100%;text-decoration:underline;font-family:inherit;' onclick='${ShowButtonId}_click();'><h1 style='$cssStyleAttr;'>${LayerName}</h1></a> + <div id='$popoutLayerId' class='' style="display:none;$cssStyleAttr"> + $(. $GetLayerContent $psBoundParameters) + </div> +</div> +"@ + + } + $null = $outSb.Append("$Content") + } elseif ($AsPopdown) { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + + + +@" +$(. $ShowLayerContent $showButtonId $popoutLayerId -exclusive -shownstate 'block') +<a id='$ShowButtonId' class='ui-widget-header ui-corner-all' $cssStyleChunk href='javascript:void(0)' onclick='${ShowButtonId}_click();'>${LayerName}</a> +"@ + + } + + $null = $outSb.Append("$Content") + $nlc = 0 + $Content = + foreach ($layerName in $Order) { + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + if (-not $layerName) {continue } + + $nlc++ + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + +@" +<div id='$popoutLayerId' class='ui-widget-content ui-corner-all' style="display:none;$cssStyleAttr"> + $(. $getlayerContent $psBoundParameters) +</div> +"@ + } + + $null = $outSb.Append("$Content") + } elseif ($AsGrid) { + + + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch + $ajaxLoad = + if ($layerUrl -and $layerUrl[$layerName]) { + . $getAjaxAndPutIntoContainer $layerUrl[$layerName] $popoutLayerId + } elseif ($LayerLink -and $LayerLink[$layerName]) { + . $getAjaxAndPutIntoContainer $LayerLink[$layerName] $popoutLayerId "Button" + } else { + "" + } + + + if (-not $cssFontAttr -and $Request.UserAgent -like "*MSIE 7.0*") { + # compatibility view + $cssFontAttr = 'font-size:medium' + } +@" +$(. $ShowLayerContent $showButtonId $popoutLayerId -shownstate 'block' -exclusive) +<a id='$ShowButtonId' class='ui-state-default ui-corner-all btn-primary' href='javascript:void(0)' style='text-align:center;vertical-align:middle;float:left;width:${GridItemWidth}px;height:${GridItemHeight}px;padding:2px;margin:5px;$cssFontAttr' onclick='${ShowButtonId}_click();'>${LayerName}</a> +"@ + + } + + $null = $outSb.Append("$Content") + $nlc = 0 + $null = $outSb.Append("<div style='clear:both'></div>") + $content = + foreach ($layerName in $Order) { + + $nlc++ + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + +@" +<div id='$popoutLayerId' style="display:none;$cssStyleAttr"> + $(. $getlayerContent $psBoundParameters) +</div> + +"@ + } + $null= $outsb.Append("$Content") + } elseif ($AsLeftSidebarMenu -or $AsRightSidebarMenu) { + $null =$outSb.Append(" +<div id='$layerId' style='margin-left:0px;margin-right:0px;border:blank'> +") + $n = 0 + $layerCount = 0 + + + $counter = 0 + $allButtonContent = + foreach ($layerName in $order) { + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $slideNames += $popoutLayerId + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + + $layerCount++ + $counter++ + @" + +$(. $ShowLayerContent $showButtonId $popoutLayerId -exclusive -shownstate block) +<a id='$ShowButtonId' style='width:100%;display:block;padding-top:2%;padding-bottom:2%;$cssStyleAttr' class='ui-widget-header ui-corner-all btn-primary' href='javascript:void(0)' onclick='${ShowButtonId}_click();'>${LayerName}</a> +"@ + + } + + + if ($AsLeftSidebarMenu) { + # Sidebar first + $null = $outSb.Append("<div style='width:${sidebarWidth}%;text-align:center;float:left' class='inverted' valign='top'>$allbuttonContent</div>") + $floatDirection = "right" + } else { + $floatDirection = "left" + } + + # Make sure we put int the content column + $null = $outSb.Append("<div style='width:$(99 - $sidebarWidth)%;border:0px;float:$floatDirection;$cssStyleAttr' >") + + $nlc = 0 + + $lc = + foreach ($layerName in $Order) { + + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + $setIfDefault = if (($defaultToFirst -or (-not $Default)) -and ($nlc -eq 0)) { + $defaultSlideId = $popoutLayerId + "display:block;" + } elseif ($default -eq $layerName) { + $defaultSlideId = $popoutLayerId + "display:block;" + } else { + "display:none;" + } + $nlc++ + +@" + <div id='$popoutLayerId' style="${setIfDefault}text-align:left"> + $(. $getlayerContent $psBoundParameters) + </div> +"@ + } + + $null = $outsb.Append("$lc</div>") + if ($AsRightSidebarMenu) { + # Sidebar second + $null = $outsb.Append("<div style='width:${sidebarWidth}%;text-align:center;float:right' class='inverted'>$allbuttonContent</div>") + } + + $null = $outSb.Append(@" +</div> +<script> + var ${LayerId}_currentSlide = '$defaultSlideId'; +</script> +"@) + } elseif ($AsPortlet) { + if (-not $ColumnWidth) { $ColumnWidth = (100 / $ColumnCount).ToString() + "%" } + $null = $outsb.Append(" + <style> + .column { width: ${ColumnWidth}; float:left; padding-bottom:100px } + .portlet-header { margin: 0.3em; padding-bottom: 4px; padding-left: 0.2em; } + .portlet-header .ui-icon { float: right; } + .portlet-content { padding: 0.4em; } + .ui-sortable-placeholder { border: 1px dotted black; visibility: visible !important; height: 50px !important; } + .ui-sortable-placeholder * { visibility: hidden; } + + </style> + ") + $itemsPerColumn = @($order).Count / $ColumnCount + $portlets = foreach ($layerName in $Order) { + + if (-not $layerName) {continue } + $putInParagraph = $layer[$layerName] -notlike "*<*>*" + $safeLayerName = . $getSafeLayerName $layerName + " + <div class='portlet' $cssStyleChunk> + <div class='portlet-header'>${LayerName}</div> + <div class='portlet-content'>$($layer[$layerName])</div> + </div> + " + + } + + $content= for ($i =0;$i-lt@($order).Count;$i+=$itemsPerColumn) { + "<div class='column'>" + ($portlets[$i..($I + ($itemsPerColumn -1))]) + "</div>" + } + + $null = $outSb.Append("$content") + $null = $outSb.Append(@' +<script> + $(function() { + $( ".column" ).sortable({ + connectWith: ".column" + }); + + $( ".portlet" ).addClass( "ui-widget ui-widget-content ui-helper-clearfix ui-corner-all" ) + .find( ".portlet-header" ) + .addClass( "ui-widget-header ui-corner-all" ) + .prepend( '<span class="ui-icon ui-icon-minusthick"></span>') + .end() + .find( ".portlet-content" ); + + $( ".portlet-header .ui-icon" ).click(function() { + $( this ).toggleClass( "ui-icon-minusthick" ).toggleClass( "ui-icon-plusthick" ); + $( this ).parents( ".portlet:first" ).find( ".portlet-content" ).toggle(); + }); + + $( ".column" ).disableSelection(); + }); +</script> +'@) + + } elseif ($AsPopIn -or $AsSlideShow) { + $slideNames = @() + $layerCount = 0 + $slideButtons = "" + if (-not ($AsSlideShow -and $HideSlideNameButton)) { + $slideButtons += " +<div id='${LayerId}_MenuContainer' style='$(if ($MenuBackgroundColor) {"background-color:$MenuBackgroundColor" });text-align:center;margin-left:0%;margin-right:0%;padding-top:0%;padding-bottom:0%' > + " + } + $slideButtons += + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + $safeLayerName = . $getSafeLayerName $layerName + + $popoutLayerId = "${LayerID}_$safeLayerName" + $slideNames += $popoutLayerId + $showButtonId = "${LayerID}_${safeLayerName}_ShowButton" + $hideButtonId = "${LayerID}_${safeLayerName}_HideButton" + $setIfDefault = if (($defaultToFirst -or ((-not $Default)) -and ($layerCount -eq 0))) { + $defaultSlide = $popOutLayerId + "${LayerId}_CurrentSlide = '$popOutLayerId'" + } elseif ($default -eq $layerName.Trim()) { + $defaultSlide = $popOutLayerId + "${LayerId}_CurrentSlide = '$popOutLayerId'" + } else { + "" + } + $stopSlideShowIfNeeded = if ($ASSlideshow) { + "clearInterval(${LayerId}_SlideshowTimer);" + } else { + "" + } + $layerCount++ + # Carefully intermixed JQuery and PowerShell Variable Embedding. Do Not Touch + if (-not ($AsSlideShow -and $HideSlideNameButton)) { + + + if ($layerUrl[$layerName]) { + +$SlidebuttonText = if ($UseDotInsteadOfName) { + "<a style='$cssFontAttr;text-decoration:none' id='$ShowButtonId' href='$($layerUrl[$layerName])'><span style='font-size:6em'>·</span></a>" +} else { + "<a style='padding:5px;$cssFontAttr' id='$ShowButtonId' class='ui-widget-header ui-corner-all' href='$($layerUrl[$layerName])'>${LayerName}</a>" +} + +@" +$SlidebuttonText +"@ + } else { + +$SlidebuttonText= + if ($UseDotInsteadOfName) { + "<a style='$cssFontAttr;text-decoration:none' id='$ShowButtonId' href='javascript:void(0)' onclick='${ShowButtonId}_click();'><span style='font-size:6em'>·</span></a>" + } else { + "<a style='padding:5px;$cssFontAttr' id='$ShowButtonId' href='javascript:void(0)' onclick='${ShowButtonId}_click();'>${LayerName}</a>" + } + +@" +$(. $ShowLayerContent $showButtonId $popoutlayerId -Exclusive -shownstate block -StopSlideShow:$AsSlideShow -IsCurrentSlide:$($defaultSlide -eq $popoutLayerId) ) +$SlidebuttonText +$(if ($pipeworksManifest.UseJQueryUI) { "<script>`$('#$showButtonId').button()</script> "} ) +"@ + } + } + + } + + if (-not $AsSlideShow) { + $slideButtons += "</div>" + $null = $outSb.Append("$slideButtons") + } + + $LayerContent = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + + $safeLayerName = . $getSafeLayerName $layerName + $popoutLayerId = "${LayerID}_$safeLayerName" + +@" +<div id='$popoutLayerId' class='ui-widget-content clickable' style="display:none;border:0px"> + $(. $GetLayerContent $psBoundParameters) +</div> +"@ + } + + + if ($AsSlideShow) { +$null = $outSb.Append(@" + <div style='float:right'> + $slideButtons + </div> +"@) + } + + $null = $outSb.Append(@" +<div id='${LayerId}_InnerContainer' style='margin-left:auto;margin-right:auto;border:0px;$(if ($AsSlideShow) {'margin-top:1%;'})'> + $LayerContent +</div> + +<script> + var ${LayerId}_currentSlide = '$defaultSlide'; +$(if ($ASSlideShow) { @" + var ${LayerId}_SlideNames = new Array('$($slidenames -join "','")'); + + function showNext${LayerId}Slide() { + c = 0 + do { + if (${LayerId}_SlideNames[c] == ${LayerId}_currentSlide) { + break; + } + c++ + } while (${LayerId}_SlideNames[c]) + if (c == ${LayerId}_SlideNames.length) { + return; + } + + slideIndex = c; + document.getElementById(${LayerId}_currentSlide).style.display = 'none'; + if ((slideIndex + 1) == ${LayerId}_SlideNames.length) { + nextSlideHtml = document.getElementById(${LayerId}_SlideNames[0]).innerHTML; + ${LayerId}_currentSlide = ${LayerId}_SlideNames[0] + } else { + nextSlideHtml = document.getElementById(${LayerId}_SlideNames[slideIndex + 1]).innerHTML + ${LayerId}_currentSlide = ${LayerId}_SlideNames[slideIndex + 1] + } + document.getElementById(${LayerId}_currentSlide).style.display = 'inline'; + } + var ${LayerId}_SlideshowTimer = setInterval('showNext${LayerId}Slide()', $($autoSwitch.TotalMilliseconds)); +"@}) + + + document.getElementById(${LayerId}_currentSlide).style.display = 'inline'; + +</script> + +"@) + if ($default) { + $defaultSlide = $LayerID + "_" + (. $getSafeLayerName $default) + $null = $outSb.Append(@" +<script> +if (document.getElementById('$defaultSlide')) { + document.getElementById('$defaultSlide').style.display = 'inline' +} +var ${LayerId}_CurrentSlide = '$defaultSlide' +</script> +"@) + } + } else { + $Content = + foreach ($layerName in $Order) { + if (-not $layerName) {continue } + + $safeLayerName = . $getSafeLayerName $layerName +@" + $(if (-not $DisableLayerSwitcher) {"<script type='text/javascript'> + + function switchTo${LayerID}${safeLayerName}() { + select${LayerID}Layer('${LayerID}_$safeLayerName', true); + } + + </script> + "}) + <div id='${LayerID}_$safeLayerName' style='visibility:hidden;opacity=0;position:absolute;margin-top:0px;margin-left:0px;margin-bottom:0px;margin-right:0px;'> + $($layer[$layerName]) + </div> + $(if (-not $FixedSize) { " + <script> + document.getElementById('${LayerID}_${safeLayerName}').setAttribute('friendlyName', '$layerName'); + $(if ($layerUrl -and $layerUrl[$layerName]) { "document.getElementById('${LayerID}_${safeLayerName}').setAttribute('layerUrl', '$($layerUrl[$layerName])')" }) + layer = document.getElementById('${LayerID}_${safeLayerName}') + if (layer.addEventListener) { + layer.addEventListener('onscroll', function(e) { + reset${LayerID}Size(); + }); + } else { + if (layer.attachEvent) { + layer.attachEvent('onscroll', function(e) { + reset${LayerID}Size(); + }); + } + } + + </script> + "}) +"@ + } + + $null = $outSb.Append("$Content") + } + + + $requestHandler = if (-not $NotRequestAware) { + " + var queryParameters = new Array(); + var query = window.location.search.substring(1); + var parms = query.split('&'); + for (var i=0; i<parms.length; i++) { + var pos = parms[i].indexOf('='); + if (pos > 0) { + var key = parms[i].substring(0,pos); + var val = parms[i].substring(pos+1); + queryParameters[key] = val; + } + } + + if (queryParameters['$LayerID'] != null) { + var realLayerName = '${LayerID}_' + queryParameters['$LayerID']; + if (document.getElementById(realLayerName)) { + select${LayerID}Layer(realLayerName, false); + } + } + " + } else { + "" + } + + $resizeChunk =if (-not $FixedSize) { +@" + function isIPad() { + return !!(navigator.userAgent.match(/iPad/)); + } + + function isIPhone() { + return !!(navigator.userAgent.match(/iPhone/)); + } + + function reset${LayerID}Size() { + var element = document.getElementById('$LayerID'); + // Late bound centering by absolute position. + + leftMargin = document.body.clientWidth * $($LeftMargin / 100); + rightMargin = document.body.clientWidth * $((100 -$RightMargin) / 100); + topMargin = document.body.clientHeight * $($TopMargin / 100); + bottomMargin = document.body.clientHeight * $($BottomMargin / 100); + layerwidth = Math.abs(rightMargin - leftMargin); + layerheight = Math.abs(bottomMargin - topMargin); + element.style.position = 'absolute'; + element.style.marginLeft = leftMargin + 'px'; + element.style.marginRight = rightMargin + 'px'; + element.style.width = layerwidth + 'px'; + element.style.marginTop = topMargin + 'px'; + element.style.marginBottom = bottomMargin + 'px'; + element.style.height = (document.body.clientHeight -(topMargin + bottomMargin)) + 'px'; + element.style.border = '$Border'; + element.style.borderRadius = '1em'; + element.style.MozBorderRadius = '1em'; + element.style.WebkitBorderRadius = '1em;' + //element.style.borderRadius = 1em; + //element.style.borderTopLeftRadius = 1em; + //element.style.borderTopRightRadius = 1em; + //element.style.borderBottomLeftRadius = 1em; + //element.style.borderBottomRightRadius = 1em; + element.style.clip = 'rect(auto, auto, ' + (element.style.height - 10) + 'px, ' + (element.style.width - 10) + 'px)' + if (isIPhone() || isIPad()) { + element.style.overflow = 'scroll' + } else { + element.style.overflow = 'auto' + } + element.style.webkitoverflowscrolling = 'touch' + + var toolbar = document.getElementById('${LayerID}_Toolbar') + if (toolbar != null) { + toolbar.style.position = 'absolute' + toolbar.style.zIndex= '5' + toolbar.style.width = (layerwidth - 15) + 'px'; + $(if (-not $ToolbarAbove) { + "toolbar.style.bottom = 0 +'px';" + "toolbar.style.marginBottom = 0 +'px';" + } else { + 'toolbar.style.marginTop = ${ToolbarMargin} + "px";' + }) + toolbar.style.textAlign = 'right'; + toolbar.style.marginLeft = '${ToolbarMargin}px'; + toolbar.style.marginRight = '${ToolbarMargin}5px'; + toolbar.style.marginBottom = '${ToolbarMargin}5px'; + } + } + + if (window.addEventListener) { + window.addEventListener("onresize", function() { + reset${LayerID}Size(); + }); + + window.addEventListener("onorientationchange", function() { + reset${LayerID}Size(); + }); + } else { + if (window.attachEvent) { + window.attachEvent("onresize", function(e) { + reset${LayerID}Size(); + }); + } + } + + $enablePanChunk + $dragChunk + + var original${LayerID}ClientWidth = document.body.clientWidth; + var original${LayerID}Orientation = window.orientation; + function checkAndReset${LayerID}Size() { + if (original${LayerID}ClientWidth != document.body.clientWidth) { + original${LayerID}ClientWidth = document.body.clientWidth + reset${LayerID}Size(); + } + if (original${LayerID}Orientation != window.orientation) { + original${LayerID}Orientation = window.orientation + reset${LayerID}Size(); + } + } + setInterval("checkAndReset${LayerID}Size();", 100); + reset${LayerID}Size(); +"@ + } else { +"" + } + + $autoSwitcherChunk = if ($psBoundParameters.AutoSwitch -and (-not $asslideshow)) { + " + setInterval('moveToNext${LayerID}Item()', $([int]$autoSwitch.TotalMilliseconds)); +" + } else { + "" + } + + $selectDefaultChunk = if (-not $disableLayerSwitcher) { +@" + var layers = get${LayerID}Layer(); + var layerCount = 0; + while (layers[layerCount]) { + layerCount++; + } + + var defaultValue = '${LayerID}_$("$default".Replace(' ','_').Replace('-', '_'))' + if (defaultValue != '${LayerID}_') { + select${LayerID}Layer(defaultValue); + } else { + $(if ($DefaultToFirst) { + "var whichLayer = 0" + } else {"var whichLayer=Math.round(Math.random()*(layerCount - 1));"}) + if (layers[whichLayer] != null) { + if (typeof select${LayerID}Layer == "function") { + select${LayerID}Layer(layers[whichLayer].id); + } else { + layers[whichLayer].style.visibility = 'visible' + layers[whichLayer].style.opacity = '1' + } + } + } +"@ + } else { + "" + } + $MouseOverEvent = if ($OpenOnMouseOver) { + "event: `"mouseover`"" + } else { + $null + } + + + + $AccordianChunk = if ($AsAccordian) { + # Join all settings that exists with newlines. + # Powershell list operator magic filters out settings that are null. + $settings = + $MouseOverEvent, 'autoHeight: false', 'navigation: true' -ne $null + $settingString = $settings -join ",$([Environment]::NewLine)" + "`$(function() { + `$( `"#${LayerID}`" ).accordion({ + $settingString + }); + })" + } else { "" } + + + $carouselChunk = if ($AsCarousel) { + # Join all settings that exists with newlines. + # Powershell list operator magic filters out settings that are null. + "`$(function() { + `$( `"#${LayerID}`" ).carousel(); + })" + } else { "" } + + + $TabChunk = if ($AsTab) { + $settings = + $MouseOverEvent, 'autoHeight: false', 'navigation: true' -ne $null + + $tabsBelowChunk = if ($tabBelow) { + " + `$( `".tabs-bottom .ui-tabs-nav, .tabs-bottom .ui-tabs-nav > *`" ) + .removeClass( `"ui-corner-all ui-corner-top`" ) + .addClass( `"ui-corner-bottom`" ); + " + } else { + "" + } + $settingString = $settings -join ",$([Environment]::NewLine)" + "`$(function() { + `$( `"#${LayerID}`" ).tabs({ + $mouseOverEvent + + }); + $tabsBelowChunk + + })" + + } else { "" } + + $javaScriptChunk = if ($SelectDefaultChunk -or + $ResizeChunk -or + $AutoSwitcherChunk -or + $Requesthandler -or + $tabChunk -or $carouselChunk -or + $AccordianChunk) { @" + <script type='text/javascript'> + $selectDefaultChunk + $resizeChunk + $autoSwitcherChunk + $requestHandler + $TabChunk + $carouselChunk + $AccordianChunk + </script> +"@ + } else { + "" + } + + if ($tabBelow) { + $null = $outSb.Append(@" +<style> + .tabs-bottom { position: relative; } + .tabs-bottom .ui-tabs-panel { height: 140px; overflow: auto; } + .tabs-bottom .ui-tabs-nav { position: absolute !important; left: 0; bottom: 0; right:0; padding: 0 0.2em 0.2em 0; } + .tabs-bottom .ui-tabs-nav li { margin-top: -2px !important; margin-bottom: 1px !important; border-top: none; border-bottom-width: 1px; } + .ui-tabs-selected { margin-top: -3px !important; } +</style> +"@) + } + $null = $outSb.Append(@" + $(if (-not $IsStaticRegion) { + " + <div id='${LayerID}_Toolbar' style='margin-top:${ToolbarMargin}px;margin-right:${ToolbarMargin}px;margin-bottom:${ToolbarMargin}px;z-index:-1' NotALayer='true'> + $directionButtonText + $moreButtonText + </div>" + }) + + + $(if ($AsCarousel) { @" + </div> +<a class="left carousel-control" href="#$LayerId" data-slide="prev">‹</a> +<a class="right carousel-control" href="#$LayerId" data-slide="next">›</a> + +"@ +}) + $(if (-not ($AsResizable -or $AsDraggable -or $asWidget)) {' + + </div> + +'}) + $javaScriptChunk + + +"@) + } + $pageAsXml = $outSb.ToString() -as [xml] + + if ($pageAsXml -and + $out -notlike "*<pre*") { + $strWrite = New-Object IO.StringWriter + $pageAsXml.Save($strWrite) + $strOut = "$strWrite" + $strOut.Substring($strOut.IndexOf(">") + 3) + } else { + "$outSb" + } + + + + } +} diff --git a/New-RssItem.ps1 b/New-RssItem.ps1 new file mode 100644 index 0000000..5cd2a9d Binary files /dev/null and b/New-RssItem.ps1 differ diff --git a/New-SQLDatabase.ps1 b/New-SQLDatabase.ps1 new file mode 100644 index 0000000..532e38b Binary files /dev/null and b/New-SQLDatabase.ps1 differ diff --git a/New-Webpage.ps1 b/New-Webpage.ps1 new file mode 100644 index 0000000..1cc465f Binary files /dev/null and b/New-Webpage.ps1 differ diff --git a/Open-EC2Port.ps1 b/Open-EC2Port.ps1 new file mode 100644 index 0000000..ecb2254 Binary files /dev/null and b/Open-EC2Port.ps1 differ diff --git a/Open-Port.ps1 b/Open-Port.ps1 new file mode 100644 index 0000000..bb60181 Binary files /dev/null and b/Open-Port.ps1 differ diff --git a/Out-AzureService.ps1 b/Out-AzureService.ps1 new file mode 100644 index 0000000..b814b87 --- /dev/null +++ b/Out-AzureService.ps1 @@ -0,0 +1,293 @@ +function Out-AzureService +{ + <# + .Synopsis + Creates a an Azure Service Deployment pack, definition, and configuration file + .Description + Uses the Azure SDK tool CSPack to create a deployment package (cspkg) and associated deployment files. + .Link + New-AzureServiceDefinition + .Link + Publish-AzureService + #> + [OutputType([IO.FileInfo])] + param( + # The Service DefinitionXML + [Parameter(Mandatory=$true, + ValueFromPipeline=$true, + ValueFromPipelineByPropertyName=$true)] + [ValidateScript({ + $isServiceDefinition = $_.NameTable.Get("http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition") + if (-not $IsServiceDefinition) { + throw "Input must be a ServiceDefinition XML" + } + return $true + })] + [Xml] + $ServiceDefinition, + + # The output directory for the azure service. + [Parameter(Mandatory=$true)] + [string] + $OutputPath, + + # If set, will look for a specific Azure SDK Version + [Version] + $SdkVersion, + + # The number of instances to create + [Uint32] + $InstanceCount = 2, + + # The operating system family + [ValidateSet("2K8R2","2012")] + [string] + $Os = "2012" + ) + + begin { + #region Find CSPack + $programFiles = if ($env:ProgramW6432) { + $env:ProgramW6432 + } else { + $env:ProgramFiles + } + $azureSdkDir = Get-ChildItem "$programFiles\Windows Azure SDK", "$programFiles\Microsoft SDKs\Windows Azure\.NET SDK" -Force -ErrorAction SilentlyContinue + if ($azureSdkDir) { + $latestcsPack = $azureSdkDir | + Sort-Object { $_.Name.Replace('v', '') -as [Version] } | + Where-Object { + if ($sdkVersion) { + $_.Name.Replace('v', '') -eq $SdkVersion + } else { + return $true + } + } | + Select-Object -Last 1 | + Get-ChildItem -Filter 'bin' | + Get-ChildItem -Filter 'cspack.exe' + + if ($latestCsPack) { + $csPack = Get-Command $latestCsPack.fullname + } + } else { + $latestCSPack = $csPack = Get-Command $psScriptRoot\Tools\cspack.exe + } + #endregion Find CSPAck + } + + process { + if (-not $latestCSPack) { + Write-Error "Azure SDK tool CSPack not found" + return + } + + $osFamily = if ($os -eq '2K8R2') { + 2 + } elseif ($os -eq '2012') { + 3 + } + $temporaryServiceDirectory = New-Item -ItemType Directory -Path "$env:Temp\$(Get-Random).azureService" + + $serviceName = $ServiceDefinition.ServiceDefinition.name + try { $null = $ServiceDefinition.CreateXmlDeclaration("1.0", "utf8", $null) } catch {} + $serviceDefinitionFile = Join-Path $temporaryServiceDirectory "$serviceName.csdef" + $ServiceDefinition.Save($serviceDefinitionFile) + + + $workingDirectory = Split-Path $serviceDefinitionFile + $leaf = Split-Path $serviceDefinitionFile -Leaf + $configurationFile = "$serviceName.cscfg" + + $arguments = @("$leaf") + + + + + $roles = @($ServiceDefinition.ServiceDefinition.WebRole), @($ServiceDefinition.ServiceDefinition.WorkerRole) + @($ServiceDefinition.ServiceDefinition.VirtualMachineRole) + $xmlNamespace = @{'ServiceDefinition'='http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition'} + $selectXmlParams = @{ + XPath = '//ServiceDefinition:WebRole|//ServiceDefinition:WorkerRole|//ServiceDefinition:VirtualMachineRole' + Namespace = $xmlNamespace + } + $roles = @(Select-Xml -Xml $ServiceDefinition @selectXmlParams | + Select-Object -ExpandProperty Node) + + #$roles[0] + $startupBin = "$temporaryServiceDirectory\Startup\bin" + New-Item $startupBin -ErrorAction SilentlyContinue -Force -ItemType Directory | Out-Null + + + #region Create roles + $firstSitePhysicalDirectory = $null + foreach ($role in $roles) { + $roleDir = Join-Path $temporaryServiceDirectory $role.Name + $null = New-Item -ItemType Directory -Path $roleDir + $roleBinDir = Join-Path $temporaryServiceDirectory "$($role.Name)_bin" + $null = New-Item -ItemType Directory -Path $roleBinDir + $roleBin = Join-Path $roleBinDir "bin" + $null = New-Item -ItemType Directory -Path $roleBin + # The azure sdk requires a binary, so give them a binary + Add-Type -OutputAssembly "$roleBin\Placeholder.dll" -TypeDefinition @" +namespace Namespace$(Get-Random) { + public class Stuff + { + public int StuffCount; + } +} +"@ + $configSettingsChunk = "<ConfigurationSettings />" + $arguments+= "/role:$($role.Name);$($role.Name)_bin" + if ($role.ConfigurationSettings) { + $configSettingsChunk = "<ConfigurationSettings>" + foreach ($configSetting in $role.ConfigurationSettings.Setting) { + $configSettingsChunk += $configSetting.innerXml + $null = $configSetting.RemoveAttribute('value') + } + $configSettingsChunk += "</ConfigurationSettings>" + $ServiceDefinition.Save($serviceDefinitionFile) + } + + if ($role.Startup) { + $c = 0 + foreach ($task in $role.Startup.Task) { + $c++ + # Translate ScriptBlock start up tasks, which, alas, don't directly exist in Azure, into .cmd startup tasks + if ($task.ScriptBlock) { + $null = $task.SetAttribute('commandLine', "startupScript${c}.cmd") + # Create the cmd file + $scriptFile = "`$scriptBlockParameters = @{} +`$serviceName = '$($serviceDefinition.Name)' +" + + # If parameters were supplied, put them into a hashtable + if ($task.Parameters) { + foreach ($parameter in $task.Parameters) { + $scriptFile += " + +`$scriptBlockParameters.'$($Parameter.Name)' = '$($parameter.Value)' + + " + } + } + + # Run the PowerShell script and exit + $scriptFile += " + +`$exitCode = 0 +& { + $($task.ScriptBlock.'#text') +} @scriptBlockParameters +exit `$exitCode + " + + # Convert script into a base64 encoded file, then run it + $b64 = [Convert]::ToBase64String([Text.Encoding]::unicode.GetBytes($scriptFile)) + $cmdFile = "powershell.exe -executionpolicy bypass -encodedCommand $b64" + + $cmdFile | Set-Content "$roleBin\startupScript${c}.cmd" + #$scriptFile > "$roleBin\startupScript${c}.ps1" + } + # Batch scripts are easier, just add a .cmd file and away we go. + if ($task.Batch) { + $null = $task.SetAttribute('commandLine', "startupScript${c}.cmd") + $cmdFile = $task.Batch + $cmdFile | Set-Content "$roleBin\startupScript${c}.cmd" + } + foreach ($i in @($task.GetEnumerator())) { + $null = try { $task.RemoveChild($i) } catch { } + } + } + $ServiceDefinition.Save($serviceDefinitionFile) + } + $roleConfigChunk += "<Role name='$($role.Name)'> + $configSettingsChunk + <Instances count='$InstanceCount' /> + </Role>" + $sites = $roles = @(Select-Xml -Xml $ServiceDefinition -Namespace $xmlNamespace -XPath //ServiceDefinition:Site | + Select-Object -ExpandProperty Node) + if ($sites) { + foreach ($site in $sites ) { + if (-not $firstSitePhysicalDirectory) { $firstSitePhysicalDirectory= $site.PhysicalDirectory} + $webConfigFile = Join-Path $site.PhysicalDirectory "Web.Config" + if (-not (Test-Path $webConfigFile)) { + ' +<configuration> + <system.web> + <customErrors mode="Off"/> + </system.web> +</configuration> + ' | Set-Content -path $webConfigFile + } + + } + } + } + #endregion Create roles + + $cscfgXml = [xml]@" +<ServiceConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" serviceName="$serviceName" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily='$osFamily' osVersion='*'> + $RoleConfigChunk +</ServiceConfiguration> +"@ + + + #region Push to the output directory, then run CSPack + Push-Location $workingDirectory + $results = & $csPack $arguments + Pop-Location + #endregion Push to the output directory, then run CSPack + + $errs =$results -like "*Error*:*" + if ($errs) { + foreach ($err in $errs) { + Write-Error $err.Substring($err.IndexOf(":") + 1) + } + return + } + + + $csdef = $serviceDefinitionFile + $cspkg = Join-Path $workingDirectory "$serviceName.cspkg" + + if (-not $outputPath) { + $serviceDeploymentRoot = "$psScriptRoot\AzureServices" + if (-not (Test-Path $serviceDeploymentRoot)) { + $null = New-Item -ItemType Directory -Path $serviceDeploymentRoot + } + + $serviceDropDirectory = "$serviceDeploymentRoot\$serviceName" + if (-not (Test-Path $serviceDropDirectory)) { + $null = New-Item -ItemType Directory -Path $serviceDropDirectory + } + + $nowString = (Get-Date | Out-String).Trim().Replace(":", "-") + $thisDropDirectory =Join-Path $serviceDropDirectory $nowString + if (-not (Test-Path $thisDropDirectory)) { + $null = New-Item -ItemType Directory -Path $thisDropDirectory + } + } else { + $unResolvedPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($outputPath) + if (-not (Test-Path $unResolvedPath)) { + $newPath = New-Item -ItemType Directory $unResolvedPath + if ($newPath) { + $thisDropDirectory = "$newPath" + } + } else { + $thisDropDirectory = "$unResolvedPath" + } + + } + + + #Move-Item -LiteralPath $cscfg -Destination "$thisDropDirectory" + $cscfg = Join-Path $thisDropDirectory $configurationFile + if (Test-Path $cscfg) { Remove-Item -Force $cscfg } + $cscfgXml.Save("$cscfg") + Move-Item -LiteralPath $csdef -Destination "$thisDropDirectory" -Force + Move-Item -LiteralPath $cspkg -Destination "$thisDropDirectory" -Force + + Remove-Item -Recurse -Force $workingDirectory + Get-ChildItem $thisDropDirectory -Force + } +} diff --git a/Out-Daemon.ps1 b/Out-Daemon.ps1 new file mode 100644 index 0000000..47d4be0 Binary files /dev/null and b/Out-Daemon.ps1 differ diff --git a/Out-HTML.ps1 b/Out-HTML.ps1 new file mode 100644 index 0000000..e168d18 --- /dev/null +++ b/Out-HTML.ps1 @@ -0,0 +1,1050 @@ +function Out-HTML { + <# + .Synopsis + Produces HTML output from the PowerShell pipeline. + .Description + Produces HTML output from the PowerShell pipeline, doing the best possible to obey the formatting rules in PowerShell. + .Example + Get-Process | Out-HTML + .Link + New-Webpage + .Link + Write-Link + .Link + New-Region + #> + [OutputType([string])] + [CmdletBinding(DefaultParameterSetName='DefaultFormatter')] + param( + # The input object + [Parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + + # If set, writes the response directly + [switch] + $WriteResponse, + + # If set, escapes the output + [switch] + $Escape, + + # The id of the table that will be created + [string] + $Id, + + # The vertical alignment of rows within the generated table. By default, aligns to top + [ValidateSet('Baseline', 'Top', 'Bottom', 'Middle')] + $VerticalAlignment = 'Top', + + # The table width, as a percentage + [ValidateRange(1,100)] + [Uint32] + $TableWidth = 100, + + # The CSS class to apply to the table. + [string] + $CssClass, + + # A CSS Style + [Hashtable] + $Style, + + # If set, will enclose the output in a div with an itemscope and itemtype attribute + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]]$ItemType, + + # If more than one view is available, this view will be used + [string]$ViewName, + + # If set, will use the table sorter plugin + [Switch] + $UseTableSorter, + + # If set, will use the datatable plugin + [Switch] + $UseDataTable, + + # If set, will show the output as a pie graph + [Parameter(Mandatory=$true,ParameterSetName='AsPieGraph')] + [Switch] + $AsPieGraph, + + # If set, will show the output as a bar graph + [Parameter(Mandatory=$true,ParameterSetName='AsBarGraph')] + [Switch] + $AsBarGraph, + + # If set, the bar graph will be horizontal, not vertical + [Parameter(ParameterSetName='AsBarGraph')] + [Switch] + $Horizontal, + + # The list of colors in the graph + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [string[]] + $ColorList = @("#468966", "#FFF0A5", "#FF870C", "#CA0016", "#B0A5CF", "#2B85BA", "#11147D", "#EE56A9", "#ADDC6C", "#108F34"), + + # The width of the canvas for a graph + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [Double] + $GraphWidth = 400, + + # The height of the canvas for a graph + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [Double] + $GraphHeight = 400, + + # The header of a graph + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [string] + $Header, + + # The text alignment of the header. By default, center + [ValidateSet("Left", "Center", "Right")] + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [string] + $HeaderAlignment = "Center", + + # The size of the header + [ValidateRange(1,6)] + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [Uint32] + $HeaderSize = 3, + + # If set, no legend will be displayed + [Parameter(ParameterSetName='AsPieGraph')] + [Parameter(ParameterSetName='AsBarGraph')] + [Switch] + $HideLegend + ) + + begin { + + + $tablesForTypeNames = @{} + $tableCalculatedProperties = @{} + if (-not $Script:CachedformatData) { + $Script:CachedformatData = @{} + } + $stopLookingFor = @{} + $CachedControls = @{} + $loadedViewFiles= @{} + $htmlOut = New-Object Text.StringBuilder + $typeNamesEncountered = @() + if (-not $script:LoadedFormatFiles) { + $script:loadedFormatFiles = @(dir $psHome -Filter *.format.ps1xml | + Select-Object -ExpandProperty Fullname) + + @(Get-Module | Select-Object -ExpandProperty ExportedformatFiles) + + $script:loadedViews = $loadedFormatFiles | Select-Xml -Path {$_ } "//View" + } + if ($useTableSorter) { + if ($CssClass) { + $CssClass+="tableSorterTable" + } else { + $CssClass ="tableSorterTable" + } + } + + if ($useDataTable) { + if ($CssClass) { + $CssClass+="aDataTable" + } else { + $CssClass ="aDataTable" + } + } + + $userandomSalt = $false + + $graphData = $null + $graphItemOrder = @() + if ($AsPieGraph) { + $userandomSalt = $true + + $graphData= @{} + } + + if ($AsBarGraph) { + $userandomSalt = $true + + $graphData = @{} + } + + + } + + process { + # In case nulls come in, exit politely + if (-not $InputObject ) { return } + $randomSalt = if ($userandomSalt) { + "_$(Get-Random)" + } else { + "" + } + $classChunk = if ($cssClass) { + "class='$($cssClass -join ' ')'" + } else { + "" + + } + $cssStyleChunk = if ($psBoundParameters.Style) { + if ($AsPieGraph -or $AsBarGraph) { + if (-not $style['width']) { + $style['width'] = "${GraphWidth}px" + } else { + $GraphWidth = $style['Width'] + } + if (-not $style['height']) { + $style['height'] = "$($GraphHeight + 100)px" + } else { + $GraphHeight = $style['Height'] + } + } + "style='" +(Write-CSS -Style $style -OutputAttribute) + "'" + } else { + if ($AsPieGraph -or $AsBarGraph) { + "style='" +(Write-CSS -Style @{"width"="${GraphWidth}px";"height"="$($GraphHeight + 50)px"} -OutputAttribute) + "'" + } else { + "style='width:100%'" + } + } + + if ($inputObject -is [string]) { + # Strings are often simply passed thru, but could potentially be escaped. + $trimmedString = $inputObject.TrimStart([Environment]::NewLine).TrimEnd([Environment]::NewLine).TrimStart().TrimEnd() + # If the string looks @ all like markup or HTML, pass it thru + if ($graphData) { + if (-not $graphData.$trimmedString) { + $graphData.$trimmedString = 0 + } + $graphData.$trimmedString++ + if ($graphItemOrder -notcontains $trimmedString) { + $graphItemOrder += $trimmedString + } + + } else { + if (($trimmedString -like "*<*") -and + ($trimmedString -like "*>*") -and + ($trimmedString -notlike "*<?xml*")) { + if ($escape) { + $null = $htmlOut.Append(" +$([Web.HttpUtility]::HtmlEncode($inputObject).Replace([Environment]::NewLine, '<BR/>').Replace('`n', '<BR/>').Replace(' ', ' ')) +") + } else { + $null = $htmlOut.Append(" +$inputObject +") + } + + + } else { + # Otherwise, include it within a <pre> tag + $null= $htmlOut.Append(" +$([Web.HttpUtility]::HtmlEncode($inputObject)) +") + } + } + } elseif ([Double], [int], [uint32], [long], [byte] -contains $inputObject.GetType()) { + if ($graphData) { + if (-not $graphData.$inputObject) { + $graphData.$inputObject = 0 + } + $graphData.$inputObject += $graphData.$inputObject + if ($graphItemOrder -notcontains $inputObject) { + $graphItemOrder += $inputObject + } + } else { + # If it's a number, simply print it out + $null= $htmlOut.Append(" +<span class='Number' style='font-size:2em'> +$inputObject +</span> +") + } + } elseif ([DateTime] -eq $inputObject.GetType()) { + # If it's a date, out Out-String to print the long format + if ($graphData) { + if (-not $graphData.$inputObject) { + $graphData.$inputObject = 0 + } + $graphData.$inputObject++ + if ($graphItemOrder -notcontains $inputObject) { + $graphItemOrder += $inputObject + } + } else { + $null= $htmlOut.Append(" +<span class='DateTime'> +$($inputObject | Out-String) +</span> +") + } + + } elseif (($inputObject -is [Hashtable]) -or ($inputObject -is [Collections.IDictionary])) { + $null = $psBoundParameters.Remove('InputObject') + $inputObjecttypeName = "" + $inputObjectcopy = @{} + $inputObject + if ($inputObjectcopy.PSTypeName) { + $inputObjecttypeName = $inputObject.PSTypeName + $inputObjectcopy.Remove('PSTypeName') + } + + foreach ($kv in @($inputObjectcopy.GetEnumerator())) { + if ($kv.Value -is [Hashtable]) { + $inputObjectcopy[$kv.Key] = Out-HTML -InputObject $kv.Value + } + } + + if ($inputObjectCopy) { + + + New-Object PSObject -Property $inputObjectcopy | + ForEach-Object { + $_.pstypenames.clear() + foreach ($inTypeName in $inputObjectTypeName) { + if (-not $inTypeName) {continue } + + $null = $_.pstypenames.add($inTypeName) + } + if (-not $_.pstypenames) { + $_.pstypenames.add('PropertyBag') + } + $psBoundparameters.ItemType = $inputObjectTypeName + $_ + } | Out-HTML @psboundParameters + } + } else { + $matchingTypeName = $null + #region Match TypeName to Formatter + foreach ($typeName in $inputObject.psObject.typenames) { + # Skip out of + $typeName = $typename.TrimStart("Deserialized.") + if ($stopLookingFor[$typeName]) { continue } + if ($Script:CachedformatData[$typeName] ) { + $matchingTypeName = $typename + break + } + + if (-not $Script:CachedformatData[$typeName] -and -not $stopLookingFor[$TypeName]) { + + + $Script:CachedformatData[$typeName] = + if ([IO.File]::Exists("$pwd\Presenters\$typeName")) { + if ($loadedViewFiles[$typeName]) { + $loadedViewFiles[$typeName] = [IO.File]::ReadAllText( + $ExecutionContext.SessionState.Path.GetResolvedProviderPathFromPSPath(".\Presenters\$typeName")) + + } else { + $loadedViewFiles[$typeName] + } + } else { + Get-FormatData -TypeName $typeName -ErrorAction SilentlyContinue + } + + if (-not $Script:CachedformatData[$TypeName]) { + # This covers custom action + $Script:CachedformatData[$typeName] = + foreach ($view in $loadedViews) { + + if ($view.Node.ViewselectedBy.TypeName -eq $typeNAme) { + if ($ViewName -and $view.Node.Name -eq $viewNAme) { + $view.Node + break + + } else { + $view.Node + break + + } + } + } + + if ($Script:CachedformatData[$typeName]) { + # Custom Formatting or SelectionSet + if ($Script:CachedformatData[$typeName]) { + + } + $matchingTypeName = $typeName + } else { + + # At this point, we're reasonably certain that no formatter exists, so + # Make sure we stop looking for the typename, or else this expensive check is repeated for each item + if (-not $Script:CachedformatData[$typeName]) { + $stopLookingFor[$typeName] = $true + } + } + } else { + $matchingTypeName = $typeName + break + } + } + } + + $TypeName = $MatchingtypeName + + + + #endregion Match TypeName to Formatter + if ($GraphData) { + # Formatted type doesn't really matter when we're graphing, so skip all the logic related to it + foreach ($pr in $InputObject.PSObject.Properties) { + if (-not $graphData.($pr.Name)) { + $graphData.($pr.Name) = 0 + } + if ($pr.Value) { + if ([Double], [int], [uint32], [long], [byte] -contains $pr.Value.GetType()) { + $graphData.($pr.Name)+=$pr.Value + } else { + $graphData.($pr.Name)++ + } + } + if ($graphItemOrder -notcontains $pr.Name) { + $graphItemOrder += $pr.Name + } + } + } elseif ($matchingTypeName) { + $formatData = $Script:CachedformatData[$typeName] + $cssSafeTypeName =$typename.Replace('.','').Replace('#','') + if ($Script:CachedformatData[$typeName] -is [string]) { + # If it's a string, just set $_ and expand the string, which allows subexpressions inside of HTML + $_ = $inputObject + foreach ($prop in $inputObject.psobject.properties) { + Set-Variable $prop.Name -Value $prop.Value -ErrorAction SilentlyContinue + } + $ExecutionContext.SessionState.InvokeCommand.ExpandString($Script:CachedformatData[$typeName]) + } elseif ($Script:CachedformatData[$typeName] -is [Xml.XmlElement]) { + # SelectionSet or Custom Formatting Action + + + $frame = $Script:CachedformatData[$typeName].CustomControl.customentries.customentry.customitem.frame + foreach ($frameItem in $frame) { + $item =$frameItem.customItem + foreach ($expressionItem in $item) { + if (-not $expressionItem) { continue } + $expressionItem | + Select-Xml "ExpressionBinding|NewLine" | + ForEach-Object -Begin { + if ($itemType) { + #$null = $htmlOut.Append("<div itemscope='' itemtype='$($itemType -join "','")' class='ui-widget-content'>") + } + } { + if ($_.Node.Name -eq 'ExpressionBinding') { + $finalExpr =($_.Node.SelectNodes("ScriptBlock") | + ForEach-Object { + $_."#text" + }) -ireplace "Write-Host", "Write-Host -AsHtml" -ireplace + "Microsoft.PowerShell.Utility\Write-Host", "Write-Host" + $_ = $inputObject + $null = $htmlOut.Append("$(Invoke-Expression $finalExpr)") + } elseif ($_.Node.Name -eq 'Newline') { + $null = $htmlOut.Append("<br/>") + } + } -End { + if ($itemType) { + #$null = $htmlOut.Append("</div>") + } + }| + Where-Object { $_.Node.Name -eq 'ExpressionBinding' } + if (-not $expressionBinding.firstChild.ItemSelectionCondition) { + + + + } + + } + } + + $null = $null + # Lets see what to do here + } else { + if (-not $CachedControls[$typeName]) { + $control = foreach ($_ in $formatData.FormatViewDefinition) { + if (-not $_) { continue } + $result = foreach ($ctrl in $_.Control) { + if ($ctrl.Headers) { + $ctrl + break + } + } + if ($result) { + $result + break + } + } + $CachedControls[$typeName]= $control + if (-not $cachedControls[$TypeName]) { + $control = foreach ($_ in $formatData.CustomControl) { + if (-not $_) { continue } + + + } + $CachedControls[$typeName]= $control + } + } + $control = $CachedControls[$typeName] + + if (-not ($tablesForTypeNames[$typeName])) { + $tableCalculatedProperties[$typeName] = @{} + if (-not $psBoundParameters.id) { + $id = "TableFor$($TypeName.Replace('/', '_Slash_').Replace('.', "_").Replace(" ", '_'))$(Get-Random)" + } else { + $id = $psBoundParameters.id + } + + + $tableHeader = New-Object Text.StringBuilder + $null = $tableHeader.Append(" +$(if ($useTableSorter) { + '<script> + $(function() { + $(".tableSorterTable").tablesorter(); + }) + </script>' +}) + +$(if ($useDataTable) { + '<script> + $(function() { + $(".aDataTable").dataTable(); + }) + </script>' +}) + +<table id='${id}${randomSalt}' $classChunk $cssstyleChunk> + <thead> + <tr>") + $labels = @() + $headerCount = $control.Headers.Count + $columns = @($control.Rows[0].Columns) + for ($i=0; $i-lt$headerCount;$i++) { + $header = $control.Headers[$i] + $label = $header.Label + if (-not $label) { + $label = $columns[$i].DisplayEntry.Value + } + + if ($label) { + if ($columns[$i].DisplayEntry.ValueType -eq 'Property') { + $prop = $columns[$i].DisplayEntry.Value + $tableCalculatedProperties[$label] = [ScriptBlock]::Create("`$inputObject.'$prop'") + } elseif ($columns[$i].DisplayEntry.ValueType -eq 'ScriptBlock') { + $tableCalculatedProperties[$label] = [ScriptBlock]::Create($columns[$i].DisplayEntry.Value) + } + $labels+=$label + } + + $null = $tableHeader.Append(" + <th style='font-size:1.1em;text-align:left;line-height:133%'>$([Security.SecurityElement]::Escape($label))<hr/></th>") + + } + $null = $tableHeader.Append(" + </tr> + </thead> + <tbody> + ") + $tablesForTypeNames[$typeName] = $tableHeader + $typeNamesEncountered += $typeName + } + + $currentTable = $tablesForTypeNames[$typeName] + + # Add a row + $null = $currentTable.Append(" + <tr itemscope='' itemtype='$($typeName)'>") + + foreach ($label in $labels) { + $value = "&nsbp;" + if ($tableCalculatedProperties[$label]) { + $_ = $inputObject + $value = . $tableCalculatedProperties[$label] + } + $value = "$($value -join ([Environment]::NewLine))".Replace([Environment]::NewLine, '<BR/> ') + if ($value -match '^http[s]*://') { + $value = Write-Link -Url $value -Caption $value + } + $null = $currentTable.Append(" + <td style='vertical-align:$verticalAlignment' itemprop='$([Security.SecurityElement]::Escape($label))'>$($value.Replace('&', '&'))</td>") + } + $null = $currentTable.Append(" + </tr>") + } + } else { + + # Default Formatting rules + $labels = @(foreach ($pr in $inputObject.psObject.properties) { $pr.Name }) + if (-not $labels) { return } + [int]$percentPerColumn = 100 / $labels.Count + if ($inputObject.PSObject.Properties.Count -gt 4) { + + $null = $htmlOut.Append(" +<div class='${cssSafeTypeName}Item'> +") + foreach ($prop in $inputObject.psObject.properties) { + $null = $htmlOut.Append(" + <p class='${cssSafeTypeName}PropertyName'>$($prop.Name)</p> + <blockquote> + <pre class='${cssSafeTypeName}PropertyValue'>$($prop.Value)</pre> + </blockquote> +") + + } + $null = $htmlOut.Append(" +</div> +<hr class='${cssSafeTypeName}Separator' /> +") + } else { + $widthPercentage = 100 / $labels.Count + $typeName = $inputObject.pstypenames[0] + if (-not ($tablesForTypeNames[$typeName])) { + $tableCalculatedProperties[$typeName] = @{} + if (-not $psBoundParameters.id) { + $id = "TableFor$($TypeName.Replace('/', '_Slash_').Replace('.', "_").Replace(" ", '_'))$(Get-Random)" + } else { + $id = $psBoundParameters.id + } + $tableHeader = New-Object Text.StringBuilder + + $null = $tableHeader.Append(" +$(if ($useTableSorter) { + '<script> + $(function() { + $(".tableSorterTable").tablesorter(); + }) + </script>' +}) + +$(if ($useDataTable) { + '<script> + $(function() { + $(".aDataTable").dataTable(); + }) + </script>' +}) + + +<table id='${id}${randomSalt}' $cssStyleChunk $classChunk > + <thead> + <tr>") + + + foreach ($label in $labels) { + $null = $tableHeader.Append(" + <th style='font-size:1.1em;text-align:left;line-height:133%;width:${widthPercentage}%'>$([Security.SecurityElement]::Escape($label))<hr/></th>") + + + } + $null = $tableHeader.Append(" + </tr> + </thead> + <tbody>") + $tablesForTypeNames[$typeName] = $tableHeader + $typeNamesEncountered += $typeName + } + + $currentTable = $tablesForTypeNames[$typeName] + + # Add a row + $null = $currentTable.Append(" + <tr itemscope='' itemtype='$($typeName)'>") + + foreach ($label in $labels) { + $value = "&nsbp;" + $value = $inputObject.$label + $value = "$($value -join ([Environment]::NewLine))".Replace([Environment]::NewLine, '<BR/> ') + if ($value -match '^http[s]*://') { + $value = Write-Link $value + } + $null = $currentTable.Append(" + <td style='vertical-align:$verticalAlignment' itemprop='$([Security.SecurityElement]::Escape($label))'>$($value.Replace('&', '&'))</td>") + } + $null = $currentTable.Append(" + </tr>") + + } + } + } + + } + + end { + $htmlOut = "$htmlOut" + $htmlOut += if ($tablesForTypeNames.Count) { + foreach ($table in $typeNamesEncountered) { + if ($AsBarGraph) { + $null = $tablesForTypeNames[$table].Append(@" +</tbody></table> +<div id='${id}_Holder_${RandomSalt}'> +</div> +<div style='clear:both'> </div> +<script> + +`$(function () { + // Grab the data + colors = ["$($ColorList -join '","')"] + var data = [], + labels = []; + `$("#${Id}${RandomSalt} thead tr th").each( + function () { + labels.push(`$(this).text()); + + + + }); + + `$("#${Id}${RandomSalt} tbody tr td").each( + function () { + data.push( + parseInt( + `$(this).text(), 10) + ); + + }); + `$("#${Id}${RandomSalt}").hide(); + + chartHtml = '<table valign="bottom"><tr><td valign="bottom">' + valueTotal = 0 + for (i =0; i< labels.length;i++) { + chartHtml += ("<div id='${RandomSalt}_" + i + "' style='min-width:50px;float:left;ver' > <div id='${RandomSalt}_" + i + "_Rect' style='height:1px;background-color:" + colors[i] + "'> </div><br/><div class='chartLabel'>"+ labels[i] + '<br/>(' + data[i] + ")</div></div>"); + valueTotal += data[i]; + + chartHtml+= '</td>' + + if (i < (labels.length - 1)) { + chartHtml+= '<td valign="bottom">' + } + } + chartHtml += '</tr></table>' + + + `$(${id}_Holder_${RandomSalt}).html(chartHtml); + + + for (i =0; i< labels.length;i++) { + newRelativeHeight = (data[i] / valueTotal) * 200; + `$(("#${RandomSalt}_" + i + "_Rect")).animate({ + height:newRelativeHeight + }, 500); + + } + +}); + +</script> +"@) + } else { + $null = $tablesForTypeNames[$table].Append(" +</tbody></table>") + } + + if ($escape) { + [Web.HttpUtility]::HtmlEncode($tablesForTypeNames[$table].ToString()) + } else { + $tablesForTypeNames[$table].ToString() + + } + + } + } + + if ($itemType) { + $htmlout = "<div itemscope='' itemtype='$($itemType -join ' ')'> +$htmlOut +</div>" + } + + if ($graphData) { + $legendHtml = "" + if (-not $HideLegend) { + $c =0 + $legendHtml = + foreach ($graphItem in $graphItemOrder) { + $val = $graphData[$graphItem] + $lang = Get-Culture + + if ($request -and $request["Accept-Language"]) { + + $matchingLang = [Globalization.CultureInfo]::GetCultures("All") | Where-Object {$_.Name -eq $request["Accept-Language"]} + if ($matchingLang) { + $lang = $matchingLang + } + } + + $formattedVal = if ($AsCurrency) { + $v = $val.ToString("c", $lang) + if ($v -like "*$($lang.NumberFormat.CurrencyDecimalSeparator)00") { + $v.Substring(0, $v.Length - 3) + } else { + $v + } + } else { + $v = $val.ToString("n", $lang) + if ($v -like "*$($lang.NumberFormat.NumberDecimalSeparator)00") { + $v.Substring(0, $v.Length - 3) + } else { + $v + } + } + + + + + +" +<div style='margin-top:5px'> + <div style='background-color:$($colorList[$c]);width:20px;height:20px;content:"";'> + <div style='font-weight:bold;height:20px;vertical-align:middle;display:inline;margin-left:25px;position:absolute'> + $graphItem $(if (-not $HideValue) { "($formattedVal)" }) + </div> + </div> +</div> +" + $C++ + } + + } + if ($AsPieGraph) { +$pieStyle = " + #Graph$RandomSalt .pie { + position:absolute; + width:$($GraphWidth / 2)px; + height:$($GraphHeight)px; + overflow:hidden; + left:$($graphWidth * .75)px; + -moz-transform-origin:left center; + -ms-transform-origin:left center; + -o-transform-origin:left center; + -webkit-transform-origin:left center; + transform-origin:left center; + } + + #Graph$RandomSalt .pie.big { + position:absolute; + width:${GraphWidth}px; + height:${GraphHeight}px; + left:$($GraphWidth * .25)px; + -moz-transform-origin:center center; + -ms-transform-origin:center center; + -o-transform-origin:center center; + -webkit-transform-origin:center center; + transform-origin:center center; + } + + + #Graph$RandomSalt .pie:BEFORE { + content:''; + position:absolute; + width:$($GraphWidth / 2)px; + height:$($GraphHeight)px; + left:-$($GraphWidth / 2)px; + border-radius:$($GraphWidth / 2)px 0 0 $($GraphWidth / 2)px; + -moz-transform-origin:right center; + -ms-transform-origin:right center; + -o-transform-origin:right center; + -webkit-transform-origin:right center; + transform-origin:right center; + + } + + #Graph$RandomSalt .pie.big:BEFORE { + left:0px; + } + + #Graph$RandomSalt .pie.big:AFTER { + content:''; + position:absolute; + width:$($GraphWidth / 2)px; + height:${GraphHeight}px; + left:$($GraphWidth / 2)px; + border-radius:0 $($GraphWidth / 2)px $($GraphWidth / 2)px 0; + } + +$( + $c = 0 + foreach ($color in $ColorList) { + $c++ +@" + + #Graph$RandomSalt .pie:nth-of-type($c):BEFORE, + #Graph$RandomSalt .pie:nth-of-type($c):AFTER { + background-color:$color; + } +"@ + }) + + +$( + $dataStart = 0 + $totalSliced = 0 + $totalSliced = $graphData.Values | Measure-Object -Sum | Select-Object -ExpandProperty Sum + $dc = 0 + $pieHtml = '' + foreach ($graphItem in $graphItemOrder) { + $val = $graphData[$graphItem] + $percentOfTotal = $val / $totalSliced + $percentInDegrees = 360 * $percentOfTotal + $dataEnd = $dataStart + [int]$percentInDegrees + $pieHtml += + if ($percentInDegrees -lt 180) { + " +<div class='pie' data-start='$DataStart' data-value='$([int]$percentInDegrees)'></div> +" + } else { + " +<div class='pie big' data-start='$DataStart' data-value='$([int]$percentInDegrees)'></div> +" + } + +@" + #Graph$RandomSalt .pie[data-start="$dataStart"] { + -moz-transform: rotate(${DataStart}deg); /* Firefox */ + -ms-transform: rotate(${DataStart}deg); /* IE */ + -webkit-transform: rotate(${DataStart}deg); /* Safari and Chrome */ + -o-transform: rotate(${DataStart}deg); /* Opera */ + transform:rotate(${DataStart}deg); + filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=${DataStart}); + } +"@ + if ($dc -lt ($graphData.Count - 1)) { +@" + + #Graph$RandomSalt .pie[data-value="$([int]$percentInDegrees)"]:BEFORE { + -moz-transform: rotate($([int]($percentInDegrees + 1))deg); /* Firefox */ + -ms-transform: rotate($([int]($percentInDegrees + 1))deg); /* IE */ + -webkit-transform: rotate($([int]($percentInDegrees + 1))deg); /* Safari and Chrome */ + -o-transform: rotate($([int]($percentInDegrees + 1))deg); /* Opera */ + transform:rotate($([int]($percentInDegrees + 1))deg); + filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$([int]($percentInDegrees + 1))); + } + +"@ + } else { +@" + + #Graph$RandomSalt .pie[data-value="$([int]$percentInDegrees)"]:BEFORE { + -moz-transform: rotate($([int]($percentInDegrees))deg); /* Firefox */ + -ms-transform: rotate($([int]($percentInDegrees))deg); /* IE */ + -webkit-transform: rotate($([int]($percentInDegrees))deg); /* Safari and Chrome */ + -o-transform: rotate($([int]($percentInDegrees))deg); /* Opera */ + transform:rotate($([int]($percentInDegrees))deg); + filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=$([int]($percentInDegrees))); + } + +"@ + } + $dc++ + $dataStart = $dataEnd + } +) +" + + } elseif ($asBarGraph) { +$pieStyle = @" +$( + $dataStart = 0 + $totalSliced = 0 + $totalSliced = $graphData.Values | Measure-Object -Maximum + $totalMax = $totalSliced | Select-Object -ExpandProperty Maximum + $totalSliced = $totalSliced | Select-Object -ExpandProperty Sum + $dc = 0 + $pieHtml = '' + foreach ($graphItem in $graphItemOrder) { + $val = $graphData[$graphItem] + $percentOfTotal = $val / ($totalMax * 1.1) + if (-not $Horizontal) { + $percentHeight = $GraphHeight * $percentOfTotal + + $pieHtml += + + " +<div style='float:left;position:relative;vertical-align:bottom;bottom:0;width:$($graphWidth * .66 / $graphData.Count)px;height:$([Math]::round($percentHeight))px;margin-left:$($graphWidth * .33/ $graphData.Count)px'> + <div class='barItemTop' style='height:$($graphHeight * (1-$percentOfTotal))px;'> + + </div> + <div class='barChartItem' data-value='$val' data-percent='$percentOfTotal' style='width:$([Math]::Round($graphWidth * .66 / $graphData.Count))px;height:$($percentHeight)px;background-color:$($colorList[$dc])'> + </div> + +</div> +" + + } else { + $percentWidth = $GraphWidth * $percentOfTotal + $pieHtml += " +<div style='width:$($percentWidth)px;height:$($GraphHeight * .66 / $graphData.Count)px;margin-bottom:$($graphHeight * .33/ $graphData.Count)px;background-color:$($colorList[$dc])'> +</div> +" + + } + $dc++ + $dataStart = $dataEnd + } +) + +"@ + } + $htmlOut = if ($graphData.Count) { +$cssStyleChunk = if ($psBoundParameters.Style) { + if ($AsPieGraph -or $AsBarGraph) { + + $style['width'] = "${GraphWidth}px" + + + $style['height'] = "$($GraphHeight + $graphData.Count * 55)px" + + } + "style='" +(Write-CSS -Style $style -OutputAttribute) + "'" +} else { + if ($AsPieGraph -or $AsBarGraph) { + + + + + + "style='" +(Write-CSS -Style @{"width"="${GraphWidth}px";"height"="$($GraphHeight + $graphData.Count * 55)px"} -OutputAttribute) + "'" + } else { + "style='width:100%'" + } +} + + +" + + +<div id='Graph$RandomSalt' $cssStyleChunk> +$(if ($Header) { + "<h$HeaderSize style='text-align:$($headerAlignment.ToLower());width:${graphWidth}px'>$($header)</h$HeaderSize>" +}) + +<div style='width:$GraphWidth;height;$GraphHeight;position:relative'> +$(if ($pieStyle) { +"<style> + $PieStyle +</style> +"}) +$pieHtml +</div> +<div class='GraphLegend' style='clear:both;$(if ($AsPieGraph) { "position:relative;top:$($GraphHeight)px" })'> + $legendHtml +</div> + +</div> + + +" + } + + $null = $null + } + if ($WriteResponse -and $Response.Write) { + $Response.Write("$htmlOut") + } else { + $htmlOut + } + + } +} diff --git a/Out-RssFeed.ps1 b/Out-RssFeed.ps1 new file mode 100644 index 0000000..fc78f96 Binary files /dev/null and b/Out-RssFeed.ps1 differ diff --git a/Out-Zip.ps1 b/Out-Zip.ps1 new file mode 100644 index 0000000..329af69 Binary files /dev/null and b/Out-Zip.ps1 differ diff --git a/Pages/favicon.ico b/Pages/favicon.ico new file mode 100644 index 0000000..f37f169 Binary files /dev/null and b/Pages/favicon.ico differ diff --git a/Pipeworks.EzFormat.ps1 b/Pipeworks.EzFormat.ps1 new file mode 100644 index 0000000..4a96f2d Binary files /dev/null and b/Pipeworks.EzFormat.ps1 differ diff --git a/Pipeworks.Format.ps1xml b/Pipeworks.Format.ps1xml new file mode 100644 index 0000000..ed85df3 --- /dev/null +++ b/Pipeworks.Format.ps1xml @@ -0,0 +1,2066 @@ +<?xml version="1.0" encoding="utf-16"?> +<Configuration> + <ViewDefinitions> + <View> + <Name>http://schema.org/Place</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Place</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + + + # A Place will display a header with the name of the place + # it will display the address, telephone. + # if latitude and longitude are available, + # it will display a Bing map, and if present, the mapSize parameter will determine resolution + + + $canDisplayMap = $true + $obj = $_ + if ($obj.Latitude -and $obj.longitude) { + $displayMode = "AerialWithLabels" + + if ($obj.StreetAddress) { + $displayMode = "Road" + } + } + + + $lat = $obj.Latitude + $long = $obj.Longitude + + $displayName = if ($obj.Name) { + $obj.Name + } elseif ($obj.StreetAddress -and $obj.Locality) { + "$($obj.StreetAddress) $($obj.Locality)" + } elseif ($obj.Latitude -and $obj.Longitude) { + "$($obj.Latitude)x$($obj.Longitude)" + } + if ($canDisplayMap ) { + $mapSize = if ($obj.MapSize) { + $obj.MapSize + } else { + "420,315" + } + if ($lat -notlike "*.*" -or $long -notlike "*.*") { + $zoomLevel = 7 + } else { + $latp = @($lat.ToString().Split("."))[-1].Length + $longp = @($long.ToString().Split("."))[-1].Length + if ($latP -lt $longp) + { + $zoomLevel = 7 + (1.5 * $latP) + } else { + $zoomLevel = 7 + (1.5 * $longP) + } + } + $mapId = "Map_" + $DisplayName.Replace(" ", "_").Replace(",", "_").Replace(".","_") + $width,$height = $mapSize -split "," + + $address= + if ($obj.Address) { + $obj.Address + "<BR/>" + } else { + "" + } + + + $telephone= + if ($obj.Telephone) { + $obj.Telephone + "<BR/>" + } else { + "" + } + + "<h4>$displayName</h4><div style='text-align:center'> + + $address + $telephone + + + + + + <iframe width='$width' height='$height' frameborder='0' scrolling='no' marginheight='0' marginwidth='0' src='http://dev.virtualearth.net/embeddedMap/v1/ajax/${displayMode}?zoomLevel=$zoomLevel&center=${lat}_${long}&pushpins=${lat}_${long}' ></iframe></div>" + + } else { + $obj | Select * | Out-HTML + } +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Graph</Name> + <ViewSelectedBy> + <TypeName>Graph</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $thisObject = $_ + + $GraphType = if ($thisObject.GraphType) { + $thisObject.GraphType + $thisObject.psobject.properties.remove("GraphType") + } else { + "Bar" + } + + $colorList = if ($thisObject.ColorList) { + $thisObject.ColorList + $thisObject.psobject.properties.remove("ColorList") + } else { + $null + } + + + $OutHtmlParams = @{"As$($GraphType)Graph"=$true} + + if ($colorList) { + $OutHtmlParams.ColorList = $colorList + } + + $outHtmlCmd = $ExecutionContext.SessionState.InvokeCommand.GetCommand("Out-HTML", "Function") + + if (-not $outHtmlCmd.Parameters."As$($GraphType)Graph") { + $OutHtmlParams.Remove("As$($GraphType)Graph") + } + + $thisObject.pstypenames.clear() + $thisObject.pstypenames.add('GraphData') + $thisObject | Out-HTML @OutHtmlParams + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/JobPosting</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/JobPosting</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $item = $_ + $jobTitle = if ($item.Name) { + $item.Name + } elseif ($item.Title) { + $item.Title + } + $description = if ($item.Description -like "*<*") { + # Treat as HTML + $item.Description + } elseif ($item.Description) { + # Treat as markdown + + ConvertFrom-Markdown $item.Description + } else { + "" + } + + $responsibilities =if ($item.responsibilities) { + " + <h3>Roles & Responsibilities</h3> + <blockquote> + $($item.Responsibilities.Replace("`n", "<BR/>")) + </blockquote> + " + + } else { + "" + } + + $experienceRequirements =if ($item.experienceRequirements) { + " + <h3>Experience Required</h3> + <blockquote> + $(ConvertFrom-Markdown $item.experienceRequirements.Replace("`n", "<BR/>")) + </blockquote> + " + + } else { + "" + } + + $educationRequirement =if ($item.educationRequirements) { + " + <h3>Education Required</h3> + <blockquote> + $(ConvertFrom-Markdown $item.educationRequirements.Replace("`n", "<BR/>")) + </blockquote> + " + + } else { + "" + } + + $qualifications =if ($item.qualifications) { + " + <h3>Special Qualifications</h3> + <blockquote> + $(ConvertFrom-Markdown $item.qualifications.Replace("`n", "<BR/>")) + </blockquote> + " + + } else { + "" + } + + $skills =if ($item.Skills) { + " + <h3>Skills</h3> + <blockquote> + $(ConvertFrom-Markdown $item.skills.Replace("`n", "<BR/>")) + </blockquote> + " + + } else { + "" + } + + $locationAndOrg =if ($item.JobLocation -or $item.HiringOrganization) { + "$($item.JobLocation) $(if ($item.HiringOrganization) { "for $($item.HiringOrganization)" })" + } + + + + $ApplyButton = if ($item.ApplyToEmail) { + "<div style='float:right'>$( + Write-Link -Button -Caption "Apply" -Url "mailto:$($item.ApplyToEmail)?subject=$jobtitle" + )</div> + <div style='clear:both'></div>" + } else { + "" + } + + if ($item.Url) { + $jobTitle = "<a href='$($item.Url)'>$jobTitle</a>" + } + + $jobId = if ($item.RowKey) { + "<div style='float:right;text-size:x-small'>JobId: $($item.RowKey)</div> + <div style='clear:both'> + </div>" + } else { + "" + } + + "<h2>$jobTitle</h2> + $locationAndOrg + <blockquote> + $description + </blockquote> + $responsibilities + $experienceRequirements + $skills + $qualifications + $educationRequirements + $ApplyButton + $JobId + " +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/Person</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Person</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $person = $_ + + if ($Request ) + { + $name = $person.Name + + $nameLink = $name + if ($person.Url) { + $nameLink = "<a href='$($person.Url)'>$name</a>" + } + + $itsMe = if ($session -and ($person.UserID -eq $session["User"].UserID -and $person.UserEmail -eq $session["User"].UserEmail)) { + $true + } else { + $false + } + + + $imageAndDescription = "" + + $imageAndDescription += + if ($person.Image) { + "<img src='$($person.Image)' style='border:0' />" + } elseif ($person.FacebookId -and $person.FacebookAccessToken) { + # They may not have an image, but they have a Facebook ID and access token, so show their facebook photo + "<img src='http://graph.facebook.com/$($person.FacebookId)/picture?type=large' style='border:0' border='0' />" + } elseif ($person.LiveID -and $person.LiveIDAccessToken) { + # They have a liveID, so show their liveID + "<img src='https://apis.live.net/v5.0/$($person.LiveId)/picture' style='border:0' border='0'/>" + } elseif ($person.ThumbnailPhoto) { + "<img src='data:image/jpg;base64,$($person.ThumbnailPhoto)' style='border:0' border='0'/>" + } + $imageAndDescription += + if ($person.Description -and $person.Description -like "*<*") { + $person.Description + } elseif ($person.Description) { + ConvertFrom-Markdown $person.Description + } else { + "" + } + + if ($person.Age) { + $age = $person.Age -as [int] + } elseif ($person.Birthday -as [DateTime]) { + $daysOld = ((Get-date) - ($person.Birthday -as [Datetime])).Days + $age = [Math]::Floor($daysOld / 365) + + } + + + $billMe = if ($itsMe -and $person.Balance -and ($person.Balance -as [Double] -gt 1) -and $serviceUrl) { + $payUpUrl = if ($serviceUrl.ToString().Contains("?")) { + "$serviceUrl" + "&settle=true" + } else { + "$serviceUrl" + "?settle=true" + } + New-Region -LayerId "SettlePersonalAccountBalanceFor$($person.UserID)" -AsPopdown -LayerUrl @{"Account Balance - $($Person.Balance)."= $payUpUrl} -Layer @{"Account Balance - $($Person.Balance)."="Please take care of your bill when you can"} -Style @{"font-size"="xx-small";"float" = "right"} + } else { + "" + } + + + + $imageAndDescription += + if ($age) { + if ($person.Gender) { + "<BR/>$age | $($person.Gender)" + } else { + "<BR/>$age" + } + } else { + "" + } + + $imageAndDescription += + if ($person.Location -and $person.Location.Name) { + "<BR/>" + $($person.Location.Name) + } + + $imageAndDescription += + if ($person.LastLogin -and $person.Location.Name) { + "<BR/>" + $($person.Location.Name) + } + + $AdditionalContent = "" + + $AdditionalContent += + if ($person.Title) { + "<h3>" + $person.Title + + $(if ($person.Department) { + "<br/>$($person.Department)" + }) +"</h3>" + } + + $AdditionalContent += + if ($person.Bio) { + "<BR/>" + $person.Bio + ("<BR/>" * 3) + } + + $CloseMailLink = if ($person.Email -or $person.UserEmail) { + "</a>" + } else { + "" + } + + $openMailLink = if ($person.UserEmail) { + "<a href='mailto:$($person.UserEmail)'>" + } elseif ($person.Email) { + "<a href='mailto:$($person.Email)'>" + } else { + "" + } + + $callMeLink = if ($person.Telephone) { + "<BR/>" + $person.Telephone + } else { + "" + } + + $officeLocationLink = if ($person.Office) { + "<BR/>" + $person.Office + } else { + "" + } + + + $additionalContent += if ($person.InteractionCount) { + + + } else { + "" + } + + "<h3 style='float:left'> + $NameLink + </h3> + <div style='clear:both'> </div> + <div style='float:right'> + $($BillMe) + </div> + + <hr/> + <br/> + <div style='clear:both'> </div> + <div style='float:right;text-align:center'> + $OpenMailLink $imageAndDescription $CloseMailLink $callMeLink $officeLocationLink + </div> + <div> + $AdditionalContent + </div> + $(if ($person.Awards) { + " + <div style='float:right;width:33%;margin:30px'> + $( + $awardsList = @($person.Awards -split ';') + + $awardOrder = if ($pipeworksManifest.AwardOrder) { + @($pipeworksManifest.AwardOrder) + } elseif ($pipeworksManifest.AwardsOrder) { + @($pipeworksManifest.AwardsOrder) + } else { + $awardsList | Sort-Object + } + + foreach ($a in $awardOrder) { + if ($awardsList -contains $a) { + if ($pipeworksManifest.Award.$a) { + + } elseif ($pipeworksManifest.Awards.$a) { + + } else { + "<span style='font-size:1.33em;float:right'>$a</span> + <br style='clear:both'/>" + } + } + } + ) + </div> + <br style='clear:both;line-height:400%'/>" + + }) + $( + if ($person.InteractionCount) { + " + <div style='float:right;width:33%;margin:30px'> + $( + $interactionContent = ConvertFrom-StringData "$($person.InteractionCount)".Replace(":", "=") -ErrorAction SilentlyContinue + if ($interactionContent) { + $interactionOrder = + if ($pipeworksManifest.Interaction) { + @($pipeworksManifest.Interaction) + } elseif ($pipeworksManifest.Interactions) { + @($pipeworksManifest.Interactions) + } else { + $interactionContent.Keys | Sort-Object + } + foreach ($k in $interactionOrder) { + $V = $interactionContent[$k] + "<div itemscope='' itemtype='http://schema.org/UserInteraction'> + <div style='float:left;width:50%'><span itemprop='Count'>$v</span></div> + <div style='float:right;width:50%'><span itemprop='Name'>$k</span></div> + <br style='clear:both' /> + </div>" + + } + + } + ) + </div> + <br style='clear:both;line-height:400%'/> + " + } + ) + + " + + } else { + + Write-Host $person.Name + } + + + + + + + + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Deserialized.http://schema.org/Person</Name> + <ViewSelectedBy> + <TypeName>Deserialized.http://schema.org/Person</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $person = $_ + + if ($Request ) + { + $name = $person.Name + + $nameLink = $name + if ($person.Url) { + $nameLink = "<a href='$($person.Url)'>$name</a>" + } + + $itsMe = if ($session -and ($person.UserID -eq $session["User"].UserID -and $person.UserEmail -eq $session["User"].UserEmail)) { + $true + } else { + $false + } + + + $imageAndDescription = "" + + $imageAndDescription += + if ($person.Image) { + "<img src='$($person.Image)' style='border:0' />" + } elseif ($person.FacebookId -and $person.FacebookAccessToken) { + # They may not have an image, but they have a Facebook ID and access token, so show their facebook photo + "<img src='http://graph.facebook.com/$($person.FacebookId)/picture?type=large' style='border:0' border='0' />" + } elseif ($person.LiveID -and $person.LiveIDAccessToken) { + # They have a liveID, so show their liveID + "<img src='https://apis.live.net/v5.0/$($person.LiveId)/picture' style='border:0' border='0'/>" + } elseif ($person.ThumbnailPhoto) { + "<img src='data:image/jpg;base64,$($person.ThumbnailPhoto)' style='border:0' border='0'/>" + } + $imageAndDescription += + if ($person.Description -and $person.Description -like "*<*") { + $person.Description + } elseif ($person.Description) { + ConvertFrom-Markdown $person.Description + } else { + "" + } + + if ($person.Age) { + $age = $person.Age -as [int] + } elseif ($person.Birthday -as [DateTime]) { + $daysOld = ((Get-date) - ($person.Birthday -as [Datetime])).Days + $age = [Math]::Floor($daysOld / 365) + + } + + + $billMe = if ($itsMe -and $person.Balance -and ($person.Balance -as [Double] -gt 1) -and $serviceUrl) { + $payUpUrl = if ($serviceUrl.ToString().Contains("?")) { + "$serviceUrl" + "&settle=true" + } else { + "$serviceUrl" + "?settle=true" + } + New-Region -LayerId "SettlePersonalAccountBalanceFor$($person.UserID)" -AsPopdown -LayerUrl @{"Account Balance - $($Person.Balance)."= $payUpUrl} -Layer @{"Account Balance - $($Person.Balance)."="Please take care of your bill when you can"} -Style @{"font-size"="xx-small";"float" = "right"} + } else { + "" + } + + + + $imageAndDescription += + if ($age) { + if ($person.Gender) { + "<BR/>$age | $($person.Gender)" + } else { + "<BR/>$age" + } + } else { + "" + } + + $imageAndDescription += + if ($person.Location -and $person.Location.Name) { + "<BR/>" + $($person.Location.Name) + } + + $imageAndDescription += + if ($person.LastLogin -and $person.Location.Name) { + "<BR/>" + $($person.Location.Name) + } + + $AdditionalContent = "" + + $AdditionalContent += + if ($person.Title) { + "<h3>" + $person.Title + + $(if ($person.Department) { + "<br/>$($person.Department)" + }) +"</h3>" + } + + $AdditionalContent += + if ($person.Bio) { + "<BR/>" + $person.Bio + ("<BR/>" * 3) + } + + $CloseMailLink = if ($person.Email -or $person.UserEmail) { + "</a>" + } else { + "" + } + + $openMailLink = if ($person.UserEmail) { + "<a href='mailto:$($person.UserEmail)'>" + } elseif ($person.Email) { + "<a href='mailto:$($person.Email)'>" + } else { + "" + } + + $callMeLink = if ($person.Telephone) { + "<BR/>" + $person.Telephone + } else { + "" + } + + $officeLocationLink = if ($person.Office) { + "<BR/>" + $person.Office + } else { + "" + } + + + $additionalContent += if ($person.InteractionCount) { + + + } else { + "" + } + + "<h3 style='float:left'> + $NameLink + </h3> + <div style='clear:both'> </div> + <div style='float:right'> + $($BillMe) + </div> + + <hr/> + <br/> + <div style='clear:both'> </div> + <div style='float:right;text-align:center'> + $OpenMailLink $imageAndDescription $CloseMailLink $callMeLink $officeLocationLink + </div> + <div> + $AdditionalContent + </div> + $(if ($person.Awards) { + " + <div style='float:right;width:33%;margin:30px'> + $( + $awardsList = @($person.Awards -split ';') + + $awardOrder = if ($pipeworksManifest.AwardOrder) { + @($pipeworksManifest.AwardOrder) + } elseif ($pipeworksManifest.AwardsOrder) { + @($pipeworksManifest.AwardsOrder) + } else { + $awardsList | Sort-Object + } + + foreach ($a in $awardOrder) { + if ($awardsList -contains $a) { + if ($pipeworksManifest.Award.$a) { + + } elseif ($pipeworksManifest.Awards.$a) { + + } else { + "<span style='font-size:1.33em;float:right'>$a</span> + <br style='clear:both'/>" + } + } + } + ) + </div> + <br style='clear:both;line-height:400%'/>" + + }) + $( + if ($person.InteractionCount) { + " + <div style='float:right;width:33%;margin:30px'> + $( + $interactionContent = ConvertFrom-StringData "$($person.InteractionCount)".Replace(":", "=") -ErrorAction SilentlyContinue + if ($interactionContent) { + $interactionOrder = + if ($pipeworksManifest.Interaction) { + @($pipeworksManifest.Interaction) + } elseif ($pipeworksManifest.Interactions) { + @($pipeworksManifest.Interactions) + } else { + $interactionContent.Keys | Sort-Object + } + foreach ($k in $interactionOrder) { + $V = $interactionContent[$k] + "<div itemscope='' itemtype='http://schema.org/UserInteraction'> + <div style='float:left;width:50%'><span itemprop='Count'>$v</span></div> + <div style='float:right;width:50%'><span itemprop='Name'>$k</span></div> + <br style='clear:both' /> + </div>" + + } + + } + ) + </div> + <br style='clear:both;line-height:400%'/> + " + } + ) + + " + + } else { + + Write-Host $person.Name + } + + + + + + + + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Walkthru</Name> + <ViewSelectedBy> + <TypeName>Walkthru</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $name = $_.Name + $walkthru = Get-Walkthru -Text $_.Description + New-Region -ItemType walkthru -AsWidget -Style @{ + 'text-align' = 'left' + } -Layer @{ + $Name = Write-WalkthruHTML -WalkThru $walkthru -StepByStep + } +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Module</Name> + <ViewSelectedBy> + <TypeName>Module</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock>"<h3 class='ui-widget-header' itemprop='name'>$($_.Name)</h3> + $( + if ($_.Description -and + $_.Description -ne $_.Name -and + $_.Description -ne $_.ArticleText) { + "<div class='Description' itemprop='Description' style='text-align:left'>$($_.Description)</div>" + } + "<br/>" + if ($_.Url) { + if (-not $_.ArticleText) { + Write-Link -Button -Url $_.Url -Caption "<span class='ui-icon ui-icon-extlink'> + </span> + <br/> + <span style='text-align:center'> + Visit Website + </span>" + + } + "<meta style='display:none' content='$($_.Url)' itemprop='url' />" + } + if ($_.Author) { + "<meta style='display:none' itemprop='author' content='$($_.Author)' />" + if ($_.Author -like "@*") { + "By $($_.Author) - $(Write-Link ('twitter:follow' + $_.Author))" + } else { + "By $($_.Author)" + } + } + if ($_.Image) { + Write-Link -Caption "<img border='0' src='$($_.Image)' />" -Simple -Url $_.Url + "<meta style='display:none' content='$($_.Image)' itemprop='image'>" + } + if ($_.Image -or $_.Url) { + '<br />' + } + ) + "</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Example</Name> + <ViewSelectedBy> + <TypeName>Example</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock>"<h3 class='ui-widget-header' itemprop='name'>$($_.Name)</h3> + <div itemprop='description'> + $($sb = [ScriptBlock]::Create($_.Description); Write-ScriptHTML -Text $sb) + </div>"</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>WolframAlphaResult</Name> + <ViewSelectedBy> + <TypeName>WolframAlphaResult</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + if ($Request ) { + $item = $_ + $podHtml = + $_.psobject.properties | + ForEach-Object { + if ('Pods','OutputXml','InputInterpretation', 'PSComputerName', 'RunspaceId' -contains $_.Name) { + return + } + + "$($_.Name)<br/> + <blockquote> + $( + $val = $_.Value + if ($val -like "http://*image*") { + "<img src='$val' style='border:0' />" | Out-HTML + } else { + $val | Out-HTML + } + + ) + </blockquote>" + } + " + $($item.InputInterpretation) + <blockquote> + $podHtml + </blockquote> + " + } else { + Write-Host "Input :" $_.InputInterpretation + + $_.psobject.properties | + ForEach-Object { + if ('Pods','OutputXml','InputInterpretation', 'PSComputerName', 'RunspaceId' -contains $_.Name) { + return + } + $displayName = $_.Name + Write-Host $displayName + $content = $_.Value | Out-String + $newContent = $content -split "[$([Environment]::NewLine)]" | + Where-Object { $_ } | + ForEach-Object { + " " + $_ + } + Write-Host ($newContent -join ([Environment]::NewLine)) + } + } + $null +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/ContactPoint</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/ContactPoint</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock>" +<h3 class='ui-widget-header' itemprop='name'>$($_.Name)</h3> +$( +if ($_.ContactType){ +"<span itemprop='contactType' class='contactType'>$($_.ContactType)</span><br/>" +} +if ($_.Email){ +"<a href='mailto:$($_.Email)' itemprop='email' class='contactEmail'>$($_.Email)</a><br/>" +} +if ($_.Telephone){ +"Phone: <span itemprop='telephone' class='contactPhone'>$($_.Telephone)</span><br/>" +} +if ($_.FaxNumber){ +"Phone: <span itemprop='telephone' class='contactPhone'>$($_.Telephone)</span><br/>" +} +) +"</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/Articlehttp://schema.org/NewsArticlehttp://schema.org/BlogPosting</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Article</TypeName> + <TypeName>http://schema.org/NewsArticle</TypeName> + <TypeName>http://schema.org/BlogPosting</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + # Calculate the depth of the virtual URL compared to the real page. + # This gets used to convert links to local resources, such as a custom JQuery theme + $depth = 0 + if ($context -and $context.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]) { + + $originalUrl = $context.Request.ServerVariables["HTTP_X_ORIGINAL_URL"] + + $pathInfoUrl = $request.Url.ToString().Substring(0, $request.Url.ToString().LastIndexOf("/")) + + + + $pathInfoUrl = $pathInfoUrl.ToLower() + $protocol = ($request['Server_Protocol'] -split '/')[0] # Split out the protocol + $serverName= $request['Server_Name'] # And what it thinks it called the server + + $fullOriginalUrl = $protocol.ToLower() + "://" + $serverName + $request.Params["HTTP_X_ORIGINAL_URL"] + $FullUrl = $fullOriginalUrl + $relativeUrl = $fullOriginalUrl.Replace("$pathInfoUrl", "") + + if ($relativeUrl -like "*/*") { + $depth = @($relativeUrl -split "/" -ne "").Count - 1 + if ($depth -eq 0) { $depth = 1 } + } else { + $depth = 0 + } + + } + + $item = $_ + + $itemUrl = + if ($item.Url) { + $item.Url + } elseif ($item.Link) { + $item.Link + } + $typeName = @($item.pstypenames)[-1].TrimStart("Deserialized.") + " +<div itemscope='' itemtype='$typeName'> +<h1 class='ui-widget-header' itemprop='name'>$( + if ($_.Name -and $itemUrl) { + "<a href='$($itemUrl)' itemprop='url'>$($_.Name)</a>" + } elseif ($_.Name) { + $_.Name + } elseif ($_.Title -and $itemUrl) { + "<a href='$($itemUrl)' itemprop='url'>$($_.Title)</a>" + } elseif ($_.Title) { + $_.Title + } +)</h1> +<span style='display:none' itemprop='name'>$($_.Name)</span> +<br style='line-height:300%;clear:both' /> + $( + if ($_.Url) { + if (-not $_.ArticleText -and -not $_.ArticleBody) { + Write-Link -Button -Url $_.Url -Caption "<span class='ui-icon ui-icon-extlink'> + </span> + <br/> + <span style='text-align:center'> + From $( + $urlHost=([uri]$_.Url).DnsSafeHost + if ($urlHost){ + $urlHost.Replace('www.', '') + } else { + $_.Url + } + ) + </span>" + + } + "<meta style='display:none' content='$($_.Url)' itemprop='url' />" + } + if ($_.Author -and -not $_.NoByline) { + "<meta style='display:none' itemprop='author' content='$($_.Author)' />" + if (-not $_.NoByline) { + if ($_.Author -like "@*") { + "By $($_.Author) - $(Write-Link ('twitter:follow' + $_.Author))" + } else { + "By $($_.Author)" + } + } + } + if ($_.DatePublished) { + "<meta style='display:none' itemprop='datePublished' content='$($_.DatePublished)' />" + if (-not $_.NoByline) { + "Published $(($_.DatePublished -as [DateTime]).ToLongDateString())" + } + + } + + if ($_.Image) { + Write-Link -Caption "<img border='0' src='$($_.Image)' />" -Simple -Url $_.Url + "<meta style='display:none' content='$($_.Image)' itemprop='image'>" + } + if ($_.Image -or $_.Url) { + '<br />' + } + if ($_.Description -and + $_.Description -ne $_.Name -and + $_.Description -ne $_.ArticleText -and + $_.Description -ne $_.ArticleBody) { + "<div class='Description' itemprop='Description' style='text-align:left'>$($_.Description)</div>" + } + if ($_.ArticleBody -and $_.ArticleBody -ne $_.Name) { + "<div class='ArticleBody' itemprop='ArticleBody' style='text-align:left'>$($_.ArticleBody)</div>" + } elseif ($_.ArticleText -and $_.ArticleText -ne $_.Name) { + "<div class='ArticleText' itemprop='ArticleText' style='text-align:left'>$($_.ArticleText)</div>" + } elseif ($_.CompressedArticleBody) { + $expandedArticleBody = Expand-Data -CompressedData $_.CompressedArticleBody -ErrorAction SilentlyContinue + "<div class='ArticleText' itemprop='ArticleText' style='text-align:left'>$expandedArticleBody</div>" + } + + if ((-not $_.Url) -and $pipeworksManifest.Blog.Name) { + "<div style='text-align:right'>" + ( + Write-Link -Style @{'font-size'='xx-small'} -Button -Url "$('../' * $depth)?Post=$([Web.HttpUtility]::UrlEncode($_.Name))" -Caption "<span class='ui-icon ui-icon-extlink'> + </span> + <br/> + <span style='text-align:center'> + Permalink + </span>") + "</div>" + "<meta style='display:none' itemprop='url' content='$('../' * $depth)?Post=$([Web.HttpUtility]::UrlEncode($_.Name))' />" + } + if ($pipeworksManifest.Facebook.AppId) { + "<div class='fb-like' data-send='true' data-width='450' data-show-faces='true'></div> <BR/>" + + "<div class='fb-comments' data-href='$(if ($FullUrl) { $fullUrl } else { $request.Url })' data-num-posts='2' data-width='470'></div>" + + } + + if ($item.Keyword) { + $keywordChunk = @( + $keywordurls = @($item.KeywordUrl) + $keyword = @($item.Keyword) + + for ($i= 0;$i-lt $keyword.Count;$i++) { + if ($keyword[$i] -and $keywordurls[$i]) { + "<a href='$($keywordUrls[$i])'><span itemprop='keyword'>$($keyword[$i])</span></a>" + } else { + "<span itemprop='keyword'>" + $keyword[$i] + "</span>" + } + } + ) -join '<br/>' + "<div style='float:right;font-size:1.11em;text-align:right'>$keywordChunk</div><br style='clear:both'/>" + + } + + + + + + + ) + </div> + " + + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/VideoObjecthttp://schema.org/ImageObjecthttp://schema.org/MediaObject</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/VideoObject</TypeName> + <TypeName>http://schema.org/ImageObject</TypeName> + <TypeName>http://schema.org/MediaObject</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $linkType = if ("http://schema.org/VideoObject" -eq @($_.pstypenames)[-1].TrimStart("Deserialized.")) { + "play" + } else { + "extlink" + } + $media = $_ + + $fromString = if ($_.Publisher) { + $_.Publisher + } elseif ($_.From) { + $_.From + } + + + if ($fromString) { + $fromString= "- $fromString" + } + + + + $width = 320 + $height = 240 + + if ($_.Width) { + $width = "$($_.Width)".Trim("px") -as [double] + $height = "$($_.Height)".Trim("px") -as [double] + } + + " + $(if ($media.Url -notlike "*.youtube.com") { + + + if ($_.Name) { + "<h3 class=`'ui-widget-header`' itemprop=`'name`'>$($_.Name) $fromString</h3>" + } + + + if ($_.Image) { + "<p style='text-align:center'>" + ( + Write-Link -Caption "<img border='0' style='width:${width}px;height:${height}px' src='$($_.Image)' />" -Url $_.Image) + "</p>" + "<meta style='display:none' content='$($_.Image)' itemprop='image'>" + } elseif ($_.ThumbnailUrl) { + "<p style='text-align:center'>" + ( + Write-Link -Caption "<img border='0' style='width:${width}px;height:${height}px' src='$($_.ThumbnailUrl)' />" -Url $_.ThumbnailUrl) + "</p>" + "<meta style='display:none' content='$($_.ThumbnailUrl)' itemprop='image'>" + } elseif ($_.Thumbnail) { + "<p style='text-align:center'>" + ( + Write-Link -Caption "<img border='0' style='width:${width}px;height:${height}px' src='$($_.Thumbnail)' />" -Url $_.Thumbnail) + "</p>" + "<meta style='display:none' content='$($_.Thumbnail)' itemprop='image'>" + } elseif ($_.AssociatedMedia) { + "<p style='text-align:center'>" + ( + Write-Link -Caption "<img border='0' style='width:${width}px;height:${height}px' src='$($_.AssociatedMedia)' />" -Url $_.AssociatedMedia) + "</p>" + "<meta style='display:none' content='$($_.AssociatedMedia)' itemprop='image'>" + } + }) + $(if ($_.Url) { + if ("http://schema.org/ImageObject" -eq @($_.pstypenames)[-1].TrimStart("Deserialized.")) { + "<p style='text-align:center'>" + ( + Write-Link -Caption "<img border='0' style='width:${width}px;height:${height}px' src='$($_.Url)' />" -Url $_.Url) + "</p>" + "<meta style='display:none' content='$($_.Url)' itemprop='url'>" + } else { + "<p style='text-align:center'>" + (Write-Link -Button -Url $_.Url -Caption "<span class='ui-icon ui-icon-$linkType'> </span>") + "</p>" + "<meta style='display:none' content='$($_.Url)' itemprop='url'>" + } + + + }) + $(if ($_.Image -or $_.Url) { + '<br />' + }) + $(if ($_.Caption) { + ('<br />' + $_.Caption + '<br />') + }) + $(if ($_.CopyrightHolder) { + ('<br /> &copy; ' + $_.CopyrightHolder + '<br />') + }) + $(if ($_.Description -and $_.Description -ne $_.Name) { + "<div class='Description' itemprop='Description' style='text-align:left'>$($_.Description)</div>" + } + + $(if ($pipeworksManifest.Facebook.AppId) { + "<div class='fb-like' data-send='true' data-width='450' data-show-faces='true'></div> <BR/>" + + "<div class='fb-comments' data-href='$(if ($FullUrl) { $fullUrl } else { $request.Url })' data-num-posts='2' data-width='470'></div>" + + }) + + ) + + "</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>ColorizedScript</Name> + <ViewSelectedBy> + <TypeName>ColorizedScript</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + if ($Request ) { + Write-ScriptHTML -Text $_ + } else { + $_ + } +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/Event</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Event</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock>" +<div class='product'> +<h3 class='ui-widget-header' itemprop='name'>$($_.Name)</h3> +<p class='ProductDescription'> +$(if ($_.Image) { "<img class='product-image' src='$($_.Image)' align='right' />"}) +<span itemprop='description'>$($_.Description)</span> + +</p> +$( +if ($_.Performers){ + if ($_.Performers -is [string]) { + "<span itemprop='performers' class='eventPerformer'>$($_.Performers)</span><br/>" + } else { + $_.Performers | Out-String -Width 1kb + } +} +if ($_.Location){ + if ($_.Location -is [string]) { + "<span itemprop='location' class='eventLocation'>$($_.Location)</span><br/>" + } else { + $_.Location | Out-String -Width 1kb + } +} +if ($_.StartDate){ + "<meta style='display:none' itemprop='startDate' content='$(([DateTime]$_.StartDate).ToString('r'))' /> + $($_.StartDate | Out-String) + <br/> + " +} +if ($_.EndDate){ + "<meta style='display:none' itemprop='endDate' content='$(([DateTime]$_.EndDate).ToString('r'))' /> + $($_.EndDate | Out-String) + <br/> + " +} +if ($_.Offers) { + foreach ($o in $_.Offers) { + $doubleO = $o -as [Double] + if ($doubleO) { + "<span class='product-price' itemprop='offer'>${doubleO}</span>" + } else { + $o | Out-String + } + } +} +if ($pipeworksManifest -and $PipeworksManifest.GoogleMerchantId) { +'<div role="button" alt="Add to cart" tabindex="0" class="googlecart-add-button"> +</div>' +}) +</div>"</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>Amazon.EC2.Model.Image</Name> + <ViewSelectedBy> + <TypeName>Amazon.EC2.Model.Image</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $obj = $_ + if ($obj.Name) { + Write-Host $obj.Name -NoNewline + + } else { + Write-Host $obj.ImageLocation -NoNewline + } + + Write-Host (" :$($obj.ImageId)") + + Write-Host ' ' + if ($obj.Description) { + Write-Host $obj.Description + } + return $null +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://shouldbeonschema.org/Class</Name> + <ViewSelectedBy> + <TypeName>http://shouldbeonschema.org/Class</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $item = $_ + + $itemHtml = @" +<h1 class="page-title"> + <a href='$($item.Url)' class='shouldbelink' itemprop='name'>$($item.Name)</a> +</h1> +"@ + if ($item.Description) { + $itemHtml += @" +<span itemprop='description'> + $($item.Description) +</span> +"@ + } + + + $itemHtml += @" +<style> + + table.definition-table + { + margin: 1em 0 0 0; + border: 1px solid #98A0A6; + } + .definition-table th + { + text-align: left; + background: #C7CBCE; + padding-left: 5px; + } + .definition-table td + { + padding: 0 5px 2px 5px; + margin: 0; + vertical-align: top; + } + .definition-table td p + { + padding: 0 0 .6em 0; + margin: 0; + } + .definition-table td ul + { + padding-top: 0; + margin-top: 0; + } + .definition-table tr.alt + { + background: #E9EAEB; + } + .h .space + { + width: 20px + } + .h .bar + { + background-color: #000; + width: 1px + } + .h .tc + { + text-indent: -21px; + padding-left: 21px + } +</style> +<table cellspacing="3" class="definition-table SchemaContent" > + <thead> + <tr> + <th>Property</th> + <th>Type</th> + <th>Description</th> + </tr> + </thead> +"@ + + + $itemClassId = if ($item.Url) { + $item.Url + } else { + $item.Name + } + $classHierarchy =@($item.ParentClass) + $itemClassId + foreach ($parentClass in $classHierarchy) { + if (-not $parentClass) { continue } + + + $declaringTypeString = + if ($parentClass.Url) { + if ($parentClass.Url -like "*http*") { + "<a href='$($parentClass.Url)'>$(([uri]$ParentClass.Url).Segments[-1].TrimEnd("/"))</a>" + } else { + "<a href='$($ParentClass.Url)'>$($ParentClass.Url)</a>" + } + + } elseif ($parentClass.Name) { + "$($ParentClass.Name)" + } elseif ($parentClass -like "*http*") { + "<a href='$($parentClass)'>$(([uri]$ParentClass).Segments[-1].Trim("/"))</a>" + } else { + $parentClass + } + + $itemHtml += $item.Property | + Where-Object { + if ($_.DeclaringType) { + $_.DeclaringType -and ( + $_.DeclaringType -eq $parentClass -or + $_.DeclaringType -eq $parentClass.Url -or + $_.DeclaringType -eq $parentClass.Name -or + $declaringTypeString -eq $parentClass.Name + ) + } else { + $parentClass -eq $itemClassId + } + } | + ForEach-Object -Begin { +@" + + <thead class="supertype"><tr> + <th class="supertype-name" colspan="3"> + Properties from $declaringTypeString</th> + </tr> + </thead> + <tbody class="supertype"> +"@ + } -Process { + $property = $_ + + $typeChunk = + if ($property.PropertyType -like "http://*") { + + "<a href='$($property.PropertyType)' itemprop='PropertyType'>$(([uri]$property.PropertyType).Segments[-1])</a>" + } elseif ($property.PropertyType -is [string]) { + + "$($property.PropertyType) + <meta style='display:none' itemprop='PropertyType' content='Text' /> + " + + } else { + "Text + <meta style='display:none' itemprop='PropertyType' content='Text' /> + " + } + @" + <tr itemscope='' itemprop='Property' itemtype='http://shouldbeonschema.org/Property'> + <th class="prop-nam" scope="row"> + <code itemprop='Name'>$($property.Name)</code> + </th> + <td class="prop-ect"> + $typeChunk + </td> + <td class="prop-desc" itemprop='Description'> + $($property.Description) + </td> + </tr> +"@ + } -End { +@" + </tbody> +"@ + } + + + + } + $itemHtml += @" +</table> + +"@ + + + + " +<div itemscope='' itemtype='http://shouldbeonschema.org/Class' style='margin:0;padding:0'> + $itemHtml +</div> + " +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://shouldbeonschema.org/Topic</Name> + <ViewSelectedBy> + <TypeName>http://shouldbeonschema.org/Topic</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $page = "" + $item = $_ + $depth = 0 + $fullOriginalUrl = $request.Url + if ($context -and $context.Request.ServerVariables["HTTP_X_ORIGINAL_URL"]) { + + $originalUrl = $context.Request.ServerVariables["HTTP_X_ORIGINAL_URL"] + + $pathInfoUrl = $request.Url.ToString().Substring(0, $request.Url.ToString().LastIndexOf("/")) + + + + $pathInfoUrl = $pathInfoUrl.ToLower() + $protocol = ($request['Server_Protocol'] -split '/')[0] # Split out the protocol + $serverName= $request['Server_Name'] # And what it thinks it called the server + + $fullOriginalUrl = $protocol.ToLower() + "://" + $serverName + $request.Params["HTTP_X_ORIGINAL_URL"] + + $relativeUrl = $fullOriginalUrl.Replace("$pathInfoUrl", "") + + if ($relativeUrl -like "*/*") { + $depth = @($relativeUrl -split "/" -ne "").Count - 1 + if ($depth -eq 0) { $depth = 1 } + } else { + $depth = 0 + } + + } + if ($item.Content) { + + $content = if (-not $item.Content.Contains(" ")) { + # Treat compressed + Expand-Data -CompressedData $item.Content + } else { + $item.Content + } + $content = if (-not $Content.Contains("<")) { + # Treat as markdown + ConvertFrom-Markdown -Markdown $content + } else { + # Treat as HTML + $content + } + $hasContent = $true + $page += $content + } + + + + if ($item.Video) { + $hasContent = $true + $page += "<br/>$(Write-Link $item.Video)<br/><br/>" | New-Region -Style @{'text-align'='center'} + + } + + if ($item.ItemId) { + $hasContent = $true + $part,$row = $item.ItemId -split ":" + $page += Get-AzureTable -TableName $table -Partition $part -Row $row | + ForEach-Object $unpackItem| + Out-HTML -ItemType { + $_.pstypenames | Select-Object -Last 1 + } + } + + + + if ($item.Related) { + $hasContent = $true + $page += + ((ConvertFrom-Markdown -Markdown $item.Related) -replace "\<a href", "<a class='RelatedLink' href") | + New-Region -Style @{'text-align'='right';'padding'='10px'} + $page += @' +<script> +$('.RelatedLink').button() +</script> +'@ + + } + if ($item.Next -or $item.Previous) { + $hasContent = $true + $previousChunk = + if ($item.Previous) { + $previousCaption = "<span class='ui-icon ui-icon-seek-prev'> + </span> + <br/> + <span style='text-align:center'> + Last + </span>" + + Write-Link -Caption $previousCaption -Url $item.Previous -Button + } else { + "" + } + + $nextChunk = + if ($item.Next) { + $nextCaption = "<span class='ui-icon ui-icon-seek-next'> + </span> + <br/> + <span style='text-align:center'> + Next + </span>" + Write-Link -Caption $nextCaption -Url $item.Next -Button + } else { + "" + } + $page+= " +<table style='width:100%'> + <tr> + <td style='50%;text-align:left'> + $previousChunk + </td> + <td style='50%;text-align:right'> + $nextChunk + </td> + <tr> +</table>" + } + + if ($item.Subtopic) { + + } + + + if (-not $hasContent) { + $page += $item | + Out-HTML -ItemType { $_.pstypenames | Select-Object -Last 1 } + } + + + if ($pipeworksManifest.Facebook.AppId) { + $page += "<div class='fb-like' data-send='true' data-width='450' data-show-faces='true'></div> <BR/>" + + "<div class='fb-comments' data-href='$fullOriginalUrl' data-num-posts='2' data-width='470'></div>" + + } + + $page + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/Product</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Product</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + + + +$item = $_ + +$productImage = if ($item.Image) { + if (@($item.Image).Count -gt 1) { + foreach ($img in $item.Image) { + "<img class='product-image' src='$($img)' style='float:right' /> + <br style='clear:both' />" + } + } else { + "<img class='product-image' src='$($item.Image)' style='float:right' />" + } +} elseif ($item.ImageUrls) { + foreach ($img in $item.ImageUrls) { + foreach ($splitImg in ($img -split ' ' -ne '')) { + "<img class='product-image' src='$($splitImg)' style='float:right' /> + <br style='clear:both' />" + } + } +} elseif ($item.Image_Urls) { + foreach ($img in $item.Image_Urls) { + foreach ($splitImg in ($img -split ' ' -ne '')) { + "<img class='product-image' src='$($splitImg)' style='float:right' /> + <br style='clear:both' />" + } + } +} else { + $null + +} + +$productName = if ($item.Name) { + $item.Name +} elseif ($item.Product_Name) { + $item.Product_Name +} else { + "" +} + + +$hideBrandIfRedundant = if ($productName -and + $item.Brand -and + $productName.ToLower().StartsWith($item.Brand.Tolower())) { + "display:none" +} else { + "" +} + +" +<div class='product' itemscope='' itemtype='http://schema.org/Product'> + <h2 class=`'ui-widget-header`'> + $(if ($item.Url) { "<a href='$($item.Url)' itemprop='url'>"}) + $(if ($item.Brand -and -not $hideBrandIfRedunant) {"<span itemprop='brand' style='$hideBrandIfRedunant'>$($_.Brand)</span>" }) + <span class='product-title' itemprop=`'name`'>$productName</span> + $(if ($item.Url) { "</a>"})</h2> + $(if ($item.Manufacturer) {"by <span style='font-size:small' itemprop='manufacturer'>$($_.Manufacturer)</span> <br/>" }) + <p class=`'ProductDescription`'> + $(if ($productImage) { + if (@($productImage).Count -gt 1) { + $layers = @{} + $layerOrder = @() + + foreach ($pi in $productImage) { + $layers["Layer$($layerOrder.Count + 1)"] = $pi + $layerOrder += "Layer$($layerOrder.Count + 1)" + } + + if ($pipeworksManifest.UseBootstrap -or $pipeworksManifest.UseJQueryUI) { + New-Region -AsSlideShow -UseDotInsteadOfName -Layer $layers -Order $layerOrder -LayerID "ProductImage_$(Get-Random)" -Style @{ + 'float' = 'right' + 'max-width' = '50%' + } + } else { + $productImage -join ([Environment]::NewLine)} + + } else { + $productImage + } + }) + <span itemprop=`'description`' >$($_.Description)</span> + + </p> + $( + $itemPrice = 0 + $offerList = @() + if ($item.Offers) { + foreach ($o in $item.offers) { + + $OLink = '' + $OLinkStart = '' + $OLinkEnd = '' + $doubleO = $o -as [Double] + if ($doubleO) { + $itemPrice= $doubleO + "<span class='product-price' itemprop='offer'>${doubleO}</span>" + } else { + + if ($o.Price -and $o.Name) { + if ($o.Url) { + + $OLinkStart = '<a href="' + $o.Url + '">' + $OLinkEnd = '</a>' + } + $offerList += "<tr><td style='font-size:1.33em'>$OLinkStart$($o.Price)$OLinkEnd</td>" + $offerList += "<td style='font-size:1.66em'>$OLinkStart$($o.Name)$OLinkEnd</td></tr>" + } + + } + } + } + + if ($offerList) { + "<table> + $($offerList -join ([Environment]::NewLine)) + </table>" + } + + if ($itemPrice -and $pipeworksManifest -and $PipeworksManifest.PaymentProcessing.PaypalEmail) { + Write-Link -ItemName "$($item.Name)" -ItemPrice $itemPrice -PaypalEmail $PipeworksManifest.PaymentProcessing.PaypalEmail + } + + if ($itemPrice -and $pipeworksManifest -and $PipeworksManifest.AmazonPaymentsAccountId -and $PipeworksManifest.AmazonAccessKey ) { + Write-Link -ItemName "$($item.Name)" -ItemPrice $itemPrice -AmazonPaymentsAccountId $PipeworksManifest.AmazonPaymentsAccountId -AmazonAccessKey $PipeworksManifest.AmazonAccessKey + } + + if ($itemPrice -and + $pipeworksManifest -and + $pipeworksManifest.PaymentProcessing.Stripe -and + $pipeworksManifest.PaymentProcessing.Stripe.PublishableKey -and + $pipeworksManifest.PaymentProcessing.Stripe.PrivateKeySetting) { + + if ($request["StripeToken"]) { + Get-Paid -StripeToken $request["StripeToken"] -StripeKeySetting $pipeworksManifest.PaymentProcessing.Stripe.PrivateKeySetting + + } else { + Write-Link -ItemName "$($item.Name)" -ItemPrice $itemPrice -StripePublishableKey $pipeworksManifest.PaymentProcessing.Stripe.PublishableKey + } + + } + + if ($itemPrice -and $pipeworksManifest -and $PipeworksManifest.GoogleMerchantId) { + '<div role="button" alt="Add to cart" tabindex="0" class="googlecart-add-button"> + </div>' + (Write-Link -ItemName "$($item.Name)" -ItemPrice $itemPrice -GoogleCheckoutMerchantId $PipeworksManifest.GoogleMerchantId) + } + + if ($itemPrice) { + '<br/>' + } + + + if ($item.Quantity) { + " + <div style='float:right'> + <span style='font-size:1.33em' itemprop='Quantity'>$($item.Quantity)</span> In Stock + </div> + <br style='clear:both'> + " + } + + + ) +</div> +"</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/UserInteraction</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/UserInteraction</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $interaction = $_ + + " + <div itemscope='' itemtype='http://schema.org/UserInteraction'> + <div style='float:left;font-size:1.33em'> + $(if ($interaction.Count) { + $interaction.Count + " " + }) + $($interaction.Name) + </div> + </div> + " + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + <View> + <Name>http://schema.org/Thing</Name> + <ViewSelectedBy> + <TypeName>http://schema.org/Thing</TypeName> + </ViewSelectedBy> + <CustomControl> + <CustomEntries> + <CustomEntry> + <CustomItem> + <Frame> + <CustomItem> + <ExpressionBinding> + <ScriptBlock> + $thing= $_ + + " + <div itemscope='' itemtype='http://schema.org/Thing'> + <div style='float:left;'> + + $(if ($thing.Url) { + Write-Link -Url $thing.Url -ItemProp url -Caption "<span style='font-size:1.33em' itemprop='name'>$($thing.Name)</span>" + } else { + "<span style='font-size:1.33em' itemprop='name'>$($thing.Name)</span>" + }) + + + </div> + $(if ($thing.Description) { + "<div style='float:right' itemprop='description'> + $($thing.Description) + </div>" + }) + </div> + " + +</ScriptBlock> + </ExpressionBinding> + </CustomItem> + </Frame> + </CustomItem> + </CustomEntry> + </CustomEntries> + </CustomControl> + </View> + </ViewDefinitions> +</Configuration> diff --git a/Pipeworks.Pipeworks.psd1 b/Pipeworks.Pipeworks.psd1 new file mode 100644 index 0000000..3485346 Binary files /dev/null and b/Pipeworks.Pipeworks.psd1 differ diff --git a/Pipeworks.Scriptcop.psd1 b/Pipeworks.Scriptcop.psd1 new file mode 100644 index 0000000..6a2bb11 Binary files /dev/null and b/Pipeworks.Scriptcop.psd1 differ diff --git a/Pipeworks.VersionHistory.psd1 b/Pipeworks.VersionHistory.psd1 new file mode 100644 index 0000000..128f54d Binary files /dev/null and b/Pipeworks.VersionHistory.psd1 differ diff --git a/Pipeworks.psd1 b/Pipeworks.psd1 new file mode 100644 index 0000000..563f8fc --- /dev/null +++ b/Pipeworks.psd1 @@ -0,0 +1,12 @@ +@{ + ModuleVersion = '1.9.9.4' + ModuleToProcess = 'Pipeworks.psm1' + FormatsToProcess = 'Pipeworks.Format.ps1xml' + Copyright = 'Copyright 2012-2014 Start-Automating' + Guid = 'fe3a0426-9f76-488d-b1ee-c5c409d795ad' + Author = 'Start-Automating' + Description = 'Putting it all together with PowerShell' + VariablesToExport = '*' + CmdletsToExport = '*' + CompanyName='Start-Automating' +} diff --git a/Pipeworks.psm1 b/Pipeworks.psm1 new file mode 100644 index 0000000..75bc2ca Binary files /dev/null and b/Pipeworks.psm1 differ diff --git a/Publish-AzureService.ps1 b/Publish-AzureService.ps1 new file mode 100644 index 0000000..0513110 Binary files /dev/null and b/Publish-AzureService.ps1 differ diff --git a/Publish-Deployment.ps1 b/Publish-Deployment.ps1 new file mode 100644 index 0000000..1326b21 Binary files /dev/null and b/Publish-Deployment.ps1 differ diff --git a/Push-Deployment.ps1 b/Push-Deployment.ps1 new file mode 100644 index 0000000..15d1939 --- /dev/null +++ b/Push-Deployment.ps1 @@ -0,0 +1,264 @@ +function Push-Deployment +{ + <# + .Synopsis + Pushes a deployment to Azure + .Description + Pushes an existing deployment to an Azure service + .Example + Push-Deployment "StartAutomating" ".\startautomating.cspkg" ".\startautomating.cscfg" -FirstLabel Start-Scripting -Second Update-Web + .Link + Get-Deployment + .Link + Import-Deployment + .Link + Remove-Deployment + #> + [CmdletBinding(DefaultParameterSetName='PushAzureDeployment')] + [OutputType([Nullable])] + param( + # The name of the service + [Parameter(Mandatory=$true,ParameterSetName='PushAzureDeployment',ValueFromPipelineByPropertyName=$true)] + [string] + $ServiceName, + + # The path to the CSPackage (.cspkg) file + [Parameter(Mandatory=$true,ParameterSetName='PushAzureDeployment',ValueFromPipelineByPropertyName=$true)] + [string] + $PackagePath, + + # The path to the CSConfigurationFile (.cscfg) file + [Parameter(Mandatory=$true,ParameterSetName='PushAzureDeployment',ValueFromPipelineByPropertyName=$true)] + [string] + $ConfigurationPath, + + # The label of the first deployment slot + [Parameter(ParameterSetName='PushAzureDeployment',ValueFromPipelineByPropertyName=$true)] + [string] + $FirstLabel = "Primary", + + # The label of the second deployment slot + [Parameter(ParameterSetName='PushAzureDeployment',ValueFromPipelineByPropertyName=$true)] + [string] + $SecondLabel = "Secondary", + + # The name of the storage account that will contain the bits + [Parameter(Mandatory=$true,ParameterSetName='PushToAzureVMs')] + [string] + $StorageAccount, + + # The storage key of the storage account that will contain the bits + [Parameter(Mandatory=$true,ParameterSetName='PushToAzureVMs')] + [string] + $StorageKey, + + # If set, will push the deployment to Azure VMs + [Parameter(Mandatory=$true,ParameterSetName='PushToAzureVMs')] + [Switch] + $ToAzureVM, + + # The name of the computers that will receive the deployment + [Parameter(ParameterSetName='PushToAzureVMs')] + [string[]] + $ComputerName, + + + # The name of the computers that will receive the deployment + [Parameter(Mandatory=$true,ParameterSetName='PushToAzureVMs')] + [Management.Automation.PSCredential] + $Credential + + + ) + + process { + if ($PSCmdlet.ParameterSetName -eq 'PushAzureDeployment') { + #region Push a deployment package to Azure + $azureModuleInstalled = Import-Module Azure -Global -PassThru + if (-not $azureModuleInstalled) { + Write-Error "Must install Azure module" + return + } + $currentDeployment = Get-AzureDeployment -ServiceName $ServiceName + + $newlabel = if ($currentDeployment.label -ne $FirstLabel) { + $FirstLabel + } else { + $SecondLabel + } + + $resolvedPackagePath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($PackagePath) + if (-not $resolvedPackagePath) { return } + $resolvedConfigPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($ConfigurationPath) + if (-not $resolvedConfigPath) { return } + + Remove-AzureDeployment -ServiceName $ServiceName -Slot Staging -Force -ErrorAction SilentlyContinue + + $deploymentParameters = @{ + Package= "$resolvedPackagePath" + Configuration = "$resolvedConfigPath" + Label = $newLabel + } + + + New-AzureDeployment @deploymentParameters -ServiceName $ServiceName -Slot Staging + #endregion Push a deployment package to Azure + } elseif ($pscmdlet.ParameterSetName -eq 'PushToAzureVMs') { + #region Push a deployment stored in blob storage into Azure VMs + + $params = @{} + $PSBoundParameters +$sb = { + Add-Type -AssemblyName System.Web + +}.ToString() + @" +function Expand-Zip { + $((Get-Command Expand-Zip).Definition) +} + +function Get-Web { + $((Get-Command Get-Web).Definition) +} + +function Import-Blob { + $((Get-Command Import-Blob).Definition) +} + +function Get-Blob { + $((Get-Command Get-Blob).Definition) +} +"@ + + +$sb = [ScriptBlock]::Create($sb) + + +$syncScript = " +`$null = New-module -Name Pipeworks -ScriptBlock {$sb} +`$storageAccount = '$storageAccount' +`$storageKey = '$storageKey' +" +$syncScript += { +# Move modules to old modules + + +Get-Blob -StorageAccount $StorageAccount -StorageKey $StorageKey | + Where-Object {$_.Container -like "*-source" } | + ForEach-Object { + + $innerData = $_ | + Get-Blob | + Sort-Object { $_.LastModified } -Descending | + Select-Object -First 1 | + Get-Blob + + + + $tempDir = [IO.Path]::GetTempPath() + $theFile = Join-Path $tempDir ($innerData.Container + $innerData.Name) + $theTempDir = Join-Path $tempDir $innerData.Container + + [IO.FILE]::WriteAllBytes("$theFile", $innerData.BlobData) + + + + + Expand-Zip -ZipPath "$theFile" -OutputPath $theTempDir + + $moduleDir = + dir $theTempDir -Recurse -Filter "$($innerData.Container.Replace("-source", '')).psm1" | + Split-Path | + Get-Item + + + + if (-not $moduleDir) { return } + $destModuleDir = "$env:UserProfile\Documents\WindowsPowerShell\Modules\$($moduleDir.Name.Replace('-source', ''))" + + + if (Test-Path $destModuleDir) { + $destModuleDir | Remove-Item -Recurse -Force + } + + $null = New-Item -ItemType Directory -path $destModuleDir -ErrorAction SilentlyContinue + + + + $moduleName = $moduleDir | Split-Path -Leaf + + $dir = Get-Item $destModuleDir + $filesExist = dir $moduleDir -Recurse -Force | + Where-Object { -not $_.psIsContainer } + + if ($filesExist) { + Move-Item "$env:UserProfile\Documents\WindowsPowerShell\Modules\$moduleName" "$env:UserProfile\Documents\WindowsPowerShell\Modules.Old.$((Get-Date).ToShortDateString().Replace('/','-'))" -ErrorAction SilentlyContinue -Force + + $filesExist | + ForEach-Object { + $_ | + Copy-Item -Destination { + $newPath = $_.FullName.Replace("$($moduleDir.Fullname)", "$($dir.FullName)") + + $newDir = $newPAth |Split-Path + if (-not (Test-Path $newDir)) { + $null = New-Item -ItemType Directory -Path "$newDir" -Force + } + + + Write-Progress "Copying $($req.name)" "$newPath" + $newPath + + } -Force + } + } + + + + Remove-Item -LiteralPath $theTempDir -Force -Recurse + + + + + + + + + + + } + + + + #Move-Item "$home\Documents\WindowsPowerShell\SyncedModules" "$home\Documents\WindowsPowerShell\SyncedModules" + +} + + +$syncScript = [ScriptBlock]::Create($syncScript) +Get-AzureVM | + Where-Object { + $comp = $_ + if ($params.ComputerName) { + foreach ($cn in $ComputerName) { + if ($comp.Name -like $cn) { + return $true + } + } + } else { + return $true + } + } | + ForEach-Object { + Invoke-Command -ComputerName "$($_.Name).cloudapp.net" -Credential $Credential -Authentication Credssp -ScriptBlock $syncScript -AsJob -JobName $_.Name + } + #endregion Push a deployment stored in blob storage into Azure VMs + } elseif ($PSCmdlet.ParameterSetName -eq 'PushToLan') { + + + + + } + + + } +} + diff --git a/Push-FTP.ps1 b/Push-FTP.ps1 new file mode 100644 index 0000000..8808b3a Binary files /dev/null and b/Push-FTP.ps1 differ diff --git a/ReadMe.md b/ReadMe.md new file mode 100644 index 0000000..5df8e3b --- /dev/null +++ b/ReadMe.md @@ -0,0 +1,81 @@ + + +[Official Website](http://PowerShellPipeworks.com) + + + + +PowerShell Pipeworks is a framework for writing Sites and Software Services in Windows PowerShell modules. + + +Pipeworks provides powerful tools to write websites in PowerShell. + + +It helps you pipe together tons of tech to write smarter software as a service. + + + +The easiest way to explore pipeworks to to visit the web site [http://powershellpipeworks.com/](http://powershellpipeworks.com), or to build yourself a local copy. + + +To do this, make sure you: + +1. Download and Install PowerShell Pipeworks +2. Enable IIS and ASP.NET +3. Install the [IIS URL Rewrite](http://www.iis.net/downloads/microsoft/url-rewrite) extension +4. Change your local PowerShell execution policy to Bypass (Set-ExecutionPolicy Bypass -Force) + + +Once you've done this, just open up PowerShell and run these three lines to create your local Pipeworks site and run it. + + + Import-Module Pipeworks + ConvertTo-ModuleService Pipeworks -AllowDownload -Force + Start-Process http://localhost/pipeworks + + + +#### Getting Started + + +* [About PowerShell Pipeworks](http://PowerShellPipeworks.com/About PowerShell Pipeworks/) +* [Pipeworks Quickstart](http://PowerShellPipeworks.com/Pipeworks Quickstart/) +* [The Pipeworks Manifest](http://PowerShellPipeworks.com/The Pipeworks Manifest/) +* [From Script To Software Service](http://PowerShellPipeworks.com/From Script To Software Service/) +* [What Pipeworks Does](http://PowerShellPipeworks.com/What Pipeworks Does/) +* [Scripting Securely with SecureSettings](http://PowerShellPipeworks.com/Scripting Securely with SecureSettings/) +* [NOHtml Sites](http://PowerShellPipeworks.com/NOHtml Sites/) + +#### Play with Pipeworks + + +* [Write-ASPDotNetScriptPage](http://PowerShellPipeworks.com/Write-ASPDotNetScriptPage/) +* [ConvertFrom-Markdown](http://PowerShellPipeworks.com/ConvertFrom-Markdown/) +* [Making Editing Easier With Markdown](http://PowerShellPipeworks.com/Making Editing Easier With Markdown/) +* [Write-ScriptHTML](http://PowerShellPipeworks.com/Write-ScriptHTML/) +* [Making Tables with Out-HTML](http://PowerShellPipeworks.com/Making Tables with Out-HTML/) +* [Working with Write-Link](http://PowerShellPipeworks.com/Working with Write-Link/) + +#### Connecting the Clouds + + +* [Get-Paid with Stripe](http://PowerShellPipeworks.com/Get-Paid with Stripe/) +* [Getting GitIt](http://PowerShellPipeworks.com/Getting GitIt/) +* [Get-Web Content From Anywhere](http://PowerShellPipeworks.com/Get-Web Content From Anywhere/) +* [Pick up the Phone with Pipeworks](http://PowerShellPipeworks.com/Pick up the Phone with Pipeworks/) +* [Implicit Texting with Twilio](http://PowerShellPipeworks.com/Implicit Texting with Twilio/) +* [The Wonders of Wolfram Alpha](http://PowerShellPipeworks.com/The Wonders of Wolfram Alpha/) +* [Using Azure Table Storage in Pipeworks](http://PowerShellPipeworks.com/Using Azure Table Storage in Pipeworks/) +* [Simplified SQL](http://PowerShellPipeworks.com/Simplified SQL/) +* [Building with Blob Storage](http://PowerShellPipeworks.com/Building with Blob Storage/) +* [Publishing Pipeworks to Azure](http://PowerShellPipeworks.com/Publishing Pipeworks to Azure/) +* [Looking Up Locations With Resolve-Location](http://PowerShellPipeworks.com/Looking Up Locations With Resolve-Location/) + +#### Join Windows and Web + + +* [Why Windows](http://PowerShellPipeworks.com/Why Windows/) +* [Scripting with Superglue](http://PowerShellPipeworks.com/Scripting with Superglue/) +* [Integrated Intranet](http://PowerShellPipeworks.com/Integrated Intranet/) +* [Simpler SEO](http://PowerShellPipeworks.com/Simpler SEO/) + diff --git a/Remove-Daemon.ps1 b/Remove-Daemon.ps1 new file mode 100644 index 0000000..81875a7 Binary files /dev/null and b/Remove-Daemon.ps1 differ diff --git a/Remove-Deployment.ps1 b/Remove-Deployment.ps1 new file mode 100644 index 0000000..8d61a6c Binary files /dev/null and b/Remove-Deployment.ps1 differ diff --git a/Remove-EC2.ps1 b/Remove-EC2.ps1 new file mode 100644 index 0000000..9e74c00 Binary files /dev/null and b/Remove-EC2.ps1 differ diff --git a/Remove-EC2KeyPair.ps1 b/Remove-EC2KeyPair.ps1 new file mode 100644 index 0000000..e49ed3d Binary files /dev/null and b/Remove-EC2KeyPair.ps1 differ diff --git a/Remove-EC2SecurityGroup.ps1 b/Remove-EC2SecurityGroup.ps1 new file mode 100644 index 0000000..dfec736 Binary files /dev/null and b/Remove-EC2SecurityGroup.ps1 differ diff --git a/Remove-SQL.ps1 b/Remove-SQL.ps1 new file mode 100644 index 0000000..a6d3188 --- /dev/null +++ b/Remove-SQL.ps1 @@ -0,0 +1,224 @@ +function Remove-SQL +{ + <# + .Synopsis + Removes SQL data + .Description + Removes SQL data or databases + .Example + Remove-Sql -TableName ATable -ConnectionSetting SqlAzureConnectionString + .Example + Remove-Sql -TableName ATable -Where 'RowKey = 1' -ConnectionSetting SqlAzureConnectionString + .Example + Remove-Sql -TableName ATable -Clear -ConnectionSetting SqlAzureConnectionString + .Link + Add-SqlTable + .Link + Update-SQL + #> + [CmdletBinding(DefaultParameterSetName='DropTable',SupportsShouldProcess=$true,ConfirmImpact='High')] + [OutputType([nullable])] + param( + # The table containing SQL results + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true)] + [Alias('Table','From', 'Table_Name')] + [string]$TableName, + + + # The where clause. Beware: different SQL engines treat this differently. For instance, SQL server Compact requires the format: + # ([RowName] = 'Value') + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true,ParameterSetName='DeleteRows')] + [string]$Where, + + # The set of specific rows to be deleted + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true,ParameterSetName='DeleteRowBatch')] + [string[]]$WhereIn, + + # The name of the properties + [Parameter(Mandatory=$true,Position=2,ValueFromPipelineByPropertyName=$true,ParameterSetName='DeleteRowBatch')] + [string]$PropertyName, + + # If set, will clear the table's contents, but will not remove the table. + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true,ParameterSetName='ClearTable')] + [Switch]$Clear, + + # A connection string or setting. + [Alias('ConnectionString', 'ConnectionSetting')] + [string]$ConnectionStringOrSetting, + + # The name of the SQL server. This is used with a database name to craft a connection string to SQL server + [string] + $Server, + + # The database on a SQL server. This is used with the server name to craft a connection string to SQL server + [string] + $Database, + + # If set, will output the SQL, instead of executing the remove. + [Switch] + $OutputSql, + + # If set, will use SQL server compact edition + [Switch] + $UseSQLCompact, + + # The path to SQL Compact. If not provided, SQL compact will be loaded from the GAC + [string] + $SqlCompactPath, + + # If set, will use SQL lite + [Alias('UseSqlLite')] + [switch] + $UseSQLite, + + # The path to SQL Lite. If not provided, SQL compact will be loaded from Program Files + [string] + $SqlitePath, + + # The path to a SQL compact or SQL lite database + [Alias('DBPath')] + [string] + $DatabasePath + ) + + begin { + #region Resolve Connection String + if ($PSBoundParameters.ConnectionStringOrSetting) { + if ($ConnectionStringOrSetting -notlike "*;*") { + $ConnectionString = Get-SecureSetting -Name $ConnectionStringOrSetting -ValueOnly + } else { + $ConnectionString = $ConnectionStringOrSetting + } + $script:CachedConnectionString = $ConnectionString + } elseif ($psBoundParameters.Server -and $psBoundParameters.Database) { + $ConnectionString = "Server=$Server;Database=$Database;Integrated Security=True;" + $script:CachedConnectionString = $ConnectionString + } elseif ($script:CachedConnectionString){ + $ConnectionString = $script:CachedConnectionString + } else { + $ConnectionString = "" + } + #endregion Resolve Connection String + + # Exit if we don't have a connection string, + # and are not using SQLite or SQLCompact (which don't need one) + if (-not $ConnectionString -and -not ($UseSQLite -or $UseSQLCompact)) { + throw "No Connection String" + return + } + + #region If we're not just going to output SQL, we might as well connect + if (-not $OutputSQL) { + if ($UseSQLCompact) { + # If we're using SQL compact, make sure it's loaded + if (-not ('Data.SqlServerCE.SqlCeConnection' -as [type])) { + if ($SqlCompactPath) { + $resolvedCompactPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($SqlCompactPath) + $asm = [reflection.assembly]::LoadFrom($resolvedCompactPath) + } else { + $asm = [reflection.assembly]::LoadWithPartialName("System.Data.SqlServerCe") + } + } + # Find the absolute path + $resolvedDatabasePath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($DatabasePath) + # Craft a connection string + $sqlConnection = New-Object Data.SqlServerCE.SqlCeConnection "Data Source=$resolvedDatabasePath" + # Open the DB + $sqlConnection.Open() + } elseif ($UseSqlite) { + # If we're using SQLite, make sure it's loaded + if (-not ('Data.Sqlite.SqliteConnection' -as [type])) { + if ($sqlitePath) { + $resolvedLitePath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($sqlitePath) + $asm = [reflection.assembly]::LoadFrom($resolvedLitePath) + } else { + $asm = [Reflection.Assembly]::LoadFrom("$env:ProgramFiles\System.Data.SQLite\2010\bin\System.Data.SQLite.dll") + } + } + + # Find the absolute path + $resolvedDatabasePath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($DatabasePath) + # Craft a connection string + $sqlConnection = New-Object Data.Sqlite.SqliteConnection "Data Source=$resolvedDatabasePath" + # Open the DB + $sqlConnection.Open() + + } else { + # We're using SQL server (or SQL Azure), just use the connection string we've got + $sqlConnection = New-Object Data.SqlClient.SqlConnection "$connectionString" + # Open the DB + $sqlConnection.Open() + } + + + } + #endregion If we're not just going to output SQL, we might as well connect + } + + process { + if ($TableName -and $where) { + # If we know a table and a where clause, craft the SQL + $sqlStatement = "DELETE FROM $tableName WHERE $where".TrimEnd("\").TrimEnd("/") + } elseif ($clear) { + # If we're going to clear the table.. + $sqlStatement = if ($UseSQLCompact -or $UseSQLite) { + # Use DELETE FROM on SqlCompact or SqLite + "DELETE FROM $tableName" + } else { + # Use Truncate Table on SQL Server + "TRUNACATE TABLE $tableName" + } + + } elseif ($tableNAme -and $wherein -and $PropertyName) { + + # We're deleting a batch of items, use WHERE ... IN + $sqlStatement = + "DELETE FROM $TableName WHERE $PropertyName IN ('$(($WhereIn | + Foreach-Object { + $_.Replace("'", "''") + })-join "','")')" + + } else { + + # We're removing the whole table, use DROP TABLE + $sqlStatement = "DROP TABLE $tableName" + } + + if ($outputSql) { + # If we're outputting SQL, just output it and be done + $sqlStatement + } elseif (-not $outputSql -and $psCmdlet.ShouldProcess($sqlStatement)) { + # If we're not, be so nice as to use ShouldProcess first to confirm + Write-Verbose "$sqlStatement" + #region Execute SQL Statement + if ($UseSQLCompact) { + $sqlAdapter = New-Object "Data.SqlServerCE.SqlCeDataAdapter" $sqlStatement, $sqlConnection + $dataSet = New-Object Data.DataSet + $rowCount = $sqlAdapter.Fill($dataSet) + } elseif ($UseSQLite) { + $sqliteCmd = New-Object Data.Sqlite.SqliteCommand $sqlStatement, $sqlConnection + $rowCount = $sqliteCmd.ExecuteNonQuery() + } else { + $sqlAdapter= New-Object "Data.SqlClient.SqlDataAdapter" ($sqlStatement, $sqlConnection) + $sqlAdapter.SelectCommand.CommandTimeout = 0 + $dataSet = New-Object Data.DataSet + $rowCount = $sqlAdapter.Fill($dataSet) + + } + #endregion Execute SQL Statement + } + + } + + end { + + #region If a SQL connection exists, close it and Dispose of it + if ($sqlConnection) { + $sqlConnection.Close() + $sqlConnection.Dispose() + } + #endregion If a SQL connection exists, close it and Dispose of it + + } +} + diff --git a/Remove-SecureSetting.ps1 b/Remove-SecureSetting.ps1 new file mode 100644 index 0000000..dfc89f2 Binary files /dev/null and b/Remove-SecureSetting.ps1 differ diff --git a/Request-CommandInput.ps1 b/Request-CommandInput.ps1 new file mode 100644 index 0000000..aa0a8b8 --- /dev/null +++ b/Request-CommandInput.ps1 @@ -0,0 +1,1331 @@ +function Request-CommandInput +{ + <# + .Synopsis + Generates a form to collect input for a command + .Description + Generates a form to collect input for a PowerShell command. + + + Get-WebInput is designed to handle the information submitted by the user in a form created with Request-CommandInput. + .Link + Get-WebInput + .Example + Request-CommandInput -CommandMetaData (Get-Command Get-Command) -DenyParameter ArgumentList + #> + [CmdletBinding(DefaultParameterSetName='ScriptBlock')] + [OutputType([string])] + param( + # The metadata of the command. + [Parameter(Mandatory=$true,ParameterSetName='Command',ValueFromPipeline=$true)] + [Management.Automation.CommandMetaData] + $CommandMetaData, + + # A script block containing a PowerShell function. Any code outside of the Powershell function will be ignored. + [Parameter(Mandatory=$true,ParameterSetName='ScriptBlock')] + [ScriptBlock] + $ScriptBlock, + + # The name of the parameter set to request input + [string] + $ParameterSet, + + # Explicitly allowed parameters (by default, all are allowed unless they are explictly denied) + [string[]] + $AllowedParameter, + + # Explicitly denied parameters. + [Alias('HideParameter')] + [string[]] + $DenyParameter, + + # The text on the request button + [string] + $ButtonText, + + # The url to a button image button + [string] + $ButtonImage, + + # If set, does not display a button + [switch] + $NoButton, + + # The order of items + [string[]] + $Order, + + # The web method the form will use + [ValidateSet('POST','GET')] + [string] + $Method = "POST", + + # The css margin property to use for the form + [string] + $Margin = "1%", + + # The action of the form + [string] + $Action, + + # The platform the created input form will work. + # This is used to created an XML based layout for any device + [ValidateSet('Web', 'Android', 'AndroidBackend', 'CSharpBackEnd', 'iOS', 'WindowsMobile', 'WindowsMetro', 'Win8', 'WPF', 'GoogleGadget', 'TwilML', 'PipeworksDirective')] + [string] + $Platform = 'Web', + + # If set, uses a Two column layout + [Switch] + $TwoColumn, + + # If provided, focuses a given parameter's input + [string] + $Focus, + + # If provided, uses the supplied values as parameter defaults + [Alias('ParameterDefaultValues')] + [Hashtable] + $ParameterDefaultValue = @{}, + + # If provided, uses the supplied values as potential options for a parameter + [Alias('ParameterOptions')] + [Hashtable] + $ParameterOption = @{}, + + # If set, will load the inner control with ajax + [Switch] + $Ajax + ) + + begin { + $allPipeworksDirectives = @{} + $firstcomboBox = $null + Add-Type -AssemblyName System.Web + function ConvertFrom-CamelCase + { + param([string]$text) + + $r = New-Object Text.RegularExpressions.Regex "[a-z][A-Z]", "Multiline" + $matches = @($r.Matches($text)) + $offset = 0 + foreach ($m in $matches) { + $text = $text.Insert($m.Index + $offset + 1," ") + $offset++ + } + $text + } + + + + + + function New-TextInput($defaultNumberOfLines, [switch]$IsNumber, [string]$CssClass,[string]$type) { + $linesForInput = if ($pipeworksDirectives.LinesForInput -as [Uint32]) { + $pipeworksDirectives.LinesForInput -as [Uint32] + } else { + $defaultNumberOfLines + } + + $columnsForInput = + if ($pipeworksDirectives.ColumnsForInput -as [Uint32]) { + $pipeworksDirectives.ColumnsForInput -as [Uint32] + } else { + if ($Request -and $Request['Snug']) { + 30 + } else { + 60 + } + + } + + + if ($Platform -eq 'Web') { + if ($pipeworksDirectives.ContentEditable) { + "<div id='${ParameterIdentifier}_Editable' style='width:90%;padding:10px;margin:3px;min-height:5%;border:1px solid' contenteditable='true' designMode='on'>$(if ($pipeworksDirectives.Default) { "<i>" + $pipeworksDirectives.Default + "</i>" })</div> + + + <input type='button' id='${inputFieldName}saveButton' value='Save' onclick='save${inputFieldName};' /> + <input type='button' id='${inputFieldName}clearButton' value='Clear' onclick='clear${inputFieldName};' /> + <input type='hidden' id='$parameterIdentifier' name='$inputFieldName' /> + <script> + `$(function() { + `$( `"#$($inputFieldName)saveButton`" ).button().click( + function(event) { + document.getElementById(`"${ParameterIdentifier}`").value = document.getElementById(`"${ParameterIdentifier}_Editable`").innerHTML; + }); + + `$( `"#$($inputFieldName)clearButton`" ).button().click( + function(event) { + document.getElementById(`"${ParameterIdentifier}`").value = ''; + document.getElementById(`"${ParameterIdentifier}_Editable`").innerHTML = ''; + }); + }) + </script> + " + + } else { + + if ($LinesForInput -ne 1) { + "<textarea style='width:100%;' $(if ($cssClass) { "class='$cssClass'"}) $(if($type) { "type='$type'" }) name='$inputFieldName' rows='$LinesForInput' cols='$ColumnsForInput'>$($pipeworksDirectives.Default -join ([Environment]::NewLine))</textarea>" + } else { + "<input type='text' style='width:100%' $(if ($cssClass) { "class='$cssClass'"}) $(if($type) { "type='$type'" }) name='$inputFieldName' $(if ($isnumber) {'type=''number'''}) $(if ([Double], [float] -contains $parameterType) {'step=''0.01'''}) value='$($pipeworksDirectives.Default)' />" + } + + if ($cssClass -eq 'dateTimeField') { + "<script> + `$(function() { + `$( `".dateTimeField`" ).datepicker({ + showButtonPanel : true, + changeMonth: true, + changeYear: true, + showOtherMonths: true, + selectOtherMonths: true + }); + }) + </script>" + } + } + } elseif ($Platform -eq 'Android') { + @" +<EditText + android:id="@+id/$parameterIdentifier" + $theDefaultAttributesInAndroidLayout + android:minLines='${LinesForInput}' + $(if ($isnumber) {'type=''number'''}) + $(if ($defaultValue) { "android:text='$defaultValue'" } )/> + +"@ + } elseif ($Platform -eq 'AndroidBackEnd') { + $extractTextFromEditTextAndUseForQueryString + } elseif ($Platform -eq 'CSharpBackEnd') { + $extractTextFromTextBoxAndUseForQueryString + } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) { + if ($DefaultNumberOfLines -eq 1) { + @" +<TextBox + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Margin="7, 5, 7, 5" + x:Name="$parameterIdentifier" + $(if ($defaultValue) { "Text='$defaultValue'" }) + Tag='Type:$($parameterType.Fullname)' /> +"@ + } else { + @" +<TextBox + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Margin="7, 5, 7, 5" + x:Name="$parameterIdentifier" + $(if ($defaultValue) { "Text='$defaultValue'" }) + Tag='Type:$($parameterType.Fullname)' + AcceptsReturn='true' + MinLines='${DefaultNumberOfLines}' /> +"@ + + } + } elseif ('GoogleGadget' -eq $platform) { +@" +<UserPref name="$parameterIdentifier" display_name="$friendlyParameterName" default_value="$($pipeworksDirectives.Default)"/> +"@ + } elseif ('TwilML' -eq $Platform) { + if ($IsNumber) { +@" +<Gather finishOnKey="*" $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" })> + <Say>$([Security.SecurityElement]::Escape($parameterHelp)). Press * when you are done.</Say> +</Gather> +"@ + } elseif ($friendlyParameterName -like "Record*" -or $friendlyParameterName -like "*Recording") { +@" +<Say>$([Security.SecurityElement]::Escape($parameterHelp))</Say> +<Record $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" })> + +</Record> +"@ + + } elseif ($friendlyParameterName -like "Transcribe*" -or $friendlyParameterName -like "*Transcription") { +@" +<Say>$([Security.SecurityElement]::Escape($parameterHelp))</Say> +<Record $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" }) transcribe='true' /> +"@ + + } elseif ($pipeworksDirectives.RecordInput -or $pipeworksDirectives.Record -or $pipeworksDirectives.Recording) { +@" +<Say>$([Security.SecurityElement]::Escape($parameterHelp))</Say> +<Record $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" }) /> +"@ + + } else { +@" +<Say>$([Security.SecurityElement]::Escape($parameterHelp))</Say> +<Record $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" }) transcribe='true' /> +"@ + + } + } + } + + # Some chunks of code are reused so often, they need to be variables + $extractTextFromEditTextAndUseForQueryString = @" + try { + // Cast the item to an EditText control + EditText text = (EditText)foundView; + + // Extract out the value + String textValue = text.getText().toString(); + + // If it is set... + if (textValue != null && textValue.getLength() > 0) { + // Append the & to separate parameters + if (! initializedQueryString) { + initializedQueryString = true; + } else { + queryString.append("&"); + } + + queryString.append(textFieldID); + queryString.append("="); + queryString.append(URLEncoder.encode(textValue)); + } + } catch (Exception e) { + e.printStackTrace(); + } + +"@ + + $extractTextFromTextBoxAndUseForQueryString = @" + try { + // Cast the item to an TextBox control + TextBox text = (TextBox)foundView; + + // Extract out the value + String textValue = text.Text; + + // If it is set... + if (! String.IsNullOrEmpty(textValue)) { + // Append the & to separate parameters + if (! initializedQueryString) { + initializedQueryString = true; + } else { + queryString.Append("&"); + } + + queryString.Append(textFieldID); + queryString.Append("="); + queryString.Append(HttpUtility.UrlEncode(textValue)); + } + } catch (Exception ex) { + throw ex; + } + +"@ + + # Most android UI requires these two lines + $theDefaultAttributesInAndroidLayout = @" + android:layout_width="match_parent" + android:layout_height="wrap_content" +"@ + } + + process { + $carrySnug = if ($Action -and $request -and $request["Snug"] -and $Platform -eq 'Web') { + $true + + } else { + $false + } + # The ScriptBlock parameter set will just take the first command declared within a scriptblock + if ($psCmdlet.ParameterSetName -eq 'ScriptBlock') { + $func = Get-FunctionFromScript -ScriptBlock $ScriptBlock | Select-Object -First 1 + . ([ScriptBlock]::Create($func)) + $matched = $func -match "function ((\w+-\w+)|(\w+))" + if ($matched -and $matches[1]) { + $command=Get-Command $matches[1] + } + $CommandMetaData = [Management.Automation.CommandMetaData]$command + } + + $inputForm = New-Object Text.StringBuilder + $idSafeCommandName = $commandMetaData.Name.Replace('-','') + + # Extract out help + if (-not $script:CachedHelp) { + $script:CachedHelp = @{} + } + + if (-not $script:CachedHelp[$CommandMetaData.Name]) { + $script:CachedHelp[$CommandMetaData.Name] = Get-Help -Name $CommandMetaData.Name + } + + + $help = $script:CachedHelp[$CommandMetaData.Name] + + if ($help -isnot [string]) { + + } + + if (-not $buttonText) { + $ButtonText = ConvertFrom-CamelCase $commandMetaData.Name.Replace('-', ' ') + } + + #region Start of Form + if ($platform -eq 'Web') { + $RandomSalt = Get-Random + $Action =if ($carrySnug) { + if ($Action.Contains("?")) { + $Action+="&snug=$true" + $Action + } else { + $Action+="?snug=$true" + $Action + } + + } else { + $Action + } + $cssBaseName = "$($commandMetaData.Name)_Input" + # If the platform is web, it's a <form> input + $null = $inputForm.Append(" +<div class='$($commandMetadata.Name)_InlineOutputContainer' id='$($commandMetaData.Name)_InlineOutputContainer_$RandomSalt' style='margin-top:3%;margin-bottom:3%' > +</div> +<form method='$method' $(if ($action) {'action=`"' + $action + '`"' }) class='$cssBaseName' id='${cssBaseName}_$RandomSalt' enctype='multipart/form-data'> + <div style='border:0px'> + <style> + textarea:focus, input:focus { + border: 2px solid #009; + } + </style>") + } elseif ($Platform -eq 'Android') { + + # If the platform is Android, it's a ViewSwitcher containing a ScrollView containing a LinearLayout + $null = $inputForm.Append(@" +<ViewSwitcher xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/$($idSafeCommandName + '_Switcher')" + $theDefaultAttributesInAndroidLayout > + <ScrollView + android:id="@+id/$($idSafeCommandName + '_ScrollView')" + $theDefaultAttributesInAndroidLayout> + <LinearLayout + $theDefaultAttributesInAndroidLayout + android:orientation="vertical" > + +"@) + } elseif ($Platform -eq 'AndroidBackEnd') { + # In an android back end, create a class contain a static method to collect the parameters + $null = $inputForm.Append(@" + public String Get${IdSafeCommandName}QueryString() { + // Save getResources() and getPackageName() so that each lookup is slightly quicker + Resources allResources = getResources(); + String packageName = getPackageName(); + StringBuilder queryString = new StringBuilder(); + Boolean initializedQueryString = false; + Object foundView; + String textFieldID; +"@) + } elseif ($Platform -eq 'CSharpBackEnd') { + # In an android back end, create a class contain a static method to collect the parameters + $null = $inputForm.Append(@" + public String Get${IdSafeCommandName}QueryString() { + // Save getResources() and getPackageName() so that each lookup is slightly quicker + StringBuilder queryString = new StringBuilder(); + bool initializedQueryString = false; + Object foundView; + String textFieldID; +"@) + } elseif ('WPF', 'WindowsMobile', 'WindowsMetro', 'Win8' -contains $Platform) { + # On WPF, WindowsMobile, or WindowsMetro it's a Grid containing a ScrollView and a StackPanel + $null = $inputForm.Append(@" +<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> + <ScrollViewer> + <StackPanel> +"@) + } elseif ('GoogleGadget' -eq $platform) { + $null = $inputForm.Append(@" +<Module> + <ModulePrefs title="$ButtonText" height="400"/> +"@) + } elseif ('TwilML' -eq $platform) { + $null = $inputForm.Append(@" +<Response> +"@) + } + + #endregion Start of Form + + + #region Filter Parameters + # Without an explicit whitelist, get all parameters + + if (-not $AllowedParameter) { + $allowedParameter = $CommandMetaData.Parameters.Keys + } + + # If a parameter set was provided, filter out parameters from other parameter sets + if ($parameterSet) { + $allParameters = $allowedParameter | + Where-Object { + $commandMetaData.Parameters[$_].Attributes | + Where-Object { $_.ParameterSetName -eq $parameterSet } + } + } + + # Remove the denied parameters + $allParameters = foreach ($param in $allowedParameter) { + if ($DenyParameter -notcontains $param) { + $param + } + } + + # Order parameters if they are not explicitly ordered + if (-not $order) { + $order = + $allParameters | + Select-Object @{ + Name = "Name" + Expression = { $_ } + },@{ + Name= "NaturalPosition" + Expression = { + $p = @($commandMetaData.Parameters[$_].ParameterSets.Values)[0].Position + if ($p -ge 0) { + $p + } else { 1gb } + } + } | + Sort-Object NaturalPosition| + Select-Object -ExpandProperty Name + } + #endregion Filter Parameters + $mandatoryFields = New-Object Collections.ArrayList + $parameterIds= New-Object Collections.ArrayList + $inputFields = New-Object Collections.ArrayList + foreach ($parameter in $order) { + if (-not $parameter) { continue } + if (-not ($commandMetaData.Parameters[$parameter])) { continue } + $parameterType = $commandMetaData.Parameters[$parameter].ParameterType + $IsMandatory = foreach ($pset in $CommandMetaData.Parameters[$parameter].ParameterSets.Values) { + if ($pset.IsMandatory) { $true; break} + } + $friendlyParameterName = ConvertFrom-CamelCase $parameter + $inputFieldName = "$($CommandMetaData.Name)_$parameter" + $null = $inputFields.Add($inputFieldName) + $parameterIdentifier = "${idSafeCommandName}_${parameter}" + $null = $parameterIds.Add($parameterIdentifier) + if ($IsMandatory) { + $null = $mandatoryFields.Add($parameterIdentifier) + } + + $parameterHelp = + foreach ($p in $help.Parameters.Parameter) { + if ($p.Name -eq $parameter) { + $p.Description | Select-Object -ExpandProperty Text + } + } + + + $parameterVisibleHelp = $parameterHelp -split ("[`n`r]") |? { $_ -notlike "|*" } + + $pipeworksDirectives = @{} + foreach ($line in $parameterHelp -split ("[`n`r]")) { + if ($line -like "|*") { + $directiveEnd= $line.IndexofAny(": `n`r".ToCharArray()) + if ($directiveEnd -ne -1) { + $name, $rest = $line.Substring(1, $directiveEnd -1).Trim(), $line.Substring($directiveEnd +1).Trim() + $pipeworksDirectives.$Name = $rest + } else { + $name = $line.Substring(1).Trim() + $pipeworksDirectives.$Name = $true + } + + + } + } + + + if ($ParameterDefaultValue.$parameter) { + $pipeworksDirectives.Default = $ParameterDefaultValue.$parameter + } + + if ($ParameterOption.$parameter) { + $pipeworksDirectives.Options = $ParameterOption.$parameter + } + + # The Select directive uses a Select-COMMAND to produce input for a parameter. + # If the command contains a -Platform parameter, it will be passed the platform + if ($pipeworksDirectives.Select) { + $selectCmd = Get-Command -Name "Select-$($pipeworksDirectives.Select)" -ErrorAction SilentlyContinue + + $selectParams = @{} + if ($selectCmd.Parameters.Platform) { + $selectParams.Platform = $Platform + } + $inputForm.Append("$(& $selectCmd)") + continue + } + + if ($Platform -eq 'Web') { + + $boldIfMandatory = if ($IsMandatory) { "<b>" } else { "" } + $unboldIfMandatory = if ($IsMandatory) { "</b>" } else { "" } + if (-not $TwoColumn) { + $null = $inputForm.Append(" + <div style='margin-top:2%;margin-bottom:2%;$(if ($pipeworksDirectives.Float) {'float:left'} else {'clear:both'})'> + <div style='width:37%;float:left;'> + <label for='$inputFieldName' style='text-align:left;font-size:1.3em'> + ${boldIfMandatory}${friendlyParameterName}${unboldIfMandatory} + </label> + ") + "" + } else { + $null = $inputForm.Append(" + <tr> + <td style='width:25%'><p>${boldIfMandatory}${friendlyParameterName}${unboldIfMandatory}</p></td> + <td style='width:75%;text-align:center;margin-left:15px;padding:15px;font-size:medium'>") + } + } elseif ($platform -eq 'Android') { + # Display parameter name, unless it's a checkbox (then the parameter name is inline) + if ([Switch], [Bool] -notcontains $parameterType) { + + $null = $inputForm.Append(@" + <TextView + $theDefaultAttributesInAndroidLayout + android:text="$friendlyParameterName" + android:padding="5px" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle='bold' /> + +"@) + } + } elseif ($platform -eq 'AndroidBackend') { + + # If it's android backend, simply add a lookup for the values to the method + $null = $inputForm.Append(@" + + String textFieldID = "$parameterIdentifier"; + View foundView = findViewById( + allResources. + getIdentifier(textFieldID, + "id", + packageName)); + +"@) + } elseif ($platform -eq 'CSharpBackend') { + + # If it's android backend, simply add a lookup for the values to the method + $null = $inputForm.Append(@" + + textFieldID = "$parameterIdentifier"; + foundView = this.FindName(textFieldID); + +"@) + } elseif ($platform -eq 'WindowsMobile' -or + $platform -eq 'WPF' -or + $platform -eq 'Metro' -or + $Platform -eq 'Win8') { + + $MajorStyleChunk = if ($Platform -ne 'WindowsMobile') { + "FontSize='19'" + } else { + "Style='{StaticResource PhoneTextExtraLargeStyle}'" + } + $includeHelp = if ([Switch], [Bool] -notcontains $parameterType) { + " + <TextBlock + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + $MajorStyleChunk + Margin='28 2 0 3' + FontWeight='Bold' + Text='$FriendlyParameterName' /> + " + } else { + "" + } + $null = $inputForm.Append(@" + $includeHelp +"@) + } + + $StyleChunk = if ($Platform -ne 'WindowsMobile') { + "FontSize='14'" + } else { + "Style='{StaticResource PhoneTextSubtleStyle}'" + } + + + + if ($pipeworksDirectives.FileName) { + continue + } + + $parameterHelp= $parameterVisibleHelp -join ([Environment]::NewLine) + if ($parameterHelp) { + if ($Platform -eq 'Web') { + $null = $inputForm.Append("<br style='line-height:150%' />$( +ConvertFrom-Markdown -md $parameterHelp)") + } elseif ($Platform -eq 'Android') { + if ([Switch], [Bool] -notcontains $parameterType) { + $null = $inputForm.Append(" + <TextView + $theDefaultAttributesInAndroidLayout + android:text=`"$([Web.HttpUtility]::HtmlAttributeEncode($parameterHelp))`" + android:padding='2px' + android:textAppearance='?android:attr/textAppearanceSmall' />") + } + } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) { + if ([Switch], [Bool] -notcontains $parameterType) { + $null = $inputForm.Append(" + <TextBlock + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Name='${parameter}_Description_TextBlock' + $StyleChunk + Margin='7,0, 5,0' + TextWrapping='Wrap'> + $([Security.SecurityElement]::Escape($parameterHelp)) + </TextBlock>") + } + } + } + + if ($platform -eq 'Web') { + $null = $inputForm.Append("</div> + <div style='float:right;width:60%;'> + + ") + + } + + + + $validateSet = + foreach ($attribute in $commandMetaData.Parameters[$parameter].Attributes) { + if ($attribute.TypeId -eq [Management.Automation.ValidateSetAttribute]) { + $attribute + break + } + } + + $defaultValue = $pipeworksDirectives.Default + if ($pipeworksDirectives.Options -or $validateSet -or $parameterType.IsSubClassOf([Enum])) { + $optionList = + if ($pipeworksDirectives.Options) { + Invoke-Expression -Command "$($pipeworksDirectives.Options)" -ErrorAction SilentlyContinue + } elseif ($ValidateSet) { + $ValidateSet.ValidValues + } elseif ($parameterType.IsSubClassOf([Enum])) { + [Enum]::GetValues($parameterType) + } + + if ($Platform -eq 'Web') { + $options = foreach ($option in $optionList) { + $selected = if ($defaultValue -eq $option) { + " selected='selected'" + } else { + "" + } + "$(' ' * 20)<option $selected>$option</option>" + } + $options = $options -join ([Environment]::NewLine) + if (-not $firstcomboBox) { + $firstcomboBox = $optionList + + } + $null = $inputForm.Append("<select class='comboboxfield' name='$inputFieldName' id='$($parameterIdentifier)' value='$($pipeworksDirectives.Default)'> + $(if (-not $IsMandatory) { "<option> </option>" }) + $options + </select> + ") + } elseif ($Platform -eq 'Android') { + # Android is a bit of a pita. There are two nice controls for this: Spinner and AutoCompleteEditText, but both + # cannot specify the resources in the same XML file as the control. + + if ($optionList.Count -gt 10) { + # Text box + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 1)") + } else { + + $null = $inputForm.Append(@" + <RadioGroup + android:id="@+id/$($idSafeCommandName + '_' + $parameter)" + $theDefaultAttributesInAndroidLayout> +"@) + foreach ($value in $optionList) { + + $null = $inputForm.Append(" + <RadioButton + $theDefaultAttributesInAndroidLayout + android:text='$value' />") + + } + + $null = $inputForm.Append(@" + </RadioGroup> +"@) + } + } elseif ('TwilML' -eq $platform) { + # Twilio + $friendlyParameterName = ConvertFrom-CamelCase $parameter + + $optionNumber = 1 + $phoneFriendlyHelp = + foreach ($option in $optionList) { + "Press $OptionNumber for $Option" + $optionNumber++ + } + $phoneFriendlyHelp = $phoneFriendlyHelp -join ". $([Environment]::NewLine)" + + $null = $inputForm.Append(@" + <Gather numDigits="$($optionNumber.ToString().Length)" $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" })> + <Say> + $([Security.SecurityElement]::Escape($phoneFriendlyHelp )) + </Say> + </Gather> +"@) + + } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) { + # XAML does not have this limitation + if ($optionList.Count -lt 5) { + # Radio Box + # Combo Box + $null = $inputForm.Append(" +<Border x:Name='$($idSafeCommandName + '_' + $parameter)'> + <StackPanel>") + + + foreach ($value in $optionList) { + + $null = $inputForm.Append(" + <RadioButton + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + GroupName='$($idSafeCommandName + '_' + $parameter)'>$([Security.SecurityElement]::Escape($value))</RadioButton>") + + } + $null = $inputForm.Append(" + </StackPanel> +</Border>") + + + } else { + # Combo Box + $null = $inputForm.Append(@" + <ComboBox + x:Name='$($idSafeCommandName + '_' + $parameter)' + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) > +"@) + foreach ($value in $optionList) { + + $null = $inputForm.Append(" + <ComboBoxItem>$([Security.SecurityElement]::Escape($value))</ComboBoxItem>") + + } + + $null = $inputForm.Append(@" + </ComboBox> +"@) + } + } elseif ('GoogleGadget' -eq $platform ) { + $enumItems = foreach ($option in $optionList) { + "<EnumValue value='$option'/>" + } + $null = $inputForm.Append( @" +<UserPref name="$parameterIdentifier" display_name="$FriendlyParameterName" datatype="enum" > + $enumItems +</UserPref> +"@) + } + } elseif ([int[]], [uint32[]], [double[]], [int], [uint32], + [double], [Timespan], [Uri], [DateTime], [type], [version] -contains $parameterType) { + # Numbers and similar primitive types become simple input boxes. When possible, use one of the new + # HTML5 input types to leverage browser support. + if ([int[]], [uint32[]], [double[]], [int], [uint32], [double] -contains $parameterType) { + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 1 -IsNumber)") + } elseif ($parameterType -eq [DateTime]) { + # Add A JQueryUI DatePicker + + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 1 -CssClass 'dateTimeField' -type text)") + } else { + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 1)") + } + } elseif ($parameterType -eq [byte[]]) { + if ($platform -eq 'Web') { + if ($pipeworksDirectives.File) { + $null = $inputForm.Append("<input type='file' name='$inputFieldName' chars='40' style='width:100%' $(if ($pipeworksDirectives.Accept) {"accept='$($pipeworksDirectives.Accept)'"}) />") + } elseif ($pipeworksDirectives.FilePickerIO) { + $null = $inputForm.Append("<input type='filepicker' data-fp-apikey='$($pipeworksManifest.FilePickerIOKey)' name='$InputFieldName' $(if ($pipeworksDirectives.Accept) {"data-fp-mimetype='$($pipeworksDirectives.Accept)"})' />") + } + } + } elseif ($parameterType -eq [Security.SecureString]) { + if ($platform -eq 'Web') { + $null = $inputForm.Append("<input type='password' name='$inputFieldName' style='width:100%'>") + } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) { + $null = $inputForm.Append(@" + <PasswordBox + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Margin="7, 5, 7, 5" + x:Name="$parameterIdentifier" + $(if ($defaultValue) { "Password='$defaultValue'" }) + Tag='Type:$($parameterType.Fullname)' /> +"@) + } + } elseif ($parameterType -eq [string]) { + if ($pipeworksDirectives.Contains("Color") -and $Platform -eq 'Web' -and $pipeworksManifest.UseJQueryUI) { + # Show a JQueryUI Color Picker + + if ($pipeworksDirectives.Default) { + $dcolor = $pipeworksDirectives.Default.Trim('#').ToCharArray() + $red = "0x$($dcolor[0,1] -join '')" -as [uint32] + $green = "0x$($dcolor[2,3] -join '')" -as [uint32] + $blue = "0x$($dcolor[4,5] -join '')" -as [uint32] + } else { + $red = Get-Random -Maximum 255 + $green = Get-Random -Maximum 255 + $blue = Get-Random -Maximum 255 + } + + + +$colorInput = @" +<style> + #red_$parameterIdentifier, #green_$parameterIdentifier, #blue_$parameterIdentifier { + float: left; + clear: left; + width: 300px; + margin: 15px; + } + #swatch_$parameterIdentifier { + width: 120px; + height: 100px; + margin-top: 18px; + margin-left: 350px; + background-image: none; + } + #red_$parameterIdentifier .ui-slider-range { background: #ef2929; } + #red_$parameterIdentifier .ui-slider-handle { border-color: #ef2929; } + #green_$parameterIdentifier .ui-slider-range { background: #8ae234; } + #green_$parameterIdentifier .ui-slider-handle { border-color: #8ae234; } + #blue_$parameterIdentifier .ui-slider-range { background: #729fcf; } + #blue_$parameterIdentifier .ui-slider-handle { border-color: #729fcf; } + </style> + <script> + function hexFromRGB(r, g, b) { + var hex = [ + r.toString( 16 ), + g.toString( 16 ), + b.toString( 16 ) + ]; + `$.each( hex, function( nr, val ) { + if ( val.length === 1 ) { + hex[ nr ] = "0" + val; + } + }); + return hex.join( "" ).toUpperCase(); + } + function refresh${ParameterIdentifier}Swatch() { + var red = `$( "#red_$parameterIdentifier" ).slider( "value" ), + green = `$( "#green_$parameterIdentifier " ).slider( "value" ), + blue = `$( "#blue_$parameterIdentifier" ).slider( "value" ), + hex = hexFromRGB( red, green, blue ); + `$( "#swatch_$parameterIdentifier" ).css( "background-color", "#" + hex ); + `$('#$parameterIdentifier').val(hex) + } + `$(function() { + `$( "#red_$parameterIdentifier, #green_$parameterIdentifier, #blue_$parameterIdentifier " ).slider({ + orientation: "horizontal", + range: "min", + max: 255, + value: 127, + slide: refresh${ParameterIdentifier}Swatch, + change: refresh${ParameterIdentifier}Swatch + }); + `$( "#red_$parameterIdentifier" ).slider( "value", $red ); + `$( "#green_$parameterIdentifier " ).slider( "value", $green ); + `$( "#blue_$parameterIdentifier " ).slider( "value", $blue); + }); +</script> +<div id="red_$parameterIdentifier"></div> +<div id="green_$parameterIdentifier"></div> +<div id="blue_$parameterIdentifier"></div> + +<div id="swatch_$parameterIdentifier" class="ui-widget-content ui-corner-all"></div> +<input name='$inputFieldName' id='$parameterIdentifier' type='text' value='' style='width:120px;margin-top:18px;margin-left: 350px;'> +"@ + $null = $inputForm.Append($colorInput) + } else { + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 1)") + } + + } elseif ([string[]], [uri[]] -contains $parameterType) { + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 4)") + } elseif ([ScriptBlock] -eq $parameterType -or + [PSObject] -eq $parameterType -or + [PSObject[]] -eq $parameterType -or + [Hashtable[]] -eq $parameterType) { + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 6)") + } elseif ([Hashtable] -eq $parameterType) { + if ($pipeworksDirectives.Default -is [Hashtable]) { + $d = foreach ($kv in $pipeworksDirectives.Default.GetEnumerator()) { + "$($kv.Key) = $($kv.Value)" + } + $d = $d -join ([Environment]::NewLine) + $pipeworksDirectives.Default = $d + } + + + $null = $inputForm.Append("$(. New-TextInput -defaultNumberOfLines 6)") + } elseif ([switch], [bool] -contains $parameterType) { + if ($platform -eq 'Web') { + $null = $inputForm.Append("<input name='$inputFieldName' type='checkbox' $(if ($pipeworksDirectives.Default -and $pipeworksDirectives.Default -like "*true*") { "checked='yes'"})/>") + } elseif ($platform -eq 'Android') { + $null = $inputForm.Append(@" + <CheckBox + android:id="@+id/$($commandMetaData.Name.Replace("-", "") + "_" + $parameter)" + $theDefaultAttributesInAndroidLayout + android:text="$friendlyParameterName" + android:textAppearance="?android:attr/textAppearanceMedium" + android:textStyle='bold' /> + + <TextView + $theDefaultAttributesInAndroidLayout + android:text="$([Web.HttpUtility]::HtmlAttributeEncode($parameterHelp))" + android:padding="2px" + android:textAppearance="?android:attr/textAppearanceSmall" /> +"@) + } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) { + $null = $inputForm.Append(@" + <CheckBox + Margin="5, 5, 2, 0" + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + x:Name='$($idSafeCommandName + '_' + $parameter)'> + <StackPanel Margin="3,-5,0,0"> + <TextBlock + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Name='${parameter}_ParameterName_TextBlock' + $MajorStyleChunk + FontWeight='Bold' + Text='$friendlyParameterName' /> + + <TextBlock + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + Name='${parameter}_Description_TextBlock' + $StyleChunk + TextWrapping='Wrap'> + $([Security.SecurityElement]::Escape($parameterHelp)) + </TextBlock> + </StackPanel> + </CheckBox> +"@) + } elseif ($platform -eq 'AndroidBackEnd') { + $null = $inputForm.Append(@" + try { + CheckBox checkbox = (CheckBox)foundView; + + if (! initializedQueryString) { + initializedQueryString = true; + } else { + queryString.append("&"); + } + + queryString.append(textFieldID); + queryString.append("="); + + + if (checkbox.isChecked()) { + queryString.append("true"); + } else { + queryString.append("false"); + } + } catch (Exception e) { + e.printStackTrace(); + } +"@) + } elseif ($platform -eq 'CSharpBackEnd') { + $null = $inputForm.Append(@" + try { + CheckBox checkbox = (CheckBox)foundView; + + if (! initializedQueryString) { + initializedQueryString = true; + } else { + queryString.Append("&"); + } + + queryString.Append(textFieldID); + queryString.Append("="); + + if ((bool)(checkbox.IsChecked)) { + queryString.Append("true"); + } else { + queryString.Append("false"); + } + } catch (Exception ex){ + throw ex; + } + +"@) + } elseif ($Platform -eq 'TwilML') { + # Twilio + $friendlyParameterName = ConvertFrom-CamelCase $parameter + $phoneFriendlyHelp = "$(if ($parameterHelp) { "$ParameterHelp "} else { "Is $FriendlyParameterName" }) If so, press 1. If not, press 0." + + $null = $inputForm.Append(@" + <Gather numDigits="1" $(if ($Action) { "action='$([Security.SecurityElement]::Escape($action))'" }) > + <Say> + $([Security.SecurityElement]::Escape($phoneFriendlyHelp )) + </Say> + </Gather> +"@) + } + } + + + # Close the parameter input + if ($platform -eq 'Web') { + + $null = $inputForm.Append(" + </div> + <div style='clear:both'> + </div> + </div> + + ") + } elseif ('WindowsMobile', 'Metro', 'WPF', 'Win8' -contains $platform) { + $null = $inputForm.Append(" +") + + + + } elseif ('PipeworksDirective' -eq $platform ) { + if ($pipeworksDirectives.Count) { + $allPipeworksDirectives.$parameter = $pipeworksDirectives + } + + } + + $null = $null + } + + + #region Button + if (-not $NoButton) { + if ($Platform -eq 'Web') { + + $checkForMandatory = "" + if( $mandatoryFields) { + foreach ($check in $mandatoryFields) { + + $checkForMandatory += " +if (`$(`".$check`").val() == `"`") { + event.preventDefault(); + return false; +} +" + } + } + $ajaxPart = if ($Ajax) { + + $ajaxAction = + if ($action.Contains('?') -and $action -notlike "*snug=true*") { + $action + "&Snug=true" + } elseif ($Action -notlike "*snug=true*") { + if ($action.EndsWith('/')) { + $action + "?Snug=true" + } else { + $action + "/?Snug=true" + } + } +@" + `$('#${cssbaseName}_$RandomSalt').submit(function(event){ + var data = `$(this).serialize(); + if (Form_${RandomSalt}_Submitted == true) { + event.preventDefault(); + return false; + } + + $checkForMandatory + + `$('input[type=submit]', this).prop('disabled', true); + Form_${RandomSalt}_Submitted =true; + setTimeout( + function() { + `$.ajax({ + url: '$ajaxAction', + async: false, + data: data + }).done(function(data) { + `$('#$($commandMetadata.Name)_InlineOutputContainer_$RandomSalt').html(data); + `$('#${cssBaseName}_$RandomSalt').hide() + `$('html, body').animate({scrollTop: `$(`"#$($commandMetadata.Name)_InlineOutputContainer_$RandomSalt`").offset().top}, 400); + }) + }, 125); + `$( `"#$($commandMetadata.Name)_Undo_$RandomSalt`" ).show(); + `$( `"#$($commandMetadata.Name)_Undo_$RandomSalt`" ).button().click( + function(event) { + `$('input[type=submit]').prop('disabled', false); + Form_${RandomSalt}_Submitted = false; + `$('#$($commandMetadata.Name)_Undo_$RandomSalt').hide() + `$('#${cssBaseName}_$RandomSalt').show() + `$('html, body').animate({scrollTop: `$(`"#${cssBaseName}_$RandomSalt`").offset().top}, 400); + event.preventDefault(); + }); + //event.preventDefault(); + return false; + }); +"@ + } else { + "" + } + if (-not $TwoColumn) { + $null = $inputForm.Append(" + <br style='clear:both'/> + <div style='width:50%;float:right'> + ") + } else { + + $null = $inputForm.Append(" + <tr> + <td style='text-align:right;margin-right:15px;padding:15px'> + </td>") + } + if ($buttonImage) { + $null = $inputForm.Append(" + <p style='text-align:center;margin-left:15px;padding:15px'> + ") + } else { + $null = $inputForm.Append(" + <p style='text-align:center;margin-left:15px;padding:15px'> + $(if ($Ajax) { + "<script>" + + (Write-Ajax -InputId $parameterIds -InputQuery $inputFields -Name "submit$($commandMetadata.Name.Replace('-', ''))" -Url $Action -UpdateId "$($commandMetaData.Name)_InlineOutputContainer_$RandomSalt" -Method $Method) + + "</script>" + }) + <input type='submit' class='$($commandMetadata.Name)_SubmitButton btn btn-primary rounded' value='$buttonText' style='padding:5px;font-size:large' $(if ($ajax) { "onclick='submit$($commandMetadata.Name.Replace("-", ''))();event.preventDefault();'" }) /> + + <script> + $(if ($pipeworksManifest.UseJQueryUI -or $pipeworksManifest.UseBootstrap -or $pipeworksManifest.UseJQuery) { +@" + `$(function() { + $(if ($pipeworksManifest.UseJQueryUI -or $pipeworksManifest.UseBootstrap) { @" + `$( `".$($commandMetadata.Name)_SubmitButton`" ).button(); +"@}) + `$( `".$($commandMetadata.Name)_Undo`" ).hide(); + var Form_${RandomSalt}_Submitted = false; + /* + $ajaxPart + */ + }) +"@}) + </script> + </p> + " + + + ) + + } + if (-not $twoColumn) { + $null = $inputForm.Append("</div><br style='clear:both' />") + } else { + $null = $inputForm.Append("</tr>") + } + } elseif ($Platform -eq 'Android') { + $null = $inputForm.Append(@" + <TableLayout + $theDefaultAttributesInAndroidLayout> + + <Button + android:id="@+id/$($idSafeCommandName)_Invoke" + $theDefaultAttributesInAndroidLayout + android:text="$ButtonText" /> + </TableLayout> +"@) + } elseif ('WPF', 'WindowsMobile', 'WindowsMetro', 'Win8' -contains $Platform) { + $null = $inputForm.Append(@" + <Button HorizontalAlignment='Stretch' x:Name='$($idSafeCommandName)_Invoke' Margin='7'> + <TextBlock + $(if ($Platform -eq 'Win8') { "Foreground='{StaticResource TheForegroundColor}'" }) + $(if ($Platform -eq 'Win8') { "Background='{StaticResource TheBackgroundColor}'" }) + $MajorStyleChunk + FontWeight='Bold' + Text='$ButtonText' /> + </Button> + +"@) + } + } + + + #endregion + if ($platform -eq 'Web') { + $null = $inputForm.Append(" + </div> + $(if ($Focus) {@" +<script>`$('input[name*=`"$($command.Name)_${focus}`"]').focus();</script> +"@ +}) +</form> +<div class='$($commandMetadata.Name)_ErrorContainer'> +</div> +<div class='$($commandMetadata.Name)_CenterButton' style='text-align:center'> + <a href='javascript:void' class='$($CommandMetaData.Name)_Undo btn btn-primary' id='$($CommandMetaData.Name)_Undo_$RandomSalt' style='display:none;text-align:center;font-size:small'><div class='ui-icon ui-icon-pencil' style='margin-left:auto;margin-right:auto'> </div>Change Input</a> +</div> + + +") + } elseif ($platform -eq 'Android') { + $null = $inputForm.Append(" + </LinearLayout> +</ScrollView> +</ViewSwitcher> +") + } elseif ('AndroidBackEnd' -eq $platform) { + $null = $inputForm.Append(" + return queryString.toString(); + } +") + } elseif ('CSharpBackEnd' -eq $platform) { + $null = $inputForm.Append(" + return queryString.ToString(); + } +") + } elseif ('WPF', 'WindowsMobile', 'WindowsMetro', 'Win8' -contains $Platform) { + $null = $inputForm.Append(@" + </StackPanel> +</ScrollViewer> +</Grid> +"@) + } elseif ('GoogleGadget' -eq $platform) { + $null = $inputForm.Append(@" +</Module> +"@) + } elseif ('TwilML' -eq $platform) { + $null = $inputForm.Append(@" +</Response> +"@) + } + + $output = "$inputForm" + + if ('Android', 'WPF','SilverLight', 'WindowsMobile', 'Metro', 'Win8', 'Web','GoogleGadget', 'TwilML' -contains $platform) { + if ($output -as [xml]) { + # Nice XML Trick + $strWrite = New-Object IO.StringWriter + ([xml]$output).Save($strWrite) + $strWrite = "$strWrite" + $strWrite.Substring($strWrite.IndexOf('>') + 3) + } else { + $output + } + } elseif ($Platform -eq 'PipeworksDirective') { + $allPipeworksDirectives + } else { + $output + } + } +} diff --git a/Reset-EC2.ps1 b/Reset-EC2.ps1 new file mode 100644 index 0000000..249c7f4 Binary files /dev/null and b/Reset-EC2.ps1 differ diff --git a/Resolve-Location.ps1 b/Resolve-Location.ps1 new file mode 100644 index 0000000..91fe967 Binary files /dev/null and b/Resolve-Location.ps1 differ diff --git a/Schematics/Blog/Use-BlogSchematic.ps1 b/Schematics/Blog/Use-BlogSchematic.ps1 new file mode 100644 index 0000000..ef1b453 Binary files /dev/null and b/Schematics/Blog/Use-BlogSchematic.ps1 differ diff --git a/Schematics/Bookshelf/Use-BookshelfSchematic.ps1 b/Schematics/Bookshelf/Use-BookshelfSchematic.ps1 new file mode 100644 index 0000000..e14746b Binary files /dev/null and b/Schematics/Bookshelf/Use-BookshelfSchematic.ps1 differ diff --git a/Schematics/Bot/Use-BotSchematic.ps1 b/Schematics/Bot/Use-BotSchematic.ps1 new file mode 100644 index 0000000..b405e4e Binary files /dev/null and b/Schematics/Bot/Use-BotSchematic.ps1 differ diff --git a/Schematics/CheckpointData/Use-CheckpointDataSchematic.ps1 b/Schematics/CheckpointData/Use-CheckpointDataSchematic.ps1 new file mode 100644 index 0000000..2a00ca6 Binary files /dev/null and b/Schematics/CheckpointData/Use-CheckpointDataSchematic.ps1 differ diff --git a/Schematics/Crudbin/Use-CrudBinSchematic.ps1 b/Schematics/Crudbin/Use-CrudBinSchematic.ps1 new file mode 100644 index 0000000..9971047 Binary files /dev/null and b/Schematics/Crudbin/Use-CrudBinSchematic.ps1 differ diff --git a/Schematics/Dashboard/Use-DashboardSchematic.ps1 b/Schematics/Dashboard/Use-DashboardSchematic.ps1 new file mode 100644 index 0000000..334cb34 Binary files /dev/null and b/Schematics/Dashboard/Use-DashboardSchematic.ps1 differ diff --git a/Schematics/Gallery/Use-GallerySchematic.ps1 b/Schematics/Gallery/Use-GallerySchematic.ps1 new file mode 100644 index 0000000..09eb72a Binary files /dev/null and b/Schematics/Gallery/Use-GallerySchematic.ps1 differ diff --git a/Schematics/Interested/Use-InterestedSchematic.ps1 b/Schematics/Interested/Use-InterestedSchematic.ps1 new file mode 100644 index 0000000..c46dbc7 Binary files /dev/null and b/Schematics/Interested/Use-InterestedSchematic.ps1 differ diff --git a/Schematics/ObjectPage/Use-ObjectPageSchematic.ps1 b/Schematics/ObjectPage/Use-ObjectPageSchematic.ps1 new file mode 100644 index 0000000..b7330a1 Binary files /dev/null and b/Schematics/ObjectPage/Use-ObjectPageSchematic.ps1 differ diff --git a/Schematics/ParkingMeter/Use-ParkingMeterSchematic.ps1 b/Schematics/ParkingMeter/Use-ParkingMeterSchematic.ps1 new file mode 100644 index 0000000..e840794 Binary files /dev/null and b/Schematics/ParkingMeter/Use-ParkingMeterSchematic.ps1 differ diff --git a/Schematics/ParkingMeter/Watch-Meter.ps1 b/Schematics/ParkingMeter/Watch-Meter.ps1 new file mode 100644 index 0000000..2fa12be Binary files /dev/null and b/Schematics/ParkingMeter/Watch-Meter.ps1 differ diff --git a/Schematics/PartWiki/Use-PartWikiSchematic.ps1 b/Schematics/PartWiki/Use-PartWikiSchematic.ps1 new file mode 100644 index 0000000..a123d3d Binary files /dev/null and b/Schematics/PartWiki/Use-PartWikiSchematic.ps1 differ diff --git a/Schematics/SimpleSearch/Use-SimpleSearchSchematic.ps1 b/Schematics/SimpleSearch/Use-SimpleSearchSchematic.ps1 new file mode 100644 index 0000000..e8bd51c Binary files /dev/null and b/Schematics/SimpleSearch/Use-SimpleSearchSchematic.ps1 differ diff --git a/Schematics/StagePage/Use-StagePageSchematic.ps1 b/Schematics/StagePage/Use-StagePageSchematic.ps1 new file mode 100644 index 0000000..f475fc5 Binary files /dev/null and b/Schematics/StagePage/Use-StagePageSchematic.ps1 differ diff --git a/Schematics/Win8/Use-Win8Schematic.ps1 b/Schematics/Win8/Use-Win8Schematic.ps1 new file mode 100644 index 0000000..345f048 Binary files /dev/null and b/Schematics/Win8/Use-Win8Schematic.ps1 differ diff --git a/Search-Engine.ps1 b/Search-Engine.ps1 new file mode 100644 index 0000000..7e711c5 Binary files /dev/null and b/Search-Engine.ps1 differ diff --git a/Search-WolframAlpha.ps1 b/Search-WolframAlpha.ps1 new file mode 100644 index 0000000..59b88c3 Binary files /dev/null and b/Search-WolframAlpha.ps1 differ diff --git a/Select-DataTable.ps1 b/Select-DataTable.ps1 new file mode 100644 index 0000000..455794f Binary files /dev/null and b/Select-DataTable.ps1 differ diff --git a/Select-Sql.ps1 b/Select-Sql.ps1 new file mode 100644 index 0000000..e94e760 Binary files /dev/null and b/Select-Sql.ps1 differ diff --git a/Select-Wmi.ps1 b/Select-Wmi.ps1 new file mode 100644 index 0000000..37218af Binary files /dev/null and b/Select-Wmi.ps1 differ diff --git a/Send-Email.ps1 b/Send-Email.ps1 new file mode 100644 index 0000000..6f4f387 Binary files /dev/null and b/Send-Email.ps1 differ diff --git a/Send-PhoneCall.ps1 b/Send-PhoneCall.ps1 new file mode 100644 index 0000000..2cf22bf Binary files /dev/null and b/Send-PhoneCall.ps1 differ diff --git a/Send-TextMessage.ps1 b/Send-TextMessage.ps1 new file mode 100644 index 0000000..c7189ba Binary files /dev/null and b/Send-TextMessage.ps1 differ diff --git a/Set-AWSConnectionInfo.ps1 b/Set-AWSConnectionInfo.ps1 new file mode 100644 index 0000000..48c3c09 Binary files /dev/null and b/Set-AWSConnectionInfo.ps1 differ diff --git a/Show-WebObject.ps1 b/Show-WebObject.ps1 new file mode 100644 index 0000000..b68b355 Binary files /dev/null and b/Show-WebObject.ps1 differ diff --git a/Start-MapReduce.ps1 b/Start-MapReduce.ps1 new file mode 100644 index 0000000..b89ce21 Binary files /dev/null and b/Start-MapReduce.ps1 differ diff --git a/Start-PSNode.ps1 b/Start-PSNode.ps1 new file mode 100644 index 0000000..c569a0a Binary files /dev/null and b/Start-PSNode.ps1 differ diff --git a/Switch-TestDeployment.ps1 b/Switch-TestDeployment.ps1 new file mode 100644 index 0000000..4e71b50 Binary files /dev/null and b/Switch-TestDeployment.ps1 differ diff --git a/Templates/Command-InvertedHeader.pswt b/Templates/Command-InvertedHeader.pswt new file mode 100644 index 0000000..6914f84 Binary files /dev/null and b/Templates/Command-InvertedHeader.pswt differ diff --git a/Templates/Command.pswt b/Templates/Command.pswt new file mode 100644 index 0000000..d5eb370 Binary files /dev/null and b/Templates/Command.pswt differ diff --git a/Templates/Module-InvertedHeader.pswt b/Templates/Module-InvertedHeader.pswt new file mode 100644 index 0000000..898f5a0 Binary files /dev/null and b/Templates/Module-InvertedHeader.pswt differ diff --git a/Templates/Module.pswt b/Templates/Module.pswt new file mode 100644 index 0000000..7b326e3 Binary files /dev/null and b/Templates/Module.pswt differ diff --git a/Templates/Topic-InvertedHeader.pswt b/Templates/Topic-InvertedHeader.pswt new file mode 100644 index 0000000..9f2dee8 Binary files /dev/null and b/Templates/Topic-InvertedHeader.pswt differ diff --git a/Templates/Topic.pswt b/Templates/Topic.pswt new file mode 100644 index 0000000..c1b8cd2 Binary files /dev/null and b/Templates/Topic.pswt differ diff --git a/Test-W3C.ps1 b/Test-W3C.ps1 new file mode 100644 index 0000000..53e2442 Binary files /dev/null and b/Test-W3C.ps1 differ diff --git a/Tests/SQL/Create_And_Update_SqlCompact.test.ps1 b/Tests/SQL/Create_And_Update_SqlCompact.test.ps1 new file mode 100644 index 0000000..59fa338 Binary files /dev/null and b/Tests/SQL/Create_And_Update_SqlCompact.test.ps1 differ diff --git a/Tests/SQL/Create_And_Update_SqlServer.test.ps1 b/Tests/SQL/Create_And_Update_SqlServer.test.ps1 new file mode 100644 index 0000000..abc6c4b Binary files /dev/null and b/Tests/SQL/Create_And_Update_SqlServer.test.ps1 differ diff --git a/Tests/SQL/Create_And_Update_Sqlite.test.ps1 b/Tests/SQL/Create_And_Update_Sqlite.test.ps1 new file mode 100644 index 0000000..e970e8f Binary files /dev/null and b/Tests/SQL/Create_And_Update_Sqlite.test.ps1 differ diff --git a/Update-DataTable.ps1 b/Update-DataTable.ps1 new file mode 100644 index 0000000..cb4ebc5 Binary files /dev/null and b/Update-DataTable.ps1 differ diff --git a/Update-SQL.ps1 b/Update-SQL.ps1 new file mode 100644 index 0000000..930eb0f Binary files /dev/null and b/Update-SQL.ps1 differ diff --git a/Use-Less.ps1 b/Use-Less.ps1 new file mode 100644 index 0000000..bac6b4c Binary files /dev/null and b/Use-Less.ps1 differ diff --git a/Use-Schematic.ps1 b/Use-Schematic.ps1 new file mode 100644 index 0000000..10efacf Binary files /dev/null and b/Use-Schematic.ps1 differ diff --git a/Wait-Deployment.ps1 b/Wait-Deployment.ps1 new file mode 100644 index 0000000..3a7da84 Binary files /dev/null and b/Wait-Deployment.ps1 differ diff --git a/Wait-EC2.ps1 b/Wait-EC2.ps1 new file mode 100644 index 0000000..7b6a851 Binary files /dev/null and b/Wait-EC2.ps1 differ diff --git a/Watch-Daemon.ps1 b/Watch-Daemon.ps1 new file mode 100644 index 0000000..b3c948e Binary files /dev/null and b/Watch-Daemon.ps1 differ diff --git a/Write-Ajax.ps1 b/Write-Ajax.ps1 new file mode 100644 index 0000000..43980d0 --- /dev/null +++ b/Write-Ajax.ps1 @@ -0,0 +1,184 @@ +function Write-Ajax +{ + <# + .Synopsis + Writes AJAX + .Description + Writes AJAX. This will execute the URL and replace the contents of + the HTML element with the ID $updateId with the contents of the returned document + .Link + New-WebPage + + #> + [OutputType([string])] + param( + # The URL that will return updated contents + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [Uri]$Url, + + # The method to use for the request + [ValidateSet("GET", "POST")] + [string]$Method = "GET", + + # The ID to automatically update + [Parameter(Mandatory=$true,ValueFromPipelineByPropertyName=$true)] + [string]$UpdateId, + + # One or more input query values. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]]$InputQuery, + + # The InputIDs the provide the query values. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string[]]$InputId, + + # The name of the generated function. If this is not provided, it will be set automatically from the URL + [string]$Name, + + # Any post data + [string]$PostData, + + # The Property to update on the update element + [string]$UpdateProperty = "innerHTML", + + # If set, will output the ajax chunk in a <script> tag </script> + [switch]$IncludeScriptTag, + + # If set, will escape the output content. + # If generating web pages with Write-Ajax to display to the user, instead of run in the browser, use -Escape + [Switch]$Escape, + + # Runs one or more javascript functions when an ajax call completes, but before the property is set + [string[]] + $WhenResultReceived, + + # Runs one or more javascript functions when an ajax call is made + [string[]] + $WhenRequestMade, + + # Runs one or more javascript functions when before an ajax call is made + [string[]] + $BeforeRequestMade, + + # Runs one or more javascript functions when an ajax call completes and has set a property + [string[]] + $ThenRun + ) + + process { + #region If no name is provided, generate one + if (-not $psBoundParameters.Name) { + $UrlFunctionName = $url.ToString() + if ($urlFunctionName.Contains("?")) { + $UrlFunctionName = $UrlFunctionName.Substring(0 ,$urlFunctionName.IndexOf("?")) + } + $UrlFunctionName = $UrlFunctionName.Replace("/","_slash_").Replace("\","_backslash_").Replace(":", "_colon_").Replace(".", "_dot_").Replace("-","_") + $name = $UrlFunctionName + } + #endregion If no name is provided, generate one + + #region Determine the query data based off of the related controls + $updateQueryData = "" + if ($inputQuery) { + for ($i = 0; $i -lt $InputQuery.Count; $i++) { + if (@($inputId)[$i]) { + $updateQueryData += " + var element = document.getElementById('$($inputId[$i])') + if (element != null && element.value.length != 0) + { + if (queryData.length > 0) { + queryData = queryData + '&$($inputQuery[$i])=' + encodeURIComponent(document.getElementById('$($inputId[$i])').value); + } else { + queryData = '$($inputQuery[$i])=' + encodeURIComponent(document.getElementById('$($inputId[$i])').value); + } + } +" + } + } + } + #endregion Determine the query data based off of the related controls + if ($updateQueryData) { + Write-Verbose $updateQueryData + } + + $xmlHttpVar = "xmlHttp${$Name}" + if (-not $psBoundParameters.Name) { + $Name = "update${UpdateId}From_${Name}" + } + #region Code Generate the Ajax +$ajaxFunction = @" +function ${name}() { + var $xmlHttpVar; + var url = "$url"; + var queryData = "$postData"; + var method = '$($method.ToUpper())'; + if (window.XMLHttpRequest) + { + // code for IE7+, FireFox, Chrome, Opera, Safari + $xmlHttpVar = new XMLHttpRequest(); + } else + { + // code for IE5, IE6 + $xmlHttpVar = new ActiveXObject("Microsoft.XMLHTTP"); + } + + $updateQueryData + + element = document.getElementById("$UpdateId"); + if (element.nodeName == 'IFRAME') { + element.src = url + } + + $xmlHttpVar.onreadystatechange = function() { + if (${xmlHttpVar}.readyState == 4) { + if (${xmlHttpVar}.status == 200) { + + responseData = $xmlHttpVar.responseText; + $(if ($escape) { 'responseData = escape(responseData)'}) + $(if ($WhenResultReceived) { $WhenResultReceived -join (';' + [Environment]::NewLine) + (' ' * 12) }) + document.getElementById("$UpdateId").${UpdateProperty}= responseData; + $(if ($thenRun) { $thenRun -join (';' + [Environment]::NewLine) + (' ' * 12) }) + } else { + document.getElementById("$UpdateId").${UpdateProperty}= $xmlHttpVar.Status; + } + } else { + if (${xmlHttpVar}.readyState != 1) { + document.getElementById("$UpdateId").${UpdateProperty}= $xmlHttpVar.readyState; + } else { + $(if ($WhenRequestMade) { $WhenRequestMade -join (';' + [Environment]::NewLine) + (' ' * 12) }) + } + } + } + $(if ($BeforeRequestMade) { $BeforeRequestMade -join (';' + [Environment]::NewLine) + (' ' * 12) }) + + if (method == 'POST') { + $xmlHttpVar.open(method, url, true); + $xmlHttpVar.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); + $xmlHttpVar.setRequestHeader("Content-length", queryData.length); + $xmlHttpVar.setRequestHeader("Connection", "close"); + $xmlHttpVar.send(queryData); + } else { + var fullUrl; + if (url.indexOf('?') != -1) { + fullUrl = url + '&' + queryData; + } else { + fullUrl = url + '?' + queryData; + } + $xmlHttpVar.open(method, fullUrl, true); + $xmlHttpVar.send(); + } + +} +"@ + #endregion Code Generate the Ajax + if ($IncludeScriptTag) { + @" +<script type='text/javascript'> +$ajaxFunction +</script> +"@ + } else { +$ajaxFunction + } + } +} diff --git a/Write-AspNetScriptPage.ps1 b/Write-AspNetScriptPage.ps1 new file mode 100644 index 0000000..a2f2abc --- /dev/null +++ b/Write-AspNetScriptPage.ps1 @@ -0,0 +1,287 @@ +function Write-AspDotNetScriptPage +{ + <# + .Synopsis + Writes an ASP.NET page that executes PowerShell script + .Description + Runs a PowerShell script inside of an ASP.net page. + + The runspace used in the ASP.NET script page will be reused for as long as the session is active. + + Variables set while running your script will be available throughout the session. + + PowerShellV2 must be installed on the server, but no other special binaries are required. + .Example + Write-AspDotNetScriptPage -PrependBootstrapper -ScriptBlock { + $response.Write("<pre> +$(Get-Help Get-Command -full | Out-String -width 1024) +</pre>") + } | + Set-Content + .Link + about_ServerSidePowerShell + #> + [CmdletBinding(DefaultParameterSetName='BootStrapper')] + [OutputType([string])] + param( + # The script block to embed in the page. This will use the runScript function declared in the bootstrapper. + [Parameter(Mandatory=$true,Position=0,ParameterSetName='ScriptBlock',ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] + [ScriptBlock]$ScriptBlock, + + # The direct ASP.NET text to embed in the page. To run scripts inside of this text, use <% runScript(); %> + #|LinesForInput 6 + [Parameter(Mandatory=$true,Position=1,ParameterSetName='Text',ValueFromPipelineByPropertyName=$true)] + [string]$Text, + + # If set, prepends the bootstrapper code to the ASP.NET page. + # This is required the first time you want to run PowerShell inside of your ASP.NET page. + # It declares a function, runScript, which you can use to run PowerShell + [Parameter(Position=6,ParameterSetName='ScriptBlock',ValueFromPipelineByPropertyName=$true)] + [Parameter(Position=6,ParameterSetName='Text',ValueFromPipelineByPropertyName=$true)] + [switch]$NoBootstrapper, + + # If set, the page generated will include this page as the ASP.NET master page + [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] + [string]$MasterPage, + + # If set, the page generated will be a master page + [Parameter(Position=3,ValueFromPipelineByPropertyName=$true)] + [Switch]$IsMasterPage, + + # If set, uses a codefile page + [Parameter(Position=4,ValueFromPipelineByPropertyName=$true)] + [string]$CodeFile, + + # If set, inherits from another codefile page + [Parameter(Position=5,ValueFromPipelineByPropertyName=$true)] + [string]$Inherit + ) + + begin { + function issEmbed($cmd) { + + if ($cmd.Definition -like "*<script*") { +@" + string $($cmd.Name.Replace('-',''))Base64 = "$([Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($cmd.Definition)))"; + string $($cmd.Name.Replace('-',''))Definition = System.Text.Encoding.Unicode.GetString(System.Convert.FromBase64String($($cmd.Name.Replace('-',''))Base64)); + SessionStateFunctionEntry $($cmd.Name.Replace('-',''))Command = new SessionStateFunctionEntry( + "$($cmd.Name)", $($cmd.Name.Replace('-',''))Definition + ); + iss.Commands.Add($($cmd.Name.Replace('-',''))Command); +"@ + } else { +@" + SessionStateFunctionEntry $($cmd.Name.Replace('-',''))Command = new SessionStateFunctionEntry( + "$($cmd.Name)", @" + $($cmd.Definition.ToString().Replace('"','""')) + " + ); + iss.Commands.Add($($cmd.Name.Replace('-',''))Command); +"@ + } + } + $functionBlackList = 65..90 | ForEach-Object -Begin { + "ImportSystemModules", "Disable-PSRemoting", "Restart-Computer", "Clear-Host", "cd..", "cd\\", "more" + } -Process { + [string][char]$_ + ":" + } + + + $masterPageDirective = if ($MasterPage) { + "MasterPageFile='$MasterPage'" + } else { + "" + } + + + # These core functions must exist in all runspaces + if (-not $script:FunctionsInEveryRunspace) { + $script:FunctionsInEveryRunspace = 'ConvertFrom-Markdown', 'Get-Web', 'Get-WebConfigurationSetting', 'Get-FunctionFromScript', 'Get-Walkthru', + 'Get-WebInput', 'Request-CommandInput', 'New-Region', 'New-RssItem', 'New-WebPage', 'Out-Html', 'Out-RssFeed', 'Send-Email', + 'Write-Ajax', 'Write-Css', 'Write-Host', 'Write-Link', 'Write-ScriptHTML', 'Write-WalkthruHTML', + 'Write-PowerShellHashtable', 'Compress-Data', 'Expand-Data', 'Import-PSData', 'Export-PSData', 'ConvertTo-ServiceUrl', 'Get-SecureSetting', 'Search-Engine' + + + + } + + # The embed section contains them + $embedSection = foreach ($func in Get-Command -Module Pipeworks -Name $FunctionsInEveryRunspace -CommandType Function) { + issEmbed $func + } + + + $bootStrapperServerSideCode = @" +<%@ Assembly Name="System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> + +<%@ Import Namespace="System.Collections.ObjectModel" %> +<%@ Import Namespace="System.Management.Automation" %> +<%@ Import namespace="System.Management.Automation.Runspaces" %> +<script language="C#" runat="server"> + public void runScript(string script) { + PowerShell powerShellCommand = PowerShell.Create(); + bool justLoaded = false; + Runspace runspace; + if (Session["UserRunspace"] == null) { + InitialSessionState iss = InitialSessionState.CreateDefault(); + $embedSection + string[] commandsToRemove = new String[] { "$($functionBlacklist -join '","')"}; + foreach (string cmdName in commandsToRemove) { + iss.Commands.Remove(cmdName, null); + } + + Runspace rs = RunspaceFactory.CreateRunspace(iss); + rs.ApartmentState = System.Threading.ApartmentState.STA; + rs.ThreadOptions = PSThreadOptions.ReuseThread; + rs.Open(); + Session.Add("UserRunspace",rs); + justLoaded = true; + } + + runspace = Session["UserRunspace"] as Runspace; + + if (Application["Runspaces"] == null) { + Application["Runspaces"] = new Hashtable(); + } + if (Application["RunspaceAccessTimes"] == null) { + Application["RunspaceAccessTimes"] = new Hashtable(); + } + if (Application["RunspaceAccessCount"] == null) { + Application["RunspaceAccessCount"] = new Hashtable(); + } + + Hashtable runspaceTable = Application["Runspaces"] as Hashtable; + Hashtable runspaceAccesses = Application["RunspaceAccessTimes"] as Hashtable; + Hashtable runspaceAccessCounter = Application["RunspaceAccessCount"] as Hashtable; + + if (! runspaceTable.Contains(runspace.InstanceId.ToString())) { + runspaceTable[runspace.InstanceId.ToString()] = runspace; + } + + if (! runspaceAccessCounter.Contains(runspace.InstanceId.ToString())) { + runspaceAccessCounter[runspace.InstanceId.ToString()] = 0; + } + runspaceAccessCounter[runspace.InstanceId.ToString()] = ((int)runspaceAccessCounter[runspace.InstanceId.ToString()]) + 1; + runspaceAccesses[runspace.InstanceId.ToString()] = DateTime.Now; + + + runspace.SessionStateProxy.SetVariable("Request", Request); + runspace.SessionStateProxy.SetVariable("Response", Response); + runspace.SessionStateProxy.SetVariable("Session", Session); + runspace.SessionStateProxy.SetVariable("Server", Server); + runspace.SessionStateProxy.SetVariable("Cache", Cache); + runspace.SessionStateProxy.SetVariable("Context", Context); + runspace.SessionStateProxy.SetVariable("Application", Application); + runspace.SessionStateProxy.SetVariable("JustLoaded", justLoaded); + powerShellCommand.Runspace = runspace; + + PSInvocationSettings invokeWithHistory = new PSInvocationSettings(); + invokeWithHistory.AddToHistory = true; + PSInvocationSettings invokeWithoutHistory = new PSInvocationSettings(); + invokeWithHistory.AddToHistory = false; + + + + if (justLoaded) { + powerShellCommand.AddCommand("Set-ExecutionPolicy", false).AddParameter("Scope", "Process").AddParameter("ExecutionPolicy", "Bypass").AddParameter("Force", true).Invoke(null, invokeWithoutHistory); + powerShellCommand.Commands.Clear(); + } + + powerShellCommand.AddScript(@" +`$timeout = (Get-Date).AddMinutes(-20) +`$oneTimeTimeout = (Get-Date).AddMinutes(-1) +foreach (`$key in @(`$application['Runspaces'].Keys)) { + if ('Closed', 'Broken' -contains `$application['Runspaces'][`$key].RunspaceStateInfo.State) { + `$application['Runspaces'][`$key].Dispose() + `$application['Runspaces'].Remove(`$key) + continue + } + + if (`$application['RunspaceAccessTimes'][`$key] -lt `$Timeout) { + + `$application['Runspaces'][`$key].CloseAsync() + continue + } +} +").Invoke(); + + powerShellCommand.Commands.Clear(); + + powerShellCommand.AddCommand("Split-Path", false).AddParameter("Path", Request.ServerVariables["PATH_TRANSLATED"]).AddCommand("Set-Location").Invoke(null, invokeWithoutHistory); + powerShellCommand.Commands.Clear(); + + + try { + Collection<PSObject> results = powerShellCommand.AddScript(script, false).Invoke(); + foreach (Object obj in results) { + if (obj != null) { + if (obj is IEnumerable && ! (obj is String)) { + foreach (Object innerObject in results) { + Response.Write(innerObject); + } + } else { + Response.Write(obj); + } + + } + } + foreach (ErrorRecord err in powerShellCommand.Streams.Error) { + Response.Write("<span class='ErrorStyle' style='color:red'>" + err + "<br/>" + err.InvocationInfo.PositionMessage + "</span>"); + } + + } catch (Exception exception) { + Response.Write("<span class='ErrorStyle' style='color:red'>" + exception.Message + "</span>"); + } finally { + powerShellCommand.Dispose(); + } + } +</script> +"@ + } + + process { + if ($psCmdlet.ParameterSetName -eq 'BootStrapper') { + $bootStrapperServerSideCode + } elseif ($psCmdlet.ParameterSetName -eq 'ScriptBlock') { + + if (-not $NoBootstrapper) { + @" +<%@ $(if ($IsMasterPage) {'Master'} else {'Page'}) Language="C#" AutoEventWireup="True" $masterPageDirective $(if ($CodeFile) { "CodeFile='$CodeFile'" } $(if ($inherit) { "Inherits='$Inherit'" }))%> +$bootStrapperServerSideCode +$(if ($MasterPage) { '<asp:Content runat="server">' } else {'<%' }) +runScript(@"$($scriptBlock.ToString().Replace('"','""'))"); +$(if ($MasterPage) { '</asp:Content>' } else {'%>'}) + +"@ + } else { + @" +<%@ $(if ($IsMasterPage) {'Master'} else {'Page'}) Language="C#" AutoEventWireup="True" $masterPageDirective $(if ($CodeFile) { "CodeFile='$CodeFile'" } $(if ($inherit) { "Inherits='$Inherit'" }))%> +<% runScript(@"$($scriptBlock.ToString().Replace('"','""'))"); %> +"@ + + } + + } elseif ($psCmdlet.ParameterSetName -eq 'Text') { + if (-not $NoBootstrapper) { + @" +<%@ $(if ($IsMasterPage) {'Master'} else {'Page AutoEventWireup="True" '}) Language="C#" $masterPageDirective $(if ($CodeFile) { "CodeFile='$CodeFile'" } $(if ($inherit) { "Inherits='$Inherit'" }))%> + +$bootStrapperServerSideCode +$Text +"@ + } else { + @" +<%@ $(if ($IsMasterPage) {'Master'} else {'Page AutoEventWireup="True"'}) Language="C#" $masterPageDirective $(if ($CodeFile) { "CodeFile='$CodeFile'" } $(if ($inherit) { "Inherits='$Inherit'" }))%> +<%@ Assembly Name="System.Management.Automation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %> + +$Text +"@ + + } + + } + } +} + + + diff --git a/Write-CSS.ps1 b/Write-CSS.ps1 new file mode 100644 index 0000000..ffd0742 --- /dev/null +++ b/Write-CSS.ps1 @@ -0,0 +1,120 @@ +function Write-CSS +{ + <# + .Synopsis + Writes CSS styles + .Description + Writes CSS style tags, CSS attributes, and links to external stylesheets + .Example + # Create a new CSS Style named reallyimportant + Write-CSS -Name '#ReallyImportant' -Style @{ + "font-size" = "x-large" + "font-weight"="bold" + } + .Example + Write-CSS -OutputAttribute -Style @{ + "font-size" = "x-large" + "font-weight"="bold" + } + .Example + Write-CSS -ExternalStyleSheet MyStyleSheet.css + .Example + Write-CSS -Css @{ + "a"=@{ + "font-size"="x-large" + } + } + .Link + New-WebPage + .Link + Out-HTML + .Link + Write-Link + #> + [CmdletBinding(DefaultParameterSetName='StyleDefinition')] + [OutputType([string])] + param( + # The name of the css style + [Parameter(Mandatory=$true, + Position=0, + ValueFromPipelineByPropertyName=$true, + ParameterSetName="StyleDefinition")] + [string] + $Name, + + # The css values for a named style or a style attribute + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=0, + ParameterSetName="StyleAttribute")] + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + Position=1, + ParameterSetName="StyleDefinition")] + [Hashtable] + $Style, + + # A CSS table, containing nested tables of styles + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + ParameterSetName="Table")] + [Hashtable] + $Css, + + # If set, will not output a style tag when outputting a CSS table. + [Parameter( + ValueFromPipelineByPropertyName=$true, + ParameterSetName="Table")] + [switch] + $NoStyleTag, + + + # A path to an external syle sheet + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + ParameterSetName="StyleSheet")] + [uri] + $ExternalStyleSheet, + + # If set, will output the attributes of a style + [Parameter(Mandatory=$true, + ValueFromPipelineByPropertyName=$true, + ParameterSetName="StyleAttribute")] + [switch] + $OutputAttribute + ) + + process { + if ($pscmdlet.ParameterSetName -eq 'StyleSheet') { + "<link rel='stylesheet' type='text/css' href='$ExternalStyleSheet' />" + } elseif ($pscmdlet.ParameterSetName -eq 'Table') { + $cssLines = foreach ($kv in $css.GetEnumerator()) { + $name = $kv.Key + + Write-CSS -Name $name -Style $kv.Value + } +"$(if (-not $NoStyleTag) { '<style type=''text/css''>' }) +$cssLines +$(if (-not $NoStyleTag) { '</style>'})" + } elseif ($pscmdlet.ParameterSetName -eq 'StyleDefinition') { + $cssText = foreach ($kv in $Style.GetEnumerator()) { + "$($kv.Key):$($kv.Value)" + } + $cssText = $cssText -join ";$([Environment]::NewLine) " + "$name { + $cssText +}" + } elseif ($pscmdlet.ParameterSetName -eq 'StyleAttribute') { + + # Just in case they called the command with splatting, fall back on the keys + # (which will not preserve order) + + @(foreach ($kv in $style.Keys) { + if ($style[$kv]) { + "$($kv):$($style[$kv])" + } + }) -join ';' + + } + } +} diff --git a/Write-Host.ps1 b/Write-Host.ps1 new file mode 100644 index 0000000..8fa13e2 Binary files /dev/null and b/Write-Host.ps1 differ diff --git a/Write-Link.ps1 b/Write-Link.ps1 new file mode 100644 index 0000000..f414b66 --- /dev/null +++ b/Write-Link.ps1 @@ -0,0 +1,1095 @@ +function Write-Link +{ + <# + .Synopsis + Writes links to other web content + .Description + Writes links to other web content, and makes linking to rich web content easy. + .Example + Write-Link -Url "start-automating.com" + .Example + Write-Link -Url "Start-Automating.com" -Caption "Save Time, Save Money, Start Automating" + .Example + # Write links to several subpages + Write-Link -Url "a","b","c" + .Example + # A link to a twitter page + Write-Link "@jamesbru" + .Example + # A link to social sites + Write-Link "google:+1", "twitter:tweet" + .Example + Write-Link -Button -Url "Url" -Caption "<span class='ui-icon ui-icon-extlink'> + </span> + <br/> + <span style='text-align:center'> + Visit Website + </span>" + .Link + Out-HTML + #> + + [CmdletBinding(DefaultParameterSetName='ToUrl')] + [OutputType([string])] + param( + # If set, will output a simple <a href='$url'>$caption</a>. + [Parameter(ValueFromPipelineByPropertyName=$true,Position=5)] + [Switch]$Simple, + + # The caption of the link + [Parameter(ValueFromPipelineByPropertyName=$true,Position=1,ParameterSetName='ToUrl')] + [Alias('Name')] + [string[]]$Caption, + + + # The ID to use for the link + [Parameter(ValueFromPipelineByPropertyName=$true,Position=2,ParameterSetName='ToUrl')] + [string[]]$Id, + + # The url of the link. + [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Position=0,Mandatory=$true,ParameterSetName='ToUrl')] + [Alias('Href', 'Src', 'Source')] + [uri[]]$Url, + + # A table of links + [Parameter(ValueFromPipelineByPropertyName=$true,Mandatory=$true,ParameterSetName='LinkTable')] + [Alias('Links', 'LinkTable')] + [Hashtable] + $SortedLinkTable, + + # If set, will lay out multiple links horizontally. + # By default, multiple links will be displayed line by line + [Parameter(ValueFromPipelineByPropertyName=$true,Position=2)] + [Switch] + $Horizontal, + + # Will put a horizontalSeparator in between each horizontal link + [Parameter(ValueFromPipelineByPropertyName=$true,Position=3)] + [string]$HorizontalSeparator = ' | ', + + # If set, will lay out multiple links in a list + # By default, multiple links will be displayed line by line + [Parameter(ValueFromPipelineByPropertyName=$true,Position=4)] + [Alias('AsList')] + [Switch] + $List, + + # If set, will lay out multiple links in a numbered list + # By default, multiple links will be displayed line by line + [Alias('AsNumberedList')] + [Switch] + $NumberedList, + + # IF set, will make each item a jQueryUI button + [Alias('AsButton')] + [switch] + $Button, + + # If provided, will fire a javascript notify event when the link is actived. Notify events are used to communicate out of the browser. + [Alias('Notification')] + [string] + $Notify, + + + # The CSS class to use for the link + [string[]] + $CssClass, + + # A style attribute table + [Hashtable] + $Style, + + # If not set, captions taken from the URL will be stripped of any extension + [Switch] + $KeepExtension, + + # The Microdata item property + [Alias('ItemProperty')] + [string] + $ItemProp, + + # The Microdata item ID + [string] + $ItemId, + + # The name of the item + [Parameter(Mandatory=$true,ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$ItemName, + # The price of the item + [Parameter(Mandatory=$true,ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [Double]$ItemPrice, + + # If set, will make a subscription button + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [Switch]$Subscribe, + + # The billing frequency of a subscription. By default, monthly. + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [ValidateSet("Daily", "Weekly", "Monthly", "Yearly")] + [string] + $BillingFrequency = "Monthly", + + # The billing periods in a subscription. By default, one. + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [Alias('BillingPeriod')] + [Uint32] + $BillingPeriodCount = 1, + + # If set, will make a donation button + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [Switch]$Donate, + + # A Description of the item + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$ItemDescription, + + # The currency used to purchase the item + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$Currency = "USD", + + # The MerchantId for GoogleCheckout. By using this parameter, a buy button for Google Checkout will be outputted. + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$GoogleCheckoutMerchantId, + + # The Email Address for a paypal account. By using this parameter, a buy button for Paypal will be outputted + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$PaypalEmail, + + # The Publishable Key for Stripe + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$StripePublishableKey, + + # The URL for a buy code handler. By using this parameter, payment can be accepted via buy codes + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [Uri]$BuyCodeHandler, + + # The IPN url for a paypal transcation. By using this parameter, a buy button for Paypal will be outputted + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$ToPayPal, + + # The IPN url for a paypal transcation. By using this parameter, a buy button for Paypal will be outputted + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$PaypalIPN, + + # The custom property for a paypal transcation. By using this parameter, a buy button for Paypal will be outputted + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$PaypalCustom, + + # An amazon payments account id + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonPaymentsAccountId, + + # An amazon payments access key + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonAccessKey, + + # An amazon payments secret key + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonSecretKey, + + # The amazon return url + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonReturnUrl, + + # The Amazon IPN url + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonIpnUrl, + + # The Amazon transaction abandoned URL + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$AmazonAbandonUrl, + + # If provided, will collect the shipping address for a purchase on Amazon + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [switch]$CollectShippingAddress, + + # The digital key used to unlock the purchased item + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$DigitalKey, + + # The digital url the purchased item can be found at + [Parameter(ParameterSetName='ToBuyButton',ValueFromPipelineByPropertyName=$true)] + [string]$DigtialUrl, + + + # If set, will create a link to a login button on facebook + [Parameter(ParameterSetName='ToFacebookLogin',Mandatory=$true)] + [Switch]$ToFacebookLogin, + + # If set, will create a link to a login button with Live Connect + [Parameter(ParameterSetName='ToLiveConnectLogin',Mandatory=$true)] + [Switch]$ToLiveConnectLogin, + + # The live connect client ID. This is used to generate a login link to a Microsoft Live Connect web app. + [Parameter(ParameterSetName='ToLiveConnectLogin',Mandatory=$true)] + [string]$LiveConnectClientId, + + # The Module Service URL that will handle the facebook login. This URL must be relative. + [Parameter(ParameterSetName='ToFacebookLogin')] + [Parameter(ParameterSetName='ToLiveConnectLogin')] + [string]$ModuleServiceUrl = "/Module.ashx", + + # The facebook app ID. This is used to generate a facebook login link. + [Parameter(ParameterSetName='ToFacebookLogin',Mandatory=$true)] + [string]$FacebookAppId, + + # The login scope requested from Facebook users. By default, only email is requested + [Parameter(ParameterSetName='ToFacebookLogin')] + [string[]]$FacebookLoginScope = @("Email", "user_birthday"), + + # The login scope requested. By default, email, basic information, and the right to sign in automatically are included. + [Parameter(ParameterSetName='ToLiveConnectLogin')] + [string[]]$LiveConnectScope = @("wl.email", "wl.basic", "wl.signin"), + + # If provided, will user facebook OAuth, instead of the javascript API, for the actual like button + [Parameter(ParameterSetName='ToFacebookLogin')] + [Parameter(ParameterSetName='ToLiveConnectLogin')] + [Switch] + $UseOAuth + ) + begin { + $allCaptions = New-Object Collections.ArrayList + $allUrls = New-Object Collections.ArrayList + $allIds = New-Object Collections.ArrayList + } + + process { + #region Accumulate Parameters in certain parameter sets + if ($psCmdlet.ParameterSetName -eq 'ToUrl') { + if ($psBoundParameters['caption']) { + $null = $allCaptions.AddRange($caption) + } + + if ($psBoundParameters['id']) { + $null = $allIds.AddRange($Id) + } + + if ($url) { + $null = $allUrls.AddRange($url) + } + } elseif ($psCmdlet.ParameterSetName -eq 'LinkTable') { + foreach ($kv in ($SortedlinkTable.GetEnumerator() | Sort-Object Key)) { + $null = $allCaptions.Add($kv.Key) + $null = $allUrls.Add($kv.Value) + } + $psBoundParameters['Caption'] = $allCaptions + } elseif ($psCmdlet.ParameterSetName -eq 'ToBuyButton') { + + } elseif ($psCmdlet.ParameterSetName -eq 'ToFacebookLogin') { + + } + #endregion Accumulate Parameters in certain parameter sets + } + end { + $styleChunk = "" + if ($button) { + # If it's a button, add the appropriate CSS classes + $cssClass += 'fg-button', 'ui-state-default', 'ui-corner-all', 'jQueryUIButton', 'btn', 'btn-primary' + # If no horizontal separator was explicitly set, use a blank instead of a middot + if (-not $psBoundParameters.HorizontalSeparator) { + $horizontalSeparator = " " + } + $jQueryButton = "cssClass='jQueryUIButton'" + } + if ($Notify) { + $cssClass += 'fg-button', 'ui-state-default', 'ui-corner-all' + } + $classChunk = if ($cssClass) { + " class='$($cssClass -join ' ')'" + } else { + "" + + } + + $styleChunk = if ($psBoundParameters.Style) { + " style=`"$(Write-CSS -Style $style -OutputAttribute)`"" + } else { + '' + } + $c= 0 + + $links = foreach ($u in $allUrls) { + if (-not $u) { continue } + if (-not $psBoundParameters.Caption) { + if ($KeepExtension) { + $caption = $u + } else { + if ($u.ToString().Contains(".")) { + $slashCount = $u.ToString().ToCharArray() | Where-Object { $_ -eq '/' } | Measure-Object + if (($slashCount.Count -gt 3) -or ($slashCount.Count -eq 0)) { + $caption = "$($u.ToString().Substring(0, $u.ToString().LastIndexOf('.')))" + } else { + $caption = $u + } + } else { + $caption = $u + } + } + $allCaptions = @($caption) + } + + if ($allCaptions.Count -gt 1) { + $cap = $allCaptions[$c] + } else { + $cap = $allCaptions + } + + $hrefId = if ($allIds.Count -eq $allUrls.Count) { + $allIds[$c] + } else { + $null + } + $idChunk = if ($hrefId) {' id="' + $hrefId + '"'} else { "" } + if ($ItemProp) { + $idChunk += ' itemprop="' + $itemprop + '"' + } + + if ($itemId) { + $idChunk += ' itemid="' + $itemid+ '"' + } + + if ($Simple) { + # Simple links are always just <a> </a> + "<a href='$u'${idChunk}${classChunk}${styleChunk} $(if ($notify) {'onclick="try { windows.external.notify(''' + $notify.Replace("'", "''") + "'')} catch {}"})>$cap</a>" + } else { + # If the link is to a plusone, or the link is google:+1 or google:PlusOne, write a Google Plus link + if ($u -like "http://google/plusone*" -or ($u.scheme -eq "google" -and ($u.PathAndQuery -eq '+1' -or $U.PathAndQuery -eq 'PlusOne'))) { +@" +<!-- Place this tag in your head or just before your close body tag --> +<script type="text/javascript" src="https://apis.google.com/js/plusone.js"></script> + +<!-- Place this tag where you want the +1 button to render --> +<div class="g-plusone" data-size="medium"></div> +"@ + } elseif ($u -like "http://facebook/share" -or ($u.Scheme -eq 'Facebook' -and $u.PathAndQuery -eq 'share')) { + # If the url is like http://facebook/share, or facebook:share, write a share link +@" + +<script> + u = encodeURIComponent(location.href); + t = encodeURIComponent(document.title); + document.write('<a $(if ($button) {'class="jQueryUIButton"'}) style="vertical-align:text-top" href="http://www.facebook.com/sharer.php?u=' + u + '&t=' + t + '"><img border="0" src="http://facebook.com/favicon.ico" /></a>'); +</script> +"@ + } elseif ($u -like "http://facebook/like" -or ($u.Scheme -eq 'Facebook' -and $u.PathAndQuery -eq 'Like')) { + # If the url is like http://facebook/like or is facebook:like, write a Facebook like link +"<div class='fb-like' data-send='true' data-width='250' data-show-faces='false' data-layout='button_count' ></div>" + } elseif ($u -like "http://twitter/tweet" -or ($u.Scheme -eq 'Twitter' -and $u.PathAndQuery -eq 'tweet')) { + # If the url is like http://twitter/tweet, or twitter:tweet, write a Tweet this link + @" +<a href="http://twitter.com/share" data-count="none" class="twitter-share-button $(if ($button) {'jQueryUIButton'})">Tweet</a><script type="text/javascript" src="http://platform.twitter.com/widgets.js"></script> +"@ + } elseif ($u.Scheme -eq 'YouTube') { + #Youtube channel link + "<a $jQueryButton href='http://youtube.com/$($u.PathAndQuery)'><img src='http://www.youtube.com/favicon.ico' border='0' /></a>" + } elseif ($u.Scheme -eq 'Twitter' -and $u.PathAndQuery -like "@*") { + #Twitter Profile Link + if ($cap -eq $u) { $cap = $u.PathAndQuery } + "<a $jQueryButton href='http://twitter.com/$($u.PathAndQuery.Trim('@'))'>$cap</a>" + } elseif ($u.Scheme -eq 'Twitter' -and $u.PathAndQuery -like "follow@*") { + #if the url is twitter:follow@*, then create a follow button for the user + $handle = $u.PathAndQuery.Replace('follow@', '') +@" +<a href="https://twitter.com/${handle}" class="twitter-follow-button $(if ($button) {'jQueryUIButton'})" data-show-count="false" data-size="none" data-show-screen-name="false">Follow @${handle}</a> +<script src="//platform.twitter.com/widgets.js" type="text/javascript"></script> +"@ + } elseif ('OnClick', 'Click', 'On_Click' -contains $u.Scheme) { + # OnClick event + + if (-not $idChunk) { + $idChunk= "id ='OnClickButton$(Get-Random)'" + + } + + $scriptContent = $u.OriginalString -ireplace "$($u.Scheme):" + + $RealId = (($idChunk -split "[=']") -ne "")[-1] + "<a href='javascript:void(0)' $idChunk>$cap</a> + <script> + `$('#$realId').click(function() { + $scriptContent + })$(if ($Button) { '.button()' }) + </script>" + } elseif ($u.Scheme -eq 'Disqus') { + # If the scheme is like disqus, then create a link to disqus +@" +<div id="disqus_thread"></div> +<script type="text/javascript"> + /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */ + var disqus_shortname = '$($u.PathAndQuery)'; // required: replace example with your forum shortname + + /* * * DON'T EDIT BELOW THIS LINE * * */ + (function() { + var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; + dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js'; + (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); + })(); +</script> +<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript> +<a href="http://disqus.com" class="dsq-brlink">blog comments powered by <span class="logo-disqus">Disqus</span></a> +"@ + } elseif ($u.Scheme -ieq 'Play') { +#Play media in <embed> +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="true" loop="FALSE" ${idChunk}> </embed> +"@ + } elseif ($u.Scheme -ieq 'Loop') { +#Play media in hidden embed +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="true" loop="TRUE" hidden='true' ${idChunk}> </embed> +"@ + } elseif ($u.Scheme -ieq 'PlayHidden') { +#Play media in hidden embed +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="true" loop="FALSE" hidden='true' ${idChunk}> </embed> +"@ + } elseif ($u.Scheme -ieq 'Pause') { +#Play media in <embed> +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="false" loop="FALSE" ${idChunk}> </embed> +"@ + } elseif ($u.Scheme -ieq 'Loop') { +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="true" loop="true" ${idChunk}> </embed> +"@ + } elseif ($u.Scheme -ieq 'LoopHidden') { +$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length) +@" +<embed src="$newUrl" autostart="true" loop="true" hidden='true' ${idChunk}> </embed> +"@ + } elseif ($u -like "*.jpeg" -or $u -like "*.jpg" -or $u -like "*.gif" -or $u -like "*.tiff" -or $u -like "*.png") { + +@" +<a href='$u'><img ${idChunk} border='0' src='$u' /></a> +"@ + } elseif ($u -like "*.mp3") { +@" + +<script language="JavaScript" src="http://ourmedia.org/players/1pixelout/audio-player.js"> +</script> +<object type="application/x-shockwave-flash" data="http://channels.ourmedia.org/players/1pixelout/player.swf" id="audioplayer1" height="24" width="290"> + <param name="movie" value="http://channels.ourmedia.org/players/1pixelout/player.swf"> + <param name="FlashVars" value="playerID=1&soundFile=$u"> + <param name="quality" value="high"> + <param name="menu" value="false"> + <param name="wmode" value="transparent"> +</object> +<a href='$u'${idChunk}>$Cap</a> +"@ + } elseif ($u -like "http*://www.youtube.com/watch*=*") { + # YouTube Link + $type, $youTubeId = $u.Query -split '=' + $type = $type.Trim("?") +@" +<object style="height: $(if ($style.Height) {$style.Height } else {"360px"}); width: $(if ($style.Width) {$style.Width } else {"640px"})" > + <param name="movie" value="http://www.youtube.com/${type}/${youTubeId}?version=3&feature=player_detailpage" /> + <param name="allowFullScreen" value="true" /> + <param name="allowScriptAccess" value="always" /> + <embed src="http://www.youtube.com/${type}/${youTubeId}?version=3&feature=player_detailpage" type="application/x-shockwave-flash" allowfullscreen="true" allowScriptAccess="always" width="$(if ($style.Width) {$style.Width.ToString().TrimEnd("px") } else {"640"})" height="$(if ($style.Height) {$style.Height.ToString().TrimEnd("px") } else {"360"})" /> +</object> + +"@ + + } elseif ($u -like "http://player.vimeo.com/video/*") { + # Vimeo link + $vimeoId = ([uri]$u).Segments[-1] +@" +<iframe src="http://player.vimeo.com/video/${vimeoId}?title=0&byline=0&portrait=0" width="400" height="245" frameborder="0"> +</iframe><p><a href="http://vimeo.com/{$vimeoId}">$($cap)</a></p> +"@ + } elseif ($u -like "http://stackoverflow.com/users/*") { + $userId = ([uri]$u).Segments[-2] + $userName = ([uri]$u).Segments[-1] +@" +<a href="http://stackoverflow.com/users/${userId}/${UserName}"> +<img src="http://stackoverflow.com/users/flair/${UserId}.png" width="208" height="58" alt="profile for ${UserName} at Stack Overflow, Q&A for professional and enthusiast programmers" title="profile for Start-Automating at Stack Overflow, Q&A for professional and enthusiast programmers"> +</a> +"@ + } elseif ($u -like "*.jp?g" -or $u -like "*.png") { + "<a href='$u'> + <img src='$u' ${idChunk}/> + $cap + </a>" + } else { + # Just a simple link + "<a href='$u'${idChunk}${classChunk}${styleChunk} $(if ($notify) {'onclick="try { windows.external.notify(''' + $notify.Replace("'", "''") + ''')} catch {}"'})>$("$cap".Replace('&', '&'))</a>" + } + } + + $c++ + } + + $textOutput = if ($links.Count -or $List -or $NumberedList) { + if ($List) { + '<ul><li>' + ($links -join '</li><li>') + '</li></ul>' + } elseif ($NumberedList) { + '<ol><li>' + ($links -join '</li><li>') + '</li></ol>' + } elseif ($horizontal) { + $links -join "$HorizontalSeparator" + } else { + $links -join "<br/>" + } + + + } elseif ($psCmdlet.ParameterSetName -eq 'ToBuyButton') { + + if ($GoogleCheckoutMerchantId) { +@" +<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/$GoogleCheckoutMerchantId" id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm" target="_top"> + <input name="item_name_1" type="hidden" value="$([Web.httputility]::HtmlAttributeEncode($ItemName))"/> + <input name="item_description_1" type="hidden" value="$([Web.httputility]::HtmlAttributeEncode($ItemDescription))"/> + <input name="item_quantity_1" type="hidden" value="1"/> + <input name="item_price_1" type="hidden" value="$([Math]::Round($ItemPrice, 2))"/> + <input name="item_currency_1" type="hidden" value="$Currency"/> + $(if ($digitalKey) { "<input name='shopping-cart.items.item-1.digital-content.key' type='hidden' value='$DigitalKey'/> " }) + $(if ($DigtialUrl) { "<input name='shopping-cart.items.item-1.digital-content.url' type='hidden' value='$DigtialUrl'/> " }) + <input name="_charset_" type="hidden" value="utf-8"/> + <input alt="" src="https://checkout.google.com/buttons/buy.gif?merchant_id=${GoogleCheckoutMerchantId}&w=117&h=48&style=white&variant=text&loc=en_US" type="image"/> +</form> +"@ + } + + if ($PaypalEmail) { +$payPalIpnChunk = if ($payPalIpn) { + "<input type=`"hidden`" name=`"notify_url`" value=`"$payPalIpn`">" +} else { + "" +} +$payPalCmd = if ($Subscribe) { + "_xclick-subscriptions" +} elseif ($Donate) { + "_donations" +} else { + "_xclick" +} + +$payPalButtonImage = if ($subscribe) { + "https://www.paypalobjects.com/en_AU/i/btn/btn_subscribeCC_LG.gif" +} elseif ($Donate) { + "https://www.paypalobjects.com/en_AU/i/btn/btn_donateCC_LG.gif" +} else { + "http://www.paypal.com/en_US/i/btn/btn_buynow_LG.gif" +} + +$amountChunk = if ($Subscribe) { + "<input type='hidden' name='a3' value='$([Math]::Round($ItemPrice, 2))'> + <input type='hidden' name='p3' value='$($BillingPeriodCount)'> + <input type='hidden' name='src' value='1'> + <input type='hidden' name='t3' value='$($BillingFrequency.ToUpper().ToString()[0])1'> + " +} elseif ($Donate) { + "<input type='hidden' name='amount' value='$([Math]::Round($ItemPrice, 2))'>" +} else { + "<input type='hidden' name='amount' value='$([Math]::Round($ItemPrice, 2))'>" +} + +$payPalCustomChunk = if ($payPalCustom) { + "<input type=`"hidden`" name=`"custom`" value=`"$([Web.httputility]::HtmlAttributeEncode($payPalCustom))`">" +} else { + "" +} +@" +<form name="_xclick" action="https://www.paypal.com/cgi-bin/webscr" method="post"> +<input type="hidden" name="cmd" value="$payPalCmd"> +<input type="hidden" name="business" value="$payPalEmail"> +<input type="hidden" name="currency_code" value="$Currency"> +<input type="hidden" name="item_name" value="$([Web.httputility]::HtmlAttributeEncode($ItemName))"> +<input type="hidden" name="item_number" value="$([Web.httputility]::HtmlAttributeEncode($ItemId))"> +$amountChunk +$payPalIpnChunk +$payPalCustomChunk +<input type="image" src="$payPalButtonImage" border="0" name="submit" alt="Make payments with PayPal - it's fast, free and secure!"> +</form> +"@ + } + + + if ($StripePublishableKey) { +@" +<form> + <script + src="https://checkout.stripe.com/v2/checkout.js" class="stripe-button" + data-key="$StripePublishableKey" + data-amount="$ItemPrice" + data-name="Demo Site" + data-description="$ItemDescription ($ItemPrice)" + data-currency="$Currency" + data-image="/128x128.png"> + </script> +</form> +"@ + } + + if ($BuyCodeHandler) { +@" +<form name="_buyCode" action="$buyCodeHandler" method="post"> + +<div style='margin-top:2%;margin-bottom:2%;'> + <div style='width:37%;float:left;'> + <label for='PotentialBuyCode' style='text-align:left;font-size:1.3em'> + Enter Code + </label> + <br style='line-height:150%' /></div> + <div style='float:right;width:60%;'> + <input style='width:100%' name='PotentialBuyCode' value='' /> + </div> + <div style='clear:both'> + </div> + </div> + <input type='submit' class='buyCode_SubmitButton btn btn-primary' value='Enter Code' style='border:1px solid black;padding:5;font-size:large'/> +</form> +"@ + } + + if ($amazonaccessKey -and $amazonSecretKey) { + if (-not ('SimplePay1.ButtonGenerator' -as [Type])) { +# It may seem strange that one compiles C# in order to spit out strings, but, blame it on the HMAC +$simplePaybuttonGenerator = @' +/******************************************************************************* + * Copyright 2008-2010 Amazon Technologies, Inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at: http://aws.amazon.com/apache2.0 + * This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * ***************************************************************************** + */ + +namespace SimplePay1 +{ + using System; + using System.IO; + using System.Collections.Generic; + using System.Security.Cryptography; + using System.Text; + + /// <summary> + /// Amazon FPS Exception provides details of errors + /// returned by Amazon FPS service + /// </summary> + public class AmazonFPSException : Exception + { + + private String message = null; + + /// <summary> + /// Constructs AmazonFPSException with message + /// </summary> + /// <param name="message">Overview of error</param> + public AmazonFPSException(String message) + { + this.message = message; + } + + /// <summary> + /// Gets error message + /// </summary> + public override String Message + { + get { return this.message; } + } + } + + public class ButtonGenerator + { + public static readonly String SIGNATURE_KEYNAME = "signature"; + public static readonly String SIGNATURE_METHOD_KEYNAME = "signatureMethod"; + public static readonly String SIGNATURE_VERSION_KEYNAME = "signatureVersion"; + public static readonly String SIGNATURE_VERSION_2 = "2"; + public static readonly String HMAC_SHA1_ALGORITHM = "HmacSHA1"; + public static readonly String HMAC_SHA256_ALGORITHM = "HmacSHA256"; + public static readonly String COBRANDING_STYLE = "logo"; + public static readonly String AppName = "ASP"; + public static readonly String HttpPostMethod = "POST"; + public static readonly String SANDBOX_END_POINT = "https://authorize.payments-sandbox.amazon.com/pba/paypipeline"; + public static readonly String SANDBOX_IMAGE_LOCATION = "https://authorize.payments-sandbox.amazon.com/pba/images/payNowButton.png"; + public static readonly String PROD_END_POINT = "https://authorize.payments.amazon.com/pba/paypipeline"; + public static readonly String PROD_IMAGE_LOCATION = "https://authorize.payments.amazon.com/pba/images/payNowButton.png"; + + /** + * Function creates a Map of key-value pairs for all valid values passed to the function + * @param accessKey - Put your Access Key here + * @param amount - Enter the amount you want to collect for the item + * @param description - description - Enter a description of the item + * @param referenceId - Optionally enter an ID that uniquely identifies this transaction for your records + * @param abandonUrl - Optionally, enter the URL where senders should be redirected if they cancel their transaction + * @param returnUrl - Optionally enter the URL where buyers should be redirected after they complete the transaction + * @param immediateReturn - Optionally, enter "1" if you want to skip the final status page in Amazon Payments, + * @param processImmediate - Optionally, enter "1" if you want to settle the transaction immediately else "0". Default value is "1" + * @param ipnUrl - Optionally, type the URL of your host page to which Amazon Payments should send the IPN transaction information. + * @param collectShippingAddress - Optionally, enter "1" if you want Amazon Payments to return the buyer's shipping address as part of the transaction information. + * @param signatureMethod -Valid values are HmacSHA256 and HmacSHA1 + * @return - A map of key of key-value pair for all non null parameters + * @throws SignatureException + */ + public static IDictionary<String, String> getSimplePayStandardParams(String accessKey,String amount, String description, String referenceId, String immediateReturn, + String returnUrl, String abandonUrl, String processImmediate, String ipnUrl, String collectShippingAddress, + String signatureMethod, String amazonPaymentsAccountId) + { + String cobrandingStyle = COBRANDING_STYLE; + + IDictionary<String, String> formHiddenInputs = new SortedDictionary<String, String>(StringComparer.Ordinal); + + if (accessKey != null) formHiddenInputs.Add("accessKey", accessKey); + else throw new System.ArgumentException("AccessKey is required"); + if (amount != null) formHiddenInputs.Add("amount", amount); + else throw new System.ArgumentException("Amount is required"); + if (description!= null)formHiddenInputs.Add("description", description); + else throw new System.ArgumentException("Description is required"); + if (signatureMethod != null) formHiddenInputs.Add(SIGNATURE_METHOD_KEYNAME, signatureMethod); + else throw new System.ArgumentException("Signature method is required"); + if (referenceId != null) formHiddenInputs.Add("referenceId", referenceId); + if (immediateReturn != null) formHiddenInputs.Add("immediateReturn", immediateReturn); + if (returnUrl != null) formHiddenInputs.Add("returnUrl", returnUrl); + if (abandonUrl != null) formHiddenInputs.Add("abandonUrl", abandonUrl); + if (processImmediate != null) formHiddenInputs.Add("processImmediate", processImmediate); + if (ipnUrl != null) formHiddenInputs.Add("ipnUrl", ipnUrl); + if (cobrandingStyle != null) formHiddenInputs.Add("cobrandingStyle", cobrandingStyle); + if (collectShippingAddress != null) formHiddenInputs.Add("collectShippingAddress", collectShippingAddress); + if (amazonPaymentsAccountId != null) formHiddenInputs.Add("amazonPaymentsAccountId", amazonPaymentsAccountId); + formHiddenInputs.Add(SIGNATURE_VERSION_KEYNAME, SIGNATURE_VERSION_2); + return formHiddenInputs; + } + /** + * Creates a form from the provided key-value pairs + * @param formHiddenInputs - A map of key of key-value pair for all non null parameters + * @param serviceEndPoint - The Endpoint to be used based on environment selected + * @param imageLocation - The imagelocation based on environment + * @return - An html form created using the key-value pairs + */ + + public static String getSimplePayStandardForm(IDictionary<String, String> formHiddenInputs,String ServiceEndPoint,String imageLocation) + { + StringBuilder form = new StringBuilder("<form action=\"" + ServiceEndPoint + "\" method=\"" + HttpPostMethod + "\">\n"); + form.Append("<input type=\"image\" src=\""+ imageLocation + "\" border=\"0\">\n"); + foreach (KeyValuePair<String, String> pair in formHiddenInputs) + { + form.Append("<input type=\"hidden\" name=\"" + pair.Key + "\" value=\"" + pair.Value + "\" >\n"); + } + form.Append("</form>\n"); + return form.ToString(); + } + /** + * Function Generates the html form + * @param accessKey - Put your Access Key here + * @param secretKey - Put your secret Key here + * @param amount - Enter the amount you want to collect for the item + * @param description - description - Enter a description of the item + * @param referenceId - Optionally enter an ID that uniquely identifies this transaction for your records + * @param abandonUrl - Optionally, enter the URL where senders should be redirected if they cancel their transaction + * @param returnUrl - Optionally enter the URL where buyers should be redirected after they complete the transaction + * @param immediateReturn - Optionally, enter "1" if you want to skip the final status page in Amazon Payments, + * @param processImmediate - Optionally, enter "1" if you want to settle the transaction immediately else "0". Default value is "1" + * @param ipnUrl - Optionally, type the URL of your host page to which Amazon Payments should send the IPN transaction information. + * @param collectShippingAddress - Optionally, enter "1" if you want Amazon Payments to return the buyer's shipping address as part of the transaction information. + * @param signatureMethod -Valid values are HmacSHA256 and HmacSHA1 + * @param environment - Sets the environment where your form will point to can be "sandbox" or "prod" + * @throws SignatureException + */ + + public static string GenerateForm(String accessKey, String secretKey, String amount, String description, String referenceId, String immediateReturn, + String returnUrl, String abandonUrl, String processImmediate, String ipnUrl, String collectShippingAddress, String signatureMethod, String environment, String amazonPaymentsAccountId) + { + + String endPoint, imageLocation; + if (environment.Equals("prod")) + { + + endPoint = PROD_END_POINT; + imageLocation = PROD_IMAGE_LOCATION; + } + else + { + endPoint = SANDBOX_END_POINT; + imageLocation = SANDBOX_IMAGE_LOCATION; + + } + + Uri serviceEndPoint = new Uri(endPoint); + IDictionary<String, String> parameters = getSimplePayStandardParams(accessKey, amount, description, referenceId, immediateReturn, + returnUrl, abandonUrl, processImmediate, ipnUrl, collectShippingAddress, signatureMethod, amazonPaymentsAccountId); + String signature = SignatureUtils.signParameters(parameters, secretKey, HttpPostMethod, serviceEndPoint.Host, serviceEndPoint.AbsolutePath,signatureMethod); + parameters.Add(SIGNATURE_KEYNAME, signature); + String simplePayStandardForm = getSimplePayStandardForm(parameters,endPoint,imageLocation); + return simplePayStandardForm; + + + } + + } + + + + public class SignatureUtils + { + public static readonly String SIGNATURE_KEYNAME = "signature"; + // Constants used when constructing the string to sign for v2 + public static readonly String AppName = "ASP"; + public static readonly String NewLine = "\n"; + public static readonly String EmptyUriPath = "/"; + public static String equals = "="; + public static readonly String And = "&"; + public static readonly String UTF_8_Encoding = "UTF-8"; + + /** + * Computes RFC 2104-compliant HMAC signature for request parameters This + * involves 2 steps - Calculate string-to-sign and then compute signature + * + * Step 1: Calculate string-to-sign + * In Signature Version 2, string to sign is based on following: + * + * 1. The HTTP Request Method (POST or GET) followed by an ASCII newline + * (%0A) 2. The HTTP Host header in the form of lowercase host, followed by + * an ASCII newline. 3. The URL encoded HTTP absolute path component of the + * URI (up to but not including the query string parameters); if this is + * empty use a forward '/'. This parameter is followed by an ASCII newline. + * 4. The concatenation of all query string components (names and values) as + * UTF-8 characters which are URL encoded as per RFC 3986 (hex characters + * MUST be uppercase), sorted using lexicographic byte ordering. Parameter + * names are separated from their values by the '=' character (ASCII + * character 61), even if the value is empty. Pairs of parameter and values + * are separated by the '&' character (ASCII code 38). + * + * Step 2: Compute RFC 2104-compliant HMAC signature + */ + + public static String signParameters(IDictionary<String, String> parameters, String key, String HttpMethod, String Host, String RequestURI,String algorithm) + { + String stringToSign = null; + stringToSign = calculateStringToSignV2(parameters, HttpMethod, Host, RequestURI); + return sign(stringToSign, key, algorithm); + } + + + /** + * Calculate String to Sign for SignatureVersion 2 + * @param parameters + * @param httpMethod - POST or GET + * @param hostHeader - Service end point + * @param requestURI - Path + * @return + */ + private static String calculateStringToSignV2(IDictionary<String, String> parameters, String httpMethod, String hostHeader, String requestURI)// throws SignatureException + { + StringBuilder stringToSign = new StringBuilder(); + if (httpMethod == null) throw new Exception("HttpMethod cannot be null"); + stringToSign.Append(httpMethod); + stringToSign.Append(NewLine); + + // The host header - must eventually convert to lower case + // Host header should not be null, but in Http 1.0, it can be, in that + // case just append empty string "" + if (hostHeader == null) + stringToSign.Append(""); + else + stringToSign.Append(hostHeader.ToLower()); + stringToSign.Append(NewLine); + + if (requestURI == null || requestURI.Length == 0) + stringToSign.Append(EmptyUriPath); + else + stringToSign.Append(UrlEncode(requestURI, true)); + stringToSign.Append(NewLine); + + IDictionary<String, String> sortedParamMap = new SortedDictionary<String, String>(parameters, StringComparer.Ordinal); + foreach (String key in sortedParamMap.Keys) + { + if (String.Compare(key, SIGNATURE_KEYNAME, true) == 0) continue; + stringToSign.Append(UrlEncode(key, false)); + stringToSign.Append(equals); + stringToSign.Append(UrlEncode(sortedParamMap[key], false)); + stringToSign.Append(And); + } + + String result = stringToSign.ToString(); + return result.Remove(result.Length - 1); + } + + public static String UrlEncode(String data, bool path) + { + StringBuilder encoded = new StringBuilder(); + String unreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" + (path ? "/" : ""); + + foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data)) + { + if (unreservedChars.IndexOf(symbol) != -1) + { + encoded.Append(symbol); + } + else + { + encoded.Append("%" + String.Format("{0:X2}", (int)symbol)); + } + } + + return encoded.ToString(); + + } + + /** + * Computes RFC 2104-compliant HMAC signature. + */ + public static String sign(String data, String key, String signatureMethod)// throws SignatureException + { + try + { + ASCIIEncoding encoding = new ASCIIEncoding(); + HMAC Hmac = HMAC.Create(signatureMethod); + Hmac.Key = encoding.GetBytes(key); + Hmac.Initialize(); + CryptoStream cs = new CryptoStream(Stream.Null, Hmac, CryptoStreamMode.Write); + cs.Write(encoding.GetBytes(data), 0, encoding.GetBytes(data).Length); + cs.Close(); + byte[] rawResult = Hmac.Hash; + String sig = Convert.ToBase64String(rawResult, 0, rawResult.Length); + return sig; + } + catch (Exception e) + { + throw new AmazonFPSException("Failed to generate signature: " + e.Message); + } + } + + } +} +'@ + Add-Type -TypeDefinition $simplePaybuttonGenerator +} + + +$buttonGenerator = 'SimplePay1.ButtonGenerator' -as [Type] +$buttonGenerator::GenerateForm($AmazonAccessKey, + $AmazonSecretKey, + "$Currency $([Math]::Round($ItemPrice, 2))", #Price + $ItemName, #Description, + $ItemId, + 1, + $AmazonReturnUrl, + $AmazonAbandonUrl, + 1, + $AmazonIPNUrl, + "$(if ($CollectShippingAddress) { '1' } else { '0'})", + "HmacSHA256", + "Prod", + $AmazonPaymentsAccountId) + + + +<# +@" +<form action="https://authorize.payments.amazon.com/pba/paypipeline" method="post"> + <input type="hidden" name="immediateReturn" value="1" > + <input type="hidden" name="amount" value="$Currency $([Math]::Round($ItemPrice, 2))" > + <input type="hidden" name="collectShippingAddress" value="$(if ($CollectShippingAddress) { '1' } else { '0'})" > + <input type="hidden" name="isDonationWidget" value="0" > + <input type="hidden" name="description" value="$([Web.httputility]::HtmlAttributeEncode($ItemName))" > + <input type="hidden" name="amazonPaymentsAccountId" value="$amazonPaymentsAccountId" > + <input type="hidden" name="accessKey" value="$amazonaccessKey" > + <input type="hidden" name="cobrandingStyle" value="logo" > + <input type="hidden" name="processImmediate" value="1" > + <input type="image" src="http://g-ecx.images-amazon.com/images/G/01/asp/beige_small_paynow_withmsg_whitebg.gif" border="0"> +</form> +"@ +#> + } + } elseif ($psCmdlet.ParameterSetName -eq 'ToLiveConnectLogin') { + +$scope = $LiveConnectScope -join " " +$session["LiveIDRedirectURL"] = "${ModuleServiceUrl}?LiveIdConfirmed=true" +$loginLink = "https://login.live.com/oauth20_authorize.srf?client_id=$LiveConnectClientId&redirect_uri=$([Web.HttpUtility]::UrlEncode($session["LiveIDRedirectURL"]))&scope=$([Web.HttpUtility]::UrlEncode($scope).Replace("+", "%20"))&response_type=code" +@" +<a class='LiveLoginButton btn btn-primary login' href='$loginLink' style='font-size:small'>Login</a> +$(if ($pipeworksManifest.UseJQueryUI -or $pipeworksManifest.UseBootstrap) { "<script> +`$('.LiveLoginButton').button() +</script>" +}) +"@ + + } elseif ($psCmdlet.ParameterSetName -eq 'ToFacebookLogin') { +$scope = $facebookLoginScope -join "," + + +$overrideClick = if ($UseOAuth) { + @" +`$('.fb-login-button').click(function(event) { + +window.location = 'https://www.facebook.com/dialog/oauth?client_id=$FacebookAppId&redirect_uri=$([Web.HttpUtility]::UrlEncode("${ModuleServiceUrl}?FacebookConfirmed=true"))&scope=$scope&display=popup'; + event.preventDefault(); + }) +"@ +} else { + "" +} + +$activateAfterLogin = if (-not $UseOAuth) { + 'onlogin="javascript:CallAfterLogin();"' +} else { + " + + + " +} + + + +@" +<div class="resultsContainer"> +</div> +<div class="fbLoginButtonHolder"> +<div class="fb-login-button" $activateAfterLogin style='margin-top:1%;margin-bottom:1%' size="medium" scope="$scope">Connect With Facebook</div> +<script type="text/javascript"> + window.fbAsyncInit = function() { + FB.init({appId: $FacebookAppId, + cookie: true, + xfbml: true, + oauth: true} + ); + }; + (function() { + var e = document.createElement('script'); + e.async = true;e.src = document.location.protocol +'//connect.facebook.net/en_US/all.js'; + document.getElementById('fb-root').appendChild(e);}()); + + + $overrideclick + + + function CallAfterLogin(){ + FB.login(function(response) { + if (response.authResponse) { + + + `$("#LoginButton").hide(); //hide login button once user authorize the application + var accessToken = FB.getAuthResponse()['accessToken']; + `$(".resultsContainer").html('Please Wait | Connecting to Facebook'); //show loading image while we process user + + FB.api('/me', function(response) { + window.location.href = "${ModuleServiceUrl}?FacebookConfirmed=true&email=" + response.email + "&AccessToken=" + accessToken + "&ReturnTo=" + escape(window.location.href); + }); + } + }); +} + +</script> +</div> +"@ + } else { + "$links" + } + + if ($button) { +" +$textOutput +" + } else { + $textOutput + } + } +} diff --git a/Write-PowerShellHashtable.ps1 b/Write-PowerShellHashtable.ps1 new file mode 100644 index 0000000..30e6fa8 Binary files /dev/null and b/Write-PowerShellHashtable.ps1 differ diff --git a/Write-ScriptHTML.ps1 b/Write-ScriptHTML.ps1 new file mode 100644 index 0000000..ea3a166 --- /dev/null +++ b/Write-ScriptHTML.ps1 @@ -0,0 +1,172 @@ +function Write-ScriptHTML +{ + <# + .Synopsis + Writes Windows PowerShell as colorized HTML + .Description + Outputs a Windows PowerShell script as colorized HTML. + The script is wrapped in HTML PRE tags with SPAN tags defining color regions. + .Example + Write-ScriptHTML {Get-Process} + .Link + ConvertFrom-Markdown + #> + [CmdletBinding(DefaultParameterSetName="Text")] + [OutputType([string])] + param( + # The Text to colorize + [Parameter(Mandatory=$true, + ParameterSetName="Text", + Position=0, + ValueFromPipeline=$true)] + [Alias('ScriptContents')] + [ScriptBlock]$Text, + + + # The script as a string. + [Parameter(Mandatory=$true, + ParameterSetName="ScriptString", + Position=0, + ValueFromPipelineByPropertyName=$true)] + [string]$Script, + + # The start within the string to colorize + [Int]$Start = -1, + # the end within the string to colorize + [Int]$End = -1, + + # The palette of colors to use. + # By default, the colors will be the current palette for the + # Windows PowerShell Integrated Scripting Environment + $Palette = $Psise.Options.TokenColors, + + # If set, will include the script within a span instead of a pre tag + [Switch]$NoNewline, + + # If set, will treat help within the script as markdown + [Switch]$HelpAsMarkdown, + + # If set, will not put a white background and padding around the script + [Switch]$NoBackground + ) + + begin { + #region Color Palettes + function New-ScriptPalette + { + param( + $Attribute = "#FFADD8E6", + $Command = "#FF0000FF", + $CommandArgument = "#FF8A2BE2", + $CommandParameter = "#FF000080", + $Comment = "#FF006400", + $GroupEnd = "#FF000000", + $GroupStart = "#FF000000", + $Keyword = "#FF00008B", + $LineContinuation = "#FF000000", + $LoopLabel = "#FF00008B", + $Member = "#FF000000", + $NewLine = "#FF000000", + $Number = "#FF800080", + $Operator = "#FFA9A9A9", + $Position = "#FF000000", + $StatementSeparator = "#FF000000", + $String = "#FF8B0000", + $Type = "#FF008080", + $Unknown = "#FF000000", + $Variable = "#FFFF4500" + ) + + process { + $NewScriptPalette= @{} + foreach ($parameterName in $myInvocation.MyCommand.Parameters.Keys) { + $var = Get-Variable -Name $parameterName -ErrorAction SilentlyContinue + if ($var -ne $null -and $var.Value) { + if ($var.Value -is [Collections.Generic.KeyValuePair[System.Management.Automation.PSTokenType,System.Windows.Media.Color]]) { + $NewScriptPalette[$parameterName] = $var.Value.Value + } elseif ($var.Value -as [Windows.Media.Color]) { + $NewScriptPalette[$parameterName] = $var.Value -as [Windows.Media.Color] + } + } + } + $NewScriptPalette + } + } + #endregion Color Palettes + Set-StrictMode -Off + Add-Type -AssemblyName PresentationCore, PresentationFramework, System.Web + } + + process { + if (-not $Palette) { + $palette = @{} + } + + if ($psCmdlet.ParameterSetName -eq 'ScriptString') { + $text = [ScriptBLock]::Create($script) + } + + + if ($Text) { + # + # Now parse the text and report any errors... + # + $parse_errs = $null + $tokens = [Management.Automation.PsParser]::Tokenize($text, + [ref] $parse_errs) + + if ($parse_errs) { + $parse_errs | Write-Error + return + } + $stringBuilder = New-Object Text.StringBuilder + $backgroundAndPadding = + if (-not $NoBackground) { + "background-color:#fefefe;padding:5px" + } else { + "" + } + + $null = $stringBuilder.Append("<$(if (-not $NoNewline) {'pre'} else {'span'}) class='PowerShellColorizedScript' style='font-family:Consolas;$($backgroundAndPadding)'>") + # iterate over the tokens an set the colors appropriately... + $lastToken = $null + $ColorPalette = New-ScriptPalette @Palette + $scriptText = "$text" + $c = 0 + $tc = $tokens.Count + foreach ($t in $tokens) + { + $C++ + if ($c -eq $tc) { break } + if ($lastToken) { + $spaces = " " * ($t.Start - ($lastToken.Start + $lastToken.Length)) + $null = $stringBuilder.Append($spaces) + } + if ($t.Type -eq "NewLine") { + $null = $stringBuilder.Append(" +") + } else { + $chunk = $scriptText.SubString($t.start, $t.length).Trim() + if ($t.Type -eq 'Comment' -and $HelpAsMarkdown) { + if ($chunk -like "#*") { + $chunk = $chunk.Substring(1) + } + $chunk = "<p>" + (ConvertFrom-Markdown -Markdown $chunk) + "</p>" + } + + $color = $ColorPalette[$t.Type.ToString()] + $redChunk = "{0:x2}" -f $color.R + $greenChunk = "{0:x2}" -f $color.G + $blueChunk = "{0:x2}" -f $color.B + $colorChunk = "#$redChunk$greenChunk$blueChunk" + $null = $stringBuilder.Append("<span style='color:$colorChunk'>$([Web.HttpUtility]::HtmlEncode($chunk).Replace('&','&').Replace('"','`"'))</span>") + } + $lastToken = $t + } + $null = $stringBuilder.Append("</$(if (-not $NoNewline) {'pre'} else {'span'})>") + + + $stringBuilder.ToString() + } + } +} diff --git a/Write-WalkthruHTML.ps1 b/Write-WalkthruHTML.ps1 new file mode 100644 index 0000000..f26346e --- /dev/null +++ b/Write-WalkthruHTML.ps1 @@ -0,0 +1,177 @@ +function Write-WalkthruHTML +{ + <# + .Synopsis + Writes a walkthru HTML file + .Description + Writes a section of HTML to walk thru a set of code. + .Example + Write-WalkthruHTML -Text @" +#a simple demo +Get-Help about_walkthruFiles +"@ + .Link + Get-Walkthru + Write-ScriptHTML + #> + [CmdletBinding(DefaultParameterSetName='Text')] + [OutputType([string])] + param( + # The text used to generate walkthrus + [Parameter(Position=0,Mandatory=$true, + ParameterSetName="Text", + ValueFromPipeline=$true)] + [ScriptBlock]$ScriptBlock, + + # A walkthru object, containing a source file and a property named + # walkthru with several walkthru steps + [Parameter(Position=0,Mandatory=$true, + ParameterSetName="Walkthru", + ValueFromPipeline=$true)] + [PSObject]$WalkThru, + + # with a different step on each layer + [Parameter(Position=1)] + [Switch]$StepByStep, + + # If set, will run each demo step + [Parameter(Position=2)] + [Switch]$RunDemo, + + # If set, output will be treated as HTML. Otherwise, output will be piped to Out-String and embedded in <pre> tags. + [Parameter(Position=3)] + [Switch]$OutputAsHtml, + + # If set, will start with walkthru with a <h3></h3> tag, or include the walkthru name on each step + [Parameter(Position=4)] + [string]$WalkthruName, + + # If set, will embed the explanation as text, instead of converting it to markdown. + [Parameter(Position=5)] + [switch]$DirectlyEmbedExplanation, + + # If provided, will only include certain steps + [Parameter(Position=6)] + [Uint32[]]$OnlyStep + ) + + process { + if ($psCmdlet.ParameterSetName -eq 'Text') { + Write-WalkthruHTML -Walkthru (Get-Walkthru -Text "$ScriptBlock") -StepByStep:$stepByStep + } elseif ($psCmdlet.ParameterSetName -eq 'Walkthru') { + $NewRegionParameters = @{ + Layer = @{} + Order = @() + HorizontalRuleUnderTitle = $true + } + + $walkThruHTML = New-Object Text.StringBuilder + + + $count = 1 + $total = @($walkThru).Count + foreach ($step in $walkThru) { + + # if it's provided, skip stuff that's not in OnlyStep + if ($OnlySteps) { + if ($OnlySteps -notcontains $count){ + continue + } + } + + # If we're going step by step, then we need to reset the string builder each time + if ($stepByStep) { + $walkThruHTML = New-Object Text.StringBuilder + } + + if ($DirectlyEmbedExplanation -or $step.Explanation -like "*<*") { + + $null = $walkThruHtml.Append(" + + <div class='ModuleWalkthruExplanation'> + $($step.Explanation.Replace([Environment]::newline, '<BR/>')) + </div>") + } else { + $null = $walkThruHtml.Append(" + + <div class='ModuleWalkthruExplanation'> + $(ConvertFrom-Markdown -Markdown "$($step.Explanation) ") + </div>") + } + if ($step.VideoFile -and $step.VideoFile -like "http*") { + if ($step.VideoFile -like "http://www.youtube.com/watch?v=*") { + $uri = $step.VideoFile -as [uri] + $type, $youTubeId = $uri.Query -split '=' + $type = $type.Trim("?") + $null = + $walkThruHtml.Append(@" +<br/> +<embed type="application/x-shockwave-flash" width="425" height="344" src="http://www.youtube.com/${type}/${youTubeId}?hl=en&fs=1&modestbranding=true" allowscriptaccess="always" allowfullscreen="true"> +"@) + } elseif ($step.VideoFile -like "http://player.vimeo.com/video/*") { + $vimeoId = ([uri]$step.VideoFile).Segments[-1] + $null = + $walkThruHtml.Append(@" +<br/> +<iframe src="http://player.vimeo.com/video/${vimeoId}?title=0&byline=0&portrait=0" width="400" height="245" frameborder="0"> +</iframe><p><a href="http://vimeo.com/{$vimeoId}">$($walkThru.Explanation)</a></p> + +"@) + } else { + $null = + $walkThruHtml.Append(" + <br/> + <a class='ModuleWalkthruVideoLink' href='$($step.VideoFile)'>Watch Video</a>") + } + } + $null = $walkThruHtml.Append("<br/></p>") + + if (("$($step.Script)".Trim())-and ("$($step.Script)".Trim() -ne '$null')) { + $scriptHtml = Write-ScriptHTML -Text $step.Script + $null = $walkThruHtml.Append(@" +<p class='ModuleWalkthruStep'> +$scriptHtml +</p> +"@) + } + + if ($RunDemo) { + $outText = . $step.Script + if (-not $OutputAsHtml) { + $null = $walkThruHtml.Append("<pre class='ModuleWalkthruOutput' foreground='white' background='#012456'>$([Security.SecurityElement]::Escape(($outText | Out-String)))</pre>") + } else { + if ($outText -is [Hashtable]) { + $null = $walkThruHtml.Append("$(Write-PowerShellHashtable -inputObject $OutText) ") + } elseif ($outText -is [ScriptBlock]) { + $null = $walkThruHtml.Append("$(Write-ScriptHtml -Text $OutText) ") + } else { + $null = $walkThruHtml.Append("$OutText") + } + + } + } + if ($stepByStep) { + $NewRegionParameters.Layer."$Count of $Total" = "<div style='margin-left:15px;margin-top:15px;'>$walkThruHTML</div>" + $NewRegionParameters.Order+= "$Count of $Total" + + } + $Count++ + } + + if (-not $stepByStep) { + "$walkThruHTML" + } else { + if ($WalkthruName) { + New-Region @newRegionParameters -AsFeaturette -ShowLayerTitle -LayerId "Walkthru_$WalkthruName" + } else { + New-Region @newRegionParameters -AsFeaturette -ShowLayerTitle -LayerUrl "RandomWalkthru_$(Get-random)" + } + + } + + + + } + + } +} diff --git a/bin/Microsoft.Exchange.WebServices.dll b/bin/Microsoft.Exchange.WebServices.dll new file mode 100644 index 0000000..fb243a3 Binary files /dev/null and b/bin/Microsoft.Exchange.WebServices.dll differ diff --git a/css/Pipeworks.less b/css/Pipeworks.less new file mode 100644 index 0000000..c406cf9 Binary files /dev/null and b/css/Pipeworks.less differ diff --git a/en-us/A_Simple_Stockticker.walkthru.help.txt b/en-us/A_Simple_Stockticker.walkthru.help.txt new file mode 100644 index 0000000..609c715 Binary files /dev/null and b/en-us/A_Simple_Stockticker.walkthru.help.txt differ diff --git a/en-us/About_the_Pipeworks_Manifest_-_AdSense.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AdSense.help.txt new file mode 100644 index 0000000..436fad7 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AdSense.help.txt @@ -0,0 +1,22 @@ +The AdSense section of the Pipeworks manifest describes how AdSense ads will be added to a Pipeworks site. + +It is a hashtable, and contains: + +* An ID (your AdSense ID) +* (Optionally) a BottomAdSlot, containing the ad slot you will display on the bottom of each page +* (Optionally) a TopAdSlot, containing the ad slot you will display on the top of each page + + +This is an example of a manifest containing only AdSense information: + + @{ + AdSense = @{ + Id = '7086915862223923' + BottomAdSlot = '6352908833' + } + } + + +See Also: + +* [/About_the_Pipeworks_Manifest_-_PubCenter](About The Pipeworks Manifest - PubCenter) \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_Ajax.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Ajax.help.txt new file mode 100644 index 0000000..05087bb --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Ajax.help.txt @@ -0,0 +1 @@ +The Ajax section of the Pipeworks manifest is boolean. By default, it's set to false. It instructs Pipeworks to use Ajax in forms. diff --git a/en-us/About_the_Pipeworks_Manifest_-_AllowDownload.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AllowDownload.help.txt new file mode 100644 index 0000000..189db17 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AllowDownload.help.txt @@ -0,0 +1,4 @@ +AllowDownload is a Pipeworks Manifest setting that will make the published module downloadable. + + +If set, a zip file will be created containing the module and all of it's requirements. This zip file will also include an install.cmd, and, optionally, a shortcut installer. The published module will contain a link "Download" that will download the .zip file. diff --git a/en-us/About_the_Pipeworks_Manifest_-_AmazonPaymentsAccountId.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AmazonPaymentsAccountId.help.txt new file mode 100644 index 0000000..a2da3df --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AmazonPaymentsAccountId.help.txt @@ -0,0 +1 @@ +The Amazon Payments Account ID. At this point, due to breaking changes in Amazon web stores, Amazon payment is not supported in Pipeworks. diff --git a/en-us/About_the_Pipeworks_Manifest_-_AnalyticsID.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AnalyticsID.help.txt new file mode 100644 index 0000000..373578d --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AnalyticsID.help.txt @@ -0,0 +1,3 @@ +The Pipeworks setting AnalyticsID is a Google Analytics ID tracker. If provided, this will be placed on every page in the module. + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Antisocial.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Antisocial.help.txt new file mode 100644 index 0000000..5f09e8d Binary files /dev/null and b/en-us/About_the_Pipeworks_Manifest_-_Antisocial.help.txt differ diff --git a/en-us/About_the_Pipeworks_Manifest_-_AppPoolPasswordSetting.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AppPoolPasswordSetting.help.txt new file mode 100644 index 0000000..43bddb6 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AppPoolPasswordSetting.help.txt @@ -0,0 +1,10 @@ +The AppPoolPassWordSetting defines a SecureSetting that contains the password for the AppPool user. + + +If this and AppPoolUser are provided, the module will be published under a specific app pool user account. This can be very useful for Intranet modules that need to run as a particular user. + +See Also: + + +* [AppPoolUser](/About_the_Pipeworks_Manifest_-_AppPoolUser/) +* [Scripting Securely with SecureSettings](/Scripting_Securely_with_SecureSettings/) diff --git a/en-us/About_the_Pipeworks_Manifest_-_AppPoolUser.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AppPoolUser.help.txt new file mode 100644 index 0000000..4df2e52 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AppPoolUser.help.txt @@ -0,0 +1,11 @@ +The AppPoolUser defines a SecureSetting that contains the password for the AppPool user. + + +If this and AppPoolPasswordSetting are provided, the module will be published under a specific app pool user account. This can be very useful for Intranet modules that need to run as a particular user. + +See Also: + + +* [AppPoolPasswordSetting](/About_the_Pipeworks_Manifest_-_AppPoolPasswordSetting/) + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_AsIntranetSite.help.txt b/en-us/About_the_Pipeworks_Manifest_-_AsIntranetSite.help.txt new file mode 100644 index 0000000..420ba86 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_AsIntranetSite.help.txt @@ -0,0 +1 @@ +If AsIntranetSite is set, the site will be published as an Intranet site. Anonymous Authentication will be turned off, and Windows Authentication will be turned on. diff --git a/en-us/About_the_Pipeworks_Manifest_-_BingValidationKey.help.txt b/en-us/About_the_Pipeworks_Manifest_-_BingValidationKey.help.txt new file mode 100644 index 0000000..133631f --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_BingValidationKey.help.txt @@ -0,0 +1,3 @@ +The BingValidationKey will add a Bing Webmaster Tools validation key to each page. This will enable you to use the site with [Bing Webmaster Tools](http://www.bing.com/toolbox/webmaster) + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Blog.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Blog.help.txt new file mode 100644 index 0000000..4989816 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Blog.help.txt @@ -0,0 +1,30 @@ +The Blog Pipeworks Manifest setting is a hashtable that describes RSS feed information about the site. + + +If provided, the home page will have a link to the blog's RSS feed, and the page will contain the appropriate metadata for search engines. + +If only a name and a description are provided, a feed will be automatically provided by the module's topics. + +Additionally, the Blog section can be used with the Blog Schematic + +Here is an example of a completed blog section that uses the built-in RSS feed: + + + Blog = @{ + Name = "Start-Scripting" + Description = "Stop-WastingTime. Start-Scripting -with PowerShell" + } + +Here is an example that uses it's own feed: + + + Blog = @{ + Name = "Start-Scripting" + Description = "Stop-WastingTime. Start-Scripting -with PowerShell" + Link = "http://blog.start-automating.com/" + } + + + + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Bootstrap.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Bootstrap.help.txt new file mode 100644 index 0000000..59282d1 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Bootstrap.help.txt @@ -0,0 +1,7 @@ +BootStrap (or UseBootStrap) instructs Pipeworks to use Twitter Bootstrap to build a site. + + +If the theme is not present, this will download a twitter bootstrap theme according using color scheme specified in the Style section of the Pipeworks manifest. To refresh the them, delete bootstrap.js from the JS directory of the module and republish. + + +Using Bootstrap enables many features within Pipeworks, and changes the default layout. It enables a navbar that links to the topics and commands in the module. It also changes the deault view to a set of HangingSpans, which will expand out when clicked. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Branding.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Branding.help.txt new file mode 100644 index 0000000..59b3f05 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Branding.help.txt @@ -0,0 +1,7 @@ +The branding section of the Pipeworks manifest describes the branding displayed on each page. + +By default, this will display a link to Start-Automating and to PowerShell Pipeworks. + +It can be blank, or can contain any HTML or Markdown you'd like. + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_CommandOrder.help.txt b/en-us/About_the_Pipeworks_Manifest_-_CommandOrder.help.txt new file mode 100644 index 0000000..3f0be79 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_CommandOrder.help.txt @@ -0,0 +1 @@ +The CommandOrder is a list of strings, and it determines the order commands will be displayed (if there commands are not grouped). By default, commands will be alphabetized. diff --git a/en-us/About_the_Pipeworks_Manifest_-_CommandTemplate.help.txt b/en-us/About_the_Pipeworks_Manifest_-_CommandTemplate.help.txt new file mode 100644 index 0000000..8594f70 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_CommandTemplate.help.txt @@ -0,0 +1 @@ +The CommandTemplate is the PowerShell web template (.pswt) used to display each command in the site. A .pswt allows you to customize the look and feel of a pipeworks page. It directly replaces variable names within the page. By default, this is set to Command.pswt. diff --git a/en-us/About_the_Pipeworks_Manifest_-_CommandTrigger.help.txt b/en-us/About_the_Pipeworks_Manifest_-_CommandTrigger.help.txt new file mode 100644 index 0000000..93ebe09 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_CommandTrigger.help.txt @@ -0,0 +1,12 @@ +The CommandTrigger section of the PipeworksManifest describes when commands will be triggered on a device. At current, only one setting is supported: Shake. + + +Here is an example of a Pipeworks manifest with a CommandTrigger section: + + @{ + CommandTrigger = @{ + "Shake" = "Get-RandomPowerShellTip" + } + } + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Css.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Css.help.txt new file mode 100644 index 0000000..0163436 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Css.help.txt @@ -0,0 +1,15 @@ +The CSS section of the pipeworks manifest describes CSS style pages to use with each page. + + +It is a hashtable, with the key describing the style page and the value containing the path to the page. + + +Here's a quick example of the CSS section: + + @{ + Css = @{ + "PowerShellStyleSheet" = "/CSS/Gangamstyle.css" + } + } + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_DefaultCommand.help.txt b/en-us/About_the_Pipeworks_Manifest_-_DefaultCommand.help.txt new file mode 100644 index 0000000..1a841e2 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_DefaultCommand.help.txt @@ -0,0 +1,17 @@ +The DefaultCommand section of the Pipeworks manifest describes the command that will run by default when a user visits the page. + + +It is a hashtable, and must contain the name of the command. It may contain a nested hashtable called Parameter, which will provide parameters to the command. + + +DefaultCommands do not need to be registered as web commands, since they cannot accept open-ended input. + + +Here is an example: + + + @{ + DefaultCommand = @{ + Name = 'Get-RandomPowerShellTip' + } + } \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_DefaultTemplate.help.txt b/en-us/About_the_Pipeworks_Manifest_-_DefaultTemplate.help.txt new file mode 100644 index 0000000..7b566f9 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_DefaultTemplate.help.txt @@ -0,0 +1 @@ +The Default Template describes the PowerShell Web Template (.pswt) used to display the main page. A .pswt allows you to customize the look and feel of a pipeworks page. It directly replaces variable names within the page. By default, this is set to Default.pswt. diff --git a/en-us/About_the_Pipeworks_Manifest_-_DomainSchematics.help.txt b/en-us/About_the_Pipeworks_Manifest_-_DomainSchematics.help.txt new file mode 100644 index 0000000..0a15736 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_DomainSchematics.help.txt @@ -0,0 +1,14 @@ +The DomainSchematics section describes how the Pipeworks module will be published. It is a hashtable, containing a series of domains and a list of schematics to use while publishing. + + +A site can be published to multiple domains. To do this, separate each item with a | + + +Here's an example of a site that would be published to 4 different URLs, using the default schematic: + + @{ + DomainSchematics = @{ + "StartLearningPowerShell.com | Start-LearningPowershell.com | www.Start-LearningPowershell.com | www.StartLearningPowerShell.com" = + "Default" + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_Download.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Download.help.txt new file mode 100644 index 0000000..f0c3d41 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Download.help.txt @@ -0,0 +1 @@ +Download is a Hashtable of files that will be downloaded when the module is published. The key of the hashtable is the URL, and the value is the local path of the downloaded file, relative to the output directory. diff --git a/en-us/About_the_Pipeworks_Manifest_-_ExecutionTimeout.help.txt b/en-us/About_the_Pipeworks_Manifest_-_ExecutionTimeout.help.txt new file mode 100644 index 0000000..c4c9131 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_ExecutionTimeout.help.txt @@ -0,0 +1,5 @@ +The ExecutionTimeout allows you to customize the ASP.Net execution timeout for each page. + +The value must be a string that can be a timespan (for instance: '00:02:00'). + +By default, the timeout is 2 minutes. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Facebook.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Facebook.help.txt new file mode 100644 index 0000000..72b2049 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Facebook.help.txt @@ -0,0 +1,13 @@ +The facebook section adds facebook like links, and allows using Facebook as an authentication method. It is a hashtable, and contains the AppId: + + +Here is an example: + + @{ + Facebook = @{ + AppId = '452858484777409' + } + } + + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_GitIt.help.txt b/en-us/About_the_Pipeworks_Manifest_-_GitIt.help.txt new file mode 100644 index 0000000..37d450b --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_GitIt.help.txt @@ -0,0 +1,6 @@ +GitIt describes projects to pull from Git prior to deployment. + + +See Also: + +* [Getting GitIt](/Getting_GitIt/) diff --git a/en-us/About_the_Pipeworks_Manifest_-_GoogleMerchantId.help.txt b/en-us/About_the_Pipeworks_Manifest_-_GoogleMerchantId.help.txt new file mode 100644 index 0000000..fc84955 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_GoogleMerchantId.help.txt @@ -0,0 +1 @@ +If provided the GoogleMerchantID can add Google Checkout integration for items on a page. Any products rendered within the page will include Google Checkout links. diff --git a/en-us/About_the_Pipeworks_Manifest_-_GoogleSiteVerification.help.txt b/en-us/About_the_Pipeworks_Manifest_-_GoogleSiteVerification.help.txt new file mode 100644 index 0000000..d38907b --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_GoogleSiteVerification.help.txt @@ -0,0 +1 @@ +The GoogleSiteVerification setting of the Pipeworks Manifest allows adds site verification tags for Google WebMaster Tools. It also adds a +1 button to each page, until AntiSocial is specified. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Group.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Group.help.txt new file mode 100644 index 0000000..a29e8d1 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Group.help.txt @@ -0,0 +1,20 @@ +The Group describes how commands and topics will be displayed on the front page and in menus throughout the site. + + +It is a list of hashtables, displayed in order. The key of each item in the hashtable will the name of the group. The value will be a list of commands or topics within the group. + + +For example, here's the Group section for Pipeworks: + + + @{ + Group = @{ + "Getting Started" = "About PowerShell Pipeworks", "Pipeworks Quickstart", "The Pipeworks Manifest", 'From Script To Software Service', "Powerful Powershell Formatting", "Scripting Securely with SecureSettings", "NOHtml Sites", "A Simple StockTicker", "Creating a CitiBike Station Finder" + }, @{ + "Play with Pipeworks" = "Write-ASPDotNetScriptPage", "ConvertFrom-Markdown", "Making Editing Easier With Markdown", "Write-Crud", "Write-ScriptHTML", "Making Tables with Out-HTML", "Working with Write-Link" + }, @{ + "Connecting the Clouds" = "Building with Bootstrap", "Get-Paid with Stripe", "Getting GitIt", "Get-Web Content From Anywhere", "Pick up the Phone with Pipeworks", "Implicit Texting with Twilio", "The Wonders of Wolfram Alpha","JQueryUI in Pipeworks", 'New-WebPage And JQuery', 'New-Region And JQueryUI', "Pipeworks Writes CRUD", "Managing Amazon Web Services with PowerShell Pipeworks", "Using Azure Table Storage in Pipeworks", "Publishing Pipeworks to Azure", 'Looking Up Locations With Resolve-Location', 'Simplifying Slideshows' + }, @{ + "Join Windows and Web" = "Why Windows", "Scripting with Superglue", "Integrated Intranet", "Simpler SEO" + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_HiddenTopic.help.txt b/en-us/About_the_Pipeworks_Manifest_-_HiddenTopic.help.txt new file mode 100644 index 0000000..aab4485 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_HiddenTopic.help.txt @@ -0,0 +1 @@ +HiddenTopic (or HiddenTopics) is a list of topics that will be hidden from normal view. You can still visit the topic page directly to visit the topic, but it will not show up in menus. diff --git a/en-us/About_the_Pipeworks_Manifest_-_HideUngrouped.help.txt b/en-us/About_the_Pipeworks_Manifest_-_HideUngrouped.help.txt new file mode 100644 index 0000000..b365793 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_HideUngrouped.help.txt @@ -0,0 +1 @@ +If HideUngroupedHelp is set, topics that are not in a Group will not be displayed in menus. diff --git a/en-us/About_the_Pipeworks_Manifest_-_IgnoreBuiltInCommand.help.txt b/en-us/About_the_Pipeworks_Manifest_-_IgnoreBuiltInCommand.help.txt new file mode 100644 index 0000000..0f6f8ad --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_IgnoreBuiltInCommand.help.txt @@ -0,0 +1 @@ +If set, then built in commands cannot be used within a Pipeworks module (only commands from the current module can be). If not set, any command can be accessed via URL. If the command is not set in WebCommand, the help handler will be used. This is why [http://powershellpipeworks.com/Get-ChildItem/](http://powershellpipeworks.com/Get-ChildItem/) works, and isn't a threat. diff --git a/en-us/About_the_Pipeworks_Manifest_-_InnerRegion.help.txt b/en-us/About_the_Pipeworks_Manifest_-_InnerRegion.help.txt new file mode 100644 index 0000000..a833648 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_InnerRegion.help.txt @@ -0,0 +1 @@ +The InnerRegion is a Pipeworks Manifest setting that describes how the inner regions within a group will be rendered. It is as Hashtable, and the input in the Hashtable will be provided to the New-Region command. diff --git a/en-us/About_the_Pipeworks_Manifest_-_JQueryUITheme.help.txt b/en-us/About_the_Pipeworks_Manifest_-_JQueryUITheme.help.txt new file mode 100644 index 0000000..598069a --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_JQueryUITheme.help.txt @@ -0,0 +1 @@ +The JQueryUITheme describes the them used for JQueryUI. This can be any theme on the default CDNs, or 'Custom', which will use a theme located in the 'custom' directory. You can build the theme using [JQueryUI's ThemeRoller](http://jqueryUI.com/themeroller/). diff --git a/en-us/About_the_Pipeworks_Manifest_-_JavaScript.help.txt b/en-us/About_the_Pipeworks_Manifest_-_JavaScript.help.txt new file mode 100644 index 0000000..55cb9be --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_JavaScript.help.txt @@ -0,0 +1 @@ +The Javascript section of the Pipeworks manifest lists javascript files to include on all pages. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Keyword.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Keyword.help.txt new file mode 100644 index 0000000..367f5ed --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Keyword.help.txt @@ -0,0 +1 @@ +The keyword setting of the Pipeworks manifest contains a list of search keywords to add to each page of the site. diff --git a/en-us/About_the_Pipeworks_Manifest_-_LiveConnect.help.txt b/en-us/About_the_Pipeworks_Manifest_-_LiveConnect.help.txt new file mode 100644 index 0000000..132a6e2 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_LiveConnect.help.txt @@ -0,0 +1,17 @@ +The LiveConnect section of the Pipeworks manifest describes Live Connect application data. + + +It contains the ClientID of the app, as well as a secure setting pointing to the Client Secret Key. + + +Here is an example: + + + @{ + LiveConnect = @{ + ClientId = '00000000440DBD88' + ClientSecretSetting = 'StartLearningPowerShellLiveSecret' + } + } + + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Logo.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Logo.help.txt new file mode 100644 index 0000000..27fabc2 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Logo.help.txt @@ -0,0 +1,7 @@ +The logo of the pipeworks manifest is a URL to a logo file to use on all pages. The url should be root-relative (i.e. /Assets/Logo.png) + +Here is an example: + + @{ + Logo = "/Assets/PowershellPipeworks_150.png" + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_MainRegion.help.txt b/en-us/About_the_Pipeworks_Manifest_-_MainRegion.help.txt new file mode 100644 index 0000000..7832310 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_MainRegion.help.txt @@ -0,0 +1,2 @@ +The InnerRegion is a Pipeworks Manifest setting that describes how the main region of a page will be rendered. It is as Hashtable, and the input in the Hashtable will be provided to the New-Region command. + diff --git a/en-us/About_the_Pipeworks_Manifest_-_MaximumRequestLength.help.txt b/en-us/About_the_Pipeworks_Manifest_-_MaximumRequestLength.help.txt new file mode 100644 index 0000000..b695598 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_MaximumRequestLength.help.txt @@ -0,0 +1 @@ +The Maximum Request Length describes the maximum length of a request to an ASP.NET page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_MemberTopic.help.txt b/en-us/About_the_Pipeworks_Manifest_-_MemberTopic.help.txt new file mode 100644 index 0000000..d7d36eb --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_MemberTopic.help.txt @@ -0,0 +1 @@ +The MemberTopics section is a list of strings, and it describes topics only visible to a person after they have logged in. diff --git a/en-us/About_the_Pipeworks_Manifest_-_ModuleTemplate.help.txt b/en-us/About_the_Pipeworks_Manifest_-_ModuleTemplate.help.txt new file mode 100644 index 0000000..1f0eb48 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_ModuleTemplate.help.txt @@ -0,0 +1,2 @@ +The moduleTemplate is the PowerShell web template (.pswt) used to display the core module page. A .pswt allows you to customize the look and feel of a pipeworks page. It directly replaces variable names within the page. By default, this is set to Module.pswt. + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Nest.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Nest.help.txt new file mode 100644 index 0000000..449f222 Binary files /dev/null and b/en-us/About_the_Pipeworks_Manifest_-_Nest.help.txt differ diff --git a/en-us/About_the_Pipeworks_Manifest_-_Organization.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Organization.help.txt new file mode 100644 index 0000000..8080501 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Organization.help.txt @@ -0,0 +1,11 @@ +The Organization section describes information about the publishing organization, and adds some contact information to each page. + + +This example adds a phone number and an email link: + + @{ + Organization = @{ + Telephone = "+1(206)607-6555" + Email = "info@start-automating.com" + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_PaymentProcessing.help.txt b/en-us/About_the_Pipeworks_Manifest_-_PaymentProcessing.help.txt new file mode 100644 index 0000000..375a1cb --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_PaymentProcessing.help.txt @@ -0,0 +1,10 @@ +The Payment Processing section describes how the site will tie into various example platforms. This is required for Cost to work on commands. + + +This example will setup payments via PayPal for the email sales@start-automating.com + + @{ + PaymentProcessing = @{ + PaypalEmail = 'sales@start-automating.com' + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_PoolSize.help.txt b/en-us/About_the_Pipeworks_Manifest_-_PoolSize.help.txt new file mode 100644 index 0000000..bbc9c70 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_PoolSize.help.txt @@ -0,0 +1 @@ +The PoolSize sets the number of RunspacePools set up to handle requests. The default is 2. The more runspace pools used, the more memory your site will consume, and the more users it can handle at the same time. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Port.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Port.help.txt new file mode 100644 index 0000000..3def9bf --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Port.help.txt @@ -0,0 +1 @@ +The Port setting of the Pipeworks manifest determines what port to use when publishing the site as an Intranet site. diff --git a/en-us/About_the_Pipeworks_Manifest_-_PrivacyPolicy.help.txt b/en-us/About_the_Pipeworks_Manifest_-_PrivacyPolicy.help.txt new file mode 100644 index 0000000..c937312 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_PrivacyPolicy.help.txt @@ -0,0 +1,4 @@ +The PrivacyPolicy setting of the Pipeworks manifest contains any legal privacy policy required. If none is provided, a default privacy policy will be used. + + +For an example of this default, visit [http://powershellpipeworks.com/?ShowPrivacyPolicy=true](http://powershellpipeworks.com/?ShowPrivacyPolicy=true) diff --git a/en-us/About_the_Pipeworks_Manifest_-_PubCenter.help.txt b/en-us/About_the_Pipeworks_Manifest_-_PubCenter.help.txt new file mode 100644 index 0000000..fd0acdb --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_PubCenter.help.txt @@ -0,0 +1,14 @@ +The PubCenter section of the Pipeworks manifest describes how PubCenter ads will be placed in a Win8 application. + + +Like the AdSense section, it requires an ID, and a BottomAdSlot or TopAdSlot are supported: + + +Here's an example: + + @{ + PubCenter = @{ + ApplicationId = "1b9271e7-0be4-4ef1-84c3-9e8f921c1b4a" + BottomAdUnit = "10048228" + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_PublishDirectory.help.txt b/en-us/About_the_Pipeworks_Manifest_-_PublishDirectory.help.txt new file mode 100644 index 0000000..1c1c542 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_PublishDirectory.help.txt @@ -0,0 +1,2 @@ +The PublishDirectory setting in the Pipeworks manifest describes where the module should be published. By default, it will be published beneath c:\inetpub\wwwroot + diff --git a/en-us/About_the_Pipeworks_Manifest_-_Schema.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Schema.help.txt new file mode 100644 index 0000000..14792b9 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Schema.help.txt @@ -0,0 +1,111 @@ +The schema section of the Pipeworks manifest describes itemdata schemas used on the page. This is useful for making your site HTML5 compliant, and also for allowing people to interact more reliably with the site by using Get-Web -AsMicrodata + + +Here's an example that declares a few schemas: + + @{ + Schema = @{ + GamerAchievementInfo = @{ + Name='GamerAchievementInfo' + PSTypeName='http://shouldbeonschema.org/Class' + Url = 'http://UnlockAchievement.com/GamerAchievementInfo/' + Property = @{ + Name='Name' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The name of the item.' + }, @{ + Name='Description' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='A short description of the item.' + }, @{ + Name='Image' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='URL of an image of the item.' + TypeName = 'Url' + }, @{ + Name='Url' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The namespace the class is defined in.' + TypeName = 'Url' + }, @{ + Name='GamerTag' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The gamertag or ID of an the gamer' + TypeName = 'Url' + }, @{ + Name='Games' + PSTypeName='http://shouldbeonschema.org/Property' + Description='A list of games the gamer owns or has played' + TypeName = 'http://UnlockAchievement.com/GameAchievementInfo' + } + + Description="Achievement information for a gamer." + + ParentClass = @{ + Name = 'Thing' + Url = 'http://schema.org/Thing' + PSTypeName = 'http://shouldbeonschema.org/Class' + } + } + + GameAchievementInfo = @{ + Name='GameAchievementInfo' + PSTypeName='http://shouldbeonschema.org/Class' + Url = 'http://UnlockAchievement.com/GameAchievementInfo/' + Description="Achievement information for a game." + + ParentClass = @{ + Name = 'Thing' + Url = 'http://schema.org/Thing' + PSTypeName = 'http://shouldbeonschema.org/Class' + } + + + + Property = @{ + Name='Name' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The name of the item.' + }, @{ + Name='Description' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='A short description of the item.' + }, @{ + Name='Image' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='URL of an image of the item.' + TypeName = 'Url' + }, @{ + Name='Url' + DeclaringType = 'http://schema.org/Thing' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The namespace the class is defined in.' + TypeName = 'Url' + }, @{ + Name='PossibleScore' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The possible score for a game' + TypeName = 'Integer' + }, @{ + Name='MyScore' + PSTypeName='http://shouldbeonschema.org/Property' + Description='My score for a game' + TypeName = 'Integer' + }, @{ + Name='PercentComplete' + PSTypeName='http://shouldbeonschema.org/Property' + Description='The percent of achievements unlocked for the game' + TypeName = 'Float' + } + } + + + } + } \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_SecureSetting.help.txt b/en-us/About_the_Pipeworks_Manifest_-_SecureSetting.help.txt new file mode 100644 index 0000000..99a7eef --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_SecureSetting.help.txt @@ -0,0 +1,6 @@ +The SecureSetting section of the Pipeworks manifest is a list of SecureSettings that will be copied into the web.config when the module is published. Get-SecureSetting will retreive these settings when used inside of a website. This enables seamless secure scripting between a development environment and a published page. + + +See also: + +* [Scripting Securely with SecureSettings](/Scripting_Securely_with_SecureSettings/) diff --git a/en-us/About_the_Pipeworks_Manifest_-_ShortCut.help.txt b/en-us/About_the_Pipeworks_Manifest_-_ShortCut.help.txt new file mode 100644 index 0000000..a0a6f7c --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_ShortCut.help.txt @@ -0,0 +1,11 @@ +The shortcut section of the Pipeworks manifest describes local shortcuts to add to the Start Menu when the module is installed. This enables easier use of PowerShell built tools by everyday users, and easy access to online help for a module. + + +Here's an example: + + @{ + Shortcut = @{ + "Asset Inventory Tool" = "Show-Asset -Show" + "Help" = "http://startlearningpowershell.com" + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_ShowTweet.help.txt b/en-us/About_the_Pipeworks_Manifest_-_ShowTweet.help.txt new file mode 100644 index 0000000..c9632a3 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_ShowTweet.help.txt @@ -0,0 +1 @@ +If the the ShowTweet setting is provided in the Pipeworks Manifest, each page will show a Tweet this link on each page. This setting can be overridden by using AntiSocial. diff --git a/en-us/About_the_Pipeworks_Manifest_-_SimplePages.help.txt b/en-us/About_the_Pipeworks_Manifest_-_SimplePages.help.txt new file mode 100644 index 0000000..c86148e Binary files /dev/null and b/en-us/About_the_Pipeworks_Manifest_-_SimplePages.help.txt differ diff --git a/en-us/About_the_Pipeworks_Manifest_-_SlideShow.help.txt b/en-us/About_the_Pipeworks_Manifest_-_SlideShow.help.txt new file mode 100644 index 0000000..3ceb84c --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_SlideShow.help.txt @@ -0,0 +1,17 @@ +The SlideShow section of the Pipeworks manifest describes a SlideShow that will be played on the landing page. SlideShow items may be HTML or image links. + +Here's an example: + + @{ + Slideshow = @{ + Slides = "/Assets/Screenshot_NowPlaying.png", + "/Assets/screenshot_DJMode.png", + "/Assets/Screenshot_PlayingAndRaining.png", + "/Assets/Screenshot_KaraokeMode.png", + "/Assets/screenshot_Raining.png", + "/Assets/Screenshot_VisualSearch.png", + "/Assets/Screenshot_Edit_Lyrics.png", + "/Assets/Screenshot_EditMetaData.png", + "/Assets/Screenshot_PlayingAndRaining.png" + } + } \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_Stealth.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Stealth.help.txt new file mode 100644 index 0000000..7cea2dd --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Stealth.help.txt @@ -0,0 +1 @@ +If the Stealth setting is provided in the Pipeworks Manifest, the module will not generate a robots.txt or sitemap.xml, and all pages will include meta information dissuading search engines from crawling the page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Style.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Style.help.txt new file mode 100644 index 0000000..707ba14 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Style.help.txt @@ -0,0 +1,62 @@ +The Style section of the Pipeworks manifest declares a CSS style to use on each page. It is also used to provide color scheme information when generating a bootstrap theme or Win8 app. + +This section is almost exactly like CSS, except that nesting happens with Hashtables, not whitespace, and =, not : is used to separate values. + + +Here's a simple sample: + + @{ + Style = @{ + Body = @{ + "font-family" = "Segoe UI" + "font-size" = "1.05em" + + "background-color" = "#FFFFFF" + "color" = "#09952E" + } + 'a' = @{ + 'color' = "#09952E" + } + } + + } + + +Here is a more comple example: + + @{ + Style = @{ + body = @{ + "font-family" = "'Segoe UI', 'Segoe UI Symbol', Helvetica, Arial, sans-serif" + 'font-size' = "1.1em" + 'color' = '#0248B2' + 'background-color' = '#FFFFFF' + } + 'a' = @{ + 'color' = '#012456' + } + + '.MajorMenuItem' = @{ + 'font-size' = 'large' + } + '.MinorMenuItem' = @{ + 'font-size' = 'medium' + } + '.ExplanationParagraph' = @{ + 'font-size' = 'medium' + 'text-indent' = '-10px' + } + '.ModuleWalkthruExplanation' = @{ + 'font-size' = 'medium' + 'margin-right' = '3%' + } + + '.ModuleWalkthruOutput' = @{ + 'font-size' = 'medium' + } + '.PowerShellColorizedScript' = @{ + 'font-size' = 'medium' + } + + } + } \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_Table.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Table.help.txt new file mode 100644 index 0000000..82224e9 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Table.help.txt @@ -0,0 +1 @@ +The Table section of the Pipeworks manifest describes information stored in a public azure table that the module service can access. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Template.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Template.help.txt new file mode 100644 index 0000000..0e98423 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Template.help.txt @@ -0,0 +1 @@ +The template setting of the Pipeworks manifest describes a PowerShell web template (.pswt) to use for each page on the site. diff --git a/en-us/About_the_Pipeworks_Manifest_-_TopicTemplate.help.txt b/en-us/About_the_Pipeworks_Manifest_-_TopicTemplate.help.txt new file mode 100644 index 0000000..0342531 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_TopicTemplate.help.txt @@ -0,0 +1,2 @@ +The TopicTemplate is the PowerShell web template (.pswt) used to display each command in the site. A .pswt allows you to customize the look and feel of a pipeworks page. It directly replaces variable names within the page. By default, this is set to Topic.pswt. + diff --git a/en-us/About_the_Pipeworks_Manifest_-_TrustedWalkthrus.help.txt b/en-us/About_the_Pipeworks_Manifest_-_TrustedWalkthrus.help.txt new file mode 100644 index 0000000..193ccc2 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_TrustedWalkthrus.help.txt @@ -0,0 +1 @@ +The TrustedWalkthrus list is a set of walkthrus that the site trusts enough to run. These walkthrus should run quickly, not require user input, not require elevation, and yet be helpful in demonstrating functionality. diff --git a/en-us/About_the_Pipeworks_Manifest_-_Tweet.help.txt b/en-us/About_the_Pipeworks_Manifest_-_Tweet.help.txt new file mode 100644 index 0000000..0e27f2f --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_Tweet.help.txt @@ -0,0 +1 @@ +If the Pipeworks Manifest includes the Tweet setting, a tweet this link will be added to each page. This setting can be overridden with .AntiSocial. diff --git a/en-us/About_the_Pipeworks_Manifest_-_TwitterId.help.txt b/en-us/About_the_Pipeworks_Manifest_-_TwitterId.help.txt new file mode 100644 index 0000000..defbc86 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_TwitterId.help.txt @@ -0,0 +1,2 @@ +If the Pipeworks Manifest includes the TwitterID setting, a follow for that TwitterID will be added to each page. This setting can be overridden with .AntiSocial. + diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseBootstrap.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseBootstrap.help.txt new file mode 100644 index 0000000..02d8342 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseBootstrap.help.txt @@ -0,0 +1,8 @@ +BootStrap (or UseBootStrap) instructs Pipeworks to use Twitter Bootstrap to build a site. + + +If the theme is not present, this will download a twitter bootstrap theme according using color scheme specified in the Style section of the Pipeworks manifest. To refresh the them, delete bootstrap.js from the JS directory of the module and republish. + + +Using Bootstrap enables many features within Pipeworks, and changes the default layout. It enables a navbar that links to the topics and commands in the module. It also changes the deault view to a set of HangingSpans, which will expand out when clicked. + diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseGRaphael.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseGRaphael.help.txt new file mode 100644 index 0000000..2c3729f --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseGRaphael.help.txt @@ -0,0 +1 @@ +If UseGraphael is provided in the Pipeworks manifest, Pipeworks will download [GRaphael](http://g.raphaeljs.com/) for the creation of graphs within the page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseJQuery.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseJQuery.help.txt new file mode 100644 index 0000000..92a80d8 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseJQuery.help.txt @@ -0,0 +1 @@ +If UseJQuery is provided, JQuery will be downloaded and used in each page in the site. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseJQueryUI.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseJQueryUI.help.txt new file mode 100644 index 0000000..ddc6125 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseJQueryUI.help.txt @@ -0,0 +1 @@ +If UseJQueryUI is set to true in the Pipeworks manifest, JQueryUI will be used throughout the site. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseRaphael.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseRaphael.help.txt new file mode 100644 index 0000000..c206cab --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseRaphael.help.txt @@ -0,0 +1 @@ +If UseGraphael is provided in the Pipeworks manifest, Pipeworks will download [Raphael](http://raphaeljs.com/) for the creation of graphs within the page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseShiv.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseShiv.help.txt new file mode 100644 index 0000000..222f8c1 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseShiv.help.txt @@ -0,0 +1 @@ +If UseShiv is provided, the HTML5 shiv tool for IE will be downloaded, and installed on each page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UseTableSorter.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UseTableSorter.help.txt new file mode 100644 index 0000000..22e6021 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UseTableSorter.help.txt @@ -0,0 +1 @@ +If UseTableSorter is provided, the TableSorter JQuery plugin will be downloaded, and used on each page. diff --git a/en-us/About_the_Pipeworks_Manifest_-_UserTable.help.txt b/en-us/About_the_Pipeworks_Manifest_-_UserTable.help.txt new file mode 100644 index 0000000..9efebd7 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_UserTable.help.txt @@ -0,0 +1,12 @@ +The UserTable section of the Pipeworks manifest describes how users of a site will be stored in Azure Table Storage. + +Here's an example (from the site [StartLearningPowerShell.com](http://start-learningpowershell.com/) + + @{ + UserTable = @{ + Name = 'StartLearningPowerShellUsers' + Partition = 'Users' + StorageAccountSetting = 'AzureStorageAccountName' + StorageKeySetting = 'AzureStorageAccountKey' + } + } \ No newline at end of file diff --git a/en-us/About_the_Pipeworks_Manifest_-_WebCommand.help.txt b/en-us/About_the_Pipeworks_Manifest_-_WebCommand.help.txt new file mode 100644 index 0000000..5c138b9 --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_WebCommand.help.txt @@ -0,0 +1,55 @@ +The WebCommand section of the Pipeworks manifest describes which commands will be usable as web services, and what options to use when running a command. + +It is a hashtable of hashtables, where the key of each hashtable is the name of the command and the value is a hashtable containing parameters for the command Invoke-WebCommand. + + +This is the WebCommand section of the Pipework's manifest: + + @{ + WebCommand = @{ + "Write-Link" = @{ + HideParameter = "AmazonAccessKey", "AmazonSecretKey", "AmazonReturnUrl", "AmazonInputUrl", + "AmazonIpnUrl", "UseOAth", "CollectShippingAddress", "AmazonAbandonUrl", "ToFacebookLogin", + "FacebookAppId", "ModuleServiceUrl", "FacebookLoginScope", "AmazonPaymentsAccountID", "GoogleCheckoutMerchantID", "SortedLinkTable" + PlainOutput = $true + + } + "New-PipeworksManifest" = @{ + ContentType = 'text/plain' + } + + + "ConvertFrom-Markdown" = @{ + ParameterAlias = @{ + 'm' = 'Markdown' + 'md' = 'Markdown' + } + FriendlyName = "Mess With Markdown" + HideParameter = 'Splat' + } + + "Write-ScriptHTML" = @{ + + PlainOutput = $true + HideParameter = @('Palette', 'Start', 'End', 'Script') + ParameterOrder = 'Text' + ParameterAlias = @{ + 't'= 'Text' + + } + FriendlyName = "Show Scripts as HTML" + } + "Write-ASPDotNetScriptPage" = @{ + + ContentType = "text/plain" + HideParameter = @('MasterPage', 'CodeFile', 'Inherit', 'RunScriptMethod', 'FileName') + FriendlyName = "PowerShell in ASP.NET" + } + + "Write-Crud" = @{ + ContentType = "text/plain" + PlainOutput = $true + } + + } + } diff --git a/en-us/About_the_Pipeworks_Manifest_-_WebWalkthrus.help.txt b/en-us/About_the_Pipeworks_Manifest_-_WebWalkthrus.help.txt new file mode 100644 index 0000000..0f3a1db --- /dev/null +++ b/en-us/About_the_Pipeworks_Manifest_-_WebWalkthrus.help.txt @@ -0,0 +1 @@ +The WebWalkthrus setting of the Pipeworks manifest lists walkthrus whose output should be considered HTML. Each WebWalkthru should also be in the Trusted Walkthru list. If a trusted walkthru runs, and is not in WebWalkthrus, it's result will be piped into Out-HTML. diff --git a/en-us/Building_With_Bootstrap.walkthru.help.txt b/en-us/Building_With_Bootstrap.walkthru.help.txt new file mode 100644 index 0000000..50e779b Binary files /dev/null and b/en-us/Building_With_Bootstrap.walkthru.help.txt differ diff --git a/en-us/Building_with_Blob_Storage.walkthru.help.txt b/en-us/Building_with_Blob_Storage.walkthru.help.txt new file mode 100644 index 0000000..bb34f17 Binary files /dev/null and b/en-us/Building_with_Blob_Storage.walkthru.help.txt differ diff --git a/en-us/Creating_a_CitiBike_Station_Finder.walkthru.help.txt b/en-us/Creating_a_CitiBike_Station_Finder.walkthru.help.txt new file mode 100644 index 0000000..59d7aa6 Binary files /dev/null and b/en-us/Creating_a_CitiBike_Station_Finder.walkthru.help.txt differ diff --git a/en-us/From_Script_To_Software_Service.Walkthru.help.txt b/en-us/From_Script_To_Software_Service.Walkthru.help.txt new file mode 100644 index 0000000..b234719 Binary files /dev/null and b/en-us/From_Script_To_Software_Service.Walkthru.help.txt differ diff --git a/en-us/Get-Paid_with_Stripe.walkthru.help.txt b/en-us/Get-Paid_with_Stripe.walkthru.help.txt new file mode 100644 index 0000000..131d3a7 Binary files /dev/null and b/en-us/Get-Paid_with_Stripe.walkthru.help.txt differ diff --git a/en-us/Get-Web_Content_From_Anywhere.walkthru.help.txt b/en-us/Get-Web_Content_From_Anywhere.walkthru.help.txt new file mode 100644 index 0000000..4c0a3b5 Binary files /dev/null and b/en-us/Get-Web_Content_From_Anywhere.walkthru.help.txt differ diff --git a/en-us/Getting_GitIt.walkthru.help.txt b/en-us/Getting_GitIt.walkthru.help.txt new file mode 100644 index 0000000..cced0cd Binary files /dev/null and b/en-us/Getting_GitIt.walkthru.help.txt differ diff --git a/en-us/Getting_Started_With_Pipeworks.md b/en-us/Getting_Started_With_Pipeworks.md new file mode 100644 index 0000000..23eb058 Binary files /dev/null and b/en-us/Getting_Started_With_Pipeworks.md differ diff --git a/en-us/Implicit_Texting_With_Twilio.help.txt b/en-us/Implicit_Texting_With_Twilio.help.txt new file mode 100644 index 0000000..d6f618e Binary files /dev/null and b/en-us/Implicit_Texting_With_Twilio.help.txt differ diff --git a/en-us/Integrated_Intranet.help.txt b/en-us/Integrated_Intranet.help.txt new file mode 100644 index 0000000..a803e53 Binary files /dev/null and b/en-us/Integrated_Intranet.help.txt differ diff --git a/en-us/JQueryUI_In_Pipeworks.help.txt b/en-us/JQueryUI_In_Pipeworks.help.txt new file mode 100644 index 0000000..84cb2cc Binary files /dev/null and b/en-us/JQueryUI_In_Pipeworks.help.txt differ diff --git a/en-us/Looking_Up_Locations_With_Resolve-Location.walkthru.help.txt b/en-us/Looking_Up_Locations_With_Resolve-Location.walkthru.help.txt new file mode 100644 index 0000000..5bc0966 Binary files /dev/null and b/en-us/Looking_Up_Locations_With_Resolve-Location.walkthru.help.txt differ diff --git a/en-us/Making_Editing_Easier_With_Markdown.walkthru.help.txt b/en-us/Making_Editing_Easier_With_Markdown.walkthru.help.txt new file mode 100644 index 0000000..fc44242 Binary files /dev/null and b/en-us/Making_Editing_Easier_With_Markdown.walkthru.help.txt differ diff --git a/en-us/Making_Tables_With_Out-HTML.walkthru.help.txt b/en-us/Making_Tables_With_Out-HTML.walkthru.help.txt new file mode 100644 index 0000000..f641453 Binary files /dev/null and b/en-us/Making_Tables_With_Out-HTML.walkthru.help.txt differ diff --git a/en-us/Managing_Amazon_Web_Services_With_PowerShell_Pipeworks.walkthru.help.txt b/en-us/Managing_Amazon_Web_Services_With_PowerShell_Pipeworks.walkthru.help.txt new file mode 100644 index 0000000..842adcf Binary files /dev/null and b/en-us/Managing_Amazon_Web_Services_With_PowerShell_Pipeworks.walkthru.help.txt differ diff --git a/en-us/New-Region_And_JQueryUI.walkthru.help.txt b/en-us/New-Region_And_JQueryUI.walkthru.help.txt new file mode 100644 index 0000000..46a0b00 Binary files /dev/null and b/en-us/New-Region_And_JQueryUI.walkthru.help.txt differ diff --git a/en-us/New-Webpage_and_JQuery.walkthru.help.txt b/en-us/New-Webpage_and_JQuery.walkthru.help.txt new file mode 100644 index 0000000..f87814f Binary files /dev/null and b/en-us/New-Webpage_and_JQuery.walkthru.help.txt differ diff --git a/en-us/NoHTML_Sites.help.txt b/en-us/NoHTML_Sites.help.txt new file mode 100644 index 0000000..c3006ac Binary files /dev/null and b/en-us/NoHTML_Sites.help.txt differ diff --git a/en-us/Pick_up_the_Phone_with_Pipeworks.walkthru.help.txt b/en-us/Pick_up_the_Phone_with_Pipeworks.walkthru.help.txt new file mode 100644 index 0000000..9b64cd0 Binary files /dev/null and b/en-us/Pick_up_the_Phone_with_Pipeworks.walkthru.help.txt differ diff --git a/en-us/Pipeworks_Quickstart.walkthru.help.txt b/en-us/Pipeworks_Quickstart.walkthru.help.txt new file mode 100644 index 0000000..22c0111 Binary files /dev/null and b/en-us/Pipeworks_Quickstart.walkthru.help.txt differ diff --git a/en-us/Pipeworks_Writes_CRUD.walkthru.help.txt b/en-us/Pipeworks_Writes_CRUD.walkthru.help.txt new file mode 100644 index 0000000..ff3fdf7 Binary files /dev/null and b/en-us/Pipeworks_Writes_CRUD.walkthru.help.txt differ diff --git a/en-us/Powerful_Powershell_Formatting.help.txt b/en-us/Powerful_Powershell_Formatting.help.txt new file mode 100644 index 0000000..231a297 Binary files /dev/null and b/en-us/Powerful_Powershell_Formatting.help.txt differ diff --git a/en-us/Publishing_Pipeworks_To_Azure.walkthru.help.txt b/en-us/Publishing_Pipeworks_To_Azure.walkthru.help.txt new file mode 100644 index 0000000..4fa4c38 Binary files /dev/null and b/en-us/Publishing_Pipeworks_To_Azure.walkthru.help.txt differ diff --git a/en-us/Scripting_Securely_with_SecureSettings.walkthru.help.txt b/en-us/Scripting_Securely_with_SecureSettings.walkthru.help.txt new file mode 100644 index 0000000..55c85fd Binary files /dev/null and b/en-us/Scripting_Securely_with_SecureSettings.walkthru.help.txt differ diff --git a/en-us/Scripting_With_Superglue.help.txt b/en-us/Scripting_With_Superglue.help.txt new file mode 100644 index 0000000..362979c Binary files /dev/null and b/en-us/Scripting_With_Superglue.help.txt differ diff --git a/en-us/Simpler_SEO.walkthru.help.txt b/en-us/Simpler_SEO.walkthru.help.txt new file mode 100644 index 0000000..8d7eef3 --- /dev/null +++ b/en-us/Simpler_SEO.walkthru.help.txt @@ -0,0 +1,28 @@ +# Search Engine Optimization is very simple in PowerShell Pipeworks. +# PowerShell Pipeworks can easily integrate with [Google's Webmaster tools](http://www.google.com/webmasters/tools/). +New-PipeworksManifest -Name ASampleModule -GoogleSiteVerification xjCcGADm2Pnu7fF3WZnPj5UYND9SVqB3qzJuvhe0k1o + +# You can add Analytics trackers just as easily: +New-PipeworksManifest -Name ASampleModule -AnalyticsId UA-XXXXXXX-XX + +# It can also work with [Bing's webmaster tools](http://www.bing.com/toolbox/webmaster) +New-PipeworksManifest -Name ASampleModule -BingValidationKey 7B94933EC8C374B455E8263FCD4FE5EF + +# You can add meta keywords to each page +New-PipeworksManifest -Name ASampleModule -Keyword A, Sample, Module + +# You can let people Like your site by making it a Facebook app: +New-PipeworksManifest -Name ASampleModule -FacebookAppId MyFacebookAppId + +# You can increase sharing by adding Tweet links: +New-PipeworksManifest -Name ASampleModule -Tweet + +# Pipeworks also automatically does a lot of little things to help SEO: +# * [Sitemaps](http://start-automating.com/Sitemap.xml) are automatically generated +# * Pages automatically get description <meta> tags +# * Creating RSS feeds for topics and commands +# * Friendly URLs for aliases, commands, and topics, like [http://start-automating.com/Training/](http://start-automating.com/Training/) +$null + + + diff --git a/en-us/Simplified_SQL.walkthru.help.txt b/en-us/Simplified_SQL.walkthru.help.txt new file mode 100644 index 0000000..f60fb38 Binary files /dev/null and b/en-us/Simplified_SQL.walkthru.help.txt differ diff --git a/en-us/Simplifying_Slideshows.walkthru.help.txt b/en-us/Simplifying_Slideshows.walkthru.help.txt new file mode 100644 index 0000000..e91e1a5 Binary files /dev/null and b/en-us/Simplifying_Slideshows.walkthru.help.txt differ diff --git a/en-us/The_Pipeworks_Manifest.help.txt b/en-us/The_Pipeworks_Manifest.help.txt new file mode 100644 index 0000000..72eb4b7 Binary files /dev/null and b/en-us/The_Pipeworks_Manifest.help.txt differ diff --git a/en-us/The_Wonders_Of_Wolfram_Alpha.walkthru.help.txt b/en-us/The_Wonders_Of_Wolfram_Alpha.walkthru.help.txt new file mode 100644 index 0000000..caec3f0 Binary files /dev/null and b/en-us/The_Wonders_Of_Wolfram_Alpha.walkthru.help.txt differ diff --git a/en-us/Using_Azure_Table_Storage_in_Pipeworks.walkthru.help.txt b/en-us/Using_Azure_Table_Storage_in_Pipeworks.walkthru.help.txt new file mode 100644 index 0000000..a34f6e5 Binary files /dev/null and b/en-us/Using_Azure_Table_Storage_in_Pipeworks.walkthru.help.txt differ diff --git a/en-us/What_Pipeworks_Does.help.txt b/en-us/What_Pipeworks_Does.help.txt new file mode 100644 index 0000000..b5de9bc Binary files /dev/null and b/en-us/What_Pipeworks_Does.help.txt differ diff --git a/en-us/Why_Windows.help.txt b/en-us/Why_Windows.help.txt new file mode 100644 index 0000000..dd481db Binary files /dev/null and b/en-us/Why_Windows.help.txt differ diff --git a/en-us/Working_With_Write-Link.walkthru.help.txt b/en-us/Working_With_Write-Link.walkthru.help.txt new file mode 100644 index 0000000..5e24ad8 Binary files /dev/null and b/en-us/Working_With_Write-Link.walkthru.help.txt differ diff --git a/en-us/about_Pipeworks.help.txt b/en-us/about_Pipeworks.help.txt new file mode 100644 index 0000000..12b8616 Binary files /dev/null and b/en-us/about_Pipeworks.help.txt differ diff --git a/get-blob.ps1 b/get-blob.ps1 new file mode 100644 index 0000000..5d115db --- /dev/null +++ b/get-blob.ps1 @@ -0,0 +1,325 @@ +function Get-Blob +{ + <# + .Synopsis + Gets blob of cloud data + .Description + Gets blob of cloud data in Azure + .Example + # Get all containers + Get-Blob -StorageAccount MyAzureStorageAccount -StorageKey MyAzureStorageKey + .Example + # Get all items in mycontainer + Get-Blob -StorageAccount MyAzureStorageAccount -StorageKey MyAzureStorageKey -Container MyContainer + .Link + Remove-Blob + .Link + Import-Blob + .Link + Export-Blob + #> + [CmdletBinding(DefaultParameterSetName='GetAllBlobs')] + [OutputType([PSObject])] + param( + # The name of the container + [Parameter(Mandatory=$true,Position=0, ValueFromPipelineByPropertyName=$true,ParameterSetName='GetSpecificBlob')] + [Parameter(Mandatory=$true,Position=0, ValueFromPipelineByPropertyName=$true,ParameterSetName='GetSpecificContainer')] + [string]$Container, + + # The name of the blob + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true,ParameterSetName='GetSpecificBlob')] + [string]$Name, + + # The blob prefix + [string]$Prefix, + + # The storage account + [string]$StorageAccount, + + # The storage key + [string]$StorageKey + ) + + + begin { + + + if (-not $script:cachedContentTypes) { + $script:cachedContentTypes = @{} + $ctKey = [Microsoft.Win32.Registry]::ClassesRoot.OpenSubKey("MIME\Database\Content Type") + $ctKey.GetSubKeyNames() | + ForEach-Object { + $extension= $ctKey.OpenSubKey($_).GetValue("Extension") + if ($extension) { + $script:cachedContentTypes["${extension}"] = $_ + } + } + + } + + +$signMessage = { + param( + [Hashtable]$Header, + [Uri]$Url, + [Uint32]$ContentLength, + [string]$IfMatch ="", + [string]$Md5OrContentType = "", + [string]$NowString = [DateTime]::now.ToString("R", [Globalization.CultureInfo]::InvariantCulture), + [Switch]$IsTableStorage, + [string]$method = "GET", + [string]$Storageaccount, + [string]$StorageKey + ) + + $method = $method.ToUpper() + $MessageSignature = + if ($IsTableStorage) { + [String]::Format("{0}`n`n{1}`n{2}`n{3}",@( + $method, + "application/atom+xml", + $NowString, + ( & $GetCanonicalizedResource $Url $StorageAccount))) + + } else { + if ($md5OrCOntentType) { + [String]::Format("{0}`n`n`n{1}`n`n{5}`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } else { + [String]::Format("{0}`n`n`n{1}`n{5}`n`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } + + } + + $SignatureBytes = [Text.Encoding]::UTF8.GetBytes($MessageSignature) + + [byte[]]$b64Arr = [Convert]::FromBase64String($StorageKey) + $SHA256 = new-object Security.Cryptography.HMACSHA256 + $sha256.Key = $b64Arr + $AuthorizationHeader = "SharedKey " + $StorageAccount + ":" + [Convert]::ToBase64String($SHA256.ComputeHash($SignatureBytes)) + $AuthorizationHeader +} + +$GetCanonicalizedHeader = { + param( + [Hashtable]$Header + ) + + $headerNameList = new-OBject Collections.ArrayList; + $sb = new-object Text.StringBuilder; + foreach ($headerName in $Header.Keys) { + if ($headerName.ToLowerInvariant().StartsWith("x-ms-", [StringComparison]::Ordinal)) { + $null = $headerNameList.Add($headerName.ToLowerInvariant()); + } + } + $null = $headerNameList.Sort(); + [Collections.Specialized.NameValueCollection]$headers =NEw-OBject Collections.Specialized.NameValueCollection + foreach ($h in $header.Keys) { + $null = $headers.Add($h, $header[$h]) + } + + + foreach ($headerName in $headerNameList) + { + $builder = new-Object Text.StringBuilder $headerName + $separator = ":"; + foreach ($headerValue in (& $GetHeaderValues $headers $headerName)) + { + $trimmedValue = $headerValue.Replace("`r`n", [String]::Empty) + $null = $builder.Append($separator) + $null = $builder.Append($trimmedValue) + $separator = "," + } + $null = $sb.Append($builder.ToString()) + $null = $sb.Append("`n") + } + return $sb.ToString() +} + + +$GetHeaderValues = { + param([Collections.Specialized.NameValueCollection]$headers, $headerName) + $list = new-OBject Collections.ArrayList + + $values = $headers.GetValues($headerName) + if ($values -ne $null) + { + foreach ($str in $values) { + $null = $list.Add($str.TrimStart($null)) + } + } + return $list; +} + +$GetCanonicalizedResource = { + param([uri]$address, [string]$accountName) + + $str = New-object Text.StringBuilder + $builder = New-object Text.StringBuilder "/" + $null = $builder.Append($accountName) + $null = $builder.Append($address.AbsolutePath) + $null = $str.Append($builder.ToString()) + $values2 = New-Object Collections.Specialized.NameValueCollection + if (!$IsTableStorage) { + $values = [Web.HttpUtility]::ParseQueryString($address.Query) + foreach ($str2 in $values.Keys) { + $list = New-Object Collections.ArrayList + foreach ($v in $values.GetValues($str2)) { + $null = $list.add($v) + } + $null = $list.Sort(); + $builder2 = New-Object Text.StringBuilder + foreach ($obj2 in $list) + { + if ($builder2.Length -gt 0) + { + $null = $builder2.Append(","); + } + $null = $builder2.Append($obj2.ToString()); + } + $valueName = if ($str2 -eq $null) { + $str2 + } else { + $str2.ToLowerInvariant() + } + $values2.Add($valueName , $builder2.ToString()) + } + } + $list2 = New-Object Collections.ArrayList + foreach ($k in $values2.AllKeys) { + $null = $list2.Add($k) + } + $null = $list2.Sort() + foreach ($str3 in $list2) + { + $builder3 = New-Object Text.StringBuilder([string]::Empty); + $null = $builder3.Append($str3); + $null = $builder3.Append(":"); + $null = $builder3.Append($values2[$str3]); + $null = $str.Append("`n"); + $null = $str.Append($builder3.ToString()); + } + return $str.ToString(); + +} + + + } + + + process { + #region check for and cache the storage account + if (-not $StorageAccount) { + $storageAccount = $script:CachedStorageAccount + } + + if (-not $StorageKey) { + $StorageKey = $script:CachedStorageKey + } + + if (-not $StorageAccount) { + Write-Error "No storage account provided" + return + } + + if (-not $StorageKey) { + Write-Error "No storage key provided" + return + } + + if ($Container) { + $Container = $Container.ToLower() + } + + $script:CachedStorageAccount = $StorageAccount + $script:CachedStorageKey = $StorageKey + #endregion check for and cache the storage account + + if ($PSCmdlet.ParameterSetName -eq 'GetAllBlobs') { + $method = 'GET' + + $uri = "https://$StorageAccount.blob.core.windows.net/?comp=list&include=metadata$(if ($prefix) {$prefix= $prefix.ToLower(); "&prefix=$prefix"})" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method GET + $containerList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method GET -HideProgress -AsXml + $containerList | + Select-Xml //Container | + Select-object -ExpandProperty Node | + ForEach-Object { + $item = $_ + New-Object PSObject -property @{ + Container = $_.Name + Url = $_.Url + LastModified = $_.Properties.'Last-Modified' -as [Datetime] + + } + } + } elseif ($PSCmdlet.ParameterSetName -eq 'GetSpecificContainer') { + $method = 'GET' + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container&comp=list&include=metadata$(if ($prefix) {$prefix= $prefix; "&prefix=$prefix"})" + + + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method GET + $containerBlobList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method GET -HideProgress -AsXml + $containerBlobList | + Select-Xml //Blob | + Select-Object -ExpandProperty Node | + ForEach-Object { + New-Object PSObject -property @{ + Container = $Container + Name = $_.Name + Url = $_.Url + LastModified = $_.Properties.'Last-Modified' -as [Datetime] + + } + } + } elseif ($PSCmdlet.ParameterSetName -eq 'GetSpecificBlob') { + $blobData= + Import-Blob -Name $Name -Container $Container -StorageAccount $StorageAccount -StorageKey $StorageKey + + $blobs = Get-Blob -Container $Container -Prefix $Name -StorageAccount $StorageAccount -StorageKey $StorageKey + + foreach ($b in $blobs) { + if ($b.Name -eq $name) { + $b | + Add-Member NoteProperty BlobData $blobData -Force -PassThru + } + } + } + } + + end { + + + } +} \ No newline at end of file diff --git a/get-ftp.ps1 b/get-ftp.ps1 new file mode 100644 index 0000000..f88ee09 --- /dev/null +++ b/get-ftp.ps1 @@ -0,0 +1,444 @@ +function Get-FTP +{ + <# + .Synopsis + Gets files from FTP + .Description + Lists files on an FTP server, or downloads files + .Example + Get-FTP -FTP "ftp://edgar.sec.gov/edgar/full-index/1999/" -Download -Filter "*.idx", "*.xml" + .Example + Get-FTP -FTP "ftp://edgar.sec.gov/edgar/full-index/1999/" -Download -Filter "*.idx", "*.xml" -DownloadAsJob + .Link + Push-FTP + #> + [OutputType([IO.FileInfo])] + [CmdletBinding(DefaultParameterSetName='FTPSite')] + param( + # The root url of an FTP server + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName='FTPSite')] + [Alias('FTP')] + [Uri]$FtpRoot, + + # A list of specific files on an FTP server. Useful for when dealing with FTP servers that do not allow listing. + [Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,ParameterSetName='FTPFile')] + [Uri[]] + $FtpFile, + + # The credential used to connect to FTP. If not provided, will connect anonymously. + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Management.Automation.PSCredential] + $Credential, + + # If set, will download files instead of discover them + [Parameter(ValueFromPipelineByPropertyName=$true,ParameterSetName='FTPSite')] + [Switch]$Download, + + # The download path (by default, the downloads directory) + [Parameter(ValueFromPipelineByPropertyName=$true)] + [string]$DownloadPath = "$env:UserProfile\Downloads", + + # If provided, will only download files that match the filter + [Parameter(ValueFromPipelineByPropertyName=$true,Position=1,ParameterSetName='FTPSite')] + [string[]]$Filter, + + # If set, will download files that already have been downloaded and have the exact same file size. + [Parameter(ValueFromPipelineByPropertyName=$true,ParameterSetName='FTPSite')] + [Switch]$Force, + + # If set, downloads will run as a background job + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $DownloadAsJob, + + # If set, downloads will be run in parallel in a PowerShell workflow + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $UseWorkflow, + + # If set, download progress will not be displayed + [Parameter(ValueFromPipelineByPropertyName=$true)] + [Switch] + $HideProgress, + + # The size of the copy buffer. By default, this is 50kb + [Uint32] + $BufferSize = 50kb + + + ) + + begin { + + $folders = New-Object "system.collections.generic.queue[string]" + $files= New-Object "system.collections.generic.queue[string]" + + $GetFtpStream = { + param($url, $method, $Credential) + try { + + $ftpRequest = [System.Net.FtpWebRequest]::Create($url) + if ($Credential) { + $ftpRequest.Credentials = $Credential.GetNetworkCredential() + } + $ftpRequest.Method = $method + $ftpresponse = $ftpRequest.GetResponse() + $reader = New-Object IO.StreamReader $ftpresponse.GetResponseStream() + + while (($line = $reader.ReadLine()) -ne $null) { + $line.Trim() + } + if ($reader) { + $reader.Dispose() + } + + if ($ftpresponse.Close) { + $ftpresponse.Close() + } + } catch { + $err = $_ + Write-Error -Exception $err.Exception -Message "Error in FTP $method Request on $url" + return + } finally { + } + + } + + $GetFtpFile = { + + param($Source,$Target,$Credential, $HideProgress, $BufferSize) + try { + + $FileSize = + try { + $ftprequest = [Net.FtpWebRequest]::create($Source) + if ($Credential) { + $ftprequest.Credentials = $Credential.GetNetworkCredential() + } + $ftprequest.Method = [Net.WebRequestMethods+Ftp]::GetFileSize + $ftpresponse = $ftprequest.GetResponse() + $ftpresponse.ContentLength + if ($ftpresponse.Close) { + $ftpresponse.Close() + } + } catch { + + } + + + + $ftprequest = [Net.FtpWebRequest]::create($Source) + if ($Credential) { + $ftprequest.Credentials = $Credential.GetNetworkCredential() + } + $ftprequest.Method = [Net.WebRequestMethods+Ftp]::DownloadFile + $ftprequest.UseBinary = $true + $ftprequest.KeepAlive = $false + + + $ftpresponse = $ftprequest.GetResponse() + + $responsestream = $ftpresponse.GetResponseStream() + if (-not $responsestream) { return } + + $targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create) + [byte[]]$readbuffer = New-Object byte[] $BufferSize + + + $perc = 0 + $progressId = Get-Random + do{ + $readlength = $responsestream.Read($readbuffer,0,$BufferSize) + + + $targetfile.Write($readbuffer,0,$readlength) + if ($FileSize) { + if (-not $HideProgress) { + $perc = $targetfile.Position * 100 / $FileSize + Write-Progress "Downloading $Source" "To $Target" -PercentComplete $perc -Id $progressId + } + } else { + if (-not $HideProgress) { + $perc += 5 + if ($perc -gt 100) { $perc = 0 } + Write-Progress "Downloading $Source" "To $Target" -PercentComplete $perc -Id $progressId + } + } + } while ($readlength -ne 0) + + + $targetfile.close() + + if ($ftpresponse.Close) { + $ftpresponse.Close() + } + Write-Progress "Downloading $Source" "To $Target" -Completed -Id $progressId + + Get-Item -Path $target + + } catch { + $err = $_ + Write-Error -Exception $err.Exception -Message "FTP Error Downloading $source - $($err.Exception.Message)" + return + } + } + + $jobDefintion = [ScriptBlock]::Create(@" +param([Hashtable]`$Parameter) +`$getFtpFile = { $GetFtpFile } + +& `$getFtpFile @parameter +"@) + + + $workflowDefinition = @" +workflow getFtpFilesWorkflow( + [Parameter(Position=0)] + [Hashtable[]]`$ftpFileInput + ) { + foreach -parallel (`$ftpFile in `$ftpFileInput) { + `$ftpFile | + inlineScript { + `$parameter = `$(`$input) + & { $GetFtpFile } @parameter + + } + } +} +"@ + + . ([ScriptBlock]::create($workflowDefinition)) + + $Ftpjobs = @() + $AsyncDownloadPaths = @() + } + process { + + if ($PSCmdlet.ParameterSetName -eq 'FTPSite') { + $null = $folders.Enqueue("$ftpRoot") + } elseif ($PSCmdlet.ParameterSetName -eq 'FTPFile') { + foreach ($f in $FtpFile) { + $null = $files.Enqueue("$f") + } + } + + + } + + end { + $workFlowInputData = @() + while($folders.Count -gt 0 -or + $RunningFtpjobs.Count -gt 0 -or + $files.Count -gt 0){ + + if ($PSCmdlet.ParameterSetName -eq 'FTPSite' -and $folders.Count) { + + $fld = $folders.Dequeue() + + $newFiles = New-Object "system.collections.generic.list[string]" + $newDirs = New-Object "system.collections.generic.list[string]" + $operation = [System.Net.WebRequestMethods+Ftp]::ListDirectory + + foreach ($line in . $GetFtpStream $fld $operation $Credential 2>&1) { + if ($line -is [Management.Automation.ErrorRecord]) { + $line | Write-Error + } else { + [void]$newFiles.Add($line.Trim()) + } + } + + $operation = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails + foreach ($line in . $GetFtpStream $fld $operation $Credential 2>&1) { + if ($line -is [Management.Automation.ErrorRecord]) { + $line | Write-Error + } else { + [void]$newDirs.Add($line.Trim()) + } + + } + + + foreach ($d in $newDirs) { + $parts = @($d -split " " -ne '') + + + + + if ($parts.Count -eq 2) { + # First line, purely informational + continue + } + + + if ($parts.Count -eq 9) { + # 9 parts. It's likely that this is a linux based FTP + # The last part should be the file name + # The preceeding 3 parts should be the modification time + # The preceeding 1 part should be the file size + if ($parts[-1] -eq '.' -or $parts[-1] -eq '..') { + continue + } + + $FileName = $parts[-1] + $FileSize = $parts[-5] + $FileDate = ((@($parts[-4..-3]) + (Get-Date).Year + $parts[-2]) -join ' ') -as [datetime] + + + + + } elseif ($parts.Count -eq 4) { + # First two parts should be date + # Third part should be file length + # Last part should be file name + + $FileName= $parts[-1] + $FileSize = $parts[-2] + $FileDate = ($parts[0..1] -join ' ') -as [DateTime] + } + + + if (-not $FileName) {continue } + + if ($FileSize -eq 4096 -or -not $FileSize) { + $newName = $parts[-1] + Write-Verbose "Enqueing Folder $($fld + $FileName + "/")" + $null = $folders.Enqueue($fld + $FileName + "/") + } + + + $out = + New-Object PSObject -Property @{ + Ftp = $fld + "/" + $FileName + Size = $FileSize + UpdatedAt = $FileDate + } + + if ($filter) { + $matched = $false + foreach ($f in $filter) { + if ($FileName -like "$f") { + $matched = $true + break + } + } + if (-not $matched) { + continue + } + } + if ($download -or $psBoundParameters.DownloadPath) { + + $folderUri = [uri]("$fld".TrimEnd("/") + "/" + $FileName) + + $downloadTo = Join-Path $DownloadPath $folderUri.LocalPath + $downloadDir = Split-Path $downloadTo + if (-not (Test-Path $downloadDir)) { + $null = New-Item -ItemType Directory $downloadDir + } + + $item = Get-Item -Path $downloadTo -ErrorAction SilentlyContinue + if (($item.Length -ne $FileSize) -or $Force) { + if ($DownloadAsJob) { + + + $ftpJobs += Start-Job -ArgumentList @{ + Source =$folderUri + Target = $downloadTo + Credential = $Credential + HideProgress = $HideProgress + BufferSize = $BufferSize + } -ScriptBlock $jobDefintion + } elseif ($UseWorkflow) { + $workFlowInputData += @{ + Source =$folderUri + Target = $downloadTo + Credential = $Credential + HideProgress = $HideProgress + BufferSize = $BufferSize + + } + + } else { + & $GetFtpFile -Source $folderUri -Target $downloadTo -Credential:$Credential -BufferSize $BufferSize -HideProgress:$HideProgress + } + + } + + + } else { + + $out + } + + + + + } + + + } elseif ($PSCmdlet.ParameterSetName -eq 'FTPFile' -and $files.Count) { + $file= $files.Dequeue() + $folderUri =[URI]$file + $downloadTo = Join-Path $DownloadPath $folderUri.LocalPath + $downloadDir = Split-Path $downloadTo + if (-not (Test-Path $downloadDir)) { + $null = New-Item -ItemType Directory $downloadDir + } + + + if ($DownloadAsJob) { + $ftpJobs += Start-Job -ArgumentList @{ + Source =$folderUri + Target = $downloadTo + Credential = $Credential + HideProgress = $HideProgress + BufferSize = $BufferSize + + } -ScriptBlock $jobDefintion + } elseif ($UseWorkflow) { + $workFlowInputData += @{ + Source =$folderUri + Target = $downloadTo + Credential = $Credential + HideProgress = $HideProgress + BufferSize = $BufferSize + + } + } else { + + $FileResults = & $GetFtpFile -Source $folderUri -Target $downloadTo -Credential:$Credential -BufferSize $BufferSize -HideProgress:$HideProgress 2>&1 + if ($FileResults -is [Management.Automation.ErrorRecord]) { + $FileResults | Write-Error + } else { + $FileResults + } + + } + + } + + if ($Ftpjobs) { + $Ftpjobs | Receive-Job + $RunningFtpjobs = @($Ftpjobs | Where-Object { $_.JobStateInfo.State -ne 'Completed' }) + } + } + + while ($workFlowInputData.Count -or $RunningFtpjobs) { + if ($workFlowInputData) { + $Ftpjobs += getFtpFilesWorkflow $workFlowInputData -asjob + } + $workFlowInputData = $null + if ($Ftpjobs) { + $Ftpjobs | Receive-Job + $RunningFtpjobs = @($Ftpjobs | Where-Object { $_.JobStateInfo.State -ne 'Completed' }) + } + } + + if ($Ftpjobs) { + $Ftpjobs | Receive-Job + $Ftpjobs | Remove-Job + } + + + } +} + diff --git a/get-person.ps1 b/get-person.ps1 new file mode 100644 index 0000000..e58506f --- /dev/null +++ b/get-person.ps1 @@ -0,0 +1,346 @@ +function Get-Person +{ + <# + .Synopsis + Gets information about a person + .Description + Gets account information about a person. + + + + Get-Person contains the common tools to get user information from users on: + - Active Directory + - Azure Tables + - Facebook + - Local Directory + .Example + Get-Person -UserTable SuppliUsUsers -Name "James Brundage" -UserPartition Users + .Example + Get-Person -Local "James Brundage" + .Link + Confirm-Person + #> + [CmdletBinding(DefaultParameterSetName='Alias')] + [OutputType('http://schema.org/Person')] + param( + # The account alias or UserID + [Parameter(Mandatory=$true, + ParameterSetName='Alias', + ValueFromPipelineByPropertyName=$true)] + [Alias('MailNickname', 'UserID', 'SamAccountName')] + [string]$Alias, + + # If provided, will get a list of properties from the user + [string[]]$Property, + + + # If set, will look for local accounts + [Switch]$IsLocalAccount, + + # The account name + [Parameter(Mandatory=$true, + ParameterSetName='Name', + ValueFromPipelineByPropertyName=$true)] + [string]$Name, + + # The name of the domain. If provided, then Active Directory will not be queried for a list of domains. + [string]$Domain, + + # The table in Azure that stores user information. If provided, will search for accounts in Azure + [string]$UserTable, + + # The parition within a table in Azure that should have user information. Defaults to "Users" + [string]$UserPartition = "Users", + + # The storage account. If not provided, the StorageAccountSetting will be used + [string]$StorageAccount, + + # The storage key. If not provided, the StorageKeySetting will be used + [string]$StorageKey, + + # The storage account setting. This setting will be found with either Get-SecureSetting or Get-WebConfigurationSetting. Defaults to AzureStorageAccountName. + [string]$StorageAccountSetting = "AzureStorageAccountName", + + # The storage key setting. This setting will be found with either Get-SecureSetting or Get-WebConfigurationSetting. Defaults to AzureStorageAccountKey + [string]$StorageKeySetting = "AzureStorageAccountKey", + + # A facebook access token + [Parameter(Mandatory=$true,ParameterSetName='FacebookAccessToken',ValueFromPipelineByPropertyName=$true)] + + [string]$FacebookAccessToken, + + + # A Live ID Access Token + [Parameter(Mandatory=$true,ParameterSetName='LiveIDAccessToken',ValueFromPipelineByPropertyName=$true)] + [string]$LiveIDAccessToken, + + + # A facebook user ID + [Parameter(ParameterSetName='FacebookAccessToken',ValueFromPipelineByPropertyName=$true)] + [Alias('ID')] + [string]$FacebookUserID + ) + + begin { + $beginProcessingEach = { + $propertyMatch = @{} + foreach ($prop in $property) { + if (-not $prop) { continue } + $propertyMatch[$prop] = $prop + } + } + $processEach = { + if ($OnlyBasicInfo) { + $sortedKeys = "displayname", "Title,", "company", "department", "mail", "telephoneNumber", "physicaldeliveryofficename", "cn", "gn", "sn", "samaccountname", "thumbnailphoto" + } else { + if ($in.Properties.Keys) { + $sortedKeys = $in.Properties.Keys | Sort-Object + } elseif ($in.Properties.PropertyNames) { + $sortedKeys = $in.Properties.PropertyNames| Sort-Object + } + } + + $personObject = New-Object PSObject + $personObject.pstypenames.clear() + $personObject.pstypenames.Add("http://schema.org/Person") + + + foreach ($s in $sortedKeys) { + $unrolledValue = foreach($_ in $in.Properties.$s) { $_} + $noteProperty = New-Object Management.Automation.PSNoteProperty $s, $unrolledValue + if (-not $propertyMatch.Count) { + $null = $personObject.psObject.Properties.Add($noteProperty) + } elseif ($propertyMatch[$s]) { + $null = $personObject.psObject.Properties.Add($noteProperty) + } + + #Add-Member -MemberType NoteProperty -InputObject $personObject -Name $s -Value $unrolledValue + } + + $personObject + } + } + + process { + + if ($userTable -and $UserPartition) { + $storageParameters = @{} + if ($storageAccount) { + $storageParameters['StorageAccount'] =$storageAccount + } elseif ($storageAccountSetting) { + if ((Get-SecureSetting "$storageAccountSetting" -ValueOnly)) { + $storageParameters['StorageAccount'] =(Get-SecureSetting "$storageAccountSetting" -ValueOnly) + } elseif ((Get-WebConfigurationSetting -Setting "$storageAccountSetting")) { + $storageParameters['StorageAccount'] =(Get-WebConfigurationSetting -Setting "$storageAccountSetting") + } + } + + if ($storageKey) { + $storageParameters['StorageKey'] =$storageKey + } elseif ($StorageKeySetting) { + if ((Get-SecureSetting "$storagekeySetting" -ValueOnly)) { + $storageParameters['Storagekey'] =(Get-SecureSetting "$storagekeySetting" -ValueOnly) + } elseif ((Get-WebConfigurationSetting -Setting "$storagekeySetting")) { + $storageParameters['Storagekey'] =(Get-WebConfigurationSetting -Setting "$storagekeySetting") + } + } + } + + + + $parameters= @{} + $psBoundParameters + if ($pscmdlet.ParameterSetName -eq 'Alias') { + + if ($credential) { + if (-not $exchangeserver) { + $exchangeServer = "http://ps.outlook.com/Exchange" + } + } elseif ($userTable -and $UserPartition) { + Search-AzureTable @storageParameters -TableName $userTable -Filter "PartitionKey eq '$userPartition'" | + Where-Object { $_.UserEmail -eq $alias } + } elseif (((Get-WmiObject Win32_ComputerSystem).Domain -ne 'WORKGROUP') -and (-not $IsLocalAccount)) { + if (-not $domain -and -not $script:DomainList) { + $script:DomainList= + [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Domains | + Select-Object -ExpandProperty Name + + } elseif ($Domain) { + $script:DomainList = @($Domain) + } + + foreach ($d in $script:domainList) { + if (-not $d) { continue } + $searcher = New-Object DirectoryServices.DirectorySearcher ([ADSI]"LDAP://$d") + + $searcher.Filter = "(&(objectCategory=person)(samaccountname=$alias))" + $searcher.SearchScope = "Subtree" + . $beginProcessingEach + foreach ($in in $searcher.FindAll()) { + . $processEach + } + } + + } else { + $all = + ([ADSI]"WinNT://$env:computerName,computer").psbase.Children | + Where-Object { + $_.SchemaClassName -eq 'User' + } + + $found= $all | + Where-Object { + $_.Name -ieq $alias + } + + + foreach ($in in $found) { + if ($in) { + $each = . $processEach + + if ($each.Fullname) { + $each | + Add-Member NoteProperty Name $each.FullName -Force + } + + $each + + + } + } + } + } elseif ($psCmdlet.ParameterSetName -eq 'Name') { + if ($userTable -and $UserPartition) { + Search-AzureTable @storageParameters -TableName $userTable -Filter "PartitionKey eq '$userPartition'" | + Where-Object { $_.Name -eq $name } + + + } elseif (((Get-WmiObject Win32_ComputerSystem).Domain -ne 'WORKGROUP') -and (-not $IsLocalAccount)) { + if (-not $script:DomainList) { + $script:DomainList= + [DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().Domains | + Select-Object -ExpandProperty Name + + } + + foreach ($d in $script:domainList) { + if (-not $d) { continue } + $searcher = New-Object DirectoryServices.DirectorySearcher ([ADSI]"LDAP://$d") + $searcher.Filter = "(&(objectCategory=person)(cn=$Name))" + . $beginProcessingEach + + foreach ($in in $searcher.Findall()) { + . $processEach + } + } + } else { + $all = + ([ADSI]"WinNT://$env:computerName,computer").psbase.Children | + Where-Object { + $_.SchemaClassName -eq 'User' -and + $_.Name -eq $name + } + + + foreach ($in in $all) { + . $processEach + } + } + } elseif ($psCmdlet.ParameterSetName -eq 'FacebookAccessToken') { + $facebookPerson = + if ($faceboookUserId) { + + Get-Web -Url "https://graph.facebook.com/$FacebookUserId" -AsJson -UseWebRequest + } else { + + Get-Web -Url "https://graph.facebook.com/Me/?access_token=$FacebookAccessToken" -asjson -UseWebRequest + } + + if (-not $facebookPerson) { + # If at first you don't succeed, try, try again (because SOMETIMES on first boot, Get-Web barfs and then works) + $facebookPerson = + if ($faceboookUserId) { + + Get-Web -Url "https://graph.facebook.com/$FacebookUserId" -AsJson + } else { + + Get-Web -Url "https://graph.facebook.com/Me/?access_token=$FacebookAccessToken" -asjson + } + + } + + if ($facebookPerson) { + foreach ($property in @($facebookPerson.psobject.properties)) { + $value = $Property.Value + $changed = $false + if ($Value -is [string] -and $Value -like "*\u*") { + $value = [Regex]::Replace($property.Value, + "\\u(\d{4,4})", { + ("0x" + $args[0].Groups[1].Value) -as [Uint32] -as [Char] + }) + $changed = $true + } + if ($Value -is [string] -and $Value -like "*\r\n*") { + $value = [Regex]::Replace($property.Value, + "\\r\\n", [Environment]::NewLine) + $changed = $true + } + + + if ($changed) { + Add-Member -inputObject $facebookPerson NoteProperty $property.Name -Value $value -Force + } + } + + + $facebookPerson | Add-Member AliasProperty FacebookID ID + $facebookPerson.pstypenames.clear() + $facebookPerson.pstypenames.add('http://schema.org/Person') + $facebookPerson + } + + } elseif ($psCmdlet.ParameterSetName -eq 'LiveIDAccessToken') { + $liveIdPerson = + Get-Web -Url "https://apis.live.net/v5.0/me?access_token=$LiveIDAccessToken" -asjson -UseWebRequest + + if (-not $LiveIDPerson) { + # If at first you don't succeed, try, try again (because SOMETIMES on first boot, Get-Web barfs and then works) + $liveIdPerson = + Get-Web -Url "https://apis.live.net/v5.0/me?access_token=$LiveIDAccessToken" -asjson + + } + + + + if ($liveIdPerson ) { + foreach ($property in @($liveIdPerson.psobject.properties)) { + $value = $Property.Value + $changed = $false + if ($Value -is [string] -and $Value -like "*\u*") { + $value = [Regex]::Replace($property.Value, + "\\u(\d{4,4})", { + ("0x" + $args[0].Groups[1].Value) -as [Uint32] -as [Char] + }) + $changed = $true + } + if ($Value -is [string] -and $Value -like "*\r\n*") { + $value = [Regex]::Replace($property.Value, + "\\r\\n", [Environment]::NewLine) + $changed = $true + } + + + if ($changed) { + Add-Member -inputObject $liveIdPerson NoteProperty $property.Name -Value $value -Force + } + } + + $liveIdPerson | Add-Member AliasProperty LiveID ID + $liveIdPerson.pstypenames.clear() + $liveIdPerson.pstypenames.add('http://schema.org/Person') + $liveIdPerson + } + + } + + } +} diff --git a/publish-WebSite.ps1 b/publish-WebSite.ps1 new file mode 100644 index 0000000..57fbf0c --- /dev/null +++ b/publish-WebSite.ps1 @@ -0,0 +1,308 @@ +function Publish-Website +{ + <# + .Synopsis + Publishes one or more modules as websites + .Description + Publishes one or more modules as websites, according to the DomainSchematic found in the Pipeworks manifest + .Example + Get-Module Pipeworks | + Publish-WebSite + .Link + ConvertTo-ModuleService + #> + [OutputType([IO.FileInfo])] + param( + # The name of the module + [ValidateScript({ + if ($psVersionTable.psVersion -lt '3.0') { + if (-not (Get-Module $_)) { + throw "Module $_ must be loaded" + } + } + return $true + })] + [Parameter(Mandatory=$true,Position=0,ParameterSetName='LoadedModule',ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true)] + [Alias('Module')] + [string[]] + $Name, + + # If set, will publish items in a background job + [Switch] + $AsJob, + + # If set, will wait for all jobs to complete + [Switch] + $Wait, + + # The throttle for background jobs. By default, 10 + [Uint32] + $Throttle, + + # The buffer between jobs. By default, 3 seconds + [Timespan] + $Buffer = $([Timespan]::FromSeconds(3)), + + # If set, will change the authorization mechanism used for the web site. + [ValidateSet("Anonymous", "Windows")] + [string] + $Authorization = "Anonymous" + ) + + begin { + $progId = Get-Random + $serviceDirectories = @() + + $moduleNAmes = @() + + $jobs = @() + } + + process { + $moduleNAmes += $name + } + + end { + $publishData = @{} + $modulesByLocation = @{} + + + + $c = 0 + foreach ($moduleName in $moduleNames) { + + if ($psVersionTable.PSVersion -ge '3.0') { + $myModulePath = $env:PSModulePath -split ";" | Select-Object -First 1 + $moduleRoot = Join-Path $myModulePath $moduleName + } else { + $RealModule = Get-Module $moduleName + $moduleList = @($RealModule.RequiredModules | + Select-Object -ExpandProperty Name) + $realModule.Name + + $perc =($c / $moduleNames.Count) * 100 + $c++ + Write-Progress "Publishing Modules" "$moduleName" -PercentComplete $perc -Id $progId + $module = Get-Module $moduleName + if ($module.Path -like "*.ps1") { + continue + } + $moduleRoot = $module | Split-Path | Select-Object -First 1 + + } + $manifestPath = "$moduleRoot\$($modulename).pipeworks.psd1" + $pipeworksManifestPath = Join-Path $moduleRoot "$($moduleName).Pipeworks.psd1" + + + $pipeworksManifest = + if (Test-Path $pipeworksManifestPath) { + try { + & ([ScriptBlock]::Create( + "data -SupportedCommand Add-Member, New-WebPage, New-Region, Write-CSS, Write-Ajax, Out-Html, Write-Link { $( + [ScriptBlock]::Create([IO.File]::ReadAllText($pipeworksManifestPath)) + )}")) + } catch { + Write-Error "Could not read pipeworks manifest for $moduleName" + } + } + + + if (-not $pipeworksManifest) { + Write-Error "No Pipeworks manifest found for $moduleName" + continue + } + + + + if (-not $pipeworksManifest.DomainSchematics) { + Write-Error "Domain Schematics not found for $moduleName" + continue + } + + $moduleServiceParameters = @{ + Name = $moduleName + } + + + if ($pipeworksManifest.PublishDirectory) { + $baseName = $pipeworksManifest.PublishDirectory + } else { + $baseName = "${env:SystemDrive}\inetpub\wwwroot\$moduleName" + } + + + + + foreach ($domainSchematic in $pipeworksManifest.DomainSchematics.GetEnumerator()) { + if ($pipeworksManifest.AllowDownload) { + $moduleServiceParameters.AllowDownload = $true + } + $domains = $domainSchematic.Key -split "\|" | ForEach-Object { $_.Trim() } + $schematics = $domainSchematic.Value + + if ($schematics -ne "Default") { + $moduleServiceParameters.OutputDirectory = "$baseName.$($schematics -join '.')" + $moduleServiceParameters.UseSchematic = $schematics + } else { + $moduleServiceParameters.OutputDirectory = "$baseName" + $moduleServiceParameters.Remove('UseSchematic') + } + + $publishData[$moduleServiceParameters.OutputDirectory] = @($domains) + $modulesByLocation[$moduleServiceParameters.OutputDirectory] = $moduleName + + if ($AsJob) { + if ($psVersionTable.PSVersion -ge '3.0') { + $convertScript = " +Import-Module Pipeworks +Import-Module $ModuleName +" + } else { + $convertScript = " +Import-Module Pipeworks +Import-Module '$($moduleList -join "','")';" + } + + $convertScript += " +`$ModuleServiceParameters = " + $convertScript += $moduleServiceParameters | Write-PowerShellHashtable + $convertScript += " +ConvertTo-ModuleService @moduleServiceParameters -Force" + + $convertScript = [ScriptBlock]::Create($convertScript) + Write-Progress "Launching Jobs" "$modulename" + + + if ($throttle) { + $runningJobs = @($jobs | + Where-Object { $_.State -eq "Running" }) + + while ($runningJobs.Count -ge $throttle) { + $runningJobs = @($jobs | + Where-Object { $_.State -eq "Running" }) + $jobs | Wait-Job -Timeout 1 | Out-Null + $jobs | + Receive-Job + + $percent = 100 - ($runningJobs.Count * 100 / $jobs.Count) + + Write-Progress "Waiting for $Activity to Complete" "$($Jobs.COunt - $runningJobs.Count) out of $($Jobs.Count) Completed" -PercentComplete $percent + + + } + } + $jobs += Start-Job -Name $moduleName -ScriptBlock $convertScript + + if ($buffer) { + Start-Sleep -Milliseconds $buffer.TotalMilliseconds + } + } else { + + ConvertTo-ModuleService @moduleServiceParameters -Force + } + + + + $serviceDirectories += $moduleServiceParameters.OutputDirectory + + + + } + + + } + + + if ((-not $asJob) -or ($AsJob -and $Wait)) { + + $Activity = "Build $DeploymentName" + $runningJobs = $jobs | + Where-Object { $_.State -eq "Running" } + + while ($runningJobs) { + $runningJobs = @($jobs | + Where-Object { $_.State -eq "Running" }) + $jobs | Wait-Job -Timeout 1 | Out-Null + $jobs | + Receive-Job + + $percent = 100 - ($runningJobs.Count * 100 / $jobs.Count) + + Write-Progress "Waiting for $Activity to Complete" "$($Jobs.COunt - $runningJobs.Count) out of $($Jobs.Count) Completed" -PercentComplete $percent + + + } + Import-Module WebAdministration -Global -Force + foreach ($p in $publishData.GetEnumerator()) { + + $allSites = Get-Website + + $AlreadyExists = $allSites | + Where-Object {$p.key.Trim("\") -ieq ([Environment]::ExpandEnvironmentVariables($_.physicalPath).Trim("\")) } + + $ds = @($p.Value) + $ds = foreach ($d in $ds) { + if ($d -like "*.*") { + $d, "$d".Replace(".", "_") + } else { + $d + } + + } + $d = $ds | Select-Object -First 1 + $chunks = @($p.Key -split '\\' -ne '') + if (-not $AlreadyExists) { + $newWeb = New-Website -Name $chunks[-1] -PhysicalPath $p.Key -HostHeader $d + + } + + + foreach ($d in $ds) { + $binding = Get-WebBinding -Name $chunks[-1] -HostHeader $d -ErrorAction SilentlyContinue + if (-not $binding) { + New-WebBinding -Name $chunks[-1] -HostHeader $d + } + + + + if ($d -notlike "*.*") { + # Put it in etc/hosts if not present + + $hostLines = @(Get-content "$env:Windir\System32\Drivers\etc\hosts") + + $usefulHostLines= $hostLines -notlike "#*" + + + if (-not ($usefulHostLines -like "*$d*")) { + $hostLines += " 127.0.0.1 $d" + $hostLines |Set-Content "$env:Windir\System32\Drivers\etc\hosts" + } + + + + } + + + + + + } + + if ($Authorization -eq 'Anonymous') { + Set-WebConfigurationProperty -filter /system.webServer/security/authentication/anonymousAuthentication -name enabled -value true -PSPath "IIS:\" -Location "$($chunks[-1])" + Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -name enabled -value false -PSPath "IIS:\" -Location "$($chunks[-1])" + } elseif ($Authorization -eq 'Windows') { + Set-WebConfigurationProperty -filter /system.webServer/security/authentication/anonymousAuthentication -name enabled -value false -PSPath "IIS:\" -Location "$($chunks[-1])" + Set-WebConfigurationProperty -filter /system.webServer/security/authentication/windowsAuthentication -name enabled -value true -PSPath "IIS:\" -Location "$($chunks[-1])" + } + + + + } + + } + + + + + } +} diff --git a/remove-blob.ps1 b/remove-blob.ps1 new file mode 100644 index 0000000..d3bed81 --- /dev/null +++ b/remove-blob.ps1 @@ -0,0 +1,303 @@ +function Remove-Blob +{ + <# + .Synopsis + Removes blobs of cloud data + .Description + Removes blobs of cloud data in Azure + .Link + Import-Blob + .Link + Export-Blob + .Example + Remove-Blob -Container MyContainer -Name MyItem.txt + .Example + Remove-Blob -Container MyContainer + #> + [CmdletBinding(DefaultParameterSetName='RemoveContainer',ConfirmImpact='High', SupportsShouldProcess=$true)] + [OutputType([Nullable])] + param( + # The name of the container + [Parameter(Mandatory=$true,Position=0, ValueFromPipelineByPropertyName=$true,ParameterSetName='RemoveContainer')] + [Parameter(Mandatory=$true,Position=0,ValueFromPipelineByPropertyName=$true,ParameterSetName='RemoveBlob')] + [string]$Container, + + # The name of the blob + [Parameter(Mandatory=$true,Position=1,ValueFromPipelineByPropertyName=$true,ParameterSetName='RemoveBlob')] + [string]$Name, + + + # The storage account + [string]$StorageAccount, + + # The storage key + [string]$StorageKey + + ) + + + begin { + + $signMessage = { + param( + [Hashtable]$Header, + [Uri]$Url, + [Uint32]$ContentLength, + [string]$IfMatch ="", + [string]$Md5OrContentType = "", + [string]$NowString = [DateTime]::now.ToString("R", [Globalization.CultureInfo]::InvariantCulture), + [Switch]$IsTableStorage, + [string]$method = "GET", + [string]$Storageaccount, + [string]$StorageKey + ) + + $method = $method.ToUpper() + $MessageSignature = + if ($IsTableStorage) { + [String]::Format("{0}`n`n{1}`n{2}`n{3}",@( + $method, + "application/atom+xml", + $NowString, + ( & $GetCanonicalizedResource $Url $StorageAccount))) + + } else { + if ($md5OrCOntentType) { + [String]::Format("{0}`n`n`n{1}`n`n{5}`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } else { + [String]::Format("{0}`n`n`n{1}`n{5}`n`n`n`n{2}`n`n`n`n{3}{4}", @( + $method, + $(if ($method -eq "GET" -or $method -eq "HEAD") {[String]::Empty} else { $ContentLength }), + $IfMatch, + "$(& $GetCanonicalizedHeader $Header)", + "$( & $GetCanonicalizedResource $Url $StorageAccount)", + $Md5OrContentType + )); + } + + } + + $SignatureBytes = [Text.Encoding]::UTF8.GetBytes($MessageSignature) + + [byte[]]$b64Arr = [Convert]::FromBase64String($StorageKey) + $SHA256 = new-object Security.Cryptography.HMACSHA256 + $sha256.Key = $b64Arr + $AuthorizationHeader = "SharedKey " + $StorageAccount + ":" + [Convert]::ToBase64String($SHA256.ComputeHash($SignatureBytes)) + $AuthorizationHeader +} + +$GetCanonicalizedHeader = { + param( + [Hashtable]$Header + ) + + $headerNameList = new-OBject Collections.ArrayList; + $sb = new-object Text.StringBuilder; + foreach ($headerName in $Header.Keys) { + if ($headerName.ToLowerInvariant().StartsWith("x-ms-", [StringComparison]::Ordinal)) { + $null = $headerNameList.Add($headerName.ToLowerInvariant()); + } + } + $null = $headerNameList.Sort(); + [Collections.Specialized.NameValueCollection]$headers =NEw-OBject Collections.Specialized.NameValueCollection + foreach ($h in $header.Keys) { + $null = $headers.Add($h, $header[$h]) + } + + + foreach ($headerName in $headerNameList) + { + $builder = new-Object Text.StringBuilder $headerName + $separator = ":"; + foreach ($headerValue in (& $GetHeaderValues $headers $headerName)) + { + $trimmedValue = $headerValue.Replace("`r`n", [String]::Empty) + $null = $builder.Append($separator) + $null = $builder.Append($trimmedValue) + $separator = "," + } + $null = $sb.Append($builder.ToString()) + $null = $sb.Append("`n") + } + return $sb.ToString() +} + + +$GetHeaderValues = { + param([Collections.Specialized.NameValueCollection]$headers, $headerName) + $list = new-OBject Collections.ArrayList + + $values = $headers.GetValues($headerName) + if ($values -ne $null) + { + foreach ($str in $values) { + $null = $list.Add($str.TrimStart($null)) + } + } + return $list; +} + +$GetCanonicalizedResource = { + param([uri]$address, [string]$accountName) + + $str = New-object Text.StringBuilder + $builder = New-object Text.StringBuilder "/" + $null = $builder.Append($accountName) + $null = $builder.Append($address.AbsolutePath) + $null = $str.Append($builder.ToString()) + $values2 = New-Object Collections.Specialized.NameValueCollection + if (!$IsTableStorage) { + $values = [Web.HttpUtility]::ParseQueryString($address.Query) + foreach ($str2 in $values.Keys) { + $list = New-Object Collections.ArrayList + foreach ($v in $values.GetValues($str2)) { + $null = $list.add($v) + } + $null = $list.Sort(); + $builder2 = New-Object Text.StringBuilder + foreach ($obj2 in $list) + { + if ($builder2.Length -gt 0) + { + $null = $builder2.Append(","); + } + $null = $builder2.Append($obj2.ToString()); + } + $valueName = if ($str2 -eq $null) { + $str2 + } else { + $str2.ToLowerInvariant() + } + $values2.Add($valueName , $builder2.ToString()) + } + } + $list2 = New-Object Collections.ArrayList + foreach ($k in $values2.AllKeys) { + $null = $list2.Add($k) + } + $null = $list2.Sort() + foreach ($str3 in $list2) + { + $builder3 = New-Object Text.StringBuilder([string]::Empty); + $null = $builder3.Append($str3); + $null = $builder3.Append(":"); + $null = $builder3.Append($values2[$str3]); + $null = $str.Append("`n"); + $null = $str.Append($builder3.ToString()); + } + return $str.ToString(); + +} + + } + + + process { + $in = $_ + if ($in.Name) { + $name = $in.Name + } + + #region check for and cache the storage account + if (-not $StorageAccount) { + $storageAccount = $script:CachedStorageAccount + } + + if (-not $StorageKey) { + $StorageKey = $script:CachedStorageKey + } + + if (-not $StorageAccount) { + Write-Error "No storage account provided" + return + } + + if (-not $StorageKey) { + Write-Error "No storage key provided" + return + } + + $script:CachedStorageAccount = $StorageAccount + $script:CachedStorageKey = $StorageKey + #endregion check for and cache the storage account + + if ($PSCmdlet.ParameterSetName -eq 'RemoveContainer' -and -not $in.Name) { + $method = 'DELETE' + $Container = "$Container".ToLower() + $uri = "http://$StorageAccount.blob.core.windows.net/${Container}?restype=container" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method DELETE + + if ($PSCmdlet.ShouldProcess("/$Container")) { + $containerBlobList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method $method -HideProgress + } + + + } elseif ($PSCmdlet.ParameterSetName -eq 'RemoveBlob' -or $in.Name) { + + $method = 'DELETE' + $uri = "http://$StorageAccount.blob.core.windows.net/$Container/$Name" + $header = @{ + "x-ms-date" = $nowString + "x-ms-version" = "2011-08-18" + "DataServiceVersion" = "2.0;NetFx" + "MaxDataServiceVersion" = "2.0;NetFx" + + } + $header."x-ms-date" = [DateTime]::Now.ToUniversalTime().ToString("R", [Globalization.CultureInfo]::InvariantCulture) + $nowString = $header.'x-ms-date' + $header.authorization = . $signMessage -header $Header -url $Uri -nowstring $nowString -storageaccount $StorageAccount -storagekey $StorageKey -contentLength 0 -method DELETE + + if ($PSCmdlet.ShouldProcess("/$Container/$Name")) { + $containerBlobList = Get-Web -UseWebRequest -Header $header -Url $Uri -Method $method -HideProgress + } + + } + } + + end { + + foreach ($inputInfo in $inputData) { + if ($inputInfo.Name) { + $Name = $inputInfo.Name + } + + if ($inputInfo.Container) { + $Container = $inputInfo.Container + } + + $InputObject = $inputInfo.InputObject + + $containerBlobList = $null + + + + + + + + + + + + + + + } + } +} \ No newline at end of file diff --git a/start-at.ps1 b/start-at.ps1 new file mode 100644 index 0000000..ae3df4b --- /dev/null +++ b/start-at.ps1 @@ -0,0 +1,569 @@ +function Start-At +{ + <# + .Synopsis + Starts scripts at a time or event + .Description + Starts scripts at a time, an event, or a change in a table + .Example + Start-At -Boot -RepeatEvery "0:30:0" -Name LogTime -ScriptBlock { + "$(Get-Date)" | Set-Content "$($env:Public)\$(Get-Random).txt" + + } + .Link + Use-Schematic + #> + [CmdletBinding(DefaultParameterSetName='StartAtTime')] + [OutputType([Nullable])] + param( + # The scriptblock that will be run + [Parameter(Mandatory=$true,ValueFromPipeline=$true)] + [ScriptBlock[]]$ScriptBlock, + + # The time the script will start + [Parameter(Mandatory=$true, ParameterSetName='StartAtTime')] + [DateTime]$Time, + + # The event ID of interest + [Parameter(Mandatory=$true, ParameterSetName='StartAtSystemEvent')] + [Uint32]$EventId, + + # The event log where the eventID is found + [Parameter(Mandatory=$true, ParameterSetName='StartAtSystemEvent')] + [string]$EventLog, + + # The table name that contains data to process + [Parameter(Mandatory=$true, ParameterSetName='StartAtTableData')] + [Parameter(Mandatory=$true, ParameterSetName='StartAtSqlData')] + [string]$TableName, + + # The name of the user table. If an OwnerID is found on an object, and user is found in the a usertable, then the task will be run as that user + [Parameter(ParameterSetName='StartAtTableData')] + [Parameter(ParameterSetName='StartAtSqlData')] + [string]$UserTableName, + + # The filter used to scope queries for table data + [Parameter(Mandatory=$true, ParameterSetName='StartAtTableData')] + [string]$Filter, + + # The filter used to scope queries for table data + [Parameter(Mandatory=$true, ParameterSetName='StartAtSQLData')] + [string]$Where, + + # The name of the setting containing the storage account + [Parameter(ParameterSetName='StartAtTableData')] + [Parameter(ParameterSetName='StartAtSqlData')] + [Parameter(ParameterSetName='StartAtNewEmail')] + [string]$StorageAccountSetting = "AzureStorageAccountName", + + # The name of the setting containing the storage key + [Parameter(ParameterSetName='StartAtTableData')] + [Parameter(ParameterSetName='StartAtSqlData')] + [Parameter(ParameterSetName='StartAtNewEmail')] + [string]$StorageKeySetting = "AzureStorageAccountKey", + + # Clears a property on the object when the item has been handled + [Parameter(ParameterSetName='StartAtTableData')] + [string]$ClearProperty, + + # The name of the setting containing the email username + [Parameter(ParameterSetName='StartAtNewEmail',Mandatory=$true)] + [string]$EmailUserNameSetting, + + # The name of the setting containing the email password + [Parameter(ParameterSetName='StartAtNewEmail',Mandatory=$true)] + [string]$EmailPasswordSetting, + + # The display name of the inbox receiving the mail. + [Parameter(ParameterSetName='StartAtNewEmail',Mandatory=$true)] + [string]$SentTo, + + # The name of the setting containing the storage key + [Parameter(ParameterSetName='StartAtSQLData')] + [string]$ConnectionStringSetting = "SqlAzureConnectionString", + + # The partition containing user information. If the items in the table have an owner, then the will be run in an isolated account. + [Parameter(ParameterSetName='StartAtTableData')] + [string]$UserPartition = "Users", + + # The timespan in between queries + [Parameter(ParameterSetName='StartAtTableData')] + [Parameter(ParameterSetName='StartAtSQLData')] + [Parameter(ParameterSetName='StartAtNewEmail')] + [Timespan]$CheckEvery = "0:15:0", + + # The timespan in between queries + [Parameter(ParameterSetName='StartAtTableData')] + [Parameter(ParameterSetName='StartAtSQLData')] + [Parameter(ParameterSetName='StartAtNewEmail')] + [Switch]$SortDescending, + + # The randomized delay surrounding a task start time. This can be used to load-balance expensive executions + [Parameter(ParameterSetName='StartAtTime')] + [Timespan]$Jitter, + + # If set, the task will be started every day at this time + [Parameter(ParameterSetName='StartAtTime')] + [Switch]$EveryDay, + + # The interval (in days) in between running the task + [Parameter(ParameterSetName='StartAtTime')] + [Switch]$DayInterval, + + + # If set, the task will be started whenever the machine is locked + [Parameter(ParameterSetName='StartAtLock')] + [Switch]$Lock, + + + # If set, the task will be started whenever the machine is unlocked + [Parameter(Mandatory=$true,ParameterSetName='StartAtBoot')] + [Switch]$Boot, + + # If set, the task will be started whenever the machine is unlocked + [Parameter(Mandatory=$true,ParameterSetName='StartAtAnyLogon')] + [Switch]$Logon, + + # If set, the task will be started whenever the machine is unlocked + [Parameter(Mandatory=$true,ParameterSetName='StartAtUnlock')] + [Switch]$Unlock, + + # If set, the task will be started whenever a user logs onto the local machine + [Parameter(Mandatory=$true,ParameterSetName='StartAtLocalLogon')] + [Switch]$LocalLogon, + + # If set, the task will be started whenever a user logs off of a local machine + [Parameter(Mandatory=$true,ParameterSetName='StartAtLocalLogoff')] + [Switch]$LocalLogoff, + + # If set, the task will be started whenever a user disconnects via remote deskop + [Parameter(Mandatory=$true,ParameterSetName='StartAtRemoteLogon')] + [Switch]$RemoteLogon, + + # If set, the task will be started whenever a user disconnects from remote desktop + [Parameter(Mandatory=$true,ParameterSetName='StartAtRemoteLogoff')] + [Switch]$RemoteLogoff, + + # Starts the task as soon as possible + [Parameter(Mandatory=$true,ParameterSetName='StartASAP')] + [Alias('ASAP')] + [Switch]$Now, + + # IF provided, will scope logons or connections to a specific user + [Parameter(ParameterSetName='StartAtLock')] + [Parameter(ParameterSetName='StartAtUnLock')] + [Parameter(ParameterSetName='StartAtAnyLogon')] + [Parameter(ParameterSetName='StartAtAnyLogoff')] + [Parameter(ParameterSetName='StartAtLocalLogon')] + [Parameter(ParameterSetName='StartAtLocalLogoff')] + [Parameter(ParameterSetName='StartAtRemoteLogon')] + [Parameter(ParameterSetName='StartAtRemoteLogoff')] + [string]$ByUser, + + + + # The user running the script + [Management.Automation.PSCredential] + $As, + + + # The name of the computer the task will be run on. If not provided, the task will be run locally + [Alias('On')] + [string] + $ComputerName, + + # If set, the task will repeat at this frequency. + [Timespan]$RepeatEvery, + + # If set, the task will repeat for up to this timespan. If not set, the task will repeat indefinately. + [Timespan]$RepeatFor, + + # A name for the task. + [string] + $Name, + + # The name of the folder within Task Scheduler. + [string] + $Folder, + + # If set, will not exist the started task. + [Switch] + $NoExit, + + # The priority of the scheduled task + [Uint32] + $TaskPriority = 4, + + # How multiple instances of a task should be treated. By default, multiple instances are queued. + [ValidateSet("StopExisting", "Queue", "Parallel", "IgnoreNew")] + [string] + $MultipleInstancePolicy = "Queue", + + # If set, the task will self destruct after it as run once. + [Switch] + $SelfDestruct, + + # If set, tasks registered with a credential will be registered with TASK_LOGON_PASSWORD, which will prevent the scheduled task from popping up a visible window. + [Switch] + $NotInteractive + ) + + process { + #region Connect to the scheduler + $sched = New-Object -ComObject Schedule.Service + $sched.Connect() + $task = $sched.NewTask(0) + $task.Settings.Priority = $TaskPriority + #endregion Connect to the scheduler + + + $description = "" + #region Add the actions to the task + foreach ($sb in $ScriptBlock) { + + $action = $task.Actions.Create(0) + $action.Path = Join-Path $psHome "PowerShell.exe" + + + $action.Arguments = " -WindowStyle Minimized -Sta" + + + if ($NoExit) { + $Action.Arguments += " -NoExit" + } + $encodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($sb)) + $action.Arguments+= " -encodedCommand $encodedCommand" + + } + #endregion Add the actions to the task + + if ($PSCmdlet.ParameterSetName -eq 'StartAtTime') { + + $days = (Get-Culture).DateTimeFormat.DayNames + $months = (Get-Culture).DateTimeFormat.MonthNames + if ($PSBoundParameters.EveryDay -or $PSBoundParameters.DayInterval) { + $dailyTrigger = $task.Triggers.Create(2) + + if ($psBoundParameters.DayInterval) { + $dailyTrigger.DaysInterval = $psBoundParameters.DayInterval + } + } else { + # One time + $timeTrigger = $task.Triggers.Create(1) + + + } + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtLogon') { + $logonTrigger= $task.Triggers.Create(9) + $description += " At Logon " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtSystemEvent') { + $evtTrigger= $task.Triggers.Create(0) + $evtTrigger.Subscription = " +<QueryList> + <Query Id='0' Path='$($EventLog)'> + <Select Path='$($EventLog)'> + *[System[EventID=$($EventId)]] + </Select> + </Query> +</QueryList> +" + + $description += " At Event $EventId" + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtLocalLogon') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 1 + $description += " At Local Logon " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtLocalLogoff') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 2 + $description += " At Local Logoff " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtRemoteLogoff') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 3 + $description += " At Remote Logon " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtRemoteLogoff') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 4 + $description += " At Remote Logoff " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtLock') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 7 + $description += " At Lock" + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtUnlock') { + $stateTrigger= $task.Triggers.Create(11) + $stateTrigger.StateChange = 8 + $description += " At Unlock " + + + } elseif ($psCmdlet.ParameterSetName -eq 'StartASAP') { + $regTrigger = $task.Triggers.Create(7) + $description += " ASAP " + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtBoot') { + $bootTrigger = $task.Triggers.Create(8) + + $description += " OnBoot " + } elseif ('StartAtTableData', 'StartAtSqlData', 'StartAtNewEmail' -contains $PSCmdlet.ParameterSetName) { + if (-not $PSBoundParameters.As) { + Write-Error "Must supply credential for table based tasks" + return + } + # Schedule it as the user that will check + + $description += " New SQL Data from $TableName " + IF ($PSCmdlet.ParameterSetName -eq 'StartAtNewEmail') { + $check= " +Get-Email -UserNameSetting '$EmailUserNameSetting' -PasswordSetting '$EmailPasswordSetting' -Unread -Download -To '$SentTo'" + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtSqlData') { + $check= " +Select-Sql -ConnectionStringOrSetting '$ConnectionStringSetting' -FromTable '$tableName' -Where '$Where'" + } elseif ($psCmdlet.ParameterSetName -eq 'StartAtTableData') { + $check = " +Search-AzureTable -TableName '$TableName' -Filter `"$Filter`" -StorageAccount `$storageAccount -StorageKey `$storageKey" + + if ((-not $UserTableName) -and $TableName) { + $UserTableName = $TableName + } + + } + + + + + $saveMyCred = " +Add-SecureSetting -Name '$StorageAccountSetting' -String '$(Get-SecureSetting $StorageAccountSetting -ValueOnly)' +Add-SecureSetting -Name '$StorageKeySetting' -String '$(Get-SecureSetting $StorageKeySetting -ValueOnly)' + " + + $saveMyCred = [ScriptBlock]::Create($saveMyCred) + # Start-At -ScriptBlock $saveMyCred -As $As -Now + + + + + + $checkTable = " +Import-Module Pipeworks +`$storageAccount = Get-SecureSetting '$StorageAccountSetting' -ValueOnly +`$storageKey = Get-SecureSetting '$StorageKeySetting' -ValueOnly +`$verbosePreference='$VerbosePreference' +Write-Verbose 'Getting Data' +$check | + Sort-Object { + if (`$_.Timestamp) { + (`$_.Timestamp -as [Datetime]) + } elseif (`$_.DateTimeSent -as [DateTime]) { + `$_.DateTimeSent -as [DateTime] + } else { + `"`" + } + } -Descending:`$$SortDescending | + Foreach-Object -Begin { + + }-Process { + `$item = `$_ + + `$userTableName = '$UserTableName' + + if (-not `$userTableName) { + `$error.Clear() + + Write-Verbose 'Script Started' + `$scriptOutput = . { + $ScriptBlock + } + Write-Verbose 'Script Complete' + `$errorList = @(`$error) + `$updatedItem =`$item | + Add-Member NoteProperty ScriptResults `$scriptOutput -Force -PassThru + + + if (`$errorList) { + `$updatedItem = `$updatedItem | + Add-Member NoteProperty ScriptErrors `$errorList -Force -PassThru + } + + } else { + if (`$item.From.Address) { + `$userFound = Search-AzureTable -TableName '$UserTableName' -Filter `"PartitionKey eq '$UserPartition' and UserEmail eq '`$(`$item.From.Address)'`" -StorageAccount `$storageAccount -StorageKey `$storageKey + } elseif (`$item.OwnerID) { + # Run it as the owner + `$userFound = Search-AzureTable -TableName '$UserTableName' -Filter `"PartitionKey eq '$UserPartition' and RowKey eq '`$(`$item.OwnerID)'`" -StorageAccount `$storageAccount -StorageKey `$storageKey + } + if (-not `$userFound) { + Write-Error 'User Not Found' + return + } + + if (-not `$item.OwnerID) { + return + } + + + `$id = `$item.OwnerID[0..7+-1..-12] -join '' + `$userExistsOnSystem = net user `"`$id`" 2>&1 + + if (`$userExistsOnSystem[0] -as [Management.Automation.ErrorRecord]) { + # They don't exist, make them + # `$completed = net user `"`$id`" `"`$(`$userFound.PrimaryAPIKey)`" /add /y + Write-Verbose 'Creating User' + `$objOu = [ADSI]`"WinNT://`$env:ComputerName`" + `$objUser = `$objOU.Create(`"User`", `$id) + `$objUser.setpassword(`$userFound.PrimaryAPIKey) + `$objUser.SetInfo() + `$objUser.description = `$userFound.UserID + `$objUser.SetInfo() + } + + `$targetPath = Split-path `$home + `$targetPath = Join-Path `$targetPath `$id + + `$innerScript = { + `$in = `$args + + foreach (`$item in `$in) { + if (`$item -is [Array]) { + `$item = `$item[0] + } + . { + $ScriptBlock + } 2>&1 + + `$null = `$item + } + } + + `$asCred = New-Object Management.Automation.PSCredential `"`$id`", (ConvertTo-SecureString -Force -AsPlainText `"`$(`$userFound.PrimaryAPIKey)`") + `$scriptOutput = Start-Job -ScriptBlock `$innerScript -Credential `$asCred -ArgumentList `$item | + Wait-Job | + Receive-Job + + `$jobWorked = `$? + + `$compressedResults = (`$scriptOutput | Write-PowerShellHashtable) | Compress-Data -String { `$_ } + + `$updatedItem =`$item | + Add-Member NoteProperty ScriptResults (`$compressedResults) -Force -PassThru + + + if (`$jobWorked -and `$item.RowKey -and `$item.PartitionKey) { + `$clearProperty = '$ClearProperty' + if (`$clearProperty) { + `$null = `$updatedItem.psobject.properties.Remove(`$clearProperty) + } + `$updatedItem | + Update-AzureTable -TableName '$TableName' -Value { `$_ } + } + + } + + + + } +" + + $checkTable = [ScriptBlock]::Create($checkTable) + + Start-At -Boot -As $as -ScriptBlock $checkTable -RepeatEvery $CheckEvery -NoExit:$NoExit -Name:"${Name}_AtBoot" -Folder:$Folder + Start-At -Now -As $as -ScriptBlock $checkTable -RepeatEvery $CheckEvery -NoExit:$NoExit -Name:"${Name}_Now" -Folder:$Folder + } + + + + + + if ($task.Triggers.Count) { + $task.Settings.MultipleInstances = if ($MultipleInstancePolicy -eq 'StopExisting') { + 3 + } elseif ($MultipleInstancePolicy -eq 'Queue') { + 1 + } elseif ($MultipleInstancePolicy -eq 'Parallel') { + 0 + } elseif ($MultipleInstancePolicy -eq 'IgnoreNew') { + 2 + } + foreach ($trig in $task.Triggers) { + if ($PSBoundParameters.Time) { + $trig.StartBoundary = $Time.ToString("s") + } else { + $trig.StartBoundary = [DateTime]::Now.ToString("s") + } + if ($PSBoundParameters.RepeatEvery) { + $trig.Repetition.Interval = "PT$($RepeatEvery.TotalMinutes -as [uint32])M" + } + if ($PSBoundParameters.RepeatFor) { + $trig.Repetition.Duration = "PT$($RepeatFor.TotalMinutes -as [uint32])M" + } + if ($PSBoundParameters.Jitter) { + $trig.RandomDelay = "PT$($Jitter.TotalMinutes -as [uint32])M" + } + if ($psBoundParameters.ByUser) { + $trig.UserID = $PSBoundParameters.ByUser + $description += " ByUser $($psBoundParameters.ByUser.Replace('\','_'))" + } + + } + + $taskNAme = if ($Name) { + $Name + } else { + if ($as) { + "Start-At $Description as $($As.GetNetworkCredential().UserName) " + } else { + "Start-At $Description" + } + } + + + + + + $taskPath = + if ($Folder) { + Join-Path $folder $taskNAme + } else { + $taskNAme + } + + if ($selfDestruct) { + $removeAction = $task.Actions.Create(0) + $removeAction.Path = "schtasks" + $removeAction.Arguments = "/delete /tn `"$TaskPath`" /f" + + } + + if ($as) { + $task.Principal.RunLevel = 1 + if ($NotInteractive) { + $registeredTask = $sched.GetFolder("").RegisterTask($taskPath, $task.XmlText, 6, $As.UserName, $As.GetNetworkCredential().Password, 1, $null) + } else { + $registeredTask = $sched.GetFolder("").RegisterTask($taskPath, $task.XmlText, 6, $As.UserName, $As.GetNetworkCredential().Password, 6, $null) + } + + } else { + $registeredTask = $sched.GetFolder("").RegisterTask($taskPath, $task.XmlText, 6, "", "", 3, $null) + } + } + + + + + + + } +} \ No newline at end of file diff --git a/update-wmi.ps1 b/update-wmi.ps1 new file mode 100644 index 0000000..2d562b6 --- /dev/null +++ b/update-wmi.ps1 @@ -0,0 +1,283 @@ +function Update-Wmi +{ + <# + .Synopsis + Stores data in WMI + .Description + Stores data in the WMI repository + .Link + Select-Wmi + .Example + Get-ChildItem | + Select-Object Name, LastWriteTime, LastAccessTime, CreationTime | + Update-Wmi + #> + [OutputType([Nullable])] + param( + # Any input object + [Parameter(Mandatory=$true, ValueFromPipeline=$true)] + [PSObject] + $InputObject, + + # The namespace the object will be stored in + [string] + $Namespace = "root\custom\data", + + # The name of the class. If not provided, the ClassName will be taken from the object. Illegal characters in WMI class names (like ., :, or /) will be converted into _dot_, _colon_, and _slash_ respectively. + [string] + $ClassName, + + # At least one property must be registered as a key + [Parameter(Mandatory=$true,Position=0)] + [string[]] + $Key, + + # If set, will update existing instances. If not set, only new data will be added. + [Switch] + $Force + ) + + + begin { + #region Translate column types into CIM column types + $cimColumnType = { + if ($columnType -and $columnType[$prop.Name]) { + $columnType[$prop.Name] + } elseif ($prop.Value) { + if ($prop.Value -is [String]) { + [Management.CimType]::String + } elseif ($prop.Value -is [Byte]) { + [Management.CimType]::Char16 + } elseif ($prop.Value -is [Int16]) { + [Management.CimType]::SInt16 + } elseif ($prop.Value -is [Int]) { + [Management.CimType]::SInt32 + } elseif ($Prop.Value -is [Double]) { + [Management.CimType]::Real32 + } elseif ($prop.Value -is [Long]) { + [Management.CimType]::SInt64 + } elseif ($prop.Value -is [DateTime]) { + [Management.CimType]::DateTime + } elseif ($prop.Value -is [Switch] -or $prop.Value -is [Bool]) { + [Management.CimType]::Boolean + } else { + [Management.CimType]::String + } + + } else { + [Management.CimType]::String + } + } + #endregion Translate column types into CIM column types + + #region Escape unsafe class names + $getSafeClassName = { + + $cn = $ClassName.Replace(" ", "_space_").Replace("." ,"_dot_").Replace(":", "_colon_").Replace("/", "_slash_").Replace("#", "_pound_") + + + if ($(try { [uint32]::Parse($cn[0]) } catch {})) { + "Number" + $cn + } else { + $cn + } + } + #endregion Escape unsafe class names + + + #region Create the namespace if it doesn't yet exist + $namespacesToMake = New-Object Collections.Stack + $originalNamespace = $Namespace + do { + $temprootNamespace = $Namespace | Split-Path + $leafName = $Namespace | Split-Path -Leaf + + if (-not $temprootNamespace) { + $temprootNamespace = "root" + } + + $namespaceExists = Get-WmiObject -Query "Select * from __namespace Where Name ='$leafName'" -Namespace $temprootNamespace -ErrorAction SilentlyContinue + if (-not $namespaceExists) { + $null = $namespacesToMake.Push($leafName) + } else { + break + } + + $Namespace = $temprootNamespace + $rootNamespace = $temprootNamespace + + if ($Namespace -eq 'root') { + break + } + } while (-not $namespaceExists) + + + $namespace= $originalNamespace + + + foreach ($namespace in $namespacesToMake) { + if (-not $Namespace) {continue} + $mp = New-Object Management.ManagementPath + $mp.NamespacePath = "$rootNamespace" + $mp.ClassName = "__namespace" + $namespaceClass = New-Object Management.ManagementClass $mp + $newNamespace = $namespaceClass.CreateInstance() + $newNamespace.Name = $Namespace + $null = $newNamespace.put() + $rootNamespace = $rootNamespace + "\" + $Namespace + } + #endregion Create the namespace if it doesn't yet exist + } + + + + process { + + if (-not $PSBoundParameters.Classname) { + $ClassName = $InputObject.pstypenames[0] + } + + + + $classPath = New-Object Management.ManagementPath + $classPath.NamespacePath = $originalNamespace + $classPath.ClassName = . $getSafeClassName + + + $classDef = New-Object Management.ManagementClass $classPath + + $classDefExists = $(try { $classDef | Get-Member -ErrorAction SilentlyContinue } catch {}) -ne $null + + if (-not $classDefExists) { + $classDef = New-Object Management.ManagementClass $originalNamespace, "", $null + $classDef["__Class"] = . $getSafeClassName + + + if ($InputObject -is [string]) { + + } else { + + foreach ($prop in $InputObject.PSObject.Properties) { + if ($prop.Name -like "__*") { continue } + $cimType = . $cimColumnType + $classDef.Properties.Add($prop.Name, $cimType, $false) + + if ($key -contains $prop.Name) { + $classDef.Properties[$prop.Name].Qualifiers.Add("Key", $true) + } + } + $null = $classDef.Put() + } + } else { + $instances = $null + foreach ($prop in $InputObject.PSObject.Properties ) { + if ($prop.Name -like "__*") { continue } + if ($prop.MemberType -eq 'AliasProperty') { continue } + $automaticPropertiesToIgnore = 'Scope', + 'Path','Options', + 'ClassPath', 'Properties','SystemProperties','Qualifiers','Site','Container' + if ($InputObject -is [wmi] -and $automaticPropertiesToIgnore -contains $prop.Name) { + continue + } + + if ('PSComputerName', 'PSShowComputerName', 'RunspaceID' -contains $prop.Name) { + continue + } + + $cimType = . $cimColumnType + $propExists = $classDef.Properties[$prop.Name] + + if (-not $propExists) { + if (-not $instances) { + $instances = Get-WmiObject -Namespace $originalNamespace -Class (. $getSafeClassName) + } + $classDef.Properties.Add($prop.Name, $cimType, $false) + } + + if ($key -contains $prop.Name) { + if (-not $classDef.Properties[$prop.Name].Qualifiers["Key"]) { + $classDef.Properties[$prop.Name].Qualifiers.Add("Key", $true) + } + } + + } + + if ($instances) { + # Class definition changed. Rebuild objects. Ugh. + $instanceProperties = $instances | + Get-Member -MemberType Property | + Where-Object { $_.Name -notlike "__*" } + + $instances | Remove-WmiObject + $classPath = New-Object Management.ManagementPath + $classPath.NamespacePath = $originalNamespace + $classPath.ClassName = . $getSafeClassName + + + $cdef = New-Object Management.ManagementClass $classPath + $null = $cdef.Delete() + foreach ($ip in $instanceProperties) { + if (-not $classDef.Properties[$ip.Name]) { + + + $classDef.Properties.Add($ip.Name, [Management.CimType]::String, $false) + } + } + + + $null = $classDef.Put(); + $instances | Update-Wmi -Namespace $originalNamespace -ClassName (. $getSafeClassName) -Key $key + } + } + + + $where = @(foreach ($k in $key) { + "$k = '$("$($inputObject.$k)".Replace("'","''"))'" + }) -join ' AND ' + + $instanceExists = Get-WmiObject -Class $classDef["__Class"] -Namespace $originalNamespace -Filter $where + + + if ($instanceExists -and -not $force) { + return + } + + if ($force -and $instanceExists) { + foreach ($prop in $InputObject.PSObject.Properties) { + $instanceExists.($prop.Name) = + if ($prop.Value -is [DateTime]) { + [Management.ManagementDateTimeConverter]::ToDmtfDateTime($prop.Value) + } else { + $prop.Value + } + + } + $null = $instanceExists.Put() + + } else { + $classInstance = $classDef.CreateInstance() + foreach ($prop in $InputObject.PSObject.Properties) { + if ($prop.Name -like "__*") { continue } + if ($prop.MemberType -eq 'AliasProperty') { continue } + $automaticPropertiesToIgnore = 'Scope', + 'Path','Options', + 'ClassPath', 'Properties','SystemProperties','Qualifiers','Site','Container' + if ($InputObject -is [wmi] -and $automaticPropertiesToIgnore -contains $prop.Name) { + continue + } + if ('PSComputerName', 'PSShowComputerName', 'RunspaceID' -contains $prop.Name) { + continue + } + $classInstance.($prop.Name) = + if ($prop.Value -is [DateTime]) { + [Management.ManagementDateTimeConverter]::ToDmtfDateTime($prop.Value) + } else { + $prop.Value + } + } + $null = $classInstance.Put() + } + + + } +} \ No newline at end of file diff --git a/write-crud.ps1 b/write-crud.ps1 new file mode 100644 index 0000000..006105c Binary files /dev/null and b/write-crud.ps1 differ