# Bibliography2Plugin for TWiki Collaboration Platform, http://TWiki.org/ # # Copyright (C) 2006 Clif Kussmaul, clif@kussmaul.org # Copyright (C) 2004 Antonio Terceiro, asaterceiro@inf.ufrgs.br # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details, published at # http://www.gnu.org/copyleft/gpl.html # # ========================= # # Each plugin is a package that may contain these functions: VERSION: # # initPlugin ( $topic, $web, $user, $installWeb ) 1.000 # initializeUserHandler ( $loginName, $url, $pathInfo ) 1.010 # registrationHandler ( $web, $wikiName, $loginName ) 1.010 # commonTagsHandler ( $text, $topic, $web ) 1.000 # startRenderingHandler ( $text, $web ) 1.000 # outsidePREHandler ( $text ) 1.000 # insidePREHandler ( $text ) 1.000 # endRenderingHandler ( $text ) 1.000 # beforeEditHandler ( $text, $topic, $web ) 1.010 # afterEditHandler ( $text, $topic, $web ) 1.010 # beforeSaveHandler ( $text, $topic, $web ) 1.010 # writeHeaderHandler ( $query ) 1.010 Use only in one Plugin # redirectCgiQueryHandler ( $query, $url ) 1.010 Use only in one Plugin # getSessionValueHandler ( $key ) 1.010 Use only in one Plugin # setSessionValueHandler ( $key, $value ) 1.010 Use only in one Plugin # # initPlugin is required, all other are optional. # For increased performance, all handlers except initPlugin are # disabled. To enable a handler remove the leading DISABLE_ from # the function name. Remove disabled handlers you do not need. # # NOTE: To interact with TWiki use the official TWiki functions # in the TWiki::Func module. Do not reference any functions or # variables elsewhere in TWiki!! # ========================= package TWiki::Plugins::Bibliography2Plugin; # ========================= use vars qw( $web $topic $user $installWeb $VERSION $pluginName $debug ); $VERSION = '1.002'; # 07 Jun 2006 $pluginName = 'Bibliography2Plugin'; # Name of this Plugin # ========================= sub initPlugin { ( $topic, $web, $user, $installWeb ) = @_; # check for Plugins.pm versions if( $TWiki::Plugins::VERSION < 1 ) { TWiki::Func::writeWarning( "Version mismatch between $pluginName and Plugins.pm" ); return 0; } # Get plugin debug flag $debug = TWiki::Func::getPreferencesFlag( "\U$pluginName\E_DEBUG" ); # TODO: delete when testing is finished $debug = 3; # Plugin correctly initialized TWiki::Func::writeDebug( "- TWiki::Plugins::${pluginName}::initPlugin( $web.$topic ) is OK" ) if $debug; return 1; } sub parseArgs { my $args = $_[0]; # get the typed header or default. my $header = &TWiki::Func::getPreferencesValue("BIBLIOGRAPHYPLUGIN_DEFAULTHEADER"); if ($args =~ m/header="([^"]*)"/) { $header = $1; } # get the typed key link or default. my $keyLink = &TWiki::Func::getPreferencesValue("BIBLIOGRAPHYPLUGIN_DEFAULTKEYLINK"); if ($args =~ m/keyLink="([^"]*)"/) { $keyLink = $1; } # get the typed order or default. my $order = &TWiki::Func::getPreferencesValue("BIBLIOGRAPHYPLUGIN_DEFAULTSORTING"); if ($args =~ m/order="([^"]*)"/) { $order = $1; } # get the typed option strings or default. my $optionStrings = ""; while ($args =~ m/optionString="([^"]*)"/gm) { $optionStrings .= "," . $1; TWiki::Func::writeDebug("parseArgs:: optionStrings: $optionStrings") if $debug > 2; } if ("" eq $optionStrings) { $optionStrings = &TWiki::Func::getPreferencesValue("BIBLIOGRAPHYPLUGIN_DEFAULTOPTIONSTRINGS"); } TWiki::Func::writeDebug("parseArgs:: optionStrings: $optionStrings") if $debug > 1; # get the typed references topics or default. my $referencesTopics = ""; while ($args =~ m/referencesTopic="([^"]*)"/gm) { $referencesTopics .= "," . $1; TWiki::Func::writeDebug("parseArgs:: referencesTopics: $referencesTopics") if $debug > 2; } if ("" eq $referencesTopics) { $referencesTopics = &TWiki::Func::getPreferencesValue("BIBLIOGRAPHYPLUGIN_DEFAULTBIBLIOGRAPHYTOPIC"); } TWiki::Func::writeDebug("parseArgs:: referencesTopics: $referencesTopics") if $debug > 1; # return as scalars, not lists or hashes, to avoid problems return ($header, $keyLink, $optionStrings, $order, $referencesTopics); } sub parseOptions { my ($optionSet) = @_; my %options; while ( $optionSet =~ (m/([^=]*)=([^\s=]*)/gm) ) { ($key,$value) = ($1,$2); # remove leading or trailing whitespaces $key =~ s/^\s+|\s+$//g; $value =~ s/^\s+|\s+$//g; TWiki::Func::writeDebug("parseOptions:: $key: $value") if $debug > 2; $options{$key} = $value; } return %options; } sub readBibliography { my %bib, $key, $value, $options, $topic; # read references topics foreach $topic (split(/\s*,\s*/,@_[0])) { TWiki::Func::writeDebug("readBibliography:: reading $topic") if $debug > 1; ### TODO: work correctly when topic is not web.topic ### TODO: use include'd text in referenceTopic (verify that it doesn't work now) $_ = TWiki::Func::readTopicText("", $topic, "", 1); # web, topic, rev, 1=ignore permissions # match: startOfLine | anythingForKey | anythingForValue | options | endOfLine while (m/^\|([^\|]*)\|([^\|]*)\|([^\|]*)\|?$/gm) { ($key,$value,$options) = ($1,$2,$3); # remove leading or trailing whitespaces $key =~ s/^\s+|\s+$//g; $value =~ s/^\s+|\s+$//g; $options =~ s/^\s+|\s+$//g; TWiki::Func::writeDebug("readBibliography:: read $key: $value: $options") if $debug > 2; # skip formatted keys (keys starting with *_=<) if (not $key =~ m/^[*_=<]/) { $bib{$key} = { "name" => $value, "options" => $options, "order" => 0 }; } } TWiki::Func::writeDebug("readBibliography:: read " . keys( %bib ) . " from " . $topic) if $debug; } return %bib; } sub orderBibliography { my ($order, %bib) = @_; my %keySet; # precompute sort keys to improve performance if ($order eq "alpha") { #@keyOrder = sort { lc($bib{$a}{"name"}) cmp lc($bib{$b}{"name"}) } (keys %bib); foreach $key (keys %bib) { $keySet{$key} = lc($bib{$key}{"name"}); } } elsif ($order eq "key" ) { #@keyOrder = sort { lc($a) cmp lc($b) } (keys %bib); foreach $key (keys %bib) { $keySet{$key} = lc($key); } } # if needed, resort cited entries to generate numeration if (%keySet) { my $i = 1; foreach $key (sort { $keySet{$a} cmp $keySet{$b} } (keys %bib)) { $bib{$key}{"order"} = $i++; } } return %bib; } sub generateBibliography { my ($header, $keyLink, $optionStrings, %bib) = @_; # insert header iff bib is non-empty return if (not %bib); my $list = $header . "\n"; foreach $key (sort { $bib{$a}{"order"} <=> $bib{$b}{"order"} } (keys %bib) ) { $list .= " 1." . generateEntry ($key, $keyLink, $optionStrings, %bib) . "\n"; } $list .= "\n"; return $list; } sub generateBibliographyFull { my ($header, $keyLink, $optionStrings, $order, $referencesTopics) = parseArgs ($1); my %fullBib = orderBibliography ($order, readBibliography ($referencesTopics)); return generateBibliography ($header, $keyLink, $optionStrings, %fullBib); } # used by %CITE% # TODO: pass %bib{$key} instead of entire %bib (can't get this to work) sub generateCitation { my ($key, $keyLink, %bib) = @_; my $result = "??"; if (exists $bib{$key}) { $result = $bib{$key}{"order"}; # add link to key topic if ($keylink =~ m/cite/) { $result = "[[$key][$result]]"; } } return "[" . $result . "]"; } # used by %CITEFULL% and generateBibliography (for %BIBLIOGRAPHY% and %BIBLIOGRAPHYFULL%) # TODO: pass %bib{$key} instead of entire %bib (can't get this to work) sub generateEntry { my ($key, $keyLink, $optionStrings, %bib) = @_; my $result = $key; if (exists $bib{$key}) { $result = $bib{$key}{"name"}; # add link to key topic if ($keylink =~ m/end/) { $result .= " ([[$key][details]])"; } # add options @optionStrings = split(/,/,$optionStrings); my %options = parseOptions($bib{$key}{"options"}); foreach $option (keys %options) { TWiki::Func::writeDebug("generateEntry:: " . $key . " has " . $option) if $debug > 2; foreach $optionString (@optionStrings) { if ($optionString =~ s/$option/$options{$option}/) { TWiki::Func::writeDebug("generateEntry:: " . $optionString) if $debug > 2; $result .= $optionString; } } } } return $result; } # ================================== # Plugin HOOK: startRenderingHandler # ================================== sub startRenderingHandler { ### my ( $text, $web ) = @_; # do not uncomment, use $_[0], $_[1] instead # This handler is called by getRenderedVersion just before the line loop TWiki::Func::writeDebug( "- ${pluginName}::startRenderingHandler( $_[1] )" ) if $debug; # TODO: CITEFULL and CITE assume exactly one BIBLIOGRAPHY - warn if multiples if ($_[0] =~ m/%BIBLIOGRAPHY{([^}]*)}%/mg) { my ($header, $keyLink, $optionStrings, $order, $referencesTopics) = parseArgs ($1); # build bibliography: %fullBib = readBibliography ($referencesTopics); # mark cited entries: my $i = 1; $_ = $_[0]; while (m/%CITE{([^}]*)}%/mg) { if (exists $fullBib{$1} and not $fullBib{$1}{"order"}) { $fullBib{$1}{"order"} = $i++; # citation order } } # sort cited entries - delete uncited entries (with order==0) to make sorting faster my %citeBib = %fullBib; foreach $key (keys %citeBib) { delete $citeBib{$key} if not $citeBib{$key}{"order"}; } %citeBib = orderBibliography ($order, %citeBib); TWiki::Func::writeDebug("startRenderingHandler:: cited " . keys(%citeBib) . " of " . keys( %fullBib ) ) if $debug > 1; $_[0] =~ s/%CITE{([^}]*)}%/&generateCitation($1, $keyLink, %citeBib)/ge; $_[0] =~ s/%CITEFULL{([^}]*)}%/&generateEntry($1, $keyLink, $optionStrings, %fullBib)/ge; $_[0] =~ s/%BIBLIOGRAPHY{([^}]*)}%/&generateBibliography($header, $keyLink, $optionStrings, %citeBib)/ge; } # process BIBLIOGRAPHYFULL separately - it doesn't use BIBLIOGRAPHY $_[0] =~ s/%BIBLIOGRAPHYFULL{([^}]*)}%/&generateBibliographyFull($1)/ge; } 1;