Powershell GUI fronted (WPF) to run categorized console scripts

โŒˆโŒ‹ โŽ‡ branch:  ClickyColoury


Check-in [e0133cf4d1]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Remove unused Get-Machine, introduce general Read-HostCached alias. Merge new sectioned/submenu Print-Menu implementation.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e0133cf4d1413fa18432e7715104ee7f704b0005
User & Date: mario 2018-05-16 18:02:08
Context
2018-05-16
18:03
Fix plugin filters (api check for user plugins, separate NotFilter). Better init plugin stacktrace. check-in: 04cf6b4bca user: mario tags: trunk
18:02
Remove unused Get-Machine, introduce general Read-HostCached alias. Merge new sectioned/submenu Print-Menu implementation. check-in: e0133cf4d1 user: mario tags: trunk
18:00
Move input form expander into scrollviewer check-in: d90fd410d2 user: mario tags: trunk
Changes

Changes to modules/menu.psm1.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22

23

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38


39







40

41
42
43
44
45
46

47

48
49







50
51
52
53
54
55
56
57
...
162
163
164
165
166
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
...
193
194
195
196
197
198
199


















200
201
202
203
204





















205
206
207
208
209





210
211
212





213
214

215
216
217
218
219
220
221
222
223
...
228
229
230
231
232
233
234

235
236
237
238
239
240
241
...
255
256
257
258
259
260
261

262
263
264
265

266
267
268
269
270
271
272







273

274
275

276
277




278
279
280
281
282

# api: ps
# type: functions
# title: Utility code
# description: Output and input shortcuts, screen setup, menu handling, meta extraction
# doc: http://fossil.include-once.org/clickycoloury/wiki/plugin+meta+data
# version: 0.8.7
# license: PD
# category: misc
# author: mario
# config:
#    { name: cfg.hidden, type: bool, value: 0, description: also show hidden menu entries in CLI version }
# status: beta
# priority: core
#
# A couple of utility funcs:
#  ยท Get-Machine        โ†’ Read-Host "machine"

#  ยท echo_n             โ†’ echo wo/ CRLF
#  ยท Edit-Config        โ†’ edit config file
#  ยท Extrac-PluginMeta  โ†’ tokenize comment fields
#  ยท preg_match_all     โ†’ PHP-esque regex matching
#  ยท Init-Screen        โ†’ print script summary
#  ยท Print-Menu         โ†’ output $menu 

#  ยท Print-MenuHelp     โ†’ show help= entries

#  ยท Process-Menu       โ†’ input/run $menu prompt
#
# Load with:
#  Import-Module. ".\modules\menu.psm1"


#-- get $machine
function Get-Machine() {
    [CmdletBinding()]
    Param($current = $global:machine)
    # Ask for '$machine'
    Write-Host -N -f Yellow '$machine'
    # Add (default/last)
    if ($current) {
        Write-Host -N -f Gray "($current)"


    }







    Write-Host -N -f Yellow ': '

    $new = (Read-Host).trim().toUpper()
    if (!$new) {
        return $current
    }
    # Ping test
    if (!(Test-Connection -ComputerName $new -Count 1 -BufferSize 128 -Quiet -ErrorAction SilentlyContinue)) {

        Write-Host -f Red " โ†’ not online"

    }
    $global:machine = $new







    return $new
}

#-- opens notepad for editing a list/csv file in-place
function Get-NotepadCSV() {
    Param($text="", $EDITOR=$env:EDITOR)
    $tmpfn = [IO.Path]::GetTempFileName()
    $text | Out-File $tmpfn -Encoding UTF8
................................................................................
    }
    return $meta  # or return as (New-Object PSCustomObject -Prop $meta)
}


#-- script header
function Init-Screen() {
    param($x=80,$y=45)
    #-- screen size better than `mode con` as it retains scrolling:
    if ($host.Name -match "Console") {
        $con = $host.UI.RawUI
        $buf = $con.BufferSize; $buf.height = 16*$y; $buf.width = $x; $con.BufferSize = $buf;
        $win = $con.WindowSize; $win.height =    $y; $win.width = $x; $con.WindowSize = $win;

    }
    #-- header
    $meta = $cfg.main
    Write-Host -b DarkBlue -f White ("  {0,-60} {1,15} " -f $meta.title, $meta.version)
    Write-Host -b DarkBlue -f Gray  ("  {0,-60} {1,15} " -f $meta.description, $meta.category)
    try { $host.UI.RawUI.WindowTitle = $meta.title } catch { }
}
................................................................................
        $str.substring($from, $to)
    }
    else {
        $str
    }
}



















#-- Write out menu list (sorted, 3 columns, with category headers)
function Print-Menu() {
    param($menu, $cat=".+", $last_cat="", $i=0)
    # group by category
    $ls = Sort-Menu ($menu | ? { $_.title -and $_.key -and ($_.category -match $cat) -and ((!$cfg.hidden) -or !$_.hidden) })





















    $ls | % {
        if ($last_cat -ne $_.category) {
            if ($line) { Write-Host ""}
            Write-Host -f Black ("     {0,-74}" -f ($last_cat = $_.category))
            $i = 0





        }
        $line = (($i++) % 3 -ne 2)
        Write-Host -N -f Green ("{0,4}" -f (substr $_.key.split("|")[0] 0 4))





        Write-Host -N -f DarkRed  ("โ†’")
        Write-Host -N:$line -f White ("{0,-21}" -f (substr $_.title 0 21))

    }
    echo ""
}

#-- print help= entries from $menu (โ†’ not very pretty)
function Print-MenuHelp($menu) {
    $menu | ? { $_.title -and $_.key } | % {
        Write-Host -N -f Green (" " + $_.key.split("|")[0..2] -join ", ").PadRight(15)
        Write-Host -f White (" " + $_.title)
................................................................................
#-- Invoked on one menu entry โ†’ executes .command, or .func, or loads .fn script
filter Process-MenuTask() {
    Param($params)
    $host.UI.RawUI.WindowTitle = "MultiTool โ†’ $($_.title)"
    Write-Host -b DarkBlue -f Cyan ("{0,-60} {1,18}" -f $_.title, $_.version)
    echo ""
    #-- commands or function

    try {
        if ($_.command) {
            Invoke-Expression $_.command  # no options
        }
        elseif ($_.func) {
            Invoke-Expression "$($_.func) $($params)" # pass optional flags
        }
................................................................................
    }
    catch {
        Write-Host -b DarkRed -f White ($_ | Out-String)
        $Error.Clear()
    }
    $host.UI.RawUI.WindowTitle = "MultiTool"
}


#-- Promp/input loop (REPL)
function Process-Menu() {
    param($menu, $prompt="Func")


    #-- prompt+exec loop
    while ($True) {
        Write-Host -N -f Yellow "$prompt> "
        $which = (Read-Host).trim()
        if ($which -match "^([\w.-]+)(?:\b|\s|$)(.*)$") { $params = $matches[2] } else { $params = $null }








        # find according menu entry: run func or expression

        $menu  |  ? { $_.key -and $which -match "^($($_['key']))\b" }  |  % { 
            while ($true) {

                $_ | Process-MenuTask $params
                if ((!$_.repeat) -or ((Read-Host "Wiederholen?") -notmatch "^[jy1rw]")) { break; }




            }
        }
    }
}







|









|
>


|



>

>






<
<
|
|
|
|
|
|
|
>
>

>
>
>
>
>
>
>
|
>
|
<
<

<
<
>
|
>

<
>
>
>
>
>
>
>
|







 







|
|


|
|
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|

|
<
<
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
<
<
<
>
>
>
>
>
|
<
<
>
>
>
>
>
|
<
>

<







 







>







 







>




>




|


>
>
>
>
>
>
>
|
>
|
<
>
|
<
>
>
>
>





>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32


33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


53


54
55
56
57

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
...
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258




259
260
261
262
263
264


265
266
267
268
269
270

271
272

273
274
275
276
277
278
279
...
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
...
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341

342
343

344
345
346
347
348
349
350
351
352
353
# api: ps
# type: functions
# title: Utility code
# description: Output and input shortcuts, screen setup, menu handling, meta extraction
# doc: http://fossil.include-once.org/clickycoloury/wiki/plugin+meta+data
# version: 0.9.0
# license: PD
# category: misc
# author: mario
# config:
#    { name: cfg.hidden, type: bool, value: 0, description: also show hidden menu entries in CLI version }
# status: beta
# priority: core
#
# A couple of utility funcs:
#  ยท Read-HostCached    โ†’ Read-Host alias
#  ยท Get-NotepadCSV     โ†’ notepad Read-Host (long input)
#  ยท echo_n             โ†’ echo wo/ CRLF
#  ยท Edit-Config        โ†’ edit config file
#  ยท Extract-PluginMeta โ†’ tokenize comment fields
#  ยท preg_match_all     โ†’ PHP-esque regex matching
#  ยท Init-Screen        โ†’ print script summary
#  ยท Print-Menu         โ†’ output $menu 
#  ยท Print-MenuSection  โ†’ list one category
#  ยท Print-MenuHelp     โ†’ show help= entries
#  ยท Process-MenuTask   โ†’ invoke tools/scripts
#  ยท Process-Menu       โ†’ input/run $menu prompt
#
# Load with:
#  Import-Module. ".\modules\menu.psm1"




#-- implements Read-Host with remembering previous values
$CLI_previous_values = @{}
function Read-HostCached {
    Param($prompt, $prev="")
    $alias = Get-VarsAlias $prompt
    # input variants
    if (!$prompt) {
        $r = ($Host.UI.Readline()) # (& Read-Host) 
        return $r.trim()
    }
    elseif ($e.vars | ? { $_.name -eq $prompt -and $_.type -match "text|long|notepad"}) {
        $r = Get-NotepadCSV
    }
    elseif ($CLI_previous_values[$alias]) {
        Write-Host -f Yellow $prompt -N
        Write-Host -f Gray " [" -N
        Write-Host -f Blue $CLI_previous_values[$alias] -N
        Write-Host -f Gray "]" -N
        Write-Host -f Yellow "> " -N
        $r = (& Read-Host)


    }


    else {
        Write-Host -f Yellow "$prompt> " -N
        $r = (& Read-Host)
    }

    # return and/or keep value
    if ($r.length) {
        $CLI_previous_values[$alias] = $r
    }
    else {
        $r = $CLI_previous_values[$alias]
    }
    return $r.trim()
}

#-- opens notepad for editing a list/csv file in-place
function Get-NotepadCSV() {
    Param($text="", $EDITOR=$env:EDITOR)
    $tmpfn = [IO.Path]::GetTempFileName()
    $text | Out-File $tmpfn -Encoding UTF8
................................................................................
    }
    return $meta  # or return as (New-Object PSCustomObject -Prop $meta)
}


#-- script header
function Init-Screen() {
    param($x=100,$y=45)
    #-- screen size // better than `mode con` as it retains scrolling:
    if ($host.Name -match "Console") {
        $con = $host.UI.RawUI
        $buf = $con.BufferSize; $buf.height = 16*$y; $buf.width = 2*$x; $con.BufferSize = $buf;
        $win = $con.WindowSize; $win.height =    $y; $win.width =   $x; $con.WindowSize = $win;
        $buf = $con.BufferSize; $buf.height = 16*$y; $buf.width =   $x; $con.BufferSize = $buf;
    }
    #-- header
    $meta = $cfg.main
    Write-Host -b DarkBlue -f White ("  {0,-60} {1,15} " -f $meta.title, $meta.version)
    Write-Host -b DarkBlue -f Gray  ("  {0,-60} {1,15} " -f $meta.description, $meta.category)
    try { $host.UI.RawUI.WindowTitle = $meta.title } catch { }
}
................................................................................
        $str.substring($from, $to)
    }
    else {
        $str
    }
}


# Reimplements Print-Menu, but sections the menus according to categories.
# Keeps current menu in global $menu_section
$global:menu_section = ""
$global:menu_groups = @(
    @{key="m"; category=""; title="Menu"}
    @{key="cmd"; category="cmd"; title="CMD/Windows"}
    @{key="u"; category="user"; title="User"}
    @{key="ps"; category="powershell"; title="Powershell"}
    @{key="em"; category="empirum"; title="Empirum"}
    @{key="ex"; category="exchange"; title="Exchange"}
    @{key="net"; category="network|server"; title="Network"}
    @{key="i"; category="info"; title="Info tools"}
    @{key="wmi"; category="wmi"; title="WMI queries"}
    @{key="beta"; category="beta"; title="Beta scripts"}
    @{key="extra"; category="extra|config|update|cron|misc"; title="Extra tools"}
)

#-- Write out menu list 
function Print-Menu() {
    param($menu)



    # submenus
    Print-MenuSection ($menu_groups | ? {$_.title}) "Submenus" Dark 0

    # filter menu
    if ($global:menu_section -eq "") {
        Print-MenuSection ($menu | ? { $_.shortcut } | Sort shortcut) Shortcuts Dark
        $global:menu_section = ""
        Print-MenuSection ($menu | ? { $_.category -eq "cmd" }) "> CMD <"
    }
    else {
        $ls = $menu | ? { $_.category -match "^($menu_section)" }
        Print-MenuSection $ls "> $menu_section <"
    }

    echo "" 
}

#-- individual section (submenus, favorites, current menu section)
function Print-MenuSection {
    Param(
        $ls = @(),




        $header = "", $dark="", $sort=1
    )
    $ls = $ls | ? { $_.title -and $_.key -and ((!$cfg.hidden) -or !$_.hidden) }
    if ($header) {
        Write-Host -f Blue ("`r`n{0,12}" -f $header)
    }


    if ($sort) {
        $ls = Sort-Menu $ls
    }
    ForEach ($m in $ls) {
        Write-Host -N -f "$($dark)Cyan" ("{0,10}" -f (substr $m.key.split("|")[0] 0 9))
        Write-Host -N -f DarkYellow  (" โ†’ ")

        Write-Host -f "White" ("{0,-61}" -f (substr $m.title 0 61))
    }

}

#-- print help= entries from $menu (โ†’ not very pretty)
function Print-MenuHelp($menu) {
    $menu | ? { $_.title -and $_.key } | % {
        Write-Host -N -f Green (" " + $_.key.split("|")[0..2] -join ", ").PadRight(15)
        Write-Host -f White (" " + $_.title)
................................................................................
#-- Invoked on one menu entry โ†’ executes .command, or .func, or loads .fn script
filter Process-MenuTask() {
    Param($params)
    $host.UI.RawUI.WindowTitle = "MultiTool โ†’ $($_.title)"
    Write-Host -b DarkBlue -f Cyan ("{0,-60} {1,18}" -f $_.title, $_.version)
    echo ""
    #-- commands or function
    $script:e = $_
    try {
        if ($_.command) {
            Invoke-Expression $_.command  # no options
        }
        elseif ($_.func) {
            Invoke-Expression "$($_.func) $($params)" # pass optional flags
        }
................................................................................
    }
    catch {
        Write-Host -b DarkRed -f White ($_ | Out-String)
        $Error.Clear()
    }
    $host.UI.RawUI.WindowTitle = "MultiTool"
}


#-- Promp/input loop (REPL)
function Process-Menu() {
    param($menu, $prompt="Func")
    Set-Alias -Name Read-Host -Value Read-HostCached -Scope Global

    #-- prompt+exec loop
    while ($True) {
        Write-Host -N -f Yellow "$prompt> "
        $which = (Read-Host)
        if ($which -match "^([\w.-]+)(?:\b|\s|$)(.*)$") { $params = $matches[2] } else { $params = $null }

        #-- submenu match?
        if ($m = $menu_groups | ? { $which -eq $_.key }) {
            if ($which -ne "m") { $global:menu_section = $m.category }
            Print-Menu $menu
            continue
        }

        #-- find according menu entry: run func or expression
        ForEach ($m in $menu) {
            if ($m.key -and $which -match "^($($m.key))\b") {

                do {
                    $m | Process-MenuTask $params

                }
                while ($m.repeat -and ((Read-Host "Repeat?") -match "^[jay1rw]"))
                
                break
            }
        }
    }
}