The page [Wiki Revision History Feature] provides a Tcl CGI script that adds a Wiki Revision History feature to [Wikit]. However, that script assumes the existence of a CVS repository containing the history of the pages of the Wiki. The script provided on this page, revhist.cgi, makes use of [The Tcler's Wiki] tclhist feature. The script relies on four other files. See [Wiki Revision History Feature] for more details. To deploy the script, make the following changes: * replace the initial "exec /usr/local/bin/tclkit $0" line with the path to your Tcl interpreter * replace the "set ScriptURL..." line to point to the URL of this script. * replace the "set StyleURL..." line to point to the CSS file. Then let 'er rip! ---- #!/bin/sh #\ exec /usr/local/bin/tclkit $0 source utils.tcl source format.tcl source worddiff.tcl package require http # ScriptURL defines the URL of this script set ScriptURL http://mini.net/revhist.cgi # TclhistURL defines the URL of the tclhist page set TclhistURL http://mini.net/tclhist # WikiURL defines the URL of the Wiki whose history is being displayed set WikiURL http://mini.net/tcl # StyleURL defines the URL of the stylesheet used to display this page set StyleURL /wikihist.css # WikiName is the name of the Wiki to be displayed set WikiName "Tcler's Wiki" # This proc determines whether the specified page exists. # It does this by looking for the revision history from Tclhist. # If the returned page doesn't match the expected format, there is # no revision history for this page. # Note that this proc uses the revision history rather than the # actual page -- this is done because I'm guessing that the # revision history is a less taxing operation on the server. proc pageExists {PageNumber} { global TclhistURL set URL $TclhistURL/$PageNumber* set Token [::http::geturl $URL] set PageContents [::http::data $Token] ::http::cleanup $Token # The page contents returned should start with a revision number and date. # The call to regexp returns an indication of whether it does. regexp {^\d+ \d+} $PageContents - } # This proc returns the contents of the specified revision number # of the specified Wiki page. If no revision number is specified, # the latest revision is obtained. # As coded, this obtains it from the CVS repository. If desired, # this could be recoded to get it from a tclhist archive. proc getPageContents {PageNumber {RevNumber Latest}} { global TclhistURL if {[string equal $RevNumber "Latest"]} { set URL $TclhistURL/$PageNumber } else { set URL $TclhistURL/$PageNumber.$RevNumber } set Token [::http::geturl $URL] set PageContents [::http::data $Token] ::http::cleanup $Token return $PageContents } # This proc determines the title of the specified Wiki page. proc getPageTitle {PageNumber} { # Grab a copy of the latest version of the page from CVS. # The title is found on the first line of this page. set PageContents [getPageContents $PageNumber] set TitleLine [lindex [split $PageContents \n] 0] # Now, extract the title from the title line regexp {^Title:\s*(.*)$} $TitleLine junk Title return $Title } # This procedure splits long lines into lines no longer than 80 characters proc splitlongline { line } { set maxlen 80 set thisline "" set lines [list] set opentag 0 foreach word [split $line " "] { if { $opentag || [string first "<" $word] > -1 } { set opentag 1 append thisline " $word" if { [string last ">" $word] > [string last "<" $word] } { set opentag 0 } } else { if { [string equal $thisline ""] } { append thisline $word } else { if { [string length "$thisline $word"] > $maxlen } { if { [llength $lines] > 0 } { set thisline "... $thisline" } lappend lines $thisline set thisline $word } else { append thisline " $word" } } } } if { [llength $lines] > 0 } { set thisline "... $thisline" } lappend lines $thisline return [join $lines \n] } # This proc substitutes any special characters with their HTML equivalents. proc quoteHtml {s} { string map { & & < < > > } $s } # For the given Wiki page number, this proc examines Tclhist to determine # the revision number and check-in time of all revisions. The list # returned is ordered from latest revision to earliest revision. # Every revision results in two items in the list, the first being # the minor revision number, and the second being the time that # this revision was checked in, given as the number of # seconds since the Tcl epoch. proc getRevisionList {PageNumber} { global TclhistURL set RevisionList {} set URL $TclhistURL/$PageNumber* set Token [::http::geturl $URL] set AllRevs [::http::data $Token] ::http::cleanup $Token foreach RevisionEntry [split $AllRevs \n] { # Look for all lines with revision numbers. They start with the # revision number and the date in seconds since the Tcl epoch. if {[regexp {^(\d+) (\d+)} $RevisionEntry - RevNumber RevTime]} { lappend RevisionList $RevNumber $RevTime } } return $RevisionList } # This procedure generates the revision history HTML page # for the specified Wiki page number proc genRevisionHistory {PageNumber} { global WikiURL global ScriptURL global StyleURL # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Determine the title of this page set PageTitle [quoteHtml [getPageTitle $PageNumber]] # Now, generate the page header. puts "" puts "" puts "" puts " Revision History of $PageTitle" puts " " puts " " puts " " puts "" puts "" puts "" puts "

" puts " $PageTitle" puts "

" puts " Revision History" puts "" puts "

" puts " Legend: (current) = difference with current version," puts " (last) = difference with preceding version," puts " date = that day's version" puts "

" puts "" # Now, obtain a list of the revisions and the time that they were # checked in. set RevisionList [getRevisionList $PageNumber] # Now that we've got the list of revisions, we need to generate the # revision information to display on the page. puts " " puts "" puts "" puts "" puts "" } # This proc renders the specified revision of the specified page number proc renderPageRevision {PageNumber RevNumber} { global StyleURL global ScriptURL global WikiURL set PageContents [getPageContents $PageNumber $RevNumber] # Extract the page title and check-in date, # then strip off the title, date, and site # (first 4 lines of the contents). set TitleLine [lindex [split $PageContents \n] 0] regexp {^Title:\s*(.*)$} $TitleLine junk PageTitle set PageTitle [quoteHtml $PageTitle] set PageContents [join [lrange [split $PageContents \n] 3 end] \n] # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Now, generate the page header. puts "" puts "" puts "" puts " Revision $RevNumber of $PageTitle" puts " " puts " " puts " " puts "" puts "" puts "" puts "

" puts " Revision $RevNumber of $PageTitle " puts "

" puts "" set HTMLContents\ [lindex [::Wikit::Format::StreamToHTML\ [::Wikit::Format::TextToStream $PageContents]\ "$WikiURL/"] 0] puts $HTMLContents puts "" puts "" puts "" puts "" } # This proc renders the page showing the difference between two revisions # of a page. If only one revision number is given, the difference between # the current page and the specified page is shown. proc renderPageDiff {PageNumber RevNumber {OldRevNumber "Unspecified"}} { global StyleURL global ScriptURL # If the old revision number isn't specified, we do a diff between # the current contents of the page and the specified revision. if { [string equal $OldRevNumber "Unspecified"] } { set PageContents [getPageContents $PageNumber] set OldContents [getPageContents $PageNumber $RevNumber] } else { set PageContents [getPageContents $PageNumber $RevNumber] set OldContents [getPageContents $PageNumber $OldRevNumber] } # Determine the title of the page set TitleLine [lindex [split $PageContents \n] 0] regexp {^Title:\s*(.*)$} $TitleLine junk PageTitle set PageTitle [quoteHtml $PageTitle] # Strip the page header information from the pages set PageContents [join [lrange [split $PageContents \n] 3 end] \n] set OldContents [join [lrange [split $OldContents \n] 3 end] \n] # Generate the HTML description of the differences set LongHtmlChanges [doHtmlDiff context $OldContents $PageContents] # Split long lines set HtmlChanges "" foreach line [split $LongHtmlChanges \n] { append HtmlChanges "[splitlongline $line]\n" } # Generate the content type for the page (HTML) puts "Content-type: text/html" puts "Pragma: no-cache" puts "" # Now, generate the page header. puts "" puts "" puts "" if { [string equal $OldRevNumber "Unspecified"] } { puts " Differences between latest version and version $RevNumber of $PageTitle" } else { puts " Differences between version $RevNumber and version $OldRevNumber of $PageTitle" } puts " " puts " " puts " " puts "" puts "" puts "" puts "

" if { [string equal $OldRevNumber "Unspecified"] } { puts " Differences between latest version and version $RevNumber of $PageTitle " } else { puts " Differences between version $RevNumber and version $OldRevNumber of $PageTitle " } puts "

" puts {

Legend:

gray text: context matter
 red text: old text, or that which has been removed.
 green text: new text, interesting new knowledge.
 yellow text: new text, brand new page, interestly fresh knowledge.
} puts "

" puts " Differences:" puts "

" puts "" # Display the differences if { [string length $HtmlChanges] == 0 } { puts "

" puts " There were no differences between the two versions of the page." puts "

" puts "" } else { puts "
$HtmlChanges
" puts "" } puts "" puts "" puts "" } catch { if { [info exists env(PATH_INFO)] } { set PathInfo [file tail $env(PATH_INFO)] } else { set PathInfo {} } set ShowInstructions false set NoSuchFile false if { [regexp {^(\d+)(\D)(\d*)$} $PathInfo - page sep rev] } { if { [pageExists $page] } { switch -- $sep { . { renderPageRevision $page $rev } - { renderPageDiff $page $rev } default { set ShowInstructions true } } } else { set NoSuchFile true } } elseif { [regexp {^(\d+)(\D)(\d*)(\D)(\d*)$} $PathInfo\ - page sep1 rev1 sep2 rev2] } { if { [pageExists $page] } { if { ( $sep1 == "-" ) && ( $sep2 == "-" ) } { renderPageDiff $page $rev1 $rev2 } else { set ShowInstructions true } } else { set NoSuchFile true } } elseif { [regexp {^(\d+)$} $PathInfo - page] } { if { [pageExists $page] } { genRevisionHistory $page } else { set NoSuchFile true } } else { set ShowInstructions true } if { $ShowInstructions } { puts "Content-type: text/plain" puts "Pragma: no-cache" puts " This is the historical archive of the $WikiName Examples: See the revision history of page 12: $ScriptURL/12 Retrieve version 5 of page 12: $ScriptURL/12.5 Compare version 5 of page 12 with latest version: $ScriptURL/12-5 Compare version 5 of page 12 with version 2: $ScriptURL/12-5-2 " } elseif { $NoSuchFile } { puts "Content-type: text/plain" puts "Pragma: no-cache" puts "" puts "There is no revision history for Wiki page $page." } } err if 0 { puts #################### puts $err puts $errorInfo } ---- 12may04 [jcw] - Shane, as always, your contributions are a delight to try out. I've installed it on this site as an experiment for now. Need to look into server load (and making sure bots don't clog it), but I can see no issues with it. Thanks also to Pascal for sharing the diff code. Try http://mini.net/tclrevs/11426 for example. Note that the latest version is there but revisions before it may lag by up to 24 hours, since CVS updates are merged only once a day. The http://mini.net/tclhist/ mechanism also continues to be available. ---- [LV] If some day someone else, like me, wanders by using Lynx or some other non-graphical web browser, perhaps we could brain storm on how we could modify this code so that it was not color-dependant for reporting differences. Right now, non-graphical web browsers, as well as color blind users, are unable to see the differences. ---- [Category Wikit]