From 6020d83d6afddeaa41a75953ea4acd600154a8b3 Mon Sep 17 00:00:00 2001 From: jdhitsolutions Date: Fri, 5 Mar 2021 19:18:39 -0500 Subject: [PATCH] help updates and New-PSPropertySet --- Changelog.md | 37 +- License.txt | 2 +- PSTypeExtensionTools.psd1 | Bin 8716 -> 8772 bytes PSTypeExtensionTools.psm1 | 372 +----------- README.md | 37 +- docs/Add-PSTypeExtension.md | 12 +- docs/Export-PSTypeExtension.md | 15 +- docs/Get-PSType.md | 23 +- docs/Get-PSTypeExtension.md | 7 +- docs/Import-PSTypeExtension.md | 9 +- docs/New-PSPropertySet.md | 209 +++++++ docs/PSTypeExtensionTools.md | 10 +- docs/about_PSTypeExtensionTools.md | 36 +- en-us/about_PSTypeExtensionTools.help.txt | 124 ---- .../PSTypeExtension.format.ps1xml | 0 functions/public.ps1 | 554 ++++++++++++++++++ samples/README.md | 2 +- samples/fileage-type.ps1xml | 17 + samples/mydatetime.types.ps1xml | 26 + 19 files changed, 914 insertions(+), 578 deletions(-) create mode 100644 docs/New-PSPropertySet.md delete mode 100644 en-us/about_PSTypeExtensionTools.help.txt rename PSTypeExtension.format.ps1xml => formats/PSTypeExtension.format.ps1xml (100%) create mode 100644 functions/public.ps1 create mode 100644 samples/fileage-type.ps1xml create mode 100644 samples/mydatetime.types.ps1xml diff --git a/Changelog.md b/Changelog.md index c5a5da7..72674f0 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,8 +1,17 @@ # Change Log for PSTypeExtensionTools +## TODO + ++ `Export-PSTypeExtension` to allow appending to a file and to be able to store different types in the same file.([Issue #16](https://github.com/jdhitsolutions/PSTypeExtensionTools/issues/16)) + +## v1.6.0 + ++ Re-organized module structure. ++ Added `New-PSPropertySet` ([Issue #15](https://github.com/jdhitsolutions/PSTypeExtensionTools/issues/15)) + ## v1.5.1 -+ Fixed error in properly exporting aliases. ++ Fixed error in exporting aliases. + Updated license file. + Very minor help corrections. @@ -12,26 +21,22 @@ + Modified `Import-PSTypeExtension` to allow piping files into the command. + Added alias `Type` to `-MemberType` parameter of `Add-PSTypeExtension` + Added alias `Name` to `-MemberName` parameter of `Add-PSTypeExtension` -+ Added online help links -+ Updated some of the sample json files to include deserialized type versions -+ Updated help documentation -+ Updated `README.md` ++ Added online help links. ++ Updated some of the sample JSON files to include deserialized type versions. ++ Updated help documentation. ++ Updated `README.md`. ## v1.4.0 -+ Modified manifest to require PowerShell 5.1 -+ Modified manifest to support both Desktop and Core PSEditions -+ Updates to some of the sample files -+ Updated `README.md` ++ Modified manifest to require PowerShell 5.1. ++ Modified manifest to support both Desktop and Core PSEditions. ++ Updates to some of the sample files. ++ Updated `README.md`. ## v1.3.0 -+ file cleanup for the PowerShell Gallery -+ Exported Samples folder as a variable to make it easier to import -+ updated `About` documentation - -## v1.2.0 - ++ File cleanup for the PowerShell Gallery. ++ Exported Samples folder as a variable to make it easier to import. + code cleanup + markdown cleanup + help cleanup @@ -81,7 +86,7 @@ ## v0.2.0 -+ renamed commands for consistency with module name ++ renamed commands to be consistent with the module name + updated help documentation + Added `Set-PSTypeExtension` as an alias to `Add-PSTypeExtension` diff --git a/License.txt b/License.txt index b414ac5..53d55fe 100644 --- a/License.txt +++ b/License.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017-2020 JDH Information Technology Solutions, Inc. +Copyright (c) 2017-2021 JDH Information Technology Solutions, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy diff --git a/PSTypeExtensionTools.psd1 b/PSTypeExtensionTools.psd1 index 3327d40d1fdf49fc7093118c13a0da25d3116e38..54592e4257e29e24199c5b92e44723171a8c7a6f 100644 GIT binary patch delta 93 zcmeBiIpVV68Y8P2gC2vy=G%-vnHUW>Uu6m8=1*hDXDDLGWk_TwVJK#ZnXJz!zu83~ riCI;LL7l;mA(f$=L6;$bAsC2@fJzI1B1J%@l|YeHhLX*Tg){j8qS_ZV delta 38 ucmX@&(&Mt>8Y8PIgC2w7=G%-vnHUW=Uu6m8p6DXCIYc0ddGkHtG(G?byA2}% diff --git a/PSTypeExtensionTools.psm1 b/PSTypeExtensionTools.psm1 index b1a74a2..96e5b43 100644 --- a/PSTypeExtensionTools.psm1 +++ b/PSTypeExtensionTools.psm1 @@ -1,373 +1,9 @@ -Function Get-PSTypeExtension { - [cmdletbinding()] - Param( - [Parameter(Position = 0, Mandatory, HelpMessage = "Enter the name of type like System.IO.FileInfo", - ValueFromPipelineByPropertyName, ValueFromPipeline)] - [ValidateNotNullorEmpty()] - [ValidateScript( { - #check if typename can be found with Get-TypeData - if ((get-typedata).typename -contains "$_") { - $True - } - elseif ($_ -as [type]) { - #test if string resolves as a typename - $True - } - else { - Throw "$_ does not appear to be a valid type." - } - })] - [string]$TypeName, - [Parameter(HelpMessage = "Enter a comma separated list of member names", ParameterSetName = "members")] - [string[]]$Members - ) - - Begin { - Write-Verbose "Starting: $($MyInvocation.Mycommand)" - $typedata = @() - } #begin - Process { - Write-Verbose "Analyzing $typename" - $typedata += Get-TypeData -TypeName $typename - - - } #process - End { - $typedata = $typedata | Select-Object -Unique - if ($typedata) { - - if (-Not $Members) { - Write-Verbose "Getting all member names" - $Members = $typedata.members.keys - - } - foreach ($name in $Members) { - Try { - Write-Verbose "Analyzing member $name" - $member = $typedata.members[$name] - $datatype = $member.gettype().name - - Write-Verbose "Processing type $datatype" - Switch ($datatype) { - "AliasPropertyData" { - $def = [pscustomobject]@{ - MemberType = "AliasProperty" - MemberName = $member.name - Value = $member.ReferencedMemberName - } - } #alias - "ScriptpropertyData" { - if ($member.GetScriptBlock) { - $code = $member.GetScriptBlock.ToString() - } - else { - $code = $member.SetScriptBlock.ToString() - } - $def = [pscustomobject]@{ - MemberType = "ScriptProperty" - MemberName = $member.name - Value = $code - } - } #scriptproperty - "ScriptMethodData" { - $def = [pscustomobject]@{ - MemberType = "ScriptMethod" - MemberName = $member.name - Value = $member.script.ToString().trim() - } - } #scriptmethod - "NotePropertyData" { - $def = [pscustomobject]@{ - MemberType = "Noteproperty" - MemberName = $member.name - Value = $member.Value - } - } #noteproperty - "CodePropertyData" { - if ($member.GetCodeReference) { - $code = $member.GetCodeReference.ToString() - } - else { - $code = $member.SetCodeReference.ToString() - } - $def = [pscustomobject]@{ - MemberType = "CodeProperty" - MemberName = $member.name - Value = $code - } - } #codeproperty - Default { - Write-Warning "Cannot process $datatype type for $($typedata.typename)." - $def = [pscustomobject]@{ - MemberType = $datatype - MemberName = $member.name - Value = $member.Value - } - } - } - - $def | Add-Member -MemberType NoteProperty -Name TypeName -Value $typedata.typename - #insert a typename - $def.psobject.typenames.insert(0, 'PSTypeExtension') - #write the definition to the pipeline - Write-Output $def - - } - Catch { - Write-Warning "Could not find an extension member called $name" - write-Debug $_.exception.message - } - - } #foreach - - } - else { - Write-Warning "Failed to find any type extensions for [$Typename]." - } - Write-Verbose "Ending: $($MyInvocation.Mycommand)" - } - -} #end Get-PSTypeExtension - -Function Get-PSType { - [cmdletbinding()] - Param( - [Parameter(Position = 0, Mandatory, ValueFromPipeline)] - [object]$Inputobject - ) - - Begin { - Write-Verbose "Starting: $($MyInvocation.Mycommand)" - $data = @() - } - Process { - #get the type of each pipelined object - $data += ($Inputobject | Get-Member | select-object -first 1).typename - } - End { - #write unique values to the pipeline - $data | Get-Unique - Write-Verbose "Ending: $($MyInvocation.Mycommand)" - } -} #end Get-PSType - -Function Export-PSTypeExtension { - [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "Object")] - Param( - [Parameter(Position = 0, Mandatory, HelpMessage = "The type name to export like System.IO.FileInfo", ParameterSetName = "Name")] - [ValidateNotNullOrEmpty()] - [string]$TypeName, - [Parameter(Mandatory, HelpMessage = "The type extension name", ParameterSetName = "Name")] - [ValidateNotNullOrEmpty()] - [string[]]$MemberName, - [Parameter(Mandatory, HelpMessage = "The name of the export file. The extension must be .json,.xml or .ps1xml")] - [ValidatePattern("\.(xml|json|ps1xml)$")] - [string]$Path, - [Parameter(ParameterSetName = "object", ValueFromPipeline)] - [object]$InputObject - ) - Begin { - Write-Verbose "Starting: $($MyInvocation.Mycommand)" - $data = @() - } - Process { - if ($Inputobject) { - Write-Verbose "Processing input type: $($InputObject.TypeName)" - $data += $Inputobject - } - else { - Write-Verbose "Processing type: $TypeName" - foreach ($member in $membername) { - $data += Get-PSTypeExtension -TypeName $Typename -Members $Member - } - } - } - End { - if ($Path -match "\.ps1xml$") { - Write-Verbose "Saving as PS1XML" - #create a placeholder file so that I can later convert the path - New-Item -Path $path -Force | Out-Null - [xml]$Doc = New-Object System.Xml.XmlDocument - - #create declaration - $dec = $Doc.CreateXmlDeclaration("1.0", "UTF-8", $null) - #append to document - $doc.AppendChild($dec) | Out-Null - - #create a comment and append it in one line - $text = @" - -Custom type extensions generated by $($env:username) -$(Get-Date) - -"@ - $doc.AppendChild($doc.CreateComment($text)) | Out-Null - - #create root Node - $root = $doc.CreateNode("element", "Types", $null) - $main = $doc.CreateNode("element", "Type", $null) - $name = $doc.CreateElement("Name") - $name.innerText = $data[0].TypeName - $main.AppendChild($name) | out-null - $member = $doc.CreateNode("element", "Members", $null) - foreach ($extension in $data) { - Write-Verbose "Exporting $($extension.membername)" - $membertype = $doc.createNode("element", $extension.memberType, $null) - $membernameEL = $doc.CreateElement("Name") - - $membernameEL.innertext = $extension.memberName - $membertype.AppendChild($membernameEL) | out-null - - Switch ($extension.Membertype) { - "ScriptMethod" { - $memberdef = $doc.createelement("Script") - } - "ScriptProperty" { - $memberdef = $doc.createelement("GetScriptBlock") - } - "AliasProperty" { - $memberdef = $doc.createelement("ReferencedMemberName") - } - "NoteProperty" { - $memberdef = $doc.createelement("Value") - } - Default { - Throw "Can't process a type of $($extension.MemberType)" - } - } - $memberdef.InnerText = $extension.value - $membertype.AppendChild($memberdef) | out-null - $member.AppendChild($membertype) | out-null - - } #foreach - $main.AppendChild($member) | out-null - $root.AppendChild($main) | Out-Null - $doc.AppendChild($root) | out-null - $out = Convert-Path $path - if ($PSCmdlet.ShouldProcess($out)) { - $doc.save($out) - } #end Whatif - } - elseif ($path -match "\.xml$") { - Write-Verbose "Saving as XML" - $data | Export-Clixml -path $path - } - else { - Write-Verbose "Saving as JSON" - $data | ConvertTo-Json | Set-Content -Path $Path - } - - Write-Verbose "Exporting data to $path" - Write-Verbose "Ending: $($MyInvocation.Mycommand)" - } - -} #end Export-PSTypeExtension - -Function Import-PSTypeExtension { - [CmdletBinding(SupportsShouldProcess)] - Param( - [Parameter(Mandatory, - ValueFromPipeline, - ValueFromPipelineByPropertyName, - HelpMessage = "The name of the imported file. The extension must be .xml or .json")] - [ValidatePattern("\.(xml|json)$")] - [ValidateScript( { Test-Path $(Convert-Path $_) })] - [alias("fullname")] - [string]$Path - ) - - Begin { - Write-Verbose "Starting: $($myInvocation.mycommand)" - } - Process { - Write-Verbose "Importing file $(Convert-path $Path)" - if ($path -match "\.xml$") { - #xml format seems to add an extra entry - $import = Import-clixml -Path $path | Where-Object MemberType - } - else { - $import = Get-Content -path $path | ConvertFrom-Json - } - - foreach ($item in $import) { - Write-Verbose "Processing $($item.MemberType) : $($item.MemberName)" - if ($item.MemberType -match "^Code") { - Write-Warning "Skipping Code related member" - } - if ($item.MemberType -match "^Script") { - Write-Verbose "Creating scriptblock from value" - $value = [scriptblock]::create($item.value) - } - else { - $value = $item.Value - } - #add a custom -WhatIf message - if ($PSCmdlet.ShouldProcess($Item.typename, "Adding $($item.membertype) $($item.MemberName)")) { - #implement the change - Update-TypeData -TypeName $item.Typename -MemberType $item.MemberType -MemberName $item.MemberName -value $value -force - } - } #foreach - } - End { - Write-Verbose "Ending: $($myInvocation.mycommand)" - } - -} #end Import-PSTypeExtension - -Function Add-PSTypeExtension { - [cmdletbinding(SupportsShouldProcess)] - [Alias('Set-PSTypeExtension')] - - Param( - [Parameter( - Position = 0, - Mandatory, - ValueFromPipeline, - HelpMessage = "Enter the name of a type like system.io.fileinfo")] - [string]$TypeName, - [Parameter(Mandatory, HelpMessage = "The member type")] - [ValidateSet("AliasProperty", "Noteproperty", "ScriptProperty", "ScriptMethod")] - [alias("Type")] - [string]$MemberType, - [Parameter(Mandatory, HelpMessage = "The name of your type extension")] - [ValidateNotNullOrEmpty()] - [alias("Name")] - [string]$MemberName, - [Parameter(Mandatory, HelpMessage = "The value for your type extension. Remember to enclose scriptblocks in {} and use `$this")] - [ValidateNotNullOrEmpty()] - [Object]$Value, - [Parameter(HelpMessage = "Create the extension in the deserialized version of the specified type including the original type.")] - [switch]$IncludeDeserialized - - ) - Begin { - Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" - - } #begin - - Process { - #force overwrite of existing extensions - $PSBoundParameters.Add("Force", $True) - if ($PSBoundParameters.ContainsKey("IncludeDeserialized")) { - [void]$PSBoundParameters.Remove("IncludeDeserialized") - $PSBoundParameters.Typename = "deserialized.$Typename" - Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $MemberType $Membername to $($psboundparameters.TypeName)" - Update-TypeData @PSBoundParameters - $PSBoundParameters.Typename = $Typename - } - Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $MemberType $Membername to $($psboundparameters.TypeName)" - Update-TypeData @PSBoundParameters - } #process - - End { - Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" - - } #end - -} #close Add-MyTypeExtension +Get-ChildItem $PSScriptRoot\Functions\*.ps1 | +Foreach-object { . $_.fullname} #Export the Samples folder location as a variable -$PSTypeSamples = Join-path $PSScriptRoot -ChildPath samples +$PSTypeSamples = Join-Path $PSScriptRoot -ChildPath samples + Export-ModuleMember -Variable PSTypeSamples -Alias 'Set-PSTypeExtension' diff --git a/README.md b/README.md index 453f32d..982d3ee 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ [![PSGallery Version](https://img.shields.io/powershellgallery/v/PSTypeExtensionTools.png?style=for-the-badge&logo=powershell&label=PowerShell%20Gallery)](https://www.powershellgallery.com/packages/PSTypeExtensionTools/) [![PSGallery Downloads](https://img.shields.io/powershellgallery/dt/PSTypeExtensionTools.png?style=for-the-badge&label=Downloads)](https://www.powershellgallery.com/packages/PSTypeExtensionTools/) -This PowerShell module contains commands that make it easier to work with type extensions. Many of these commands are wrappers for built-in tools like [Get-TypeData](http://go.microsoft.com/fwlink/?LinkId=821805) or [Update-TypeData](http://go.microsoft.com/fwlink/?LinkId=821871). +This PowerShell module contains commands that make it easier to work with type extensions. Many of these commands are wrappers for built-in tools like [Get-TypeData](http://go.microsoft.com/fwlink/?LinkId=821805) or [Update-TypeData](http://go.microsoft.com/fwlink/?LinkId=821871). This module should work in Windows PowerShell and PowerShell 7.x. ## Release -You can the current release from the PowerShell Gallery: +You can install the current release from the PowerShell Gallery: ```powershell Install-Module PSTypeExtensionTools @@ -14,7 +14,7 @@ Install-Module PSTypeExtensionTools ## Why You Want to Use This -Let's say you want to update a number object, but you have no idea what the type name is. Once you have read help for the commands in this module you could run a PowerShell command like this: +Let's say you want to update a number object, but you have no idea what the type name is. Once you have read help for the commands in this module, you could run a PowerShell command like this: ```powershell PS C:\> 123 | Get-PSType | Add-PSTypeExtension -MemberType ScriptProperty -MemberName SquareRoot -Value { [math]::Sqrt($this)} @@ -28,7 +28,7 @@ PS C:\> $x.SquareRoot 11.0905365064094 ``` -Once you know the type name you can add other type extensions. +Once you know the type name, you can add other type extensions. ```powershell PS C:\> Add-PSTypeExtension -TypeName system.int32 -MemberType ScriptProperty -MemberName Squared -value { $this*$this} @@ -56,7 +56,7 @@ PS C:\> $x.GetPercent(110,4) 34.5455 ``` -To see what has been defined you can use `Get-PSTypeExtension`. You can choose to see all extensions or selected ones by member name. +To see what has been defined, you can use `Get-PSTypeExtension`. You can choose to see all extensions or selected ones by member name. ```powershell PS C:\> Get-PSTypeExtension system.int32 @@ -72,13 +72,13 @@ Value ScriptProperty $this GetPercent ScriptMethod Param([int32]$Total,[int32]$Round=2) [math]::Round(($this/$total)*100,$round) ``` -If you always want these extensions you would have to put the commands into your PowerShell profile script. Or you can export the extensions to a json or xml file. You can either export all members or selected ones which is helpful if you are extending a type that already has type extensions from PowerShell. +If you always want these extensions, you would have to put the commands into your PowerShell profile script. Or you can export the extensions to a JSON or XML file. You can either export all members or selected ones, which is helpful if you are extending a type that already has type extensions from PowerShell. ```powershell PS C:\> Get-PSTypeExtension system.int32 | Export-PSTypeExtension -TypeName system.int32 -Path c:\work\int32-types.json ``` -In your PowerShell profile script you can then re-import the type extension definitions. +In your PowerShell profile scrip,t you can then re-import the type extension definitions. ```powershell Import-PSTypeExtension -Path C:\work\int32-types.json @@ -86,34 +86,37 @@ Import-PSTypeExtension -Path C:\work\int32-types.json ## Create ps1xml Files -The export command makes it easy to construct a ps1xml file. All you need to do is provide the type name and the extensions you want to export, and it will create a properly formatted ps1xml file that you can import into a session with `Update-TypeData` or distribute with a module. No more clunky XML copying, pasting and hoping for the best. - +The export command makes it easy to construct a ps1xml file. All you need to do is provide the type name and the extensions you want to export, and it will create a properly formatted ps1xml file that you can import into a session with `Update-TypeData` or distribute with a module. No more clunky XML copying, pasting, and hoping for the best. ## PSTypeExtensionTools Cmdlets -### [Add-PSTypeExtension](/docs/Add-PSTypeExtension.md) +### [Add-PSTypeExtension](docs/Add-PSTypeExtension.md) Add a new type extension such as an Alias or ScriptProperty. -### [Export-PSTypeExtension](/docs/Export-PSTypeExtension.md) +### [Export-PSTypeExtension](docs/Export-PSTypeExtension.md) Export type extensions to a json, xml or ps1xml file. -### [Get-PSType](/docs/Get-PSType.md) +### [Get-PSType](docs/Get-PSType.md) Get the type name of an object. -### [Get-PSTypeExtension](/docs/Get-PSTypeExtension.md) +### [Get-PSTypeExtension](docs/Get-PSTypeExtension.md) Get type extensions for a given type. -### [Import-PSTypeExtension](/docs/Import-PSTypeExtension.md) +### [Import-PSTypeExtension](docs/Import-PSTypeExtension.md) + +Import type extension definitions from a JSON file or XML. + +### [New-PSPropertySet](docs/New-PSPropertySet.md) -Import type extension definitions from a json file or xml. +TBD ## I Want to Try -You can find a number of type extension exports in the [Samples](./samples) folder. The location will be saved to a global variable, $PSTypeSamples. This makes it a bit easier to import. +You can find a number of type extension exports in the [Samples](./samples) folder. The location will be saved to a global variable, `$PSTypeSamples`. This makes it a bit easier to import. ```powershell PS C:\> dir $PSTypeSamples @@ -156,7 +159,7 @@ Count SumGB 4 50.2031 ``` -This project was first described at http://jdhitsolutions.com/blog/powershell/5777/a-powershell-module-for-your-type-extensions +This project was first described at There is also an about topic you can read: diff --git a/docs/Add-PSTypeExtension.md b/docs/Add-PSTypeExtension.md index 40e18f0..487a9d1 100644 --- a/docs/Add-PSTypeExtension.md +++ b/docs/Add-PSTypeExtension.md @@ -14,13 +14,12 @@ Add a new type extension. ## SYNTAX ```yaml -Add-PSTypeExtension [-TypeName] -MemberType -MemberName -Value - [-IncludeDeserialized] [-WhatIf] [-Confirm] [] +Add-PSTypeExtension [-TypeName] -MemberType -MemberName -Value [-IncludeDeserialized] [-WhatIf] [-Confirm] [] ``` ## DESCRIPTION -Use this command to easily define a new type extension for a given object type. Existing members with the same name will be overwritten so you can also use this as a "Set" command. +Use this command to easily define a new type extension for a given object type. Existing members with the same name will be overwritten, so you can also use this as a "Set" command. ## EXAMPLES @@ -41,7 +40,7 @@ PS C:\> $s.IsIPAddress() True ``` -Define an script property called IsIPAddress which will return True if the string matches the pattern for an IP address. +Define a script property called IsIPAddress which will return True if the string matches the pattern for an IP address. ### EXAMPLE 3 @@ -49,7 +48,7 @@ Define an script property called IsIPAddress which will return True if the strin PS C:\ Add-PSTypeExtension -TypeName system.io.fileinfo -MemberType AliasProperty -MemberName Size -value Length -IncludeDeserialized ``` -Create an alias called Size using the Length property on file objects. This expression will also create if for the deserialized version of the type so that you can use it with remoting results. +Create an alias called Size using the Length property on file objects. This expression will also create it for the deserialized version of the type so that you can use it with remoting results. ## PARAMETERS @@ -180,7 +179,8 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES -Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/ +Learn more about PowerShell: +http://jdhitsolutions.com/blog/essential-powershell-resources/ ## RELATED LINKS diff --git a/docs/Export-PSTypeExtension.md b/docs/Export-PSTypeExtension.md index f17f581..9fae666 100644 --- a/docs/Export-PSTypeExtension.md +++ b/docs/Export-PSTypeExtension.md @@ -22,8 +22,8 @@ Export-PSTypeExtension -Path [-WhatIf] [-Confirm] [] ### Name ```yaml -Export-PSTypeExtension [-TypeName] -MemberName -Path [-WhatIf] [-Confirm] - [] +Export-PSTypeExtension [-TypeName] -MemberName +-Path [-WhatIf] [-Confirm] [] ``` ### object @@ -34,7 +34,7 @@ Export-PSTypeExtension -Path [-InputObject ] [-WhatIf] [-Confir ## DESCRIPTION -This command can be used in 2 ways. First, you can export custom type information to a json or xml file to make it easier to recreate them in another PowerShell session. Or you can export the type extensions to a properly formatted ps1xml file which you can use with Update-TypeData. The command will create the appropriate file based on the extension in the specified path. +This command can be used in 2 ways. First, you can export custom type information to a JSON or XML file to make it easier to recreate them in another PowerShell session. Or you can export the type extensions to a properly formatted ps1xml file which you can use with Update-TypeData. The command will create the appropriate file based on the extension in the specified path. ## EXAMPLES @@ -44,7 +44,7 @@ This command can be used in 2 ways. First, you can export custom type informatio PS C:\> Export-PSTypeExtension -TypeName system.string -Path c:\work\mystringtypes.json -MemberName Size,IsIPAddress ``` -Export selected type extensions for System.String to a json file. +Export selected type extensions for System.String to a JSON file. ### EXAMPLE 2 @@ -52,7 +52,7 @@ Export selected type extensions for System.String to a json file. PS C:\> Get-PSTypeExtension system.string | Export-PSTypeExtension -path c:\work\stringtypes.xml ``` -Get all type extensions for System.String and export to an xml file. +Get all type extensions for System.String and export to an XML file. ### EXAMPLE 3 @@ -151,7 +151,7 @@ This is typically the output of Get-PSSTypeExtension. ```yaml Type: Object Parameter Sets: object -Aliases: +Aliases: Required: False Position: Named @@ -174,7 +174,8 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES -Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/ +Learn more about PowerShell: +http://jdhitsolutions.com/blog/essential-powershell-resources/ ## RELATED LINKS diff --git a/docs/Get-PSType.md b/docs/Get-PSType.md index aed00a0..9facd86 100644 --- a/docs/Get-PSType.md +++ b/docs/Get-PSType.md @@ -30,7 +30,7 @@ PS C:\>123 | Get-PSType System.Int32 ``` -Pipe an object and get it's type. +Pipe an object and get its type. ### EXAMPLE 2 @@ -55,17 +55,17 @@ PS C:\> get-vm | sort VMAge -descending | select Name,Creationtime,VMAge Name CreationTime VMAge ---- ------------ ----- -UbuntuDesk 8/7/2017 1:53:47 PM 94.20:26:57.4237225 -CentOS 8/7/2017 3:43:50 PM 94.18:36:54.3394091 -DOM1 9/28/2017 9:31:34 AM 43.00:49:10.4396803 -SRV1 9/28/2017 9:31:50 AM 43.00:48:54.3284313 -SRV2 9/28/2017 9:32:01 AM 43.00:48:43.2135084 -SRV3 9/28/2017 9:32:11 AM 43.00:48:33.4681374 -WIN10 9/28/2017 9:32:21 AM 43.00:48:23.1610914 -Win10Ent 10/16/2017 9:30:46 AM 25.00:49:58.1206825 +UbuntuDesk 8/7/2020 1:53:47 PM 94.20:26:57.4237225 +CentOS 8/7/2020 3:43:50 PM 94.18:36:54.3394091 +DOM1 9/28/2020 9:31:34 AM 43.00:49:10.4396803 +SRV1 9/28/2020 9:31:50 AM 43.00:48:54.3284313 +SRV2 9/28/2020 9:32:01 AM 43.00:48:43.2135084 +SRV3 9/28/2020 9:32:11 AM 43.00:48:33.4681374 +WIN10 9/28/2020 9:32:21 AM 43.00:48:23.1610914 +Win10Ent 10/16/2020 9:30:46 AM 25.00:49:58.1206825 ``` -Get the type for a Hyper-V Virtual Machine and add a new script property which can then be used in a PowerShell expression. +Get the type for a Hyper-V Virtual Machine and add a new ScriptProperty, which can then be used in a PowerShell expression. ## PARAMETERS @@ -99,7 +99,8 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES -Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/ +Learn more about PowerShell: +http://jdhitsolutions.com/blog/essential-powershell-resources/ ## RELATED LINKS diff --git a/docs/Get-PSTypeExtension.md b/docs/Get-PSTypeExtension.md index b60da75..ef3489f 100644 --- a/docs/Get-PSTypeExtension.md +++ b/docs/Get-PSTypeExtension.md @@ -19,7 +19,7 @@ Get-PSTypeExtension [-TypeName] [-Members ] [ [-WhatIf] [-Confirm] [ ## DESCRIPTION -Use this command to import extended type definitions from a json or xml file that was created with Export-PSTypeExtension. +Use this command to import extended type definitions from a JSON or XML file that was created with Export-PSTypeExtension. ## EXAMPLES @@ -29,7 +29,7 @@ Use this command to import extended type definitions from a json or xml file tha PS C:\> Import-PSTypeExtension -Path C:\work\stringtypes.json ``` -Import definitions from a json file. +Import definitions from a JSON file. ### EXAMPLE 2 @@ -37,7 +37,7 @@ Import definitions from a json file. PS C:\> dir c:\scripts\mytypes | Import-PSTypeExtension ``` -Import definitions from files in C:\Scripts\MyTypes. Presumably, these are xml or json files created with Export-PSTypeExtension. +Import definitions from files in C:\Scripts\MyTypes. Presumably, these are XML or JSON files created with Export-PSTypeExtension. ## PARAMETERS @@ -103,7 +103,8 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## NOTES -Learn more about PowerShell: http://jdhitsolutions.com/blog/essential-powershell-resources/ +Learn more about PowerShell: + http://jdhitsolutions.com/blog/essential-powershell-resources/ ## RELATED LINKS diff --git a/docs/New-PSPropertySet.md b/docs/New-PSPropertySet.md new file mode 100644 index 0000000..66aa0e7 --- /dev/null +++ b/docs/New-PSPropertySet.md @@ -0,0 +1,209 @@ +--- +external help file: PSTypeExtensionTools-help.xml +Module Name: PSTypeExtensionTools +online version: +schema: 2.0.0 +--- + +# New-PSPropertySet + +## SYNOPSIS + +Create a new property set ps1xml file. + +## SYNTAX + +### new (Default) + +```yaml +New-PSPropertySet [-Typename] -Name -Properties -FilePath [-NoClobber] [-WhatIf] [-Confirm] [] +``` + +### append + +```yaml +New-PSPropertySet [-Typename] -Name -Properties -FilePath [-Append] [-WhatIf] [-Confirm] [] +``` + +## DESCRIPTION + +PowerShell has a concept of property sets. A property set allows you to reference a collection of properties with a single name. Property set definitions have to be stored in a .ps1xml file. This command will create the file for you. You also have the option to append to an existing file. + +Use Update-TypeData to append the file. If you import your file more than once in the same session, you might see a warning about existing property names. Existing entries won't be overwritten. + +Property set updates are not persistant. You will need to import your ps1xml, ideally in your PowerShell profile script. + +## EXAMPLES + +### Example 1 + +```powershell +PS C:\> New-PSPropertySet -typename System.IO.FileInfo -name info -properties Fullname,IsReadOnly,CreationTime,LastWriteTime -filepath c:\work\myfileinfo.type.ps1xml +PS C:\> Update-TypeData C:\work\myfileinfo.type.ps1xml +PS C:\> dir c:\work -file | select info + +FullName IsReadOnly CreationTime LastWriteTime +-------- ---------- ------------ ------------- +C:\work\a.csv False 12/1/2020 11:30:55 AM 12/1/2020 11:30:55 AM +C:\work\a.dat False 2/12/2021 5:36:55 PM 2/12/2021 5:36:55 PM +C:\work\a.md False 11/6/2020 9:50:19 AM 12/1/2020 12:40:03 PM +C:\work\a.txt False 12/31/2020 9:10:15 AM 12/31/2020 9:10:15 AM +C:\work\a.xml False 12/31/2020 12:15:44 PM 12/31/2020 12:15:44 PM +C:\work\aa.ps1 False 12/31/2020 9:13:16 AM 12/31/2020 9:13:16 AM +C:\work\aa.txt False 12/31/2020 9:11:18 AM 12/31/2020 9:11:18 AM +... +``` + +The first command creates the property set definition file. The second command loads it into PowerShell. The last command uses the custom property set. + +## PARAMETERS + +### -Append + +Append to an existing file. + +```yaml +Type: SwitchParameter +Parameter Sets: append +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Confirm + +Prompts you for confirmation before running the cmdlet. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: cf + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -FilePath + +Enter the name of the .ps1xml file to create. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Name + +Enter the new property set name. It should be alphanumeric. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -NoClobber + +Don't overwrite an existing file. + +```yaml +Type: SwitchParameter +Parameter Sets: new +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Properties + +Enter the existing property names or aliases to belong to this property set. + +```yaml +Type: String[] +Parameter Sets: (All) +Aliases: + +Required: True +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -Typename + +Enter the object typename. This is the value you would see in Get-Member. + +```yaml +Type: String +Parameter Sets: (All) +Aliases: + +Required: True +Position: 0 +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -WhatIf + +Shows what would happen if the cmdlet runs. +The cmdlet is not run. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: wi + +Required: False +Position: Named +Default value: None +Accept pipeline input: False +Accept wildcard characters: False +``` + +### CommonParameters + +This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). + +## INPUTS + +### None + +## OUTPUTS + +### System.IO.FileInfo + +## NOTES + +Learn more about PowerShell: +http://jdhitsolutions.com/blog/essential-powershell-resources/ + +## RELATED LINKS + +[Update-TypeData]() diff --git a/docs/PSTypeExtensionTools.md b/docs/PSTypeExtensionTools.md index 7767204..00a3129 100644 --- a/docs/PSTypeExtensionTools.md +++ b/docs/PSTypeExtensionTools.md @@ -1,7 +1,7 @@ --- Module Name: PSTypeExtensionTools Module Guid: f509035e-cb36-4d2f-b2c8-f4a60fb06d56 -Download Help Link: +Download Help Link: Help Version: 1.0.0.0 Locale: en-US --- @@ -20,7 +20,7 @@ Add a new type extension such as an Alias or ScriptProperty. ### [Export-PSTypeExtension](Export-PSTypeExtension.md) -Export type extensions to a json or XML file. +Export type extensions to a JSON or XML file. ### [Get-PSType](Get-PSType.md) @@ -32,4 +32,8 @@ Get type extensions for a given type. ### [Import-PSTypeExtension](Import-PSTypeExtension.md) -Import type extension definitions from a json or XML file. +Import type extension definitions from a JSON or XML file. + +### [New-PSPropertySet](New-PSPropertySet.md) + +Define a custom property set that is saved in a .ps1xml file. diff --git a/docs/about_PSTypeExtensionTools.md b/docs/about_PSTypeExtensionTools.md index c84bf25..495e073 100644 --- a/docs/about_PSTypeExtensionTools.md +++ b/docs/about_PSTypeExtensionTools.md @@ -7,7 +7,7 @@ This PowerShell module contains commands that make it easier to work with type extensions. Many of these commands are wrappers for built-in tools like `Get-TypeData` or `Update-TypeData`. The commands in this module simplify the -process of finding, creating, exporting and importing type extensions. +process of finding, creating, exporting, and importing type extensions. ## LONG DESCRIPTION @@ -21,7 +21,7 @@ Add-PSTypeExtension -MemberType ScriptProperty -MemberName SquareRoot ` -Value { [math]::Sqrt($this)} ``` -Use $this to reference the object instead of $_. Now you can get the new property. +Use `$this` to reference the object instead of $_. Now you can get the new property. ```powershell PS C:\> $x = 123 @@ -56,7 +56,7 @@ PS C:\> $x.GetPercent(110,4) 34.5455 ``` -To see what has been defined you can use `Get-PSTypeExtension`. You can choose to see all extensions or selected ones by member name. +To see what has been defined, you can use `Get-PSTypeExtension`. You can choose to see all extensions or selected ones by member name. ```powershell PS C:\> Get-PSTypeExtension system.int32 @@ -72,13 +72,13 @@ Value ScriptProperty $this GetPercent ScriptMethod Param([int32]$Total,[int32]$Round=2) [math]::Round(($this/$total)*100,$round) ``` -If you always want these extensions you would have to put the commands into your PowerShell profile script. Or you can export the extensions to a json or xml file. You can either export all members or selected ones which is helpful if you are extending a type that already has type extensions from PowerShell. +If you always want these extensions, you would have to put the commands into your PowerShell profile script. Or you can export the extensions to a JSON or XML file. You can either export all members or selected ones which is helpful if you are extending a type that already has type extensions from PowerShell. ```powershell PS C:\> Get-PSTypeExtension system.int32 -all | Export-PSTypeExtension -TypeName system.int32 -Path c:\work\int32-types.json ``` -In your PowerShell profile script you can then re-import the type extension definitions. +In your PowerShell profile script, you can then re-import the type extension definitions. ```powershell Import-PSTypeExtension -Path C:\work\int32-types.json @@ -90,8 +90,7 @@ You can also import a directory of type extensions with a single command. dir c:\scripts\mytypes | Import-PSTypeExtension ``` -A number of sample files with type extensions can be found in this modules Samples folder or in the GitHub repository at https://github.com/jdhitsolutions/PSTypeExtensionTools/tree/master/samples. When you have imported the module you can -access the samples folder using the $PSTypeSamples variable. +A number of sample files with type extensions can be found in this modules Samples folder or in the GitHub repository at https://github.com/jdhitsolutions/PSTypeExtensionTools/tree/master/samples. When you have imported the module you can access the samples folder using the $PSTypeSamples variable. ```powershell Import-PSTypeExtension $PSTypeSamples\measure-extensions.json @@ -99,28 +98,29 @@ Import-PSTypeExtension $PSTypeSamples\measure-extensions.json ### Creating ps1xml Files -The Export-PSTypeExtension command will also export extensions to a properly formatted .ps1xml file. This can be useful when building type extension files for a module where you want to use the traditional ps1xml form. You can also import these types of files with Update-TypeData with the -AppendPath or -PrependPath parameters. +The `Export-PSTypeExtension` command will also export extensions to a properly formatted .ps1xml file. This can be useful when building type extension files for a module where you want to use the traditional ps1xml form. You can also import these types of files with Update-TypeData with the -AppendPath or -PrependPath parameters. ## NOTE -PowerShell type extensions only last for the duration of your PowerShell session. If you make a mistake that is causing problems simply restart PowerShell or use the Remove-TypeData cmdlet. +PowerShell type extensions only last for the duration of your PowerShell session. If you make a mistake that is causing problems, restart PowerShell or use the `Remove-TypeData` cmdlet. ## TROUBLESHOOTING NOTE -Don't try to append or manually update an export file. If you have changes simply re-run the export command and generate the file anew. +Don't try to append or manually update an export file. If you have changes, re-run the export command and generate the file anew. -Remember to use $this to reference the object and NOT $_. +Remember to use `$this` to reference the object and NOT `$_`. Remember to enclose scriptblocks in {}. ## SEE ALSO -+ [Add-PSTypeExtension](./Add-PSTypeExtension.md) -+ [Export-PSTypeExtension](./Export-PSTypeExtension.md) -+ [Get-PSType](./Get-PSType.md) -+ [Get-PSTypeExtension](./Get-PSTypeExtension.md) -+ [Import-PSTypeExtension](./Import-PSTypeExtension.md) -+ [Update-TypeData](./Update-TypeData.md) ++ [Add-PSTypeExtension](Add-PSTypeExtension.md) ++ [Export-PSTypeExtension](Export-PSTypeExtension.md) ++ [Get-PSType](Get-PSType.md) ++ [Get-PSTypeExtension](Get-PSTypeExtension.md) ++ [Import-PSTypeExtension](Import-PSTypeExtension.md) ++ [Update-TypeData](Update-TypeData.md) ++ [New-PSPropertySet](New-PSPropertySet.md) This project was first described at http://jdhitsolutions.com/blog/powershell/5777/a-powershell-module-for-your-type-extensions @@ -129,3 +129,5 @@ This project was first described at http://jdhitsolutions.com/blog/powershell/57 - typedata - typeextension + +- propertyset diff --git a/en-us/about_PSTypeExtensionTools.help.txt b/en-us/about_PSTypeExtensionTools.help.txt deleted file mode 100644 index 794db57..0000000 --- a/en-us/about_PSTypeExtensionTools.help.txt +++ /dev/null @@ -1,124 +0,0 @@ -TOPIC - about_PSTypeExtensionTools - -SHORT DESCRIPTION - This PowerShell module contains commands that make it easier to work with - type extensions. Many of these commands are wrappers for built-in tools like - Get-TypeData or Update-TypeData. The commands in this module simplify the - process of finding, creating, exporting and importing type extensions. - -LONG DESCRIPTION - Let's say you want to update a number object, but you have no idea what the - type name is. Once you have read help for the commands in this module you - could run a PowerShell command like this: - - PS C:\> 123 | Get-PSType | - Add-PSTypeExtension -MemberType ScriptProperty -MemberName SquareRoot ` - -Value { [math]::Sqrt($this)} - - Use $this to reference the object instead of $_. Now you can get the new - property. - - PS C:\> $x = 123 - PS C:\> $x.SquareRoot - 11.0905365064094 - - Once you know the type name you can add other type extensions. - - Add-PSTypeExtension -TypeName system.int32 -MemberType ScriptProperty -MemberName Squared -value { $this*$this} - Add-PSTypeExtension -TypeName system.int32 -MemberType ScriptProperty -MemberName Cubed -value { [math]::Pow($this,3)} - Add-PSTypeExtension -TypeName system.int32 -MemberType ScriptProperty -MemberName Value -value { $this} - Add-PSTypeExtension -TypeName system.int32 -MemberType ScriptMethod -MemberName GetPercent -value {Param([int32]$Total,[int32]$Round=2) [math]::Round(($this/$total)*100,$round)} - - Here's how it might look: - - PS C:\> $x = 38 - PS C:\> $x | select * - - SquareRoot Squared Cubed Value - ---------- ------- ----- ----- - 6.16441400296898 1444 54872 38 - - PS C:\> $x.GetPercent(50) - 76 - PS C:\> $x.GetPercent(100) - 38 - PS C:\> $x.GetPercent(110,4) - 34.5455 - - To see what has been defined you can use Get-PSTypeExtension. You can choose - to see all extensions or selected ones by member name. - - PS C:\> Get-PSTypeExtension system.int32 - - TypeName: System.Int32 - - Name Type Value - ---- ---- ----- - SquareRoot ScriptProperty [math]::Sqrt($this) - Squared ScriptProperty $this*$this - Cubed ScriptProperty [math]::Pow($this,3) - Value ScriptProperty $this - GetPercent ScriptMethod Param([int32]$Total,[int32]$Round=2) [math]::Round(($this/$total)*100,$round) - - If you always want these extensions you would have to put the commands into - your PowerShell profile script. Or you can export the extensions to a json - or xml file. You can either export all members or selected ones which is - helpful if you are extending a type that already has type extensions from - PowerShell. - - PS C:\> Get-PSTypeExtension system.int32 -all | - Export-PSTypeExtension -TypeName system.int32 -Path c:\work\int32-types.json - - In your PowerShell profile script you can then re-import the type extension - definitions. - - Import-PSTypeExtension -Path C:\work\int32-types.json - - You can also import a directory of type extensions with a single command. - - dir c:\scripts\mytypes | Import-PSTypeExtension - - A number of sample files with type extensions can be found in this modules - Samples folder or in the GitHub repository at - https://github.com/jdhitsolutions/PSTypeExtensionTools/tree/master/samples. - - When you have imported the module you can access the samples folder using the - $PSTypeSamples variable. - - Import-PSTypeExtension $PSTypeSamples\measure-extensions.json - - Creating ps1xml Files - --------------------- - The Export-PSTypeExtension command will also export extensions to a properly - formatted .ps1xml file. This can be useful when building type extension - files for a module where you want to use the traditional ps1xml form. You - can also import these types of files with Update-TypeData with the - -AppendPath or -PrependPath parameters. - -NOTE - PowerShell type extensions only last for the duration of your PowerShell - session. If you make a mistake that is causing problems simply restart - PowerShell or use the Remove-TypeData cmdlet. - -TROUBLESHOOTING NOTE - Don't try to append or manually update an export file. If you have changes - simply re-run the export command and generate the file anew. Remember to use - $this to reference the object and NOT $_. - - Remember to enclose scriptblocks in {}. - -SEE ALSO - Add-PSTypeExtension - Export-PSTypeExtension - Get-PSType - Get-PSTypeExtension - Import-PSTypeExtension - Update-TypeData - - This project was first described at - http://jdhitsolutions.com/blog/powershell/5777/a-powershell-module-for-your-type-extensions - -KEYWORDS - typedata - typeextension diff --git a/PSTypeExtension.format.ps1xml b/formats/PSTypeExtension.format.ps1xml similarity index 100% rename from PSTypeExtension.format.ps1xml rename to formats/PSTypeExtension.format.ps1xml diff --git a/functions/public.ps1 b/functions/public.ps1 new file mode 100644 index 0000000..25785dd --- /dev/null +++ b/functions/public.ps1 @@ -0,0 +1,554 @@ + +Function Get-PSTypeExtension { + [cmdletbinding()] + Param( + [Parameter( + Position = 0, + Mandatory, + HelpMessage = "Enter the name of type like System.IO.FileInfo", + ValueFromPipelineByPropertyName, + ValueFromPipeline + )] + [ValidateNotNullorEmpty()] + [ValidateScript( { + #check if typename can be found with Get-TypeData + if ((Get-TypeData).typename -contains "$_") { + $True + } + elseif ($_ -as [type]) { + #test if string resolves as a typename + $True + } + else { + Throw "$_ does not appear to be a valid type." + } + })] + [string]$TypeName, + [Parameter( + HelpMessage = "Enter a comma separated list of member names", + ParameterSetName = "members" + )] + [string[]]$Members + ) + + Begin { + Write-Verbose "Starting: $($MyInvocation.Mycommand)" + $typedata = @() + } #begin + Process { + Write-Verbose "Analyzing $typename" + $typedata += Get-TypeData -TypeName $typename + + } #process + End { + $typedata = $typedata | Select-Object -Unique + if ($typedata) { + + if (-Not $Members) { + Write-Verbose "Getting all member names" + $Members = $typedata.members.keys + + } + foreach ($name in $Members) { + Try { + Write-Verbose "Analyzing member $name" + $member = $typedata.members[$name] + $datatype = $member.gettype().name + + Write-Verbose "Processing type $datatype" + Switch ($datatype) { + "AliasPropertyData" { + $def = [pscustomobject]@{ + MemberType = "AliasProperty" + MemberName = $member.name + Value = $member.ReferencedMemberName + } + } #alias + "ScriptpropertyData" { + if ($member.GetScriptBlock) { + $code = $member.GetScriptBlock.ToString() + } + else { + $code = $member.SetScriptBlock.ToString() + } + $def = [pscustomobject]@{ + MemberType = "ScriptProperty" + MemberName = $member.name + Value = $code + } + } #scriptproperty + "ScriptMethodData" { + $def = [pscustomobject]@{ + MemberType = "ScriptMethod" + MemberName = $member.name + Value = $member.script.ToString().trim() + } + } #scriptmethod + "NotePropertyData" { + $def = [pscustomobject]@{ + MemberType = "Noteproperty" + MemberName = $member.name + Value = $member.Value + } + } #noteproperty + "CodePropertyData" { + if ($member.GetCodeReference) { + $code = $member.GetCodeReference.ToString() + } + else { + $code = $member.SetCodeReference.ToString() + } + $def = [pscustomobject]@{ + MemberType = "CodeProperty" + MemberName = $member.name + Value = $code + } + } #codeproperty + Default { + Write-Warning "Cannot process $datatype type for $($typedata.typename)." + $def = [pscustomobject]@{ + MemberType = $datatype + MemberName = $member.name + Value = $member.Value + } + } + } + + $def | Add-Member -MemberType NoteProperty -Name TypeName -Value $typedata.typename + #insert a typename + $def.psobject.typenames.insert(0, 'PSTypeExtension') + #write the definition to the pipeline + Write-Output $def + + } + Catch { + Write-Warning "Could not find an extension member called $name" + Write-Debug $_.exception.message + } + + } #foreach + + } + else { + Write-Warning "Failed to find any type extensions for [$Typename]." + } + Write-Verbose "Ending: $($MyInvocation.Mycommand)" + } + +} #end Get-PSTypeExtension + +Function Get-PSType { + [cmdletbinding()] + Param( + [Parameter( + Position = 0, + Mandatory, + ValueFromPipeline + )] + [object]$Inputobject + ) + + Begin { + Write-Verbose "Starting: $($MyInvocation.Mycommand)" + $data = @() + } + Process { + #get the type of each pipelined object + $data += ($Inputobject | Get-Member | Select-Object -First 1).typename + } + End { + #write unique values to the pipeline + $data | Get-Unique + Write-Verbose "Ending: $($MyInvocation.Mycommand)" + } +} #end Get-PSType + +Function Export-PSTypeExtension { + [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "Object")] + Param( + [Parameter( + Position = 0, + Mandatory, + HelpMessage = "The type name to export like System.IO.FileInfo", + ParameterSetName = "Name" + )] + [ValidateNotNullOrEmpty()] + [string]$TypeName, + [Parameter( + Mandatory, + HelpMessage = "The type extension name", + ParameterSetName = "Name" + )] + [ValidateNotNullOrEmpty()] + [string[]]$MemberName, + [Parameter( + Mandatory, + HelpMessage = "The name of the export file. The extension must be .json,.xml or .ps1xml" + )] + [ValidatePattern("\.(xml|json|ps1xml)$")] + [string]$Path, + [Parameter(ParameterSetName = "object", ValueFromPipeline)] + [object]$InputObject + ) + Begin { + Write-Verbose "Starting: $($MyInvocation.Mycommand)" + $data = @() + } + Process { + if ($Inputobject) { + Write-Verbose "Processing input type: $($InputObject.TypeName)" + $data += $Inputobject + } + else { + Write-Verbose "Processing type: $TypeName" + foreach ($member in $membername) { + $data += Get-PSTypeExtension -TypeName $Typename -Members $Member + } + } + } + End { + if ($Path -match "\.ps1xml$") { + Write-Verbose "Saving as PS1XML" + #create a placeholder file so that I can later convert the path + New-Item -Path $path -Force | Out-Null + [xml]$Doc = New-Object System.Xml.XmlDocument + + #create declaration + $dec = $Doc.CreateXmlDeclaration("1.0", "UTF-8", $null) + #append to document + $doc.AppendChild($dec) | Out-Null + + #create a comment and append it in one line + $text = @" + +Custom type extensions generated by $($env:username) +$(Get-Date) + +"@ + $doc.AppendChild($doc.CreateComment($text)) | Out-Null + + #create root Node + $root = $doc.CreateNode("element", "Types", $null) + $main = $doc.CreateNode("element", "Type", $null) + $name = $doc.CreateElement("Name") + $name.innerText = $data[0].TypeName + $main.AppendChild($name) | Out-Null + $member = $doc.CreateNode("element", "Members", $null) + foreach ($extension in $data) { + Write-Verbose "Exporting $($extension.membername)" + $membertype = $doc.createNode("element", $extension.memberType, $null) + $membernameEL = $doc.CreateElement("Name") + + $membernameEL.innertext = $extension.memberName + $membertype.AppendChild($membernameEL) | Out-Null + + Switch ($extension.Membertype) { + "ScriptMethod" { + $memberdef = $doc.createelement("Script") + } + "ScriptProperty" { + $memberdef = $doc.createelement("GetScriptBlock") + } + "AliasProperty" { + $memberdef = $doc.createelement("ReferencedMemberName") + } + "NoteProperty" { + $memberdef = $doc.createelement("Value") + } + Default { + Throw "Can't process a type of $($extension.MemberType)" + } + } + $memberdef.InnerText = $extension.value + $membertype.AppendChild($memberdef) | Out-Null + $member.AppendChild($membertype) | Out-Null + + } #foreach + $main.AppendChild($member) | Out-Null + $root.AppendChild($main) | Out-Null + $doc.AppendChild($root) | Out-Null + $out = Convert-Path $path + if ($PSCmdlet.ShouldProcess($out)) { + $doc.save($out) + } #end Whatif + } + elseif ($path -match "\.xml$") { + Write-Verbose "Saving as XML" + $data | Export-Clixml -Path $path + } + else { + Write-Verbose "Saving as JSON" + $data | ConvertTo-Json | Set-Content -Path $Path + } + + Write-Verbose "Exporting data to $path" + Write-Verbose "Ending: $($MyInvocation.Mycommand)" + } + +} #end Export-PSTypeExtension + +Function Import-PSTypeExtension { + [CmdletBinding(SupportsShouldProcess)] + Param( + [Parameter( + Mandatory, + ValueFromPipeline, + ValueFromPipelineByPropertyName, + HelpMessage = "The name of the imported file. The extension must be .xml or .json")] + [ValidatePattern("\.(xml|json)$")] + [ValidateScript( { Test-Path $(Convert-Path $_) })] + [alias("fullname")] + [string]$Path + ) + + Begin { + Write-Verbose "Starting: $($myInvocation.mycommand)" + } + Process { + Write-Verbose "Importing file $(Convert-Path $Path)" + if ($path -match "\.xml$") { + #xml format seems to add an extra entry + $import = Import-Clixml -Path $path | Where-Object MemberType + } + else { + $import = Get-Content -Path $path | ConvertFrom-Json + } + + foreach ($item in $import) { + Write-Verbose "Processing $($item.MemberType) : $($item.MemberName)" + if ($item.MemberType -match "^Code") { + Write-Warning "Skipping Code related member" + } + if ($item.MemberType -match "^Script") { + Write-Verbose "Creating scriptblock from value" + $value = [scriptblock]::create($item.value) + } + else { + $value = $item.Value + } + #add a custom -WhatIf message + if ($PSCmdlet.ShouldProcess($Item.typename, "Adding $($item.membertype) $($item.MemberName)")) { + #implement the change + Update-TypeData -TypeName $item.Typename -MemberType $item.MemberType -MemberName $item.MemberName -Value $value -Force + } + } #foreach + } + End { + Write-Verbose "Ending: $($myInvocation.mycommand)" + } + +} #end Import-PSTypeExtension + +Function Add-PSTypeExtension { + [cmdletbinding(SupportsShouldProcess)] + [Alias('Set-PSTypeExtension')] + + Param( + [Parameter( + Position = 0, + Mandatory, + ValueFromPipeline, + HelpMessage = "Enter the name of a type like system.io.fileinfo")] + [string]$TypeName, + [Parameter( + Mandatory, + HelpMessage = "The member type" + )] + [ValidateSet("AliasProperty", "Noteproperty", "ScriptProperty", "ScriptMethod")] + [alias("Type")] + [string]$MemberType, + [Parameter( + Mandatory, + HelpMessage = "The name of your type extension" + )] + [ValidateNotNullOrEmpty()] + [alias("Name")] + [string]$MemberName, + [Parameter( + Mandatory, + HelpMessage = "The value for your type extension. Remember to enclose scriptblocks in {} and use `$this" + )] + [ValidateNotNullOrEmpty()] + [Object]$Value, + [Parameter(HelpMessage = "Create the extension in the deserialized version of the specified type including the original type.")] + [switch]$IncludeDeserialized + + ) + Begin { + Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" + + } #begin + + Process { + #force overwrite of existing extensions + $PSBoundParameters.Add("Force", $True) + if ($PSBoundParameters.ContainsKey("IncludeDeserialized")) { + [void]$PSBoundParameters.Remove("IncludeDeserialized") + $PSBoundParameters.Typename = "deserialized.$Typename" + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $MemberType $Membername to $($psboundparameters.TypeName)" + Update-TypeData @PSBoundParameters + $PSBoundParameters.Typename = $Typename + } + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding $MemberType $Membername to $($psboundparameters.TypeName)" + Update-TypeData @PSBoundParameters + } #process + + End { + Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" + + } #end + +} #close Add-MyTypeExtension + +Function New-PSPropertySet { + [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = "new")] + Param( + [Parameter(Position = 0, Mandatory, HelpMessage = "Enter the object typename.")] + [ValidateNotNullOrEmpty()] + [string]$Typename, + [Parameter(Mandatory, HelpMessage = "Enter the new property set name. It should be alphanumeric.")] + [ValidatePattern("^\w+$")] + [string]$Name, + [Parameter(Mandatory, HelpMessage = "Enter the existing property names or aliases to belong to this property set.")] + [ValidateNotNullOrEmpty()] + [string[]]$Properties, + [Parameter(Mandatory, HelpMessage = "Enter the name of the .ps1xml file to create.")] + [ValidatePattern("\.ps1xml$")] + [string]$FilePath, + [Parameter(HelpMessage = "Append to an existing file.", ParameterSetName = "append")] + [switch]$Append, + [Parameter(HelpMessage = "Don't overwrite an existing file.", ParameterSetName = "new")] + [switch]$NoClobber + ) + Begin { + Write-Verbose "[$((Get-Date).TimeofDay) BEGIN ] Starting $($myinvocation.mycommand)" + $settings = [System.Xml.XmlWriterSettings]::new() + $settings.Indent = $True + + $newComment = @" + +This file was created with New-PSPropertySet from the +PSTypeExtensionTools module which you can install from +the PowerShell Gallery. + +Use Update-TypeData to append this file in your PowerShell session. + +Created $(Get-Date) + +"@ + } #begin + + Process { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating a property set called $Name for $Typename" + #convert file path to a true file system path. + $cPath = Join-Path -Path (Convert-Path (Split-Path $filepath)) -ChildPath (Split-Path $FilePath -Leaf) + if ($Append -AND (-Not (Test-Path $FilePath))) { + Write-Warning "Failed to find $Filepath for appending." + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Ending $($myinvocation.mycommand)" + #bail out + return + } + elseif ($Append) { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Appending to $filepath" + [xml]$doc = Get-Content $cPath + $members = $doc.types.SelectNodes("Type[Name='$typeName']").Members + + if ($members) { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Appending to existing typename entry" + } + else { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating a new typename entry" + $newType = $doc.CreateNode("element", "Type", $null) + $tName = $doc.CreateElement("Name") + $tName.InnerText = $typename + [void]($newType.AppendChild($tname)) + $members = $doc.CreateNode("element", "Members", $null) + $IsNewType = $True + } + + $propSet = $doc.CreateNode("element", "PropertySet", $null) + $eName = $doc.CreateElement("Name") + $eName.InnerText = $Name + [void]($propset.AppendChild($eName)) + $ref = $doc.CreateNode("element", "ReferencedProperties", $null) + foreach ($item in $properties) { + $prop = $doc.CreateElement("Name") + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding property $item" + $prop.InnerText = $item + [void]($ref.AppendChild($prop)) + } + [void]($propset.AppendChild($ref)) + [void]($members.AppendChild($propset)) + + if ($IsnewType) { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Appending new type" + [void]($newType.AppendChild($members)) + [void]($doc.types.AppendChild($newtype)) + } + + } #else append + else { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Creating a new XML document" + <# + use a random temp name to create the xml file. At the end of the process copy the temp file + to the specified file path. This makes it possible to use -WhatIf + #> + $tmpFile = [System.IO.Path]::GetTempFileName() + $doc = [System.Xml.XmlWriter]::Create($tmpFile, $settings) + $doc.WriteStartDocument() + $doc.WriteWhitespace("`n") + $doc.WriteComment($newComment) + $doc.WriteWhitespace("`n") + $doc.WriteStartElement("Types") + + $doc.WriteStartElement("Type") + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Defining type as $Typename" + $doc.WriteElementString("Name", $TypeName) + + $doc.WriteStartElement("Members") + $doc.WriteStartElement("PropertySet") + #the property set name + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Defining property set name $Name" + $doc.WriteElementString("Name", $Name) + + $doc.WriteStartElement("ReferencedProperties") + foreach ($item in $properties) { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Adding property $item" + $doc.WriteElementString("Name", $item) + } + + #end type + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Closing and saving file." + $doc.WriteEndElement() + $doc.WriteEndDocument() + $doc.Close() + $doc.Dispose() + } + + if ($PSCmdlet.ShouldProcess($cpath)) { + if ((-Not $Append) -AND (Test-Path $tmpFile)) { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Copying temp file to $cpath" + + if ($NoClobber -AND (Test-Path $cpath)) { + Write-Warning "The file $cpath exists and NoClobber was specified." + } + else { + Copy-Item -Path $tmpFile -Destination $cpath + } + + #always clean up the temp file + Remove-Item -Path $tmpFile -WhatIf:$false -ErrorAction SilentlyContinue + } + else { + Write-Verbose "[$((Get-Date).TimeofDay) PROCESS] Saving to $cpath" + $doc.Save($cpath) + } + } + + } #process + + End { + Write-Verbose "[$((Get-Date).TimeofDay) END ] Ending $($myinvocation.mycommand)" + } #end + +} #close New-PSPropertySet \ No newline at end of file diff --git a/samples/README.md b/samples/README.md index d93f72d..13cf6bf 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,6 +1,6 @@ # PSTypeExtension Samples -These files are intended to be used with [Import-PSTypeExtension](../docs/Import-PSTypeExtension.md). Save the file locally and run the import command. You must specify the full file name with extension so the function knows how to import the data. Importing *will* overwrite any existing type members with the same name. The new type members will only last for the duration of your PowerShell session. If you always want a set of type extensions, put the `Import-PSTypeExtension` command into your PowerShell profile script. +These files are intended to be used with [Import-PSTypeExtension](../docs/Import-PSTypeExtension.md). Save the file locally and run the import command. You must specify the full file name with extension, so the function knows how to import the data. Importing *will* overwrite any existing type members with the same name. The new type members will only last for the duration of your PowerShell session. If you always want a set of type extensions, put the `Import-PSTypeExtension` command into your PowerShell profile script. ```powershell dir c:\scripts\myextensions\*.json | Import-PSTypeExtension diff --git a/samples/fileage-type.ps1xml b/samples/fileage-type.ps1xml new file mode 100644 index 0000000..57aaada --- /dev/null +++ b/samples/fileage-type.ps1xml @@ -0,0 +1,17 @@ + + + + System.IO.FileInfo + + + FileAge + + Name + Size + LastWritetime + Age + + + + + \ No newline at end of file diff --git a/samples/mydatetime.types.ps1xml b/samples/mydatetime.types.ps1xml new file mode 100644 index 0000000..98e37af --- /dev/null +++ b/samples/mydatetime.types.ps1xml @@ -0,0 +1,26 @@ + + + + + System.DateTime + + + PSDate + + Date + DayOfWeek + Month + DayOfYear + + + + + \ No newline at end of file