Michael A. Cleverly 23 Nov 2002 -- a natural follow-up to wiki-reaper (which solved the problem of cutting & pasting large chunks of code from the Wiki) is a way to edit the Wiki using a regular text editor instead of a big <textarea> box in a web browser.
Here's my Saturday morning project. It works for me under Linux. I haven't tried Windows (the $tcl_platform(platform) != "unix" code below is just my best guess as to what should work). Feel free to fix/enhance it.
Here's a related page How to edit Wikit pages with Emacs
#!/bin/sh # -*- tcl -*- \ exec tclsh8.3 $0 ${1+"$@"} package require Tcl 8.3 if {![catch { package require nstcl-html }] && ![catch { package require nstcl-http }]} { namespace import nstcl::* } else { package require http proc ns_geturl {url} { set conn [http::geturl $url] set html [http::data $conn] http::cleanup $conn return $html } proc ns_urlencode {string} { set allowed_chars {[a-zA-Z0-9]} set encoded_string "" foreach char [split $string ""] { if {[string match $allowed_chars $char]} { append encoded_string $char } else { scan $char %c ascii append encoded_string %[format %02x $ascii] } } return $encoded_string } proc util_httppost {url formvars} { set conn [http::geturl $url -query $formvars] set html [http::data $conn] http::cleanup $conn return $html } } proc environment {default args} { foreach arg $args { if {[info exists ::env($arg)] && [string length $::env($arg)]} { return $::env($arg) } } return $default } proc editor {} { if {$::tcl_platform(platform) == "unix"} { set default vi } else { set default notepad } return [environment $default EDITOR Editor editor] } proc temp_directory {} { return [environment [pwd] TEMP Temp temp TMP Tmp tmp TEMPDIR Tempdir] } proc abort {reason} { puts stderr $reason exit 1 } proc editfile {file} { global tcl_platform if {$tcl_platform(platform) == "unix"} { package require Expect log_user 0 spawn [editor] $file interact } else { exec [editor] $file } } proc main {page} { set wiki https://wiki.tcl-lang.org set url $wiki/[ns_urlencode $page] # Fetch the wiki page if {[catch { ns_geturl $url } html]} { abort "Error retrieveing $url: $html" } # find the edit URL if {![regexp {Edit <a href="((\d+)@)">} $html => edit page_num]} { abort "$url is not an editable page" } # Retrieve the edit page <form> if {[catch { ns_geturl https://wiki.tcl-lang.org/$edit } html]} { abort "Error fetching https://wiki.tcl-lang.org/$edit: $html" } set vars [list] # Figure out where the form is supposed to POST results to if {![regexp -nocase -- {form\s+[^<]*action="([^"]+)"} $html => action]} { abort "Couldn't figure out where to POST the edit to" } # normalize the action URL if it's not an absolute link switch -regexp -- $action { {(?i)^http} {} {^/*} { set action https://wiki.tcl-lang.org$action } default { set action https://wiki.tcl-lang.org/$action } } # Snag the existing content so we can edit it if {![regexp {(?i)(<textarea[^>]+>)([^<]*)<} $html => textarea content]} { abort "Couldn't find any editable content" } # Figure out what formvar we're supposed to pass the contents back as if {![regexp {(?i)name=['"]?([^'"\s]+)['"]} $textarea => textvar]} { abort "Couldn't figure out the name of the <textarea>" } # find and propagate all (hidden) form variables/defaults foreach input [regexp -inline -all -- {(?i)<input\s+[^>]+>} $html] { if {[regexp -nocase -- {name=['"]?([^'"\s]+)} $input => name]} { if {![regexp {(?i)value=['"]([^'"]+)['"]} $input => value]} { set value "" } lappend vars [ns_urlencode $name]=[ns_urlencode $value] } } # unescape HTML entities set content [string map [list "&" & \ "<" < \ ">" > \ """ \"] $content] # compute the name of the temporary file to use set tmpfile [file join [temp_directory] wiki-$page_num.tmp] # file shouldn't exist ... if {[file exists $tmpfile]} { abort "Another edit already in progress? $tmpfile already exists" } # save existing wiki content to the temporary file if {[catch { set fp [open $tmpfile w] puts -nonewline $fp $content close $fp } problem]} { catch { file delete -force $tmpfile } abort "Error writing $tmpfile ($problem)" } # edit the file if {[catch { editfile $tmpfile } problem]} { catch { file delete -force $tmpfile } abort "Error occured trying to start the editor: $problem" } # read if {[catch { set fp [open $tmpfile] set new_content [read $fp] close $fp file delete -force $tmpfile } problem]} { catch { file delete -force $tmpfile } abort "Error reading edited content: $problem" } # were there ANY changes at all? if {[string equal $content $new_content]} { # don't barf if stdout is closed catch { puts "No changes were made" } exit 0 } lappend vars $textvar=[ns_urlencode $new_content] # POST the edited version if {[catch { util_httppost $action [join $vars &] } html]} { abort "Error uploading changes: $html" } switch -regexp -- $html { {Page saved\.\.\.} { set result "Wiki updated" } {Edit conflict} { set result "Wiki NOT updated due to edit conflict" } default { set result "Warning!: Not sure if the Wiki was updated or not..." } } # don't barf if stdout is closed catch { puts $result } } if {[llength $argv] != 1} { abort "usage: wiki-edit page" } else { main [lindex $argv 0] }
MHo Using a variable instead of a fixed URL would enable this app to be used with our own wikis. Can't get uploading to work yet... Could it be because of sending action=save & action=cancel with the data??? So I applied the following hack and it works:
# find and propagate all (hidden) form variables/defaults foreach input [regexp -inline -all -- {(?i)<input\s+[^>]+>} $html] { if {[regexp -nocase -- {name=['"]?([^'"\s]+)} $input => name]} { if {![regexp {(?i)value=['"]([^'"]+)['"]} $input => value]} { set value "" } ### hier mehrmals action='...', daher folgender HACK: if {"$name" == "Action" && "$value" == "Cancel"} { } else { lappend vars [ns_urlencode $name]=[ns_urlencode $value] } } }
Also, the test if a page upload succeeded don't work (anymore...)?!