├── Bin ├── Example1.png ├── Example21.png ├── Example22.png └── cliMenu.png ├── CliMenu.psd1 ├── CliMenu.psm1 ├── Functions ├── Add-MenuItem.ps1 ├── Get-Menu.ps1 ├── Get-MenuItem.ps1 ├── Get-MenuOption.ps1 ├── New-Menu.ps1 ├── New-MenuItem.ps1 ├── Set-MenuOption.ps1 └── Show-Menu.ps1 ├── LICENSE ├── README.md ├── Rubbish ├── Set-Menu.ps1 └── Set-MenuItem.ps1 ├── Scripts ├── Example.ps1 └── menu.ps1 ├── build.ps1 └── publish.ps1 /Bin/Example1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torgro/cliMenu/d3ffea1f330030f00359fe204c3a0b55402a98d9/Bin/Example1.png -------------------------------------------------------------------------------- /Bin/Example21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torgro/cliMenu/d3ffea1f330030f00359fe204c3a0b55402a98d9/Bin/Example21.png -------------------------------------------------------------------------------- /Bin/Example22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torgro/cliMenu/d3ffea1f330030f00359fe204c3a0b55402a98d9/Bin/Example22.png -------------------------------------------------------------------------------- /Bin/cliMenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torgro/cliMenu/d3ffea1f330030f00359fe204c3a0b55402a98d9/Bin/cliMenu.png -------------------------------------------------------------------------------- /CliMenu.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/torgro/cliMenu/d3ffea1f330030f00359fe204c3a0b55402a98d9/CliMenu.psd1 -------------------------------------------------------------------------------- /CliMenu.psm1: -------------------------------------------------------------------------------- 1 | function Add-MenuItem 2 | { 3 | <# 4 | .Synopsis 5 | Add a Menu Item to a menu. 6 | .DESCRIPTION 7 | Add a Menu Item to a menu. This cmdlet support input (Menu Items) from the pipeline. 8 | .EXAMPLE 9 | C:> $items = Get-MenuItem -MenuName main 10 | C:> $items | Add-MenuItem -Menu subMenu 11 | 12 | This will copy Menu Items from the main Menu and add them to the Menu subMenu. 13 | .EXAMPLE 14 | C:> $newMenuItem = @{ 15 | Name = "UnlockUser" 16 | DisplayName = "Unlock a user" 17 | Action = { Show-Command -Name Unlock-UserObject } 18 | DisableConfirm = $true 19 | } 20 | C:> $item = New-MenuItem @newMenuItem 21 | C:> $item | Add-MenuItem -Menu main 22 | 23 | This will create a new Menu Item and add it to the main Menu using the pipeline. 24 | .EXAMPLE 25 | C:> $newMenuItem = @{ 26 | Name = "UnlockUser" 27 | DisplayName = "Unlock a user" 28 | Action = { Show-Command -Name Unlock-UserObject } 29 | DisableConfirm = $true 30 | } 31 | C:> $item = New-MenuItem @newMenuItem 32 | C:> Add-MenuItem -Menu main -MenuItem $item 33 | 34 | This will create a new Menu Item and add it to the main Menu. 35 | .NOTES 36 | NAME: Add-MenuItem 37 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 38 | LASTEDIT: Aug 2016 39 | KEYWORDS: General scripting Controller Menu 40 | #> 41 | [cmdletbinding()] 42 | Param 43 | ( 44 | [Parameter(Mandatory)] 45 | [string] 46 | $Menu 47 | , 48 | [Parameter(Mandatory, ValueFromPipeline)] 49 | [PSCustomObject] 50 | $MenuItem 51 | ) 52 | 53 | BEGIN 54 | { 55 | $f = $MyInvocation.InvocationName 56 | Write-Verbose -Message "$f - START" 57 | } 58 | 59 | PROCESS 60 | { 61 | Write-Verbose "getting menu" 62 | $menuObject = Get-Menu -Name "$Menu" 63 | 64 | if ($menuObject) 65 | { 66 | write-verbose "found menu" 67 | foreach ($Item in $menuObject.MenuItems) 68 | { 69 | if ($Item.Name -eq $MenuItem.Name) 70 | { 71 | Write-Error -Message "$f - Duplicate MenuItem name detected in menu [$($menuObject.Name)]" 72 | break 73 | } 74 | } 75 | 76 | $menuIndex = $script:Menus.IndexOf($menuObject) 77 | write-verbose "menuindex [$menuIndex]" 78 | if ($menuIndex -ge 0) 79 | { 80 | $null = $script:Menus[$menuIndex].MenuItems.Add($MenuItem) 81 | } 82 | } 83 | else { 84 | Write-Verbose "no menuobject" 85 | } 86 | } 87 | 88 | END 89 | { 90 | Write-Verbose -Message "$f - END" 91 | } 92 | } 93 | 94 | $script:Menus = New-Object -TypeName System.Collections.ArrayList 95 | 96 | $script:MenuOptions = [pscustomobject]@{ 97 | MenuFillChar = "*" 98 | MenuFillColor = [consolecolor]::White 99 | Heading = "" 100 | HeadingColor = [consolecolor]::White 101 | SubHeading = "" 102 | SubHeadingColor = [consolecolor]::White 103 | FooterText = "" 104 | FooterTextColor = [consolecolor]::White 105 | MenuItemColor = [consolecolor]::White 106 | MenuNameColor = [consolecolor]::White 107 | MaxWith = 80 108 | } 109 | 110 | function Get-Menu 111 | { 112 | <# 113 | .Synopsis 114 | Get a list of menus 115 | .DESCRIPTION 116 | Returns a list of menus by name, id or just the main menu 117 | .EXAMPLE 118 | C:> Get-Menu 119 | 120 | Returns all menus 121 | .EXAMPLE 122 | C:> Get-Menu -MainMenu 123 | 124 | Returns the Main Menu only 125 | .EXAMPLE 126 | C:> Get-Menu -MenuID 1 127 | 128 | Returns the menu of the specified index 129 | .EXAMPLE 130 | C:> Get-Menu -Name main* 131 | 132 | Returns all the menus which has a name that starts with main 133 | .NOTES 134 | NAME: Get-Menu 135 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 136 | LASTEDIT: Aug 2016 137 | KEYWORDS: General scripting Controller Menu 138 | #> 139 | [cmdletbinding(DefaultParameterSetName='none')] 140 | [OutputType([PSCustomObject])] 141 | Param 142 | ( 143 | [Parameter(ParameterSetName="MainMenu")] 144 | [switch] 145 | $MainMenu 146 | , 147 | [Parameter(ParameterSetName='ByID')] 148 | [int] 149 | $MenuID 150 | , 151 | [Parameter(ParameterSetName="ByName")] 152 | [string] 153 | $Name 154 | ) 155 | 156 | BEGIN 157 | { 158 | $f = $MyInvocation.InvocationName 159 | Write-Verbose -Message "$f - START" 160 | } 161 | 162 | PROCESS 163 | { 164 | if ($PSBoundParameters.ContainsKey("MainMenu")) 165 | { 166 | $script:Menus.Where({$_.IsMainMenu -eq $true}) 167 | } 168 | 169 | if ($PSBoundParameters.ContainsKey("MenuID")) 170 | { 171 | $script:Menus[$MenuID] 172 | } 173 | 174 | if ($PSBoundParameters.ContainsKey("Name")) 175 | { 176 | $script:Menus.Where({$_.Name -like "$Name"}) 177 | } 178 | 179 | if($PSCmdLet.ParameterSetName -eq "none") 180 | { 181 | $script:Menus 182 | } 183 | } 184 | 185 | END 186 | { 187 | Write-Verbose -Message "$f - END" 188 | } 189 | } 190 | 191 | function Get-MenuItem 192 | { 193 | <# 194 | .Synopsis 195 | Get a list of menu-items 196 | .DESCRIPTION 197 | Returns a list of menus by Menu-name, Menu-ID or the menu object 198 | .EXAMPLE 199 | C:> Get-MenuItem 200 | 201 | Returns all menu-items for all menus 202 | .EXAMPLE 203 | C:> Get-MenuItem -MenuName MainMenu 204 | 205 | Returns the menu-items for the menu with name MainMenu 206 | .EXAMPLE 207 | C:> Get-MenuItem -MenuId 1 208 | 209 | Returns the menu-items for the menu with id 1 210 | .EXAMPLE 211 | C:> $Menu = Get-Menu -Name SubMenuSkype 212 | C:> Get-MenuItem -MenuObject $Menu 213 | 214 | Returns all the menu-items for the menu with name SubMenuSkype 215 | .EXAMPLE 216 | C:> Get-Menu -Name SubMenuSkype | Get-MenuItem 217 | 218 | Returns all the menu-items for the menu with name SubMenuSkype 219 | .NOTES 220 | NAME: Get-MenuItem 221 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 222 | LASTEDIT: Aug 2016 223 | KEYWORDS: General scripting Controller Menu 224 | #> 225 | [cmdletbinding(DefaultParameterSetName='none')] 226 | [OutputType([PSCustomObject])] 227 | Param 228 | ( 229 | [Parameter(ParameterSetName="ByName")] 230 | [string[]] 231 | $MenuName 232 | , 233 | [Parameter(ParameterSetName="ById")] 234 | [int] 235 | $MenuId 236 | , 237 | [Parameter(ValueFromPipeline, ParameterSetName="ByObject")] 238 | [PSCustomObject] 239 | $MenuObject 240 | ) 241 | 242 | BEGIN 243 | { 244 | $f = $MyInvocation.InvocationName 245 | Write-Verbose -Message "$f - START" 246 | } 247 | 248 | PROCESS 249 | { 250 | if ($PSCmdlet.ParameterSetName -eq "none") 251 | { 252 | $script:Menus.MenuItems 253 | } 254 | 255 | if ($PSBoundParameters.ContainsKey("MenuName")) 256 | { 257 | write-verbose -message "$f - Getting by MenuName" 258 | $script:Menus.Where({$_.Name -eq "$MenuName"}) | Select-Object -ExpandProperty MenuItems 259 | } 260 | 261 | if ($PSBoundParameters.Containskey("MenuId")) 262 | { 263 | $script:Menus[$MenuId].MenuItems 264 | } 265 | 266 | if ($PSCmdlet.ParameterSetName -eq "ByObject") 267 | { 268 | $MenuObject.MenuItems 269 | } 270 | } 271 | 272 | END 273 | { 274 | Write-Verbose -Message "$f - END" 275 | } 276 | } 277 | 278 | function Get-MenuOption 279 | { 280 | <# 281 | .Synopsis 282 | Get a list menu options 283 | .DESCRIPTION 284 | Returns a PSCustomObject with all menu options. This CmdLet has no parameters 285 | .EXAMPLE 286 | C:> Get-MenuOption 287 | 288 | Returns all menu-items for all menus 289 | .NOTES 290 | NAME: Get-MenuItem 291 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 292 | LASTEDIT: Aug 2016 293 | KEYWORDS: General scripting Controller Menu 294 | #> 295 | [cmdletbinding()] 296 | [OutputType([PSCustomObject])] 297 | Param 298 | () 299 | $script:MenuOptions 300 | } 301 | 302 | function New-Menu 303 | { 304 | <# 305 | .Synopsis 306 | Create a new Menu 307 | .DESCRIPTION 308 | You can create as many menus you like, however you may only have one main Menu. The Menu must 309 | have a name, hence the Name parameter is Mandatory. The first Menu you create will become 310 | the main Menu even if you do not specify the IsMainMenu switch. 311 | .PARAMETER Name 312 | Normally you would like to specify a name without space and Camel-case the name. 313 | .EXAMPLE 314 | C:> New-Menu -Name "MainMenu" 315 | 316 | This will create a new Menu with name MainMenu. If this is the first Menu, it will be 317 | created as a main Menu 318 | .EXAMPLE 319 | C:> New-Menu -Name "MainMenu" -IsMainMenu 320 | 321 | This will create a new Menu with name MainMenu and set is as a main Menu 322 | .EXAMPLE 323 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" 324 | 325 | This will create a new Menu with name sub1 and DisplayName Sub-Menu for Skype 326 | .NOTES 327 | NAME: New-Menu 328 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 329 | LASTEDIT: Aug 2016 330 | KEYWORDS: General scripting Controller Menu 331 | #> 332 | [cmdletbinding()] 333 | [OutputType([PSCustomObject])] 334 | Param 335 | ( 336 | [Parameter(Mandatory)] 337 | [string] 338 | $Name 339 | , 340 | [string] 341 | $DisplayName 342 | , 343 | [switch] 344 | $IsMainMenu 345 | ) 346 | 347 | BEGIN 348 | { 349 | $f = $MyInvocation.InvocationName 350 | Write-Verbose -Message "$f - START" 351 | } 352 | 353 | PROCESS 354 | { 355 | $newMenu = [PSCustomObject]@{ 356 | Name = "$Name" 357 | DisplayName = "$DisplayName" 358 | IsMainMenu = $IsMainMenu 359 | MenuItems = New-Object -TypeName System.Collections.ArrayList 360 | } 361 | 362 | $currentMainMenu = Get-Menu -MainMenu 363 | 364 | if ($PSBoundParameters.ContainsKey("IsMainMenu")) 365 | { 366 | if ($currentMainMenu) 367 | { 368 | Write-Error -Message "$f - You can only have one Main Menu. Currently [$($currentMainMenu.Name)] is your main menu" 369 | break 370 | } 371 | } 372 | 373 | if (-not $currentMainMenu) 374 | { 375 | $newMenu.IsMainMenu = $true 376 | } 377 | 378 | write-Verbose -Message "Creating menu [$Name]" 379 | $null = $script:Menus.Add($newMenu) 380 | $newMenu 381 | } 382 | 383 | END 384 | { 385 | 386 | } 387 | } 388 | 389 | function New-MenuItem 390 | { 391 | <# 392 | .Synopsis 393 | Create a new Menu-Item for a Menu. 394 | .DESCRIPTION 395 | Menu-Items are the action elements of the Menu. You add Menu-Items to a Menu. 396 | .EXAMPLE 397 | C:> New-MenuItem -Name "PasswordReset" -DisplayName "Reset a user password" -Action { Set-UserPassword } 398 | 399 | This will create a new Menu-Item. 400 | Since no MenuId is specified, it return the new object to the console. The switch parameter 401 | DisableConfirm is not specified and the user will have to confirm the invokation after it 402 | has been selected. 403 | .EXAMPLE 404 | C:> $menu = Get-Menu -Name sub1 405 | C:> $newMenuItem = @{ 406 | Name = "UnlockUser" 407 | DisplayName = "Unlock a user" 408 | Action = { Unlock-UserObject } 409 | DisableConfirm = $true 410 | } 411 | C:> $menu | New-MenuItem @newMenuItem 412 | 413 | This will create a new Menu-Item for the menu named sub1. The Menu-object is piped into the New-MenuItem cmdlet. 414 | It will invoke a custom cmdlet Unlock-UserObject and it will not confirm with the user before invokation. 415 | .EXAMPLE 416 | C:> $newMenuItem = @{ 417 | Name = "UnlockUser" 418 | DisplayName = "Unlock a user" 419 | Action = { Unlock-UserObject } 420 | DisableConfirm = $true 421 | } 422 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" | New-MenuItem @newMenuItem 423 | 424 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 425 | It will invoke a custom cmdlet Unlock-UserObject and it will not confirm with the user before invokation. 426 | .EXAMPLE 427 | C:> $newMenuItem = @{ 428 | Name = "UnlockUser" 429 | DisplayName = "Unlock a user" 430 | Action = { Show-Command -Name Unlock-UserObject } 431 | DisableConfirm = $true 432 | } 433 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" | New-MenuItem @newMenuItem 434 | 435 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 436 | It will invoke the Show-Command cmdlet which will show a windows form with the parameters of the custom 437 | cmdlet Unlock-UserObject. It will not confirm with the user before invokation. The user may cancel the 438 | windows form without executing the cmdlet. 439 | .EXAMPLE 440 | C:> $mainMenu = New-Menu -Name Main -DisplayName "Main Menu" -IsMainMenu 441 | C:> $newMenuItem = @{ 442 | Name = "UnlockUser" 443 | DisplayName = "Unlock a user" 444 | Action = { Show-Command -Name Unlock-UserObject } 445 | DisableConfirm = $true 446 | } 447 | C:> $item = New-MenuItem $newMenuItem 448 | C:> $item | Add-MenuItem -Menu Main 449 | 450 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 451 | .NOTES 452 | NAME: New-MenuItem 453 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 454 | LASTEDIT: Aug 2016 455 | KEYWORDS: General scripting Controller Menu 456 | #> 457 | [cmdletbinding(DefaultParameterSetName="none")] 458 | [OutputType([PSCustomObject])] 459 | Param 460 | ( 461 | [string] 462 | $Name 463 | , 464 | [string] 465 | $DisplayName 466 | , 467 | [string] 468 | $Description 469 | , 470 | [scriptblock] 471 | $Action 472 | , 473 | [switch] 474 | $DisableConfirm 475 | , 476 | [Parameter(ParameterSetName="ByName")] 477 | [string] 478 | $MenuName 479 | , 480 | [Parameter(ValueFromPipeline, ParameterSetName="ByObject")] 481 | [PSCustomObject] 482 | $MenuObject 483 | ) 484 | 485 | BEGIN 486 | { 487 | $f = $MyInvocation.InvocationName 488 | Write-Verbose -Message "$f - START" 489 | } 490 | 491 | PROCESS 492 | { 493 | $menuItem = [PSCustomObject]@{ 494 | Name = "$Name" 495 | DisplayName = "$DisplayName" 496 | Description = "$Description" 497 | Action = $Action 498 | ConfirmBeforeInvoke = -not $DisableConfirm 499 | } 500 | 501 | if ($PSBoundParameters.ContainsKey("MenuName")) 502 | { 503 | $MenuObject = Get-Menu -Name $MenuName 504 | } 505 | else 506 | { 507 | #$menu = Get-Menu -MainMenu 508 | } 509 | 510 | foreach ($Item in $menu.MenuItems) 511 | { 512 | if ($Item.Name -eq "$Name") 513 | { 514 | Write-Error -Message "$f - Duplicate MenuItem name detected in menu [$($menu.Name)]" 515 | break 516 | } 517 | } 518 | 519 | if ($PSBoundParameters.ContainsKey("MenuObject") -or $MenuObject) 520 | { 521 | $menuIndex = $script:Menus.IndexOf($MenuObject) 522 | $null = $script:Menus[$menuIndex].MenuItems.Add($menuItem) 523 | } 524 | 525 | # 526 | 527 | #if ($menuIndex -eq -1) 528 | #{ 529 | # throw "$f - Error, unable to find menu" 530 | #} 531 | 532 | # 533 | $menuItem 534 | } 535 | 536 | END 537 | { 538 | Write-Verbose -Message "$f - END" 539 | } 540 | } 541 | 542 | function Set-MenuOption 543 | { 544 | [cmdletbinding()] 545 | Param 546 | ( 547 | [string] 548 | $MenuFillChar = "*" 549 | , 550 | [ConsoleColor] 551 | $MenuFillColor = [consolecolor]::white 552 | , 553 | [string] 554 | $Heading = "[Heading not set]" 555 | , 556 | [ConsoleColor] 557 | $HeadingColor = [consolecolor]::white 558 | , 559 | [string] 560 | $SubHeading = "[SubHeading not set]" 561 | , 562 | [ConsoleColor] 563 | $SubHeadingColor = [consolecolor]::white 564 | , 565 | [string] 566 | $FooterText 567 | , 568 | [ConsoleColor] 569 | $FooterTextColor = [consolecolor]::white 570 | , 571 | [consolecolor] 572 | $MenuItemColor = [consolecolor]::white 573 | , 574 | [consolecolor] 575 | $MenuNameColor = [consolecolor]::white 576 | , 577 | [int] 578 | $MaxWith = 80 579 | ) 580 | 581 | BEGIN 582 | { 583 | $f = $MyInvocation.InvocationName 584 | Write-Verbose -Message "$f - START" 585 | 586 | foreach ($key in $PSBoundParameters.Keys) 587 | { 588 | Write-Verbose -Message "$f - Setting [$key] to value $($PSBoundParameters.$key)" 589 | $script:MenuOptions.$key = $PSBoundParameters.$key 590 | } 591 | 592 | if ([string]::IsNullOrEmpty($script:MenuOptions.FooterText)) 593 | { 594 | $script:MenuOptions.FooterText = "$(Get-date) - Running as $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" 595 | } 596 | } 597 | 598 | END 599 | { 600 | Write-Verbose -Message "$f - END" 601 | } 602 | } 603 | 604 | function Show-Menu 605 | { 606 | <# 607 | .Synopsis 608 | Show a Menu. 609 | .DESCRIPTION 610 | If executed without parameters, it will build and show the main Menu. If the MenuID parameter is 611 | specified, it will show the menu with that ID. You may also use the cmdlet to invoke a specific 612 | Menu-Item on a specific menu. 613 | .EXAMPLE 614 | C:> Show-Menu 615 | 616 | This will show the main Menu if defined. 617 | .EXAMPLE 618 | C:> Show-Menu -MenuId 1 619 | 620 | This will show the menu at index 1. Use Get-Menu to find the index (zero-based array) 621 | .EXAMPLE 622 | C:> Show-Menu -InvokeItem 2 -MenuId 0 623 | 624 | This will invoke the Menu-Item at index 2 on the form at index 0. If the Menu-Item requires 625 | confirmation before invoking it, the user will be prompted before invokation. 626 | .EXAMPLE 627 | C:> Show-Menu -InvokeItem 2 -MenuId 0 -force 628 | 629 | This will invoke the Menu-Item at index 2 on the form at index 0. If the Menu-Item requires 630 | confirmation before invoking it, the user will not be prompted before invokation since the 631 | force flag has been specified. 632 | .NOTES 633 | NAME: Show-Menu 634 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 635 | LASTEDIT: Aug 2016 636 | KEYWORDS: General scripting Controller Menu 637 | #> 638 | [cmdletbinding()] 639 | Param 640 | ( 641 | [int] 642 | $InvokeItem 643 | , 644 | [switch] 645 | $Force 646 | , 647 | [string] 648 | $MenuName 649 | ) 650 | 651 | BEGIN 652 | { 653 | $f = $MyInvocation.InvocationName 654 | Write-Verbose -Message "$f - START" 655 | 656 | $mainMenu = Get-Menu -MainMenu 657 | 658 | if (-not $mainMenu) 659 | { 660 | Write-Warning -Message "Please add a menu first using the New-Menu cmdlet" 661 | break 662 | } 663 | } 664 | 665 | PROCESS 666 | { 667 | if ($PSBoundParameters.ContainsKey("InvokeItem")) 668 | { 669 | $menuSelected = (Get-Menu -Name $MenuName).MenuItems[$InvokeItem] #$script:Menus[$MenuID].MenuItems[$InvokeItem] 670 | 671 | if($menuSelected) 672 | { 673 | if ($menuSelected.ConfirmBeforeInvoke) 674 | { 675 | if (-not $Force) 676 | { 677 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 678 | If ($Continue -ne "y") 679 | { 680 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 681 | break 682 | } 683 | } 684 | } 685 | 686 | if ($menuSelected.Action) 687 | { 688 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 689 | $menuSelected.Action.Invoke() 690 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 691 | break 692 | } 693 | } 694 | } 695 | } 696 | 697 | END 698 | { 699 | $menuLines = New-Object -TypeName System.Collections.ArrayList 700 | $maxWith = $script:MenuOptions.MaxWith 701 | 702 | function Get-MenuLine 703 | { 704 | Param 705 | ( 706 | [string] 707 | $Text 708 | , 709 | [consolecolor] 710 | $Color = [System.ConsoleColor]::White 711 | , 712 | [bool] 713 | $IsMenuItem = $false 714 | ) 715 | if ($IsMenuItem) 716 | { 717 | $textLine = " " + "$Text" 718 | $textLine += " " * (($maxWith - 1) - $textLine.length - 1) 719 | } 720 | else 721 | { 722 | $maxWith = $script:MenuOptions.MaxWith 723 | $textLength = $Text.Length 724 | $textBlanks = (($maxWith - 2) - $textLength) / 2 725 | $textLine = " " * $textBlanks + $Text 726 | $textLine += " " * (($maxWith - 1) - $textLine.Length - 1) 727 | } 728 | 729 | [pscustomobject]@{ 730 | Text = "$textLine" 731 | Color = $color 732 | } 733 | } 734 | 735 | if ($PSBoundParameters.ContainsKey("MenuName")) 736 | { 737 | $menu = Get-Menu -Name $MenuName 738 | } 739 | else 740 | { 741 | $menu = Get-Menu -MainMenu 742 | } 743 | 744 | if (-not $menu) 745 | { 746 | Write-Error -Exception "$f - Could not find menu" 747 | } 748 | 749 | $menuIndex = $script:Menus.IndexOf($menu) 750 | 751 | $menuFrame = $script:MenuOptions.MenuFillChar * ($maxWith - 2) 752 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 753 | 754 | $null = $menuLines.Add((Get-MenuLine -Text $menu.DisplayName -Color $script:MenuOptions.MenuNameColor)) 755 | 756 | $menuEmptyLine = " " * ($maxWith - 2) 757 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 758 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 759 | 760 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.Heading -color $script:MenuOptions.HeadingColor)) 761 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 762 | 763 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.SubHeading -color $script:MenuOptions.SubHeadingColor)) 764 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 765 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 766 | 767 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 768 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 769 | 770 | foreach ($item in $menu.MenuItems) 771 | { 772 | $menuColor = $script:MenuOptions.MenuItemColor 773 | $menuItemIndex = $script:menus[$menuIndex].MenuItems.IndexOf($item) 774 | $null = $menuLines.Add((Get-MenuLine -Text "$menuItemIndex. $($item.DisplayName)" -IsMenuItem $true -Color $menuColor)) 775 | } 776 | 777 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 778 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 779 | 780 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.FooterText -color $script:MenuOptions.FooterTextColor)) 781 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 782 | 783 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 784 | 785 | foreach ($line in $menuLines) 786 | { 787 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor -NoNewline 788 | Write-Host -Object $line.Text -ForegroundColor $line.Color -NoNewline 789 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor 790 | } 791 | 792 | $userSelection = (Read-Host -Prompt "Please enter number to execute action") 793 | 794 | try 795 | { 796 | $actionItemSelectionIndex = [int]($userSelection) 797 | } 798 | catch 799 | { 800 | Write-Error -Message $_.Exception.Message 801 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 802 | break 803 | } 804 | 805 | $menuSelected = $menu.MenuItems[$actionItemSelectionIndex] 806 | 807 | if ($menuSelected) 808 | { 809 | if ($menuSelected.ConfirmBeforeInvoke) 810 | { 811 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 812 | If ($Continue -ne "y") 813 | { 814 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 815 | break 816 | } 817 | } 818 | if ($menuSelected.Action) 819 | { 820 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 821 | $menuSelected.Action.Invoke() 822 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 823 | } 824 | } 825 | else 826 | { 827 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 828 | } 829 | 830 | Write-Verbose -Message "$f- END" 831 | } 832 | } 833 | 834 | 835 | -------------------------------------------------------------------------------- /Functions/Add-MenuItem.ps1: -------------------------------------------------------------------------------- 1 | function Add-MenuItem 2 | { 3 | <# 4 | .Synopsis 5 | Add a Menu Item to a menu. 6 | .DESCRIPTION 7 | Add a Menu Item to a menu. This cmdlet support input (Menu Items) from the pipeline. 8 | .EXAMPLE 9 | C:> $items = Get-MenuItem -MenuName main 10 | C:> $items | Add-MenuItem -Menu subMenu 11 | 12 | This will copy Menu Items from the main Menu and add them to the Menu subMenu. 13 | .EXAMPLE 14 | C:> $newMenuItem = @{ 15 | Name = "UnlockUser" 16 | DisplayName = "Unlock a user" 17 | Action = { Show-Command -Name Unlock-UserObject } 18 | DisableConfirm = $true 19 | } 20 | C:> $item = New-MenuItem @newMenuItem 21 | C:> $item | Add-MenuItem -Menu main 22 | 23 | This will create a new Menu Item and add it to the main Menu using the pipeline. 24 | .EXAMPLE 25 | C:> $newMenuItem = @{ 26 | Name = "UnlockUser" 27 | DisplayName = "Unlock a user" 28 | Action = { Show-Command -Name Unlock-UserObject } 29 | DisableConfirm = $true 30 | } 31 | C:> $item = New-MenuItem @newMenuItem 32 | C:> Add-MenuItem -Menu main -MenuItem $item 33 | 34 | This will create a new Menu Item and add it to the main Menu. 35 | .NOTES 36 | NAME: Add-MenuItem 37 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 38 | LASTEDIT: Aug 2016 39 | KEYWORDS: General scripting Controller Menu 40 | #> 41 | [cmdletbinding()] 42 | Param 43 | ( 44 | [Parameter(Mandatory)] 45 | [string] 46 | $Menu 47 | , 48 | [Parameter(Mandatory, ValueFromPipeline)] 49 | [PSCustomObject] 50 | $MenuItem 51 | ) 52 | 53 | BEGIN 54 | { 55 | $f = $MyInvocation.InvocationName 56 | Write-Verbose -Message "$f - START" 57 | } 58 | 59 | PROCESS 60 | { 61 | Write-Verbose "getting menu" 62 | $menuObject = Get-Menu -Name "$Menu" 63 | 64 | if ($menuObject) 65 | { 66 | write-verbose "found menu" 67 | foreach ($Item in $menuObject.MenuItems) 68 | { 69 | if ($Item.Name -eq $MenuItem.Name) 70 | { 71 | Write-Error -Message "$f - Duplicate MenuItem name detected in menu [$($menuObject.Name)]" 72 | break 73 | } 74 | } 75 | 76 | $menuIndex = $script:Menus.IndexOf($menuObject) 77 | write-verbose "menuindex [$menuIndex]" 78 | if ($menuIndex -ge 0) 79 | { 80 | $null = $script:Menus[$menuIndex].MenuItems.Add($MenuItem) 81 | } 82 | } 83 | else { 84 | Write-Verbose "no menuobject" 85 | } 86 | } 87 | 88 | END 89 | { 90 | Write-Verbose -Message "$f - END" 91 | } 92 | } -------------------------------------------------------------------------------- /Functions/Get-Menu.ps1: -------------------------------------------------------------------------------- 1 | $script:Menus = New-Object -TypeName System.Collections.ArrayList 2 | 3 | $script:MenuOptions = [pscustomobject]@{ 4 | MenuFillChar = "*" 5 | MenuFillColor = [consolecolor]::White 6 | Heading = "" 7 | HeadingColor = [consolecolor]::White 8 | SubHeading = "" 9 | SubHeadingColor = [consolecolor]::White 10 | FooterText = "" 11 | FooterTextColor = [consolecolor]::White 12 | MenuItemColor = [consolecolor]::White 13 | MenuNameColor = [consolecolor]::White 14 | MaxWith = 80 15 | } 16 | 17 | function Get-Menu 18 | { 19 | <# 20 | .Synopsis 21 | Get a list of menus 22 | .DESCRIPTION 23 | Returns a list of menus by name, id or just the main menu 24 | .EXAMPLE 25 | C:> Get-Menu 26 | 27 | Returns all menus 28 | .EXAMPLE 29 | C:> Get-Menu -MainMenu 30 | 31 | Returns the Main Menu only 32 | .EXAMPLE 33 | C:> Get-Menu -MenuID 1 34 | 35 | Returns the menu of the specified index 36 | .EXAMPLE 37 | C:> Get-Menu -Name main* 38 | 39 | Returns all the menus which has a name that starts with main 40 | .NOTES 41 | NAME: Get-Menu 42 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 43 | LASTEDIT: Aug 2016 44 | KEYWORDS: General scripting Controller Menu 45 | #> 46 | [cmdletbinding(DefaultParameterSetName='none')] 47 | [OutputType([PSCustomObject])] 48 | Param 49 | ( 50 | [Parameter(ParameterSetName="MainMenu")] 51 | [switch] 52 | $MainMenu 53 | , 54 | [Parameter(ParameterSetName='ByID')] 55 | [int] 56 | $MenuID 57 | , 58 | [Parameter(ParameterSetName="ByName")] 59 | [string] 60 | $Name 61 | ) 62 | 63 | BEGIN 64 | { 65 | $f = $MyInvocation.InvocationName 66 | Write-Verbose -Message "$f - START" 67 | } 68 | 69 | PROCESS 70 | { 71 | if ($PSBoundParameters.ContainsKey("MainMenu")) 72 | { 73 | $script:Menus.Where({$_.IsMainMenu -eq $true}) 74 | } 75 | 76 | if ($PSBoundParameters.ContainsKey("MenuID")) 77 | { 78 | $script:Menus[$MenuID] 79 | } 80 | 81 | if ($PSBoundParameters.ContainsKey("Name")) 82 | { 83 | $script:Menus.Where({$_.Name -like "$Name"}) 84 | } 85 | 86 | if($PSCmdLet.ParameterSetName -eq "none") 87 | { 88 | $script:Menus 89 | } 90 | } 91 | 92 | END 93 | { 94 | Write-Verbose -Message "$f - END" 95 | } 96 | } -------------------------------------------------------------------------------- /Functions/Get-MenuItem.ps1: -------------------------------------------------------------------------------- 1 | function Get-MenuItem 2 | { 3 | <# 4 | .Synopsis 5 | Get a list of menu-items 6 | .DESCRIPTION 7 | Returns a list of menus by Menu-name, Menu-ID or the menu object 8 | .EXAMPLE 9 | C:> Get-MenuItem 10 | 11 | Returns all menu-items for all menus 12 | .EXAMPLE 13 | C:> Get-MenuItem -MenuName MainMenu 14 | 15 | Returns the menu-items for the menu with name MainMenu 16 | .EXAMPLE 17 | C:> Get-MenuItem -MenuId 1 18 | 19 | Returns the menu-items for the menu with id 1 20 | .EXAMPLE 21 | C:> $Menu = Get-Menu -Name SubMenuSkype 22 | C:> Get-MenuItem -MenuObject $Menu 23 | 24 | Returns all the menu-items for the menu with name SubMenuSkype 25 | .EXAMPLE 26 | C:> Get-Menu -Name SubMenuSkype | Get-MenuItem 27 | 28 | Returns all the menu-items for the menu with name SubMenuSkype 29 | .NOTES 30 | NAME: Get-MenuItem 31 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 32 | LASTEDIT: Aug 2016 33 | KEYWORDS: General scripting Controller Menu 34 | #> 35 | [cmdletbinding(DefaultParameterSetName='none')] 36 | [OutputType([PSCustomObject])] 37 | Param 38 | ( 39 | [Parameter(ParameterSetName="ByName")] 40 | [string[]] 41 | $MenuName 42 | , 43 | [Parameter(ParameterSetName="ById")] 44 | [int] 45 | $MenuId 46 | , 47 | [Parameter(ValueFromPipeline, ParameterSetName="ByObject")] 48 | [PSCustomObject] 49 | $MenuObject 50 | ) 51 | 52 | BEGIN 53 | { 54 | $f = $MyInvocation.InvocationName 55 | Write-Verbose -Message "$f - START" 56 | } 57 | 58 | PROCESS 59 | { 60 | if ($PSCmdlet.ParameterSetName -eq "none") 61 | { 62 | $script:Menus.MenuItems 63 | } 64 | 65 | if ($PSBoundParameters.ContainsKey("MenuName")) 66 | { 67 | write-verbose -message "$f - Getting by MenuName" 68 | $script:Menus.Where({$_.Name -eq "$MenuName"}) | Select-Object -ExpandProperty MenuItems 69 | } 70 | 71 | if ($PSBoundParameters.Containskey("MenuId")) 72 | { 73 | $script:Menus[$MenuId].MenuItems 74 | } 75 | 76 | if ($PSCmdlet.ParameterSetName -eq "ByObject") 77 | { 78 | $MenuObject.MenuItems 79 | } 80 | } 81 | 82 | END 83 | { 84 | Write-Verbose -Message "$f - END" 85 | } 86 | } -------------------------------------------------------------------------------- /Functions/Get-MenuOption.ps1: -------------------------------------------------------------------------------- 1 | function Get-MenuOption 2 | { 3 | <# 4 | .Synopsis 5 | Get a list menu options 6 | .DESCRIPTION 7 | Returns a PSCustomObject with all menu options. This CmdLet has no parameters 8 | .EXAMPLE 9 | C:> Get-MenuOption 10 | 11 | Returns all menu-items for all menus 12 | .NOTES 13 | NAME: Get-MenuItem 14 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 15 | LASTEDIT: Aug 2016 16 | KEYWORDS: General scripting Controller Menu 17 | #> 18 | [cmdletbinding()] 19 | [OutputType([PSCustomObject])] 20 | Param 21 | () 22 | $script:MenuOptions 23 | } -------------------------------------------------------------------------------- /Functions/New-Menu.ps1: -------------------------------------------------------------------------------- 1 | function New-Menu 2 | { 3 | <# 4 | .Synopsis 5 | Create a new Menu 6 | .DESCRIPTION 7 | You can create as many menus you like, however you may only have one main Menu. The Menu must 8 | have a name, hence the Name parameter is Mandatory. The first Menu you create will become 9 | the main Menu even if you do not specify the IsMainMenu switch. 10 | .PARAMETER Name 11 | Normally you would like to specify a name without space and Camel-case the name. 12 | .EXAMPLE 13 | C:> New-Menu -Name "MainMenu" 14 | 15 | This will create a new Menu with name MainMenu. If this is the first Menu, it will be 16 | created as a main Menu 17 | .EXAMPLE 18 | C:> New-Menu -Name "MainMenu" -IsMainMenu 19 | 20 | This will create a new Menu with name MainMenu and set is as a main Menu 21 | .EXAMPLE 22 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" 23 | 24 | This will create a new Menu with name sub1 and DisplayName Sub-Menu for Skype 25 | .NOTES 26 | NAME: New-Menu 27 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 28 | LASTEDIT: Aug 2016 29 | KEYWORDS: General scripting Controller Menu 30 | #> 31 | [cmdletbinding()] 32 | [OutputType([PSCustomObject])] 33 | Param 34 | ( 35 | [Parameter(Mandatory)] 36 | [string] 37 | $Name 38 | , 39 | [string] 40 | $DisplayName 41 | , 42 | [switch] 43 | $IsMainMenu 44 | ) 45 | 46 | BEGIN 47 | { 48 | $f = $MyInvocation.InvocationName 49 | Write-Verbose -Message "$f - START" 50 | } 51 | 52 | PROCESS 53 | { 54 | $newMenu = [PSCustomObject]@{ 55 | Name = "$Name" 56 | DisplayName = "$DisplayName" 57 | IsMainMenu = $IsMainMenu 58 | MenuItems = New-Object -TypeName System.Collections.ArrayList 59 | } 60 | 61 | $currentMainMenu = Get-Menu -MainMenu 62 | 63 | if ($PSBoundParameters.ContainsKey("IsMainMenu")) 64 | { 65 | if ($currentMainMenu) 66 | { 67 | Write-Error -Message "$f - You can only have one Main Menu. Currently [$($currentMainMenu.Name)] is your main menu" 68 | break 69 | } 70 | } 71 | 72 | if (-not $currentMainMenu) 73 | { 74 | $newMenu.IsMainMenu = $true 75 | } 76 | 77 | write-Verbose -Message "Creating menu [$Name]" 78 | $null = $script:Menus.Add($newMenu) 79 | $newMenu 80 | } 81 | 82 | END 83 | { 84 | 85 | } 86 | } -------------------------------------------------------------------------------- /Functions/New-MenuItem.ps1: -------------------------------------------------------------------------------- 1 | function New-MenuItem 2 | { 3 | <# 4 | .Synopsis 5 | Create a new Menu-Item for a Menu. 6 | .DESCRIPTION 7 | Menu-Items are the action elements of the Menu. You add Menu-Items to a Menu. 8 | .EXAMPLE 9 | C:> New-MenuItem -Name "PasswordReset" -DisplayName "Reset a user password" -Action { Set-UserPassword } 10 | 11 | This will create a new Menu-Item. 12 | Since no MenuId is specified, it return the new object to the console. The switch parameter 13 | DisableConfirm is not specified and the user will have to confirm the invokation after it 14 | has been selected. 15 | .EXAMPLE 16 | C:> $menu = Get-Menu -Name sub1 17 | C:> $newMenuItem = @{ 18 | Name = "UnlockUser" 19 | DisplayName = "Unlock a user" 20 | Action = { Unlock-UserObject } 21 | DisableConfirm = $true 22 | } 23 | C:> $menu | New-MenuItem @newMenuItem 24 | 25 | This will create a new Menu-Item for the menu named sub1. The Menu-object is piped into the New-MenuItem cmdlet. 26 | It will invoke a custom cmdlet Unlock-UserObject and it will not confirm with the user before invokation. 27 | .EXAMPLE 28 | C:> $newMenuItem = @{ 29 | Name = "UnlockUser" 30 | DisplayName = "Unlock a user" 31 | Action = { Unlock-UserObject } 32 | DisableConfirm = $true 33 | } 34 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" | New-MenuItem @newMenuItem 35 | 36 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 37 | It will invoke a custom cmdlet Unlock-UserObject and it will not confirm with the user before invokation. 38 | .EXAMPLE 39 | C:> $newMenuItem = @{ 40 | Name = "UnlockUser" 41 | DisplayName = "Unlock a user" 42 | Action = { Show-Command -Name Unlock-UserObject } 43 | DisableConfirm = $true 44 | } 45 | C:> New-Menu -Name "sub1" -DisplayName "Sub-Menu for Skype" | New-MenuItem @newMenuItem 46 | 47 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 48 | It will invoke the Show-Command cmdlet which will show a windows form with the parameters of the custom 49 | cmdlet Unlock-UserObject. It will not confirm with the user before invokation. The user may cancel the 50 | windows form without executing the cmdlet. 51 | .EXAMPLE 52 | C:> $mainMenu = New-Menu -Name Main -DisplayName "Main Menu" -IsMainMenu 53 | C:> $newMenuItem = @{ 54 | Name = "UnlockUser" 55 | DisplayName = "Unlock a user" 56 | Action = { Show-Command -Name Unlock-UserObject } 57 | DisableConfirm = $true 58 | } 59 | C:> $item = New-MenuItem $newMenuItem 60 | C:> $item | Add-MenuItem -Menu Main 61 | 62 | This will create a new Sub-Menu and add the UnlockUser Menu-Item to it using the pipeline. 63 | .NOTES 64 | NAME: New-MenuItem 65 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 66 | LASTEDIT: Aug 2016 67 | KEYWORDS: General scripting Controller Menu 68 | #> 69 | [cmdletbinding(DefaultParameterSetName="none")] 70 | [OutputType([PSCustomObject])] 71 | Param 72 | ( 73 | [string] 74 | $Name 75 | , 76 | [string] 77 | $DisplayName 78 | , 79 | [string] 80 | $Description 81 | , 82 | [scriptblock] 83 | $Action 84 | , 85 | [switch] 86 | $DisableConfirm 87 | , 88 | [Parameter(ParameterSetName="ByName")] 89 | [string] 90 | $MenuName 91 | , 92 | [Parameter(ValueFromPipeline, ParameterSetName="ByObject")] 93 | [PSCustomObject] 94 | $MenuObject 95 | ) 96 | 97 | BEGIN 98 | { 99 | $f = $MyInvocation.InvocationName 100 | Write-Verbose -Message "$f - START" 101 | } 102 | 103 | PROCESS 104 | { 105 | $menuItem = [PSCustomObject]@{ 106 | Name = "$Name" 107 | DisplayName = "$DisplayName" 108 | Description = "$Description" 109 | Action = $Action 110 | ConfirmBeforeInvoke = -not $DisableConfirm 111 | } 112 | 113 | if ($PSBoundParameters.ContainsKey("MenuName")) 114 | { 115 | $MenuObject = Get-Menu -Name $MenuName 116 | } 117 | else 118 | { 119 | #$menu = Get-Menu -MainMenu 120 | } 121 | 122 | foreach ($Item in $menu.MenuItems) 123 | { 124 | if ($Item.Name -eq "$Name") 125 | { 126 | Write-Error -Message "$f - Duplicate MenuItem name detected in menu [$($menu.Name)]" 127 | break 128 | } 129 | } 130 | 131 | if ($PSBoundParameters.ContainsKey("MenuObject") -or $MenuObject) 132 | { 133 | $menuIndex = $script:Menus.IndexOf($MenuObject) 134 | $null = $script:Menus[$menuIndex].MenuItems.Add($menuItem) 135 | } 136 | 137 | # 138 | 139 | #if ($menuIndex -eq -1) 140 | #{ 141 | # throw "$f - Error, unable to find menu" 142 | #} 143 | 144 | # 145 | $menuItem 146 | } 147 | 148 | END 149 | { 150 | Write-Verbose -Message "$f - END" 151 | } 152 | } -------------------------------------------------------------------------------- /Functions/Set-MenuOption.ps1: -------------------------------------------------------------------------------- 1 | function Set-MenuOption 2 | { 3 | [cmdletbinding()] 4 | Param 5 | ( 6 | [string] 7 | $MenuFillChar = "*" 8 | , 9 | [ConsoleColor] 10 | $MenuFillColor = [consolecolor]::white 11 | , 12 | [string] 13 | $Heading = "[Heading not set]" 14 | , 15 | [ConsoleColor] 16 | $HeadingColor = [consolecolor]::white 17 | , 18 | [string] 19 | $SubHeading = "[SubHeading not set]" 20 | , 21 | [ConsoleColor] 22 | $SubHeadingColor = [consolecolor]::white 23 | , 24 | [string] 25 | $FooterText 26 | , 27 | [ConsoleColor] 28 | $FooterTextColor = [consolecolor]::white 29 | , 30 | [consolecolor] 31 | $MenuItemColor = [consolecolor]::white 32 | , 33 | [consolecolor] 34 | $MenuNameColor = [consolecolor]::white 35 | , 36 | [int] 37 | $MaxWith = 80 38 | ) 39 | 40 | BEGIN 41 | { 42 | $f = $MyInvocation.InvocationName 43 | Write-Verbose -Message "$f - START" 44 | 45 | foreach ($key in $PSBoundParameters.Keys) 46 | { 47 | Write-Verbose -Message "$f - Setting [$key] to value $($PSBoundParameters.$key)" 48 | $script:MenuOptions.$key = $PSBoundParameters.$key 49 | } 50 | 51 | if ([string]::IsNullOrEmpty($script:MenuOptions.FooterText)) 52 | { 53 | $script:MenuOptions.FooterText = "$(Get-date) - Running as $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" 54 | } 55 | } 56 | 57 | END 58 | { 59 | Write-Verbose -Message "$f - END" 60 | } 61 | } -------------------------------------------------------------------------------- /Functions/Show-Menu.ps1: -------------------------------------------------------------------------------- 1 | function Show-Menu 2 | { 3 | <# 4 | .Synopsis 5 | Show a Menu. 6 | .DESCRIPTION 7 | If executed without parameters, it will build and show the main Menu. If the MenuID parameter is 8 | specified, it will show the menu with that ID. You may also use the cmdlet to invoke a specific 9 | Menu-Item on a specific menu. 10 | .EXAMPLE 11 | C:> Show-Menu 12 | 13 | This will show the main Menu if defined. 14 | .EXAMPLE 15 | C:> Show-Menu -MenuId 1 16 | 17 | This will show the menu at index 1. Use Get-Menu to find the index (zero-based array) 18 | .EXAMPLE 19 | C:> Show-Menu -InvokeItem 2 -MenuId 0 20 | 21 | This will invoke the Menu-Item at index 2 on the form at index 0. If the Menu-Item requires 22 | confirmation before invoking it, the user will be prompted before invokation. 23 | .EXAMPLE 24 | C:> Show-Menu -InvokeItem 2 -MenuId 0 -force 25 | 26 | This will invoke the Menu-Item at index 2 on the form at index 0. If the Menu-Item requires 27 | confirmation before invoking it, the user will not be prompted before invokation since the 28 | force flag has been specified. 29 | .NOTES 30 | NAME: Show-Menu 31 | AUTHOR: Tore Groneng tore@firstpoint.no @toregroneng tore.groneng@gmail.com 32 | LASTEDIT: Aug 2016 33 | KEYWORDS: General scripting Controller Menu 34 | #> 35 | [cmdletbinding()] 36 | Param 37 | ( 38 | [int] 39 | $InvokeItem 40 | , 41 | [switch] 42 | $Force 43 | , 44 | [string] 45 | $MenuName 46 | ) 47 | 48 | BEGIN 49 | { 50 | $f = $MyInvocation.InvocationName 51 | Write-Verbose -Message "$f - START" 52 | 53 | $mainMenu = Get-Menu -MainMenu 54 | 55 | if (-not $mainMenu) 56 | { 57 | Write-Warning -Message "Please add a menu first using the New-Menu cmdlet" 58 | break 59 | } 60 | } 61 | 62 | PROCESS 63 | { 64 | if ($PSBoundParameters.ContainsKey("InvokeItem")) 65 | { 66 | $menuSelected = (Get-Menu -Name $MenuName).MenuItems[$InvokeItem] #$script:Menus[$MenuID].MenuItems[$InvokeItem] 67 | 68 | if($menuSelected) 69 | { 70 | if ($menuSelected.ConfirmBeforeInvoke) 71 | { 72 | if (-not $Force) 73 | { 74 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 75 | If ($Continue -ne "y") 76 | { 77 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 78 | break 79 | } 80 | } 81 | } 82 | 83 | if ($menuSelected.Action) 84 | { 85 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 86 | $menuSelected.Action.Invoke() 87 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 88 | break 89 | } 90 | } 91 | } 92 | } 93 | 94 | END 95 | { 96 | $menuLines = New-Object -TypeName System.Collections.ArrayList 97 | $maxWith = $script:MenuOptions.MaxWith 98 | 99 | function Get-MenuLine 100 | { 101 | Param 102 | ( 103 | [string] 104 | $Text 105 | , 106 | [consolecolor] 107 | $Color = [System.ConsoleColor]::White 108 | , 109 | [bool] 110 | $IsMenuItem = $false 111 | ) 112 | if ($IsMenuItem) 113 | { 114 | $textLine = " " + "$Text" 115 | $textLine += " " * (($maxWith - 1) - $textLine.length - 1) 116 | } 117 | else 118 | { 119 | $maxWith = $script:MenuOptions.MaxWith 120 | $textLength = $Text.Length 121 | $textBlanks = (($maxWith - 2) - $textLength) / 2 122 | $textLine = " " * $textBlanks + $Text 123 | $textLine += " " * (($maxWith - 1) - $textLine.Length - 1) 124 | } 125 | 126 | [pscustomobject]@{ 127 | Text = "$textLine" 128 | Color = $color 129 | } 130 | } 131 | 132 | if ($PSBoundParameters.ContainsKey("MenuName")) 133 | { 134 | $menu = Get-Menu -Name $MenuName 135 | } 136 | else 137 | { 138 | $menu = Get-Menu -MainMenu 139 | } 140 | 141 | if (-not $menu) 142 | { 143 | Write-Error -Exception "$f - Could not find menu" 144 | } 145 | 146 | $menuIndex = $script:Menus.IndexOf($menu) 147 | 148 | $menuFrame = $script:MenuOptions.MenuFillChar * ($maxWith - 2) 149 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 150 | 151 | $null = $menuLines.Add((Get-MenuLine -Text $menu.DisplayName -Color $script:MenuOptions.MenuNameColor)) 152 | 153 | $menuEmptyLine = " " * ($maxWith - 2) 154 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 155 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 156 | 157 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.Heading -color $script:MenuOptions.HeadingColor)) 158 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 159 | 160 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.SubHeading -color $script:MenuOptions.SubHeadingColor)) 161 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 162 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 163 | 164 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 165 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 166 | 167 | foreach ($item in $menu.MenuItems) 168 | { 169 | $menuColor = $script:MenuOptions.MenuItemColor 170 | $menuItemIndex = $script:menus[$menuIndex].MenuItems.IndexOf($item) 171 | $null = $menuLines.Add((Get-MenuLine -Text "$menuItemIndex. $($item.DisplayName)" -IsMenuItem $true -Color $menuColor)) 172 | } 173 | 174 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 175 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 176 | 177 | $null = $menuLines.Add((Get-MenuLine -Text $script:MenuOptions.FooterText -color $script:MenuOptions.FooterTextColor)) 178 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 179 | 180 | $null = $menuLines.Add((Get-MenuLine -Text $menuFrame -color $script:MenuOptions.MenuFillColor)) 181 | 182 | foreach ($line in $menuLines) 183 | { 184 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor -NoNewline 185 | Write-Host -Object $line.Text -ForegroundColor $line.Color -NoNewline 186 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor 187 | } 188 | 189 | $userSelection = (Read-Host -Prompt "Please enter number to execute action") 190 | 191 | try 192 | { 193 | $actionItemSelectionIndex = [int]($userSelection) 194 | } 195 | catch 196 | { 197 | Write-Error -Message $_.Exception.Message 198 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 199 | break 200 | } 201 | 202 | $menuSelected = $menu.MenuItems[$actionItemSelectionIndex] 203 | 204 | if ($menuSelected) 205 | { 206 | if ($menuSelected.ConfirmBeforeInvoke) 207 | { 208 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 209 | If ($Continue -ne "y") 210 | { 211 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 212 | break 213 | } 214 | } 215 | if ($menuSelected.Action) 216 | { 217 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 218 | $menuSelected.Action.Invoke() 219 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 220 | } 221 | } 222 | else 223 | { 224 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 225 | } 226 | 227 | Write-Verbose -Message "$f- END" 228 | } 229 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Tore Groneng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Building interactive Menus in Powershell 2 | 3 | This is a Controller module. It uses Write-Host to create a Menu in the console. As the name implies 4 | it builds a CLI menu, however if you think your users might fancy a more GUI like experience, you 5 | should look up a cmdlet named Show-Command. Show-Command will build a GUI for you cmdlet. It will 6 | create a windows form with fields for all your parameters and parametersets. 7 | 8 | 9 | ## Design goals 10 | 11 | I have seen to many crappy menus that is a mixture of controller script and business logic. It is in 12 | essence a wild west out there, hence my ultimate goal is to create something that makes it as easy 13 | as possible to create a menu and change the way it looks. 14 | 15 | 1. Make it easy to build Menus and change them 16 | 2. Make it as "declarative" as possible 17 | 18 | 19 | ## Menus 20 | 21 | The module supports multiple Menus, however only one Main-Menu. Each menu has a collection of Menu- 22 | Items that the user can choose from. 23 | 24 | Example menu: 25 | ![ExamleMenu](/Bin/cliMenu.png) 26 | 27 | 28 | ## Menu options 29 | 30 | Currently you can control the following aspects of the menu (they are shared across all menus unless you change 31 | them before showing a sub-menu): 32 | 33 | * Choose the char that creates the outer frame of the menu 34 | * Change the color of the frame 35 | * Change the Color and DisplayName for the Menus 36 | * Change the color and Heading for the Menus 37 | * Change the color and Sub-Heading for the Menus 38 | * Change the color and DisplayName for the Menu-Items 39 | * Change the color and footer text for the menus 40 | * Change the Width of the Menu 41 | 42 | 43 | ## Menu-Items 44 | 45 | Menu-Items are the elements your users can invoke in your Menu. They have a ScriptBlock and a DisableConfirm 46 | switch parameter in addition to a Name and DisplayName. With the DisableConfirm parameter, you may selectively 47 | force the user to confirm the action before it is invoked. 48 | 49 | 50 | ## Validation and return values 51 | 52 | The goal of this module is neither. As a toolbuilder you are responsible for validating user 53 | input when they invoke the ScriptBlock assosiated with the Menu-Item. Any output from the ScriptBlock 54 | will be written in the console. As you may know, a ScriptBlock may be a small script or a call to 55 | a cmdlet with parameters. I would suggest that you stick to calling custom or built-in cmdlets and 56 | design it using the best practice guides from Microsoft in regards to mandatory parameters etc. 57 | 58 | 59 | ## Show-Menu 60 | 61 | This is the core cmdlet responsible for building the Menu and displaying it to the user. Executed 62 | without parameters it will display the Main-Menu (remember you can only have one Main-Menu). Nevertheless 63 | you may also use it to display Sub-Menus by specifying the parameter MenuId which is the index of the 64 | menu. Further you may also invoke a specific Menu-Item in a specific Menu by supplying InvokeItem and MenuId 65 | parameters. If the Menu-Item is defined to confirm with the user before invocation, it will prompt the user 66 | with a confirmation request before execution. You can override this with the -Force parameter to execute it 67 | directly. 68 | 69 | ## Examples 70 | 71 | A menu which uses the Show-Command cmdlet (complete script in [example.ps1](https://github.com/torgro/cliMenu/blob/master/Scripts/Example.ps1)): 72 | ![Example1](/Bin/Example1.png) 73 | 74 | ```powershell 75 | Import-Module .\CliMenu.psd1 76 | 77 | Set-MenuOption -Heading "Helpdesk Inteface System" -SubHeading "LOIS by Firstpoint" -MenuFillChar "#" -MenuFillColor DarkYellow 78 | Set-MenuOption -HeadingColor DarkCyan -MenuNameColor DarkGray -SubHeadingColor Green -FooterTextColor DarkGray 79 | Set-MenuOption -MaxWith 60 80 | 81 | $newItem1 = @{ 82 | Name = "WriteHost" 83 | DisplayName = "Launch Write-Host as a GUI" 84 | Action = { show-command -Name Write-host } 85 | DisableConfirm = $true 86 | } 87 | 88 | $newMenu = @{ 89 | Name = "Main" 90 | DisplayName = "Main Menu" 91 | } 92 | 93 | # Create a new menu Item 94 | $menuItem = New-MenuItem @newItem1 95 | 96 | # Create a new menu (first menu will become the main menu) 97 | $mainMenu = New-Menu @newMenu 98 | 99 | # Add menu item to the menu named 'main' 100 | $menuItem | Add-MenuItem -Menu main 101 | Clear-Host 102 | Show-Menu 103 | ``` 104 | 105 | An example with a Main-Menu and Sub-Menu: 106 | ![Example21](/Bin/Example21.png) 107 | ![Example22](/Bin/Example22.png) 108 | 109 | ```powershell 110 | Import-Module .\CliMenu.psd1 111 | 112 | Set-MenuOption -Heading "Helpdesk Inteface System" -SubHeading "LOIS by Firstpoint" -MenuFillChar "#" -MenuFillColor DarkYellow 113 | Set-MenuOption -HeadingColor DarkCyan -MenuNameColor DarkGray -SubHeadingColor Green -FooterTextColor DarkGray 114 | Set-MenuOption -MaxWith 60 115 | 116 | $newItem1 = @{ 117 | Name = "WriteHost" 118 | DisplayName = "Launch Write-Host as a GUI" 119 | Action = { show-command -Name Write-host } 120 | DisableConfirm = $true 121 | } 122 | 123 | $newMenu = @{ 124 | Name = "Main" 125 | DisplayName = "Main Menu" 126 | } 127 | 128 | # Create a new menu Item 129 | $menuItem = New-MenuItem @newItem1 130 | 131 | # Create a new menu (first menu will become the main menu) 132 | $mainMenu = New-Menu @newMenu 133 | 134 | # Add menu item to the menu named 'main' 135 | $menuItem | Add-MenuItem -Menu main 136 | 137 | $newItem2 = @{ 138 | Name = "GoToSub" 139 | DisplayName = "Go to submenu" 140 | Action = { Show-Menu -MenuName SubMenu } 141 | } 142 | 143 | # Add a menuitem to the main menu 144 | $mainMenu | New-MenuItem @newItem2 -DisableConfirm 145 | 146 | $newItemSubMenu = @{ 147 | Name = "GoToMain" 148 | DisplayName = "Go to Main Menu" 149 | Action = { Show-Menu } 150 | } 151 | 152 | # Create a new menu (sub-menu) and add a menu-item to it 153 | New-Menu -Name SubMenu -DisplayName "*** SubMenu1 ***" | New-MenuItem @newItemSubMenu -DisableConfirm 154 | 155 | clear-host 156 | Show-Menu 157 | ``` 158 | 159 | That is it. If you have any questions or issues, look me up on twitter (@toreGroneng) or file an issue! 160 | 161 | ### Credits 162 | 163 | Big thank you to Fausto Nascimento for invaluable input and suggestions! 164 | 165 | ### Disclaimer 166 | 167 | THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 168 | -------------------------------------------------------------------------------- /Rubbish/Set-Menu.ps1: -------------------------------------------------------------------------------- 1 | function Set-Menu 2 | { 3 | [cmdletbinding()] 4 | Param 5 | ( 6 | [string] 7 | $Name 8 | , 9 | [string] 10 | $DisplayName 11 | , 12 | [switch] 13 | $IsMainMenu 14 | ) 15 | 16 | BEGIN 17 | { 18 | $f = $MyInvocation.InvocationName 19 | Write-Verbose -Message "$f - START" 20 | } 21 | 22 | PROCESS 23 | { 24 | $menu = Get-Menu -Name "$Name" 25 | 26 | if ($menu) 27 | { 28 | $menuIndex = $script:Menus.IndexOf($menu) 29 | 30 | foreach ($key in $PSBoundParameters.Keys) 31 | { 32 | Write-Verbose -Message "$f - Setting [$key] to value $($PSBoundParameters.$key)" 33 | $script:Menus[$menuIndex].$key = $PSBoundParameters.$key 34 | } 35 | } 36 | } 37 | 38 | END 39 | { 40 | Write-Verbose -Message "$f - END" 41 | } 42 | } -------------------------------------------------------------------------------- /Rubbish/Set-MenuItem.ps1: -------------------------------------------------------------------------------- 1 | function Set-MenuItem 2 | { 3 | [cmdletbinding()] 4 | Param 5 | ( 6 | [string] 7 | $Name 8 | , 9 | [string] 10 | $DisplayName 11 | , 12 | [string] 13 | $Description 14 | , 15 | [scriptblock] 16 | $Action 17 | , 18 | [bool] 19 | $DisableConfirm 20 | , 21 | [int] 22 | $MenuName 23 | ) 24 | 25 | BEGIN 26 | { 27 | $f = $MyInvocation.InvocationName 28 | Write-Verbose -Message "$f - START" 29 | } 30 | 31 | PROCESS 32 | { 33 | $menu = (Get-Menu -Name "$MenuName") 34 | 35 | If (-not $menu) 36 | { 37 | write-error -Message "Unable to find menu with name [$MenuName]" 38 | break 39 | } 40 | 41 | $menuIndex = $script:Menus.IndexOf($menu) 42 | $menuItem = $script:Menus[$menuIndex].MenuItems.Where({$_.Name -eq "$Name"}) 43 | 44 | if (-not $menuItem) 45 | { 46 | Write-Error -Message "$f - Unable to find menuItem with name [$Name]" 47 | break 48 | } 49 | 50 | $menuItemIndex = $script:Menus[$menuIndex].IndexOf($menuItem) 51 | foreach ($key in $PSBoundParameters.Keys) 52 | { 53 | Write-Verbose -Message "$f - Setting [$key] to value $($PSBoundParameters.$key)" 54 | $script:Menus[$menuIndex].MenuItems[$menuItemIndex].$key = $PSBoundParameters.$key 55 | } 56 | } 57 | 58 | END 59 | { 60 | Write-Verbose -Message "$f - END" 61 | } 62 | } -------------------------------------------------------------------------------- /Scripts/Example.ps1: -------------------------------------------------------------------------------- 1 | Remove-Module cliMenu -ErrorAction SilentlyContinue; Import-Module .\CliMenu.psd1 2 | 3 | Set-MenuOption -Heading "Helpdesk Inteface System" -SubHeading "LOIS by Firstpoint" -MenuFillChar "#" -MenuFillColor DarkYellow 4 | Set-MenuOption -HeadingColor DarkCyan -MenuNameColor DarkGray -SubHeadingColor Green -FooterTextColor DarkGray 5 | Set-MenuOption -MaxWith 60 6 | 7 | $newItem1 = @{ 8 | Name = "WriteHost" 9 | DisplayName = "Launch Write-Host as a GUI" 10 | Action = { show-command -Name Write-host } 11 | DisableConfirm = $true 12 | } 13 | 14 | $newMenu = @{ 15 | Name = "Main" 16 | DisplayName = "Main Menu" 17 | } 18 | 19 | # Create a new menu Item 20 | $menuItem = New-MenuItem @newItem1 21 | 22 | # Create a new menu (first menu will become the main menu) 23 | $mainMenu = New-Menu @newMenu 24 | 25 | # Add menu item to the menu named 'main' 26 | $menuItem | Add-MenuItem -Menu main 27 | 28 | $newItem2 = @{ 29 | Name = "GoToSub" 30 | DisplayName = "Go to submenu" 31 | Action = { Show-Menu -MenuName SubMenu } 32 | } 33 | 34 | # Add a menuitem to the main menu 35 | $mainMenu | New-MenuItem @newItem2 -DisableConfirm 36 | 37 | $newItemSubMenu = @{ 38 | Name = "GoToMain" 39 | DisplayName = "Go to Main Menu" 40 | Action = { Show-Menu } 41 | } 42 | 43 | # Create a new menu (sub-menu) and add a menu-item to it 44 | New-Menu -Name SubMenu -DisplayName "*** SubMenu1 ***" | New-MenuItem @newItemSubMenu -DisableConfirm 45 | 46 | clear-host 47 | Show-Menu -------------------------------------------------------------------------------- /Scripts/menu.ps1: -------------------------------------------------------------------------------- 1 | $str = @' 2 | 3 |
4 | 5 | Execption delivery address 6 | 7 |
8 |
9 | 10 | 11 | 12 |
13 |
14 | '@ 15 | 16 | $script:menuItems = New-Object -TypeName System.Collections.ArrayList 17 | 18 | $script:MenuOptions = [pscustomobject]@{ 19 | MenuFillChar = "*" 20 | MenuFillColor = [consolecolor]::White 21 | Heading = "" 22 | HeadingColor = [consolecolor]::White 23 | SubHeading = "" 24 | SubHeadingColor = [consolecolor]::White 25 | FooterText = "" 26 | FooterTextColor = [consolecolor]::White 27 | MenuItemColor = [consolecolor]::White 28 | MenuNameColor = [consolecolor]::White 29 | MaxWith = 80 30 | } 31 | 32 | $script:Menus = New-Object -TypeName System.Collections.ArrayList 33 | 34 | function New-Menu 35 | { 36 | [cmdletbinding()] 37 | Param 38 | ( 39 | [string] 40 | $Name 41 | , 42 | [string] 43 | $DisplayName 44 | , 45 | [switch] 46 | $IsMainMenu 47 | ) 48 | 49 | BEGIN 50 | { 51 | $f = $MyInvocation.InvocationName 52 | } 53 | 54 | PROCESS 55 | { 56 | $newMenu = [PSCustomObject]@{ 57 | Id =0 58 | Name = "$Name" 59 | DisplayName = "$DisplayName" 60 | IsMainMenu = $false 61 | } 62 | 63 | if ($PSBoundParameters.ContainsKey("IsMainMenu")) 64 | { 65 | $currentMainMenu = $script:Menus.Where({$_.IsMainMenu -eq $true}) 66 | if ($currentMainMenu) 67 | { 68 | Write-Error -Message "$f - You can only have one Main Menu. Currently [$($currentMainMenu.Name)] is your main menu" 69 | break 70 | } 71 | else 72 | { 73 | $newMenu.IsMainMenu = $true 74 | } 75 | } 76 | 77 | $index = $script:Menus.Add($newMenu) 78 | $script:Menus[$index].Id = $index 79 | 80 | } 81 | 82 | END 83 | { 84 | 85 | } 86 | } 87 | 88 | function Get-Menu 89 | { 90 | [cmdletbinding()] 91 | Param 92 | ( 93 | [switch] 94 | $MainMenu 95 | ) 96 | 97 | BEGIN 98 | {} 99 | 100 | PROCESS 101 | { 102 | if ($PSBoundParameters.ContainsKey("MainMenu")) 103 | { 104 | $script:Menus.Where({$_.IsMainMenu -eq $true}) 105 | } 106 | else 107 | { 108 | $script:Menus 109 | } 110 | } 111 | 112 | END 113 | {} 114 | } 115 | 116 | function Set-Menu 117 | { 118 | [cmdletbinding()] 119 | Param 120 | ( 121 | [string] 122 | $Name 123 | , 124 | [string] 125 | $DisplayName 126 | , 127 | [switch] 128 | $IsMainMenu 129 | ) 130 | 131 | BEGIN 132 | {} 133 | 134 | PROCESS 135 | { 136 | $menu = $script:Menus.Where({$_.Name -eq "$Name"}) 137 | if ($menu) 138 | { 139 | if ($PSBoundParameters.ContainsKey("DisplayName")) 140 | { 141 | $script:Menus[$menu.id].DisplayName = "$DisplayName" 142 | } 143 | 144 | if ($PSBoundParameters.ContainsKey("IsMainMenu")) 145 | { 146 | $script:Menus[$menu.id].IsMainMenu = $true 147 | # FIXME need to check if we have a main menu 148 | } 149 | else 150 | { 151 | $script:Menus[$menu.id].IsMainMenu = $false 152 | } 153 | } 154 | } 155 | 156 | END 157 | {} 158 | } 159 | 160 | 161 | function New-MenuItem 162 | { 163 | [cmdletbinding()] 164 | Param 165 | ( 166 | [string] 167 | $Name 168 | , 169 | [string] 170 | $DisplayName 171 | , 172 | [string] 173 | $Description 174 | , 175 | [scriptblock] 176 | $ActionScriptblock 177 | , 178 | [bool] 179 | $ConfirmBeforeInvoke = $true 180 | , 181 | [int] 182 | $ParentMenuID 183 | ) 184 | 185 | BEGIN 186 | { 187 | $f = $MyInvocation.InvocationName 188 | Write-Verbose -Message "$f - START" 189 | } 190 | 191 | PROCESS 192 | { 193 | $menuItem = [PSCustomObject]@{ 194 | Id = 0 195 | Name = "$Name" 196 | DisplayName = "$DisplayName" 197 | Description = "$Description" 198 | ActionScriptblock = $ActionScriptblock 199 | ConfirmBeforeInvoke = $ConfirmBeforeInvoke 200 | ParentMenu = 0 201 | } 202 | 203 | $nameConflict = $null 204 | $nameConflict = $script:menuItems.Where({$_.Name -eq "$Name"}) 205 | 206 | if ($script:Menus.count -eq 0) 207 | { 208 | New-Menu -Name MainMenu -DisplayName "Main Menu" -IsMainMenu 209 | } 210 | 211 | if (-not $nameConflict) 212 | { 213 | $MainMenu = Get-Menu -MainMenu 214 | 215 | if (-not $MainMenu) 216 | { 217 | Write-Error -Message "$f - Unable to find Main Menu" 218 | break 219 | } 220 | 221 | $menuItem.ParentMenu = $MainMenu.Id 222 | 223 | Write-Verbose -Message "$f - Adding menuItem [$Name]" 224 | 225 | $insertedId = $script:menuItems.Add($menuItem) 226 | $script:menuItems[$insertedId].Id = $insertedId 227 | } 228 | else 229 | { 230 | Write-Warning -Message "$f - Menuitem with [$Name] already exists" 231 | } 232 | } 233 | 234 | END 235 | { 236 | Write-Verbose -Message "$f- END" 237 | } 238 | } 239 | 240 | function Set-MenuItem 241 | { 242 | [cmdletbinding()] 243 | Param 244 | ( 245 | [string] 246 | $Name 247 | , 248 | [string] 249 | $DisplayName 250 | , 251 | [string] 252 | $Description 253 | , 254 | [scriptblock] 255 | $ActionScriptblock 256 | , 257 | [bool] 258 | $ConfirmBeforeInvoke 259 | ) 260 | 261 | BEGIN 262 | { 263 | $f = $MyInvocation.InvocationName 264 | Write-Verbose -Message "$f - START" 265 | } 266 | 267 | PROCESS 268 | { 269 | $menuItem = $script:menuItems.GetEnumerator().Where({$_.Name -eq "$Name"}) 270 | 271 | if(-not $menuItem) 272 | { 273 | Write-Error -Message "$f - Unable to find menuItem with name [$Name]" 274 | break 275 | } 276 | 277 | $menuItem = $script:menuItems[$menuItem.id] 278 | 279 | if($PSBoundParameters.ContainsKey("DisplayName")) 280 | { 281 | $menuItem.DisplayName = "$DisplayName" 282 | } 283 | 284 | if($PSBoundParameters.ContainsKey("Description")) 285 | { 286 | $menuItem.Description = "$Description" 287 | } 288 | 289 | if($PSBoundParameters.ContainsKey("ActionScriptblock")) 290 | { 291 | $menuItem.ActionScriptblock = $ActionScriptblock 292 | } 293 | 294 | if($PSBoundParameters.ContainsKey("ConfirmBeforeInvoke")) 295 | { 296 | $menuItem.ConfirmBeforeInvoke = $ConfirmBeforeInvoke 297 | } 298 | 299 | } 300 | 301 | END 302 | { 303 | Write-Verbose -Message "$f- END" 304 | } 305 | } 306 | 307 | function Get-MenuItem 308 | { 309 | [cmdletbinding(DefaultParameterSetName='none')] 310 | Param 311 | ( 312 | [Parameter(ParameterSetName="ByName")] 313 | [string[]] 314 | $Name = "*" 315 | , 316 | [Parameter(ValueFromPipeline,ParameterSetName="ById")] 317 | [int[]] 318 | $Id 319 | ) 320 | 321 | BEGIN 322 | { 323 | $f = $MyInvocation.InvocationName 324 | Write-Verbose -Message "$f - START" 325 | } 326 | 327 | PROCESS 328 | { 329 | if (-not $PSBoundParameters.ContainsKey("Name") -and -not $PSBoundParameters.ContainsKey("Id")) 330 | { 331 | $script:menuItems 332 | } 333 | 334 | if ($PSBoundParameters.ContainsKey("Name")) 335 | { 336 | foreach ($menuName in $Name) 337 | { 338 | $script:menuItems.Where({$_.Name -like $Name}) 339 | } 340 | } 341 | 342 | if ($PSBoundParameters.ContainsKey("Id")) 343 | { 344 | $script:menuItems.Where({$_.Id -in $id}) 345 | } 346 | } 347 | 348 | END 349 | { 350 | Write-Verbose -Message "$f- END" 351 | } 352 | } 353 | 354 | function Show-Menu 355 | { 356 | [cmdletbinding()] 357 | Param 358 | ( 359 | [int] $InvokeItem 360 | , 361 | [switch] 362 | $Force 363 | , 364 | [int] 365 | $MenuID 366 | ) 367 | 368 | BEGIN 369 | { 370 | $f = $MyInvocation.InvocationName 371 | Write-Verbose -Message "$f - START" 372 | 373 | $mainMenu = Get-Menu -MainMenu 374 | 375 | if (-not $mainMenu) 376 | { 377 | Write-Warning -Message "Please add a menu first" 378 | break 379 | } 380 | } 381 | 382 | PROCESS 383 | { 384 | if ($PSBoundParameters.ContainsKey("InvokeItem")) 385 | { 386 | $menuSelected = $script:menuItems.Where({$_.ID -eq $InvokeItem}) 387 | 388 | if($menuSelected) 389 | { 390 | if ($menuSelected.ConfirmBeforeInvoke) 391 | { 392 | if (-not $Force) 393 | { 394 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 395 | If ($Continue -ne "y") 396 | { 397 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 398 | break 399 | } 400 | } 401 | } 402 | 403 | if ($menuSelected.ActionScriptblock) 404 | { 405 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 406 | $menuSelected.ActionScriptblock.Invoke() 407 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 408 | break 409 | } 410 | } 411 | } 412 | } 413 | 414 | END 415 | { 416 | $menuLines = New-Object -TypeName System.Collections.ArrayList 417 | $maxWith = $script:MenuOptions.MaxWith 418 | 419 | function Get-MenuLine 420 | { 421 | Param 422 | ( 423 | [string] 424 | $Text 425 | , 426 | [consolecolor] 427 | $Color = [System.ConsoleColor]::White 428 | ) 429 | [pscustomobject]@{ 430 | Text = "$Text" 431 | Color = $color 432 | } 433 | } 434 | 435 | if ($PSBoundParameters.ContainsKey("MenuID")) 436 | { 437 | $mainMenu = Get-Menu | where Id -eq $MenuID 438 | if (-not $mainMenu) 439 | { 440 | Write-Error -Exception "$f - Could not find menu with ID [$MenuID]" 441 | } 442 | } 443 | 444 | $lineFeed = ""#[System.Environment]::NewLine 445 | $menuString = ($script:MenuOptions.MenuFillChar * ($maxWith - 2)) 446 | $null = $menuLines.Add((Get-MenuLine -Text $menuString -color $script:MenuOptions.MenuFillColor)) 447 | 448 | $menuNameLength = $mainMenu.DisplayName.length 449 | $menuNameBlanks = (($maxWith - 2) - $menuNameLength) / 2 450 | $menuName = " " * $menuNameBlanks + $mainMenu.DisplayName 451 | $menuName += " " * (($maxWith - 1) - $menuName.Length - 1) 452 | $null = $menuLines.Add((Get-MenuLine -Text $menuName -Color $script:MenuOptions.MenuNameColor)) 453 | 454 | 455 | #$menuEmptyLine = $script:MenuOptions.MenuFillChar + (" " * 78) + $script:MenuOptions.MenuFillChar #+ $lineFeed 456 | $menuEmptyLine = (" " * ($maxWith - 2)) #+ $script:MenuOptions.MenuFillChar 457 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 458 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 459 | 460 | $menuHeadingLength = $script:MenuOptions.Heading.Length 461 | $menuHeadingBlanks = (($maxWith - 2) - $menuHeadingLength) / 2 462 | 463 | $menuHeading = (" " * $menuHeadingBlanks) + $script:MenuOptions.Heading #+ (" " * $menuHeadingBlanks) + $script:MenuOptions.MenuFillChar #+ $lineFeed 464 | $menuHeading += (" " * (($maxWith - 1) - $menuHeading.length - 1)) 465 | $null = $menuLines.Add((Get-MenuLine -Text $menuHeading -color $script:MenuOptions.HeadingColor)) 466 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 467 | 468 | $menuSubHeadingLength = $script:MenuOptions.SubHeading.Length 469 | $menuSubHeadingBlanks = (($maxWith - 2) - $menuSubHeadingLength) / 2 470 | $menuSubHeading = (" " * $menuSubHeadingBlanks) + $script:MenuOptions.SubHeading #+ (" " * $menuSubHeadingBlanks) + $script:MenuOptions.MenuFillChar# + $lineFeed 471 | $menuSubHeading += (" " * (($maxWith - 1) - $menuSubHeading.length - 1)) 472 | $null = $menuLines.Add((Get-MenuLine -Text $menuSubHeading -color $script:MenuOptions.SubHeadingColor)) 473 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 474 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 475 | 476 | $menuString = ($script:MenuOptions.MenuFillChar * ($maxWith - 2)) 477 | $null = $menuLines.Add((Get-MenuLine -Text $menuString -color $script:MenuOptions.MenuFillColor)) 478 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 479 | 480 | foreach($item in $script:menuItems) 481 | { 482 | $menuItemLength = $item.Displayname.length 483 | #$menuItemBlanks = (70 - $menuItemLength) / 2 484 | #$menuItem = $script:MenuOptions.MenuFillChar + (" " * $menuItemBlanks) + $item.Displayname + (" " * $menuItemBlanks) + $script:MenuOptions.MenuFillChar #+ $lineFeed 485 | $menuItem = " " + "$($item.Id). $($item.DisplayName)" 486 | $menuitem += " " * (($maxWith - 1) - $menuItem.length - 1) 487 | $null = $menuLines.Add((Get-MenuLine -Text $menuItem -Color $script:MenuOptions.MenuItemColor)) 488 | } 489 | 490 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 491 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 492 | 493 | $menuFooterLength = $script:MenuOptions.FooterText.Length 494 | $menuFooterBlanks = (($maxWith - 2) - $menuFooterLength) / 2 495 | $menuFooter = (" " * $menuFooterBlanks) + $script:MenuOptions.FooterText #+ (" " * $menuFooterBlanks) 496 | $menuFooter += (" " * (($maxWith - 1) - $menuFooter.length - 1)) 497 | $null = $menuLines.Add((Get-MenuLine -Text $menuFooter -color $script:MenuOptions.FooterTextColor)) 498 | $null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 499 | 500 | $menuEndString = ($script:MenuOptions.MenuFillChar * ($maxWith - 2)) + $lineFeed 501 | $null = $menuLines.Add((Get-MenuLine -Text $menuEndString -color $script:MenuOptions.MenuFillColor)) 502 | #$null = $menuLines.Add((Get-MenuLine -Text $menuEmptyLine -color $script:MenuOptions.MenuFillColor)) 503 | 504 | foreach ($line in $menuLines) 505 | { 506 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor -NoNewline 507 | Write-Host -Object $line.Text -ForegroundColor $line.Color -NoNewline 508 | Write-Host -Object $script:MenuOptions.MenuFillChar -ForegroundColor $script:MenuOptions.MenuFillColor 509 | } 510 | $userSelection = (Read-Host -Prompt "Please enter number to execute action") 511 | 512 | try 513 | { 514 | $actionItemSelectionIndex = [int]$userSelection 515 | } 516 | catch 517 | { 518 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 519 | break 520 | } 521 | 522 | $menuSelected = $script:menuItems.Where({$_.ID -eq $actionItemSelectionIndex}) 523 | 524 | if ($menuSelected) 525 | { 526 | if ($menuSelected.ConfirmBeforeInvoke) 527 | { 528 | $Continue = Read-Host -Prompt "Are you sure you want to execute [$($menuSelected.Name)] Y/N?" 529 | If ($Continue -ne "y") 530 | { 531 | Write-Host -Object "Execution aborted" -ForegroundColor DarkYellow 532 | break 533 | } 534 | } 535 | if ($menuSelected.ActionScriptblock) 536 | { 537 | Write-Host -Object "Invoking [$($menuSelected.Name)]" -ForegroundColor DarkYellow 538 | $menuSelected.ActionScriptblock.Invoke() 539 | Write-Host -Object "Invoke DONE!" -ForegroundColor DarkYellow 540 | } 541 | } 542 | else 543 | { 544 | Write-Host -Object "Menuitem not found [$userSelection]" -ForegroundColor DarkYellow 545 | } 546 | Write-Verbose -Message "$f- END" 547 | } 548 | } 549 | 550 | function Test-Invoke1 551 | { 552 | $F = $MyInvocation.InvocationName 553 | write-verbose -Message "$f - START" -Verbose 554 | } 555 | 556 | 557 | 558 | function Set-MenuOption 559 | { 560 | [cmdletbinding()] 561 | Param 562 | ( 563 | [string] 564 | $MenuFillChar = "*" 565 | , 566 | [ConsoleColor] 567 | $MenuFillColor = [consolecolor]::white 568 | , 569 | [string] 570 | $Heading = "[Heading not set]" 571 | , 572 | [ConsoleColor] 573 | $HeadingColor = [consolecolor]::white 574 | , 575 | [string] 576 | $SubHeading = "[SubHeading not set ]" 577 | , 578 | [ConsoleColor] 579 | $SubHeadingColor = [consolecolor]::white 580 | , 581 | [string] 582 | $FooterText = "[FooterText not set]" 583 | , 584 | [ConsoleColor] 585 | $FooterTextColor = [consolecolor]::white 586 | , 587 | [consolecolor] 588 | $MenuItemColor = [consolecolor]::white 589 | , 590 | [consolecolor] 591 | $MenuNameColor = [consolecolor]::white 592 | , 593 | [int] 594 | $MaxWith = 80 595 | ) 596 | 597 | BEGIN 598 | { 599 | $f = $MyInvocation.InvocationName 600 | Write-Verbose -Message "$f - START" 601 | 602 | foreach ($key in $PSBoundParameters.Keys) 603 | { 604 | $script:MenuOptions.$key = $PSBoundParameters.$key 605 | } 606 | 607 | if([string]::IsNullOrEmpty($script:MenuOptions.FooterText)) 608 | { 609 | 610 | $script:MenuOptions.FooterText = "$(Get-date) - Running as $([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" 611 | } 612 | } 613 | 614 | END 615 | { 616 | Write-Verbose -Message "$f- END" 617 | } 618 | } 619 | 620 | function Get-MenuOption 621 | { 622 | [cmdletbinding()] 623 | Param 624 | () 625 | 626 | $script:MenuOptions 627 | } 628 | 629 | $function = "" ; Remove-Module -Name tmp -ErrorAction SilentlyContinue 630 | foreach($action in $script:menuItems) 631 | { 632 | $funcs = New-Object -TypeName System.Collections.ArrayList 633 | 634 | $function += "function $($action.id) {$($action.ActionScriptblock.ToString())};" 635 | Write-Verbose $function -Verbose 636 | 637 | } 638 | 639 | $sb = [scriptblock]::Create($function) 640 | New-Module -Name tmp -ScriptBlock $sb | Import-Module -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | Param( 3 | [string]$ModuleFileName = 'CliMenu.psm1' 4 | , 5 | [switch]$Major 6 | , 7 | [switch]$Minor 8 | , 9 | [switch]$LoadModule 10 | , 11 | [string]$description = 'Easily build and edit CLI menus in Powershell' 12 | ) 13 | 14 | END 15 | { 16 | 17 | write-verbose -message "ModuleFilename = $moduleFileName" 18 | Set-location -Path "$PSScriptRoot" -ErrorAction SilentlyContinue 19 | 20 | $F = $MyInvocation.InvocationName 21 | Write-Verbose -Message "$F - Starting build, getting files" 22 | 23 | $fileList = Get-ChildItem -Filter .\functions\*.ps1 | where name -NotLike "*Tests*" 24 | 25 | #$ModuleName = (Get-ChildItem -Path $ModuleFileName -ErrorAction SilentlyContinue).BaseName 26 | $ModuleName = $ModuleFileName.Split('.') | Select-Object -first 1 27 | Write-Verbose -Message "$f - Modulename is $ModuleName" 28 | 29 | if([string]::IsNullOrEmpty($moduleName)) 30 | { 31 | write-warning -message "Modulename is null or empty" 32 | break 33 | } 34 | 35 | $ExportedFunctions = New-Object System.Collections.ArrayList 36 | $fileList | foreach { 37 | Write-Verbose -Message "$F - Function = $($_.BaseName) added" 38 | $null = $ExportedFunctions.Add($_.BaseName) 39 | } 40 | 41 | $ModuleLevelFunctions = $null 42 | 43 | foreach($function in $ModuleLevelFunctions) 44 | { 45 | Write-Verbose -Message "$f - Checking function $function" 46 | if($ExportedFunctions -contains $function) 47 | { 48 | write-verbose -Message "$f - Removing function $function from exportlist" 49 | $ExportedFunctions.Remove($function) 50 | } 51 | else 52 | { 53 | Write-Verbose -Message "$f - Exported functions does not contain $function" 54 | } 55 | } 56 | 57 | Write-Verbose -Message "$f - Constructing content of module file" 58 | [string]$ModuleFile = "" 59 | foreach($file in $fileList) 60 | { 61 | $filecontent = Get-Content -Path $file.FullName -Raw -Encoding UTF8 62 | $filecontent = "$filecontent`n`n" 63 | $ModuleFile += $filecontent 64 | } 65 | [System.Version]$ver = $null 66 | 67 | if((Test-Path -Path $moduleFileName -ErrorAction SilentlyContinue) -eq $true) 68 | { 69 | Write-Verbose -Message "$f - Getting version info" 70 | Import-Module -Name ".\$ModuleName.psd1" -Verbose:$false 71 | $ver = (Get-Module $Modulename).Version 72 | Remove-Module $ModuleName -Verbose:$false 73 | Write-Verbose -Message "$f - Removing previous version of $ModuleFileName" 74 | Remove-Item -Path $ModuleFileName 75 | } 76 | 77 | function Get-NextVersion 78 | { 79 | [cmdletbinding()] 80 | [outputtype([System.Version])] 81 | Param( 82 | [System.Version]$CurrentVersion 83 | , 84 | [switch]$Major 85 | , 86 | [switch]$Minor 87 | ) 88 | [System.Version]$newVersion = $null 89 | $f = $MyInvocation.InvocationName 90 | Write-Verbose -Message "$f - START" 91 | 92 | if($Major) 93 | { 94 | Write-Verbose -Message "$F - Bumping Major version" 95 | $build = $CurrentVersion.Build 96 | $ma = $CurrentVersion.Major + 1 97 | $mi = $CurrentVersion.Minor 98 | } 99 | 100 | if($Minor) 101 | { 102 | Write-Verbose -Message "$f - Bumping Minor version" 103 | $build = $CurrentVersion.Build 104 | $ma = $CurrentVersion.Major 105 | $mi = $CurrentVersion.Minor + 1 106 | } 107 | 108 | if($Minor -and $Major) 109 | { 110 | Write-Verbose -Message "$f - Bumping Major and Minor version" 111 | $build = $CurrentVersion.Build 112 | $ma = $CurrentVersion.Major + 1 113 | $mi = $CurrentVersion.Minor + 1 114 | } 115 | 116 | if(-not $Minor -and -not $Major) 117 | { 118 | Write-Verbose -Message "$f - Bumping build version" 119 | $build = $CurrentVersion.Build + 1 120 | $ma = $CurrentVersion.Major 121 | $mi = $CurrentVersion.Minor 122 | } 123 | 124 | $newVersion = New-Object System.Version("$Ma.$Mi.$build.0") 125 | return $newVersion 126 | } 127 | 128 | if(-not $ver) 129 | { 130 | Write-Verbose -Message "$f - No previous version found, creating new version" 131 | $ver = New-Object System.Version("1.0.0.0") 132 | } 133 | 134 | if($Major) 135 | { 136 | $ver = Get-NextVersion -CurrentVersion $ver -Major 137 | } 138 | 139 | if($Minor) 140 | { 141 | $ver = Get-NextVersion -CurrentVersion $ver -Minor 142 | } 143 | 144 | if($Minor -and $Major) 145 | { 146 | $ver = Get-NextVersion -CurrentVersion $ver -Minor -Major 147 | } 148 | 149 | if(-not $Minor -and -not $Major) 150 | { 151 | Write-Verbose -Message "$f - Defaults to bump build version" 152 | $ver = Get-NextVersion -CurrentVersion $ver 153 | } 154 | 155 | $versionString = $ver.ToString() 156 | Write-Verbose -Message "$f - New version is $versionString" 157 | 158 | Write-Verbose -Message "$f - Writing contents to modulefile" 159 | Set-Content -Path $ModuleFileName -Value $ModuleFile -Encoding UTF8 160 | 161 | $ManifestName = "$((Get-ChildItem -Path $ModuleFileName -ErrorAction SilentlyContinue).BaseName).psd1" 162 | Write-Verbose -Message "$f - ManifestfileName is $ManifestName" 163 | 164 | if((Test-Path -Path $ManifestName -ErrorAction SilentlyContinue) -eq $true) 165 | { 166 | Write-Verbose -Message "$f - Removing previous version of $ManifestName" 167 | Remove-Item -Path $ManifestName 168 | } 169 | 170 | $FormatsToProcess = New-Object -TypeName System.Collections.ArrayList 171 | foreach($file in (Get-ChildItem -Path "$PSScriptRoot\formats")) 172 | { 173 | Write-Verbose -Message "Adding formats file $($file.FullName)" 174 | $null = $FormatsToProcess.Add($file.FullName) 175 | } 176 | 177 | Write-Verbose -Message "$f - Creating manifestfile" 178 | 179 | $newModuleManifest = @{ 180 | Path = "$PSScriptRoot\$ManifestName" 181 | Author = "Tore Grøneng @toregroneng tore@firstpoint.no" 182 | Copyright = "(c) 2015 Tore Grøneng @toregroneng tore@firstpoint.no" 183 | CompanyName = "Firstpoint AS" 184 | ModuleVersion = $ver.ToString() 185 | FunctionsToExport = $ExportedFunctions 186 | RootModule = "$ModuleFileName" 187 | Description = "$description" 188 | PowerShellVersion = "4.0" 189 | ProjectUri = "https://github.com/torgro/cliMenu" 190 | FormatsToProcess = $FormatsToProcess.ToArray() 191 | } 192 | 193 | New-ModuleManifest @newModuleManifest 194 | 195 | Write-Verbose -Message "$f - Reading back content to convert to UTF8 (content management tracking)" 196 | Set-Content -Path $ManifestName -Value (Get-Content -Path $ManifestName -Raw) -Encoding UTF8 197 | 198 | $Answer = "n" 199 | 200 | if(-not $LoadModule) 201 | { 202 | $Answer = Read-Host -Prompt "Load module $ModuleName? (Yes/No)" 203 | } 204 | 205 | if($Answer -eq "y" -or $Answer -eq "yes" -or $LoadModule) 206 | { 207 | Write-Verbose -Message "$f - Loading module" 208 | if(Test-Path -Path $ManifestName) 209 | { 210 | Import-Module $PSScriptRoot\$ManifestName 211 | } 212 | else 213 | { 214 | Write-Warning -Message "Modulefile $ManifestName not found, module not imported" 215 | } 216 | } 217 | else 218 | { 219 | Write-Verbose -Message "$f - Module not loaded" 220 | } 221 | 222 | Write-Verbose -Message "$f - END" 223 | } 224 | -------------------------------------------------------------------------------- /publish.ps1: -------------------------------------------------------------------------------- 1 | [cmdletbinding()] 2 | Param( 3 | [Parameter(Mandatory)] 4 | [string] 5 | $APIkey 6 | ) 7 | 8 | $tags = @( 9 | "Menu" 10 | , 11 | "Cli" 12 | , 13 | "Sub-Menu" 14 | , 15 | "Main-Menu" 16 | , 17 | "Show-Command" 18 | , 19 | "Console" 20 | , 21 | "Menus" 22 | , 23 | "Main" 24 | , 25 | "Sub" 26 | , 27 | "User" 28 | , 29 | "Menu" 30 | ) 31 | 32 | $fileList = Get-ChildItem -Filter .\functions\*.ps1 | where name -NotLike "*Tests*" 33 | $ExportedFunctions = New-Object System.Collections.ArrayList 34 | $fileList | foreach { 35 | $null = $ExportedFunctions.Add($_.BaseName) 36 | } 37 | 38 | Update-ModuleManifest -Path .\CliMenu.psd1 -Tags $tags -FunctionsToExport $ExportedFunctions 39 | 40 | Publish-Module -NuGetApiKey $APIkey -Name .\CliMenu.psd1 --------------------------------------------------------------------------------