[Home]WikiPatches/RawMode

UseModWiki | WikiPatches | RecentChanges | Preferences

A very simple raw-output ability has been added to 1.0 by adding "raw=1" to a browse command. For example, visit

http://www.usemod.com/cgi-bin/wiki.pl?action=browse&id=WikiPatches/RawMode&raw=1

...to see this page in "raw" mode. --CliffordAdams


I've modded my heavily modded source to include a raw mode, which returns the raw source-text of the page, unadorned by wikihtml markup or even a page structure wrapper. It is implemented as an action parameter, so the url would look like http://iawiki.net/cgi-bin/wiki.pl?action=raw&id=EricScheid.

Contrast with:

The intended use of this feature is to make it possible to have human editable text files available in a machine readable form ... such as a community modifiable style sheet [1].

The mod is in two places:

insert the following lines into sub DoBrowseRequest:

  } elsif ($action eq 'raw') {
    &BrowsePageRaw($id)   if &ValidIdOrDie($id);
    return 1;

and then define the new subroutine

sub BrowsePageRaw {
  my ($id) = @_;
  &OpenPage($id);
  &OpenDefaultText();
        print "\n" . $Text{'text'};
  return;
}

Someone that knows more perl than I might want to explain why I couldn't just print $Text{'text'}; but instead had to prefex it with a string.


Alternative with revision history

Insert the stuff between # BEGIN Raw mode patch and # END Raw mode patch.

sub BrowsePage {
    ...
  if ($revision ne '') {
    &OpenKeptRevisions('text_default');
    $openKept = 1;
    if (!defined($KeptRevisions{$revision})) {
      $goodRevision = '';
    } else {
      &OpenKeptRevision($revision);
    }
  }

  # BEGIN Raw mode patch
  if( &GetParam('raw',0) ) {
     print &GetHttpHeader('text/plain');
     print $Text{'text'};
     return;
  }
  # END Raw mode patch

  $newText = $Text{'text'};     # For differences
  ...
}

Replace GetHttpHeader with the below:

sub GetHttpHeader {
  my ($type) = @_;
  $type = 'text/html' unless $type;
  my $cookie;
  if (defined($SetCookie{'id'})) {
    $cookie = "$CookieName="
            . "rev&" . $SetCookie{'rev'}
            . "&id&" . $SetCookie{'id'}
            . "&randkey&" . $SetCookie{'randkey'};
    $cookie .= ";expires=Fri, 08-Sep-2010 19:48:23 GMT";
    if ($HttpCharset ne '') {
      return $q->header(-cookie=>$cookie,
                        -type=>"$type; charset=$HttpCharset");
    }
    return $q->header(-cookie=>$cookie);
  }
  if ($HttpCharset ne '') {
    return $q->header(-type=>"$type; charset=$HttpCharset");
  }
  return $q->header(-type=>$type);
}


Better alternative With Redirection, Content Type, and optional Meta Information

This patch uses the normal browse action, because I want revisions to work, and I want redirection to work -- things that get done in BrowsePage. Together with RawWikiInclusion? this implements true transclusion (see TransclusionMarkup).

Here is the example output when raw=1 is used:

At the same time, in order to get get a RawWikiBrowser? to work, I need it to emit the Meta Information I need when submitting an edited page -- the TimeStamp?. This extra line is emitted when raw=2 is used:

In DoBrowseRequest, pass a second parameter to BrowsePage. This second parameter indicates wether raw=1 was passed along:

old:

    &BrowsePage($id)  if &ValidIdOrDie($id);

new:

    &BrowsePage($id, &GetParam('raw', 0))  if &ValidIdOrDie($id);

Now change BrowsePage such that the second parameter is used, too. At the beginning, add $raw as follows:

  sub BrowsePage {
    my ($id, $raw) = @_;

Then, just before $fullHtml gets set, adda shortcut:

    # short cut if we only need the raw text: no caching, no diffs, no html.
    if ($raw) {
      print &GetHttpHeader('text/plain');
      if ($raw == 2) {
        print $Section{'ts'} . " # Do not delete this line when editing!\n";
      }
      print $Text{'text'};
      return;
    }
 
    $fullHtml = &GetHeader($id, &QuoteHtml($id), $oldId);

Notice how I am passing the content type to GetHttpHeader. We need to change that as well. First, use a new variable $type for the content type. If it is not passed along, use text/html. Then use it. Note that the following copy of GetHttpHeader includes WikiPatches/PageCacheBugFix!

sub GetHttpHeader {
  my ($type) = @_;
  my $cookie;
  my $now;
  $now = gmtime;
  if (!$type) {
    $type = 'text/html';
  }
  if (defined($SetCookie{'id'})) {
    $cookie = "$CookieName="
            . "rev&" . $SetCookie{'rev'}
            . "&id&" . $SetCookie{'id'}
            . "&randkey&" . $SetCookie{'randkey'};
    $cookie .= ";expires=Fri, 08-Sep-2010 19:48:23 GMT";
    if ($HttpCharset ne '') {
        return $q->header(-cookie=>$cookie,
                          -pragma=>"no-cache",
                          -cache_control=>"no-cache",
                          -last_modified=>"$now",
                          -expires=>"+10s",
                          -type=>"$type; charset=$HttpCharset");
    }
    return $q->header(-cookie=>$cookie);
  }
  if ($HttpCharset ne '') {
      return $q->header(-type=>"$type; charset=$HttpCharset",
                        -pragma=>"no-cache",
                        -cache_control=>"no-cache",
                        -last_modified=>"$now",
                        -expires=>"+10s");
  }
  return $q->header();
}

Finally, in DoOtherRequest we have to allow a DoPost? if raw=2, even if oldtime is not set.

old:

  # Handle posted pages
  if (&GetParam("oldtime", "") ne "") {

new:

  # Handle posted pages
  if (&GetParam("oldtime", "") ne "" or (&GetParam('raw', 0) == 2)) {

In DoPost?, we have to set oldtime according to the first line of the content, if raw=2. Just add some code after the last error check.

old:

  if (($id eq 'Sample_Undefined_Page')
      || ($id eq T('Sample_Undefined_Page'))) {
    &ReportError(Ts('[[%s]] cannot be defined.', $id));
    return;
  }

new:

  if (($id eq 'Sample_Undefined_Page')
      || ($id eq T('Sample_Undefined_Page'))) {
    &ReportError(Ts('[[%s]] cannot be defined.', $id));
    return;
  }
  # Handle raw edits with the meta info on the first line
  if (&GetParam('raw', 0) == 2) {
    if (not $string =~ /^([0-9]+).*\n/) {
      &ReportError(Ts('Cannot find timestamp on the first line.'));
      return;
    }
    $oldtime = $1;
    $string = $';
  }

In order to automatically merge revisions when in text mode (ie. no real conflict handling), we need the following:

First, replace the block in DoPost? that has the "Detect editing conflicts" comment with the following block. When in raw mode, this disables manual conflict handling and just merges the text automatically.

An automatic merge requires the merge program, and the merge program requires three files: One with the submitted text, the common ancestor, and the other submitted text. In order to get the common ancestor, we use a new subroutine called GetTextAtTime, which returns a text based on a timestamp. This is what we have to do next.

      # Detect editing conflicts.  In raw mode, merge automatically, else
      # resubmit to the user.
      if (($oldrev > 0) && ($newAuthor && ($oldtime != $pgtime))) {
        if ($raw) {
          # merge incorporates all changes that lead from file2 to file3 into file1.
          $string = &MergeRevisions($string, &GetTextAtTime($oldtime), $old);
        } else {
          &ReleaseLock();
          if ($oldconflict>0) {  # Conflict again...
            &DoEdit($id, 2, $pgtime, $string, $preview);
          } else {
            &DoEdit($id, 1, $pgtime, $string, $preview);
          }
          return;
        }
      }
      if ($preview) {
        &ReleaseLock();
        &DoEdit($id, 0, $pgtime, $string, 1);
        return;
      }

This subroutine returns the text for a page, given a timestamp. Note how it does not clobber the global Section and Text hashes. The KeptList is only read when conflicts are merged, so performance is not going to suffer.

    sub GetTextAtTime {
      my ($ts) = @_;
      my (%tempSection, %tempText, $revision);
      # &OpenPage() was already called
      &OpenKeptList; # sets @KeptList
      &OpenKeptRevisions('text_default'); # sets $KeptRevisions{<revision>} = <section>
      foreach $revision (keys %KeptRevisions) {
        %tempSection = split(/$FS2/, $KeptRevisions{$revision}, -1);
        if ($tempSection{'ts'} eq $ts) {
          %tempText = split(/$FS3/, $tempSection{'data'}, -1);
          return $tempText{'text'};
        }
      }
      return '';
    }

And here is the merging code. It is modelled after the GetDiff? subroutine, including its own lock.

    sub MergeRevisions {
      my ($file1, $file2, $file3) = @_;
      my ($name1, $name2, $name3, $output);
      &CreateDir($TempDir);
      $name1 = "$TempDir/file1";
      $name2 = "$TempDir/file2";
      $name3 = "$TempDir/file3";
      &RequestMergeLock() or return T('Could not get a lock to merge!');
      &WriteStringToFile($name1, $file1);
      &WriteStringToFile($name2, $file2);
      &WriteStringToFile($name3, $file3);
      $output = `merge -p -L you -L ancestor -L other $name1 $name2 $name3`;
      &ReleaseMergeLock();
      # No need to unlink temp files--next merge will just overwrite.
      return $output;
    }

    sub RequestMergeLock {
      # 4 tries, 2 second wait, do not die on error
      return &RequestLockDir('merge', 4, 2, 0);
    }

    sub ReleaseMergeLock {
      &ReleaseLockDir('merge');
    }


WikiPatches


UseModWiki | WikiPatches | RecentChanges | Preferences
Edit text of this page | View other revisions | Search MetaWiki
Last edited February 3, 2005 12:12 am by user-10cmeae.cable.mindspring.com (diff)
Search: