+ ")
+ ""
+ } else {
+ $null = $inputForm.Append("
+
+ ${boldIfMandatory}${friendlyParameterName}${unboldIfMandatory}
+ ")
+ }
+ } 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(@"
+
+
+"@)
+ }
+ } 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) {
+ "
+
+ "
+ } 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(" $(
+ConvertFrom-Markdown -md $parameterHelp)")
+ } elseif ($Platform -eq 'Android') {
+ if ([Switch], [Bool] -notcontains $parameterType) {
+ $null = $inputForm.Append("
+ ")
+ }
+ } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) {
+ if ([Switch], [Bool] -notcontains $parameterType) {
+ $null = $inputForm.Append("
+
+ $([Security.SecurityElement]::Escape($parameterHelp))
+ ")
+ }
+ }
+ }
+
+ if ($platform -eq 'Web') {
+ $null = $inputForm.Append("
+
+
+ ")
+
+ }
+
+
+
+ $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 "
+ }
+ $options = $options -join ([Environment]::NewLine)
+ if (-not $firstcomboBox) {
+ $firstcomboBox = $optionList
+
+ }
+ $null = $inputForm.Append("
+ $(if (-not $IsMandatory) { " " })
+ $options
+
+ ")
+ } 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(@"
+
+"@)
+ foreach ($value in $optionList) {
+
+ $null = $inputForm.Append("
+ ")
+
+ }
+
+ $null = $inputForm.Append(@"
+
+"@)
+ }
+ } 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(@"
+
+
+ $([Security.SecurityElement]::Escape($phoneFriendlyHelp ))
+
+
+"@)
+
+ } 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("
+
+ ")
+
+
+ foreach ($value in $optionList) {
+
+ $null = $inputForm.Append("
+ $([Security.SecurityElement]::Escape($value)) ")
+
+ }
+ $null = $inputForm.Append("
+
+ ")
+
+
+ } else {
+ # Combo Box
+ $null = $inputForm.Append(@"
+
+"@)
+ foreach ($value in $optionList) {
+
+ $null = $inputForm.Append("
+ $([Security.SecurityElement]::Escape($value)) ")
+
+ }
+
+ $null = $inputForm.Append(@"
+
+"@)
+ }
+ } elseif ('GoogleGadget' -eq $platform ) {
+ $enumItems = foreach ($option in $optionList) {
+ "
"
+ }
+ $null = $inputForm.Append( @"
+
+ $enumItems
+
+"@)
+ }
+ } 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("
")
+ } elseif ($pipeworksDirectives.FilePickerIO) {
+ $null = $inputForm.Append("
")
+ }
+ }
+ } elseif ($parameterType -eq [Security.SecureString]) {
+ if ($platform -eq 'Web') {
+ $null = $inputForm.Append("
")
+ } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) {
+ $null = $inputForm.Append(@"
+
+"@)
+ }
+ } 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 = @"
+
+
+
+
+
+
+
+
+"@
+ $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("
")
+ } elseif ($platform -eq 'Android') {
+ $null = $inputForm.Append(@"
+
+
+
+"@)
+ } elseif ('WindowsMobile', 'WPF' ,'Metro', 'SilverLight', 'Win8' -contains $platform) {
+ $null = $inputForm.Append(@"
+
+
+
+
+
+ $([Security.SecurityElement]::Escape($parameterHelp))
+
+
+
+"@)
+ } 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(@"
+
+
+ $([Security.SecurityElement]::Escape($phoneFriendlyHelp ))
+
+
+"@)
+ }
+ }
+
+
+ # Close the parameter input
+ if ($platform -eq 'Web') {
+
+ $null = $inputForm.Append("
+
+
+
+
+
+ ")
+ } 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("
+
+
+ ")
+ } else {
+
+ $null = $inputForm.Append("
+
+
+ ")
+ }
+ if ($buttonImage) {
+ $null = $inputForm.Append("
+
+ ")
+ } else {
+ $null = $inputForm.Append("
+
+ $(if ($Ajax) {
+ ""
+ })
+
+
+
+
+ "
+
+
+ )
+
+ }
+ if (-not $twoColumn) {
+ $null = $inputForm.Append(" ")
+ } else {
+ $null = $inputForm.Append(" ")
+ }
+ } elseif ($Platform -eq 'Android') {
+ $null = $inputForm.Append(@"
+
+
+
+
+"@)
+ } elseif ('WPF', 'WindowsMobile', 'WindowsMetro', 'Win8' -contains $Platform) {
+ $null = $inputForm.Append(@"
+
+
+
+
+"@)
+ }
+ }
+
+
+ #endregion
+ if ($platform -eq 'Web') {
+ $null = $inputForm.Append("
+
+ $(if ($Focus) {@"
+
+"@
+})
+
+
+
+
+
+
+")
+ } elseif ($platform -eq 'Android') {
+ $null = $inputForm.Append("
+
+
+
+")
+ } 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(@"
+
+
+
+"@)
+ } elseif ('GoogleGadget' -eq $platform) {
+ $null = $inputForm.Append(@"
+
+"@)
+ } elseif ('TwilML' -eq $platform) {
+ $null = $inputForm.Append(@"
+
+"@)
+ }
+
+ $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
+ [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) {
+ @"
+
+"@
+ } 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("
+$(Get-Help Get-Command -full | Out-String -width 1024)
+ ")
+ } |
+ 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 "*
+"@
+ }
+
+ 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) { '' } else {'<%' })
+runScript(@"$($scriptBlock.ToString().Replace('"','""'))");
+$(if ($MasterPage) { ' ' } 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') {
+ " "
+ } elseif ($pscmdlet.ParameterSetName -eq 'Table') {
+ $cssLines = foreach ($kv in $css.GetEnumerator()) {
+ $name = $kv.Key
+
+ Write-CSS -Name $name -Style $kv.Value
+ }
+"$(if (-not $NoStyleTag) { ''})"
+ } 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 "
+
+
+
+ Visit Website
+ "
+ .Link
+ Out-HTML
+ #>
+
+ [CmdletBinding(DefaultParameterSetName='ToUrl')]
+ [OutputType([string])]
+ param(
+ # If set, will output a simple $caption .
+ [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
+ "$cap "
+ } 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'))) {
+@"
+
+
+
+
+
+"@
+ } 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
+@"
+
+
+"@
+ } 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
+"
"
+ } 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
+ @"
+
+"@
+ } elseif ($u.Scheme -eq 'YouTube') {
+ #Youtube channel link
+ " "
+ } elseif ($u.Scheme -eq 'Twitter' -and $u.PathAndQuery -like "@*") {
+ #Twitter Profile Link
+ if ($cap -eq $u) { $cap = $u.PathAndQuery }
+ "$cap "
+ } 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@', '')
+@"
+
+
+"@
+ } 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]
+ "$cap
+ "
+ } elseif ($u.Scheme -eq 'Disqus') {
+ # If the scheme is like disqus, then create a link to disqus
+@"
+
+
+Please enable JavaScript to view the comments powered by Disqus.
+blog comments powered by
+"@
+ } elseif ($u.Scheme -ieq 'Play') {
+#Play media in
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u.Scheme -ieq 'Loop') {
+#Play media in hidden embed
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u.Scheme -ieq 'PlayHidden') {
+#Play media in hidden embed
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u.Scheme -ieq 'Pause') {
+#Play media in
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u.Scheme -ieq 'Loop') {
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u.Scheme -ieq 'LoopHidden') {
+$newUrl = "http" + $u.Tostring().Substring($u.Scheme.Length)
+@"
+
+"@
+ } elseif ($u -like "*.jpeg" -or $u -like "*.jpg" -or $u -like "*.gif" -or $u -like "*.tiff" -or $u -like "*.png") {
+
+@"
+
+"@
+ } elseif ($u -like "*.mp3") {
+@"
+
+
+
+
+
+
+
+
+
+$Cap
+"@
+ } elseif ($u -like "http*://www.youtube.com/watch*=*") {
+ # YouTube Link
+ $type, $youTubeId = $u.Query -split '='
+ $type = $type.Trim("?")
+@"
+
+
+
+
+
+
+
+"@
+
+ } elseif ($u -like "http://player.vimeo.com/video/*") {
+ # Vimeo link
+ $vimeoId = ([uri]$u).Segments[-1]
+@"
+$($cap)
+"@
+ } elseif ($u -like "http://stackoverflow.com/users/*") {
+ $userId = ([uri]$u).Segments[-2]
+ $userName = ([uri]$u).Segments[-1]
+@"
+
+
+
+"@
+ } elseif ($u -like "*.jp?g" -or $u -like "*.png") {
+ "
+
+ $cap
+ "
+ } else {
+ # Just a simple link
+ "$("$cap".Replace('&', '&')) "
+ }
+ }
+
+ $c++
+ }
+
+ $textOutput = if ($links.Count -or $List -or $NumberedList) {
+ if ($List) {
+ '' + ($links -join ' ') + ' '
+ } elseif ($NumberedList) {
+ '' + ($links -join ' ') + ' '
+ } elseif ($horizontal) {
+ $links -join "$HorizontalSeparator"
+ } else {
+ $links -join " "
+ }
+
+
+ } elseif ($psCmdlet.ParameterSetName -eq 'ToBuyButton') {
+
+ if ($GoogleCheckoutMerchantId) {
+@"
+
+"@
+ }
+
+ if ($PaypalEmail) {
+$payPalIpnChunk = if ($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) {
+ "
+
+
+
+ "
+} elseif ($Donate) {
+ " "
+} else {
+ " "
+}
+
+$payPalCustomChunk = if ($payPalCustom) {
+ " "
+} else {
+ ""
+}
+@"
+
+"@
+ }
+
+
+ if ($StripePublishableKey) {
+@"
+
+"@
+ }
+
+ if ($BuyCodeHandler) {
+@"
+
+"@
+ }
+
+ 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;
+
+ ///
+ /// Amazon FPS Exception provides details of errors
+ /// returned by Amazon FPS service
+ ///
+ public class AmazonFPSException : Exception
+ {
+
+ private String message = null;
+
+ ///
+ /// Constructs AmazonFPSException with message
+ ///
+ /// Overview of error
+ public AmazonFPSException(String message)
+ {
+ this.message = message;
+ }
+
+ ///
+ /// Gets error message
+ ///
+ 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 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 formHiddenInputs = new SortedDictionary(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 formHiddenInputs,String ServiceEndPoint,String imageLocation)
+ {
+ StringBuilder form = new StringBuilder("\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 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 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 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 sortedParamMap = new SortedDictionary(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)
+
+
+
+<#
+@"
+
+"@
+#>
+ }
+ } 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"
+@"
+Login
+$(if ($pipeworksManifest.UseJQueryUI -or $pipeworksManifest.UseBootstrap) { ""
+})
+"@
+
+ } 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 {
+ "
+
+
+ "
+}
+
+
+
+@"
+
+
+
+
Connect With Facebook
+
+
+"@
+ } 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 = "" + (ConvertFrom-Markdown -Markdown $chunk) + "
"
+ }
+
+ $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("$([Web.HttpUtility]::HtmlEncode($chunk).Replace('&','&').Replace('"','`"')) ")
+ }
+ $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 tags.
+ [Parameter(Position=3)]
+ [Switch]$OutputAsHtml,
+
+ # If set, will start with walkthru with a 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("
+
+
+ $($step.Explanation.Replace([Environment]::newline, ' '))
+
")
+ } else {
+ $null = $walkThruHtml.Append("
+
+
+ $(ConvertFrom-Markdown -Markdown "$($step.Explanation) ")
+
")
+ }
+ 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(@"
+
+
+"@)
+ } elseif ($step.VideoFile -like "http://player.vimeo.com/video/*") {
+ $vimeoId = ([uri]$step.VideoFile).Segments[-1]
+ $null =
+ $walkThruHtml.Append(@"
+
+$($walkThru.Explanation)
+
+"@)
+ } else {
+ $null =
+ $walkThruHtml.Append("
+
+ Watch Video ")
+ }
+ }
+ $null = $walkThruHtml.Append(" ")
+
+ if (("$($step.Script)".Trim())-and ("$($step.Script)".Trim() -ne '$null')) {
+ $scriptHtml = Write-ScriptHTML -Text $step.Script
+ $null = $walkThruHtml.Append(@"
+
+$scriptHtml
+
+"@)
+ }
+
+ if ($RunDemo) {
+ $outText = . $step.Script
+ if (-not $OutputAsHtml) {
+ $null = $walkThruHtml.Append("$([Security.SecurityElement]::Escape(($outText | Out-String))) ")
+ } 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" = "$walkThruHTML
"
+ $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 = "
+
+
+
+ *[System[EventID=$($EventId)]]
+
+
+
+"
+
+ $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