[Home]WikiPatches/MagicContent

UseModWiki | WikiPatches | RecentChanges | Preferences

This patch adds general MagicPage functionality, in a more wiki-way than WikiPatches/MagicPages. The two should probably be mutually exclusive.

The .pm file below is completely modular. You can add commands by just dropping in more packages. Or you can remove the existing ones if you don't want to support them. see MagicContent for documentation.

Usage

Add this line to the very top of a wiki page:
 #MAGIC Commandname param1=value param2=value ...

Patch

UMW

Head

At the head of wiki.cgi:
 use MagicContent;

BrowsePage

In sub BrowsePage: The following two blocks go either side of the line that processes the wikitext into HTML. In the unmodified script, that's:
 $fullHtml .= &WikiToHTML($Text{'text'});

Block 1:

  # magiccontent patch - tarquin
  my $magicline;
  if ( ($magicline) = $Text{'text'} =~ m[^\#MAGIC(.*)\n] ) {
    $Text{'text'} =~ s/^\#MAGIC.*\n//; # now kill the magic command line in source
  }

Block 2:

  # magiccontent patch - tarquin
  if ( $magicline ){ 
    my $magicmodule;
    
    # call: MakeSection( current page, magic module, line of parameters)
    $fullHtml .= (MagicContentMaker->MakeSection( $id, $magicline =~ m[^\W*(\w*)(.*)] ) || '');
    
    #$fullHtml .= "<b>magic line is: '$magicline'</b><BR>"; # testing
    #( $magicmodule, $magicline ) = $magicline =~ m[^\W*(\w*)(.*)];
    #$fullHtml .= "<b>magic package is: '$magicmodule'</b><BR>"; # testing
    #$fullHtml .= "<b>magic line is: '$magicline'</b><BR>"; # testing
    #$fullHtml .= (MagicContentMaker->MakeSection($magicmodule, $magicline) || '');
  }

PrintLinkList

For the "Category" module to work, PrintPageList? must be change to return text instead of printing it. Any calls of it in wiki.cgi must then be changed to
 print PrintPageList?( blah);

Various

For the WantedPages? module to work, make the changes outlined on WikiPatches/MagicWantedPages


Changed to better syntax - no longer co-opts first parameter intended for specified Magic-Command.

The following enhancement adds the facility to place the generated content at the top of the page, as well as at the bottom. With this the #MAGIC line can optionally have @top or @bottom) between the #MAGIC token and the Commandname.

e.g. #MAGIC @top WantedPages threshold=2

Placing the Wiki text at the bottom make sense if it is intended to be used to allow people to comment on the auto-generated text. Placing the Wiki text at the top makes sense if it is an introduction to the auto-generated text.

Replace Block 1, Block 2 and the call to WikiToHTML as described in BrowsePage above with the code below to enable this functionality.

--DavidClaughton.

Yes, I thought of adding this functionality too, so the generated content could be put in a floated box for example. But why add syntax? Why not just have a new parameter "position=top" or "position=bottom"? -- tarquin

In fact, this is exactly what an earlier version of the code had! (seems to be gone from KeptPages now though). This made the line look like this :

#MAGIC WantedPages position=top threshold=2

However, I then decided this looked ambiguous - it looked like the 'position' parameter was being passed to the WantedPages code, when in fact it was being co-opted by the main MagicCommand code. So I changed the syntax in an attempt to make it clearer.

--DavidClaughton.

  # magiccontent patch - tarquin
  # Refactored and added position functionality --DavidClaughton.
  my ($magiccommand, $magicpos, $magicparams);
  my ($magicHTML, $magicDiv, $wikiHTML) = ("", "", "");
  if ( ($magicpos, $magiccommand, $magicparams) = $Text{'text'} =~
	m[^\#MAGIC\s*(?:\@(top|bottom))?\s*(\w*)\s*(.*)\n] )
  {
    $Text{'text'} =~ s/^\#MAGIC.*\n//; # kill the magic command line in source
    $wikiHTML = &WikiToHTML($Text{'text'});

    $magicDiv = "<hr>\n"; # Remove if using CSS.

    # call: MakeSection( current page, magic module, line of parameters)
    $magicHTML .= (MagicContentMaker->MakeSection( $id, $magiccommand, $magicparams ) || '');

    if (lc $magicpos eq "top"){
      $fullHtml .= $magicHTML . $magicDiv . $wikiHTML;
    }
    else {
      $fullHtml .= $wikiHTML . $magicDiv . $magicHTML;
    }
  }
  else {
    $fullHtml .= &WikiToHTML($Text{'text'});
  }


MagicContent perl module

 #!/usr/bin/perl
 ###############################################################################
 #
 #  MagicContent.pm
 #
 #  Generated content for Wiki pages, requested by the page text itself
 #  Syntax in wiki pages:
 #  #MAGIC ModuleName param1=value1 param2=value2 ...
 
 {
   package MagicContentMaker;
 
   our @registered;
   
   sub register {
     # borrowed from Mych's Wookee.pm - thanks Mych :-)
     # fills the @registered array with the names of the packages
 
     my $class = shift;  $class = (ref $class or $class);
 
     push @registered, $class
       unless grep /^\Q$class\E$/, @registered;
     }
   sub GenerateContent   { '' }
   sub CommandParameters { () }
   ############
   sub ListParameters {
     my $class = shift;
     my $text;
     my @parameters = $class->CommandParameters;
     
     return '' if scalar @parameters == 0;
     $text .= '<h4>Parameters for this command</h4>';
     $text .= '<UL>';
     $text .= join '', map { "<LI>$_</LI>" } @parameters ;
     $text .= '</UL>';
     return $text;
   }
 
   # call this from UseModWiki
   sub MakeSection {
     my $class       = shift;
     my $page        = shift; # the page being browsed
     my $magicmodule = shift; # the requested magicmodule   
     my %params = map { s/^"|"$//g; $_ } (shift =~ m[(\w+)\s*=\s*("[^"]*"?|\S+)]g); #"
     $params{'thispage'} = $page; # add the page to the parameters hash
     foreach ( values %params ) {
       # escape HTML. keys are fine since grabbed as "\w+"
       s[&][&]g;
       s[<][<]g;
     }
     
     my $text;
     
     # ... make an opener
     $text .= qq[<div class="magic" id="$magicmodule">\n];
     if( grep /^\Q$magicmodule\E$/ , @registered ){
       # if one of our packages matches the name of the wiki page, run the script
       $text .= $magicmodule->GenerateContent(%params);
       $text .= $magicmodule->ListParameters;
     }
     else {
       # ...or complain
       $magicmodule =~ s[&][&]g;
       $magicmodule =~ s[<][<]g;
       $text .= qq[#MAGIC module "<b>$magicmodule</b>" cannot be found. 
         Use "#MAGIC ListCommands" to see available modules.];
     }
     # ... make a closer
     $text .= qq[</div>\n];
     return $text;
   }
 }
 
 #############################################################
 #
 # command: Foobar
 # this is just a template
 {
   package Foobar;
   @ISA = qw(MagicContentMaker);
   Foobar->register();
   sub CommandParameters { () } 
     # auto-documentation: list the params this module requires here.
     # safe to delete if no parameters
   ###########################
     
   sub GenerateContent {
     my ( $class, %params ) = @_;
     # put the script for the page here
     # access parameters thus: $params{'threshold'}
     # page being browsed is:  $params{'thispage'}
     return "Generating... FooBar<BR>";  
   }
 }
 #############################################################
 #
 # command: Params
 # this lists all the parameters the page has passed to the command, just for testing
 {
   package Params;
   @ISA = qw(MagicContentMaker);
   Params->register();
   ###########################
     
   sub GenerateContent {
     my ( $class, %params ) = @_;
     my $text;
     
     $text .= '<h4>Parameters</h4><DL>';
     while (($key, $value) = each %params) {
       $text .= qq[<DT>$key</DT> <DD>$value</DD>];
     }
     $text .= '</DL>';
     return $text;  
   }
 }
 #############################################################
 #
 # command: ListCommands
 # this returns all the available commands in this perl module
 {
   package ListCommands;
   @ISA = qw(MagicContentMaker);
   ListCommands->register();
   ###########################
     
   sub GenerateContent {
     my ( $class, %params ) = @_;
     my $text;
     
     $text .= '<h4>Commands</h4><UL>';
     $text .= join '', map { "<LI>$_</LI>" } @MagicContentMaker::registered;
     $text .= '</UL>';
     return $text;  
   }
 }
 #############################################################
 #
 # command: AllPages
 # lists all the pages of the wiki
 {
   package AllPages;
   @ISA = qw(MagicContentMaker);
   AllPages->register();
   sub CommandParameters {'list = ol|ul'}
   ###########################
     
   sub GenerateContent {
     my ( $class, %params ) = @_;
     my $text;
     my $listTag = $params{'list'};
     $listTag = 'UL' unless defined $params{list};
     if( $listTag !~ m/^(UL|OL)$/i ){ 
       $listTag = 'UL';
       $text .= qq[parameter "$params{'list'}" not recognized, defaulting to OL.];
     }
     
     $text .= "<h4>List of all pages on this wiki:</h4><$listTag>";
     $text .= join '', map { '<LI>' . UseModWiki::GetPageLink($_) . '</LI>' } UseModWiki::AllPagesList();
     $text .= "</$listTag>";
     
     return $text;  
   }
 }
 #############################################################
 #
 # command: Category
 # returns the search results for the browsed page title
 {
   package Category;
   @ISA = qw(MagicContentMaker);
   Category->register();
   ###########################
     
   sub GenerateContent {
     my ( $class, %params ) = @_;
     my $text;
     $params{'thispage'} =~ s/_/ /g;
     $text = UseModWiki::PrintPageList(UseModWiki::SearchTitleAndBody(qq["[[$params{'thispage'}]]"]));
     $text =~ s[(\d+ pages? found)][Members of this category: $1];
     
     #reset currently open page, so the referrer is correct in the edit links made below
     $UseModWiki::OpenPageName = $params{'thispage'};
 
     return $text;  
   }
 }
 #############################################################
 #
 # command: WantedPages
 # lists wanted pages. refactored & better UseMod call
 {
   package WantedPages;
   @ISA = qw(MagicContentMaker);
   WantedPages->register();
   sub CommandParameters {
     'threshold = 0,1,2 ... : show only pages with more than this number of requests.' }
   ###########################
   sub SortItems {
     my ( $a , $b , $numA , $numB ) = @_; 
   }
   sub GenerateContent {
     my ( $class, %params ) = @_;
     my ($text, @links);
     #my $testoutput;
     
     $params{threshold} = 0 unless defined $params{threshold};
     if( $params{'threshold'} !~ /^\d+$/ ) {
       return q[Invalid value in parameter 'threshold'.];
     }
     
     # grab the list of links from UseModWiki::GetFullLinkList which spits out an array
     # on the way, kill any items which are just one pagename: ie pages that make no requests
     @links = grep { !/^\w+\s*$/ } UseModWiki::GetFullLinkList(1,1,1,0,0,0,0,'',1);
     #reset currently open page, so the referrer is correct in the edit links made below
     $UseModWiki::OpenPageName = $params{'thispage'};
     
     ##################
     # Data extraction
     # what we have so far: multiple lines like:
     # {Existing page} {list of wanted link plain text}\n
     my %requesters; # hash of arrays to store who wants the pages
     foreach (@links) {
       # we are chomping through each line at a time
       
       my $head;
       s[^(\S+)\s*]{
         $head = $1;
         '';
         }eg; # strip link tags and put the head name somewhere else
       # now $_ is the list of links wanted by page $head  
       
       while( m[(\S+)\s*]g ) {
         #$testoutput .= "$1<BR>";
         push @{ $requesters{$1} }, $head;
       }
     }
     # $testoutput .= join '<BR>', @links;
     
     # now we have a hash of arrays. Keys are wanted pages, value is array of requesters
     ##################
 
     # Data output
     my $wantedpage;
     # sort { SortList( $a, $b, @{$requesters{$a}}, @{$requesters{$b}} } ( grep ... )
     foreach $wantedpage (sort { @{$requesters{$b}} <=> @{$requesters{$a}}  || $a cmp $b }
         (grep { @{$requesters{$_}} > $params{'threshold'} } keys %requesters ) ) {
       # run through the hash keys
       # BUT sort them by size of the associated array, 
       #     AND only let through the arrays > threshold parameter
       $text .= '<dt>' 
         # get an edit link for the wanted page
         . $wantedpage . &UseModWiki::GetEditLink($wantedpage,"?")
         #. &UseModWiki::GetEditLink($wantedpage,$wantedpage)
         # give number of requests
         . ' ('. @{$requesters{$wantedpage}} . ' requests)</dt><dd>Requested by: '
         # list the requester, as page links
         . join(', ', map {UseModWiki::GetPageLink($_)} @{$requesters{$wantedpage}} )
         . '</dd>';
     }
     
     #return $testoutput . '<HR>'; 
     my $thresholdmessage = ( $params{'threshold'} == 0 ) ? '' : 'May be further pages. ';
     return qq[<style type="text/css">
       \#WantedPages dd {font-size:smaller; margin:1px 0px 1.5ex 4em;}
       \#WantedPages dl {margin:0px;}
       \#WantedPages dt {font-weight:normal;}
       </style>
       <DL>$text</DL>
       $thresholdmessage
       Threshold is $params{'threshold'}.];
   }
 }
 
 #############################
 1;


This module is extremely useful, thank you. I noticed that the wantedpages command finds subpages which exist. Since we never notice any real wanted subpages this fix just skips over them:

 next if $wantedpage =~ /^\//; # added
 # run through the hash keys

UseModWiki | WikiPatches | RecentChanges | Preferences
Edit text of this page | View other revisions | Search MetaWiki
Last edited September 5, 2007 2:22 pm by MarkusLude (diff)
Search: