#!c:/w/r/c/bin/perl -w # # TWiki Collaboration Platform, http://TWiki.org/ # # Copyright (C) 1999-2003 Peter Thoeny, peter@thoeny.com # # Based on parts of Ward Cunninghams original Wiki and JosWiki. # Copyright (C) 1998 Markus Peter - SPiN GmbH (warpi@spin.de) # Some changes by Dave Harris (drh@bhresearch.co.uk) incorporated # # For licensing info read license.txt file in the TWiki root. # 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 # Set library paths in @INC, at compile time BEGIN { unshift @INC, '.'; require 'setlib.cfg' } use CGI::Carp qw( fatalsToBrowser ); use CGI; use TWiki; use strict; &main(); use vars qw( $cacheFileRights $cacheDirRights $cacheGroup $adminUseCache $adminPerUserCache $cacheDir ); # Uncomment the body of either routine to enable debugging sub writeDebug { my( $text ) = @_; #TWiki::writeDebug( $text ); } sub writeDebugTimes { my( $text ) = @_; #TWiki::writeDebugTimes( $text ); } sub main { my $query= new CGI; $adminUseCache = $TWiki::useCache || "0"; $adminPerUserCache = $TWiki::usePerUserCache || "0"; $cacheFileRights = $TWiki::cacheFileRights || 0777; $cacheDirRights = $TWiki::cacheDirRights || 0777; $cacheGroup = $TWiki::cacheGroup || ""; ##### for debug only: Remove next 2 comments (but redirect does not work) #print "Content-type: text/html\n\n"; #open(STDERR,'>&STDOUT'); # redirect error to browser #$| = 1; # no buffering my $thePathInfo = $query->path_info(); my $theRemoteUser = $query->remote_user(); my $theTopic = $query->param( 'topic' ); my $theUrl = $query->url; writeDebugTimes( "view - start $thePathInfo" ); my( $topic, $webName, $scriptUrlPath, $userName ) = &TWiki::initialize( $thePathInfo, $theRemoteUser, $theTopic, $theUrl, $query ); writeDebugTimes( "view - initialized" ); my $tmpl = ""; my $text = ""; my $meta = ""; my $rev = $query->param( "rev" ); my $maxrev = 1; my $extra = ""; my $wikiUserName = &TWiki::userToWikiName( $userName, 1 ); my $revdate = ""; my $revuser = ""; my $viewRaw = $query->param( "raw" ) || ""; my $unlock = $query->param( "unlock" ) || ""; my $skin = $query->param( "skin" ) || &TWiki::Prefs::getPreferencesValue( "SKIN" ); my $cacheSearch = &TWiki::Prefs::getPreferencesValue( "CACHESEARCH" ) || "off"; my $useCache = &TWiki::Prefs::getPreferencesValue( "USECACHE" ) || "off"; my $noCache = $query->param( "nocache" ) || ""; my $noHtml = $query->param( "noHtml" ) || ""; my @params = $query->param(); my $paramCount = $#params; if ($query->param( "meta" )) { $viewRaw = "debug"; } $cacheDir = $TWiki::cacheDir; checkCacheDir( $cacheDir ); if ( $adminPerUserCache ) { $cacheDir .= "/$wikiUserName"; } writeDebug( "wikiUserName $wikiUserName " ); # Set page generation mode to RSS if using an RSS skin if( $skin =~ /^rss/ ) { TWiki::setPageMode( 'rss' ); } # check access permission my $viewAccessOK = &TWiki::Access::checkAccessPermission( "view", $wikiUserName, $text, $topic, $webName ); if( $TWiki::readTopicPermissionFailed ) { # Can't read requested topic and/or included (or other accessed topics # user could not be authenticated, may be not logged in yet? my $viewauthFile = $ENV{'SCRIPT_FILENAME'}; $viewauthFile =~ s|/view|/viewauth|o; if( ( ! $theRemoteUser ) && (-e $viewauthFile ) ) { # try again with authenticated viewauth script # instead of non authenticated view script my $url = $ENV{"REQUEST_URI"}; if( $url ) { # $url i.e. is "twiki/bin/view.cgi/Web/Topic?cms1=val1&cmd2=val2" $url =~ s|/view|/viewauth|o; $url = "$TWiki::urlHost$url"; } else { $url = "$TWiki::urlHost$scriptUrlPath/$viewauthFile/$webName/$topic"; } TWiki::redirect( $query, $url ); return; } } if( ! $viewAccessOK ) { my $url = &TWiki::getOopsUrl( $webName, $topic, "oopsaccessview" ); TWiki::redirect( $query, $url ); return; } # Most recent topic read in even if earlier topic requested - makes # code simpler and performance impact should be minimal my $topicExists = &TWiki::Store::topicExists( $webName, $topic ); if( $topicExists ) { if( $viewRaw ) { $text = &TWiki::Store::readTopicRaw( $webName, $topic ); } else { ( $meta, $text ) = &TWiki::Store::readTopic( $webName, $topic ); } ( $revdate, $revuser, $maxrev ) = &TWiki::Store::getRevisionInfoFromMeta( $webName, $topic, $meta, "isoFormat" ); writeDebug( "maxrev = $maxrev" ); if( $rev ) { $rev =~ s/r?1\.//go; # cut 'r' and major if( $rev < 1 ) { $rev = 1; } if( $rev > $maxrev ) { $rev = $maxrev; } } else { $rev = $maxrev; } if( $rev < $maxrev ) { if( $viewRaw ) { $text = &TWiki::Store::readTopicRaw( $webName, $topic, "1.$rev" ); } else { ( $meta, $text ) = &TWiki::Store::readTopicVersion( $webName, $topic, "1.$rev" ); } ( $revdate, $revuser ) = &TWiki::Store::getRevisionInfo( $webName, $topic, "1.$rev", 1 ); $extra .= "r1.$rev"; } } else { $rev = 1; if( &TWiki::isWikiName( $topic ) || &TWiki::isAbbrev( $topic ) ) { ( $meta, $text ) = &TWiki::Store::readTemplateTopic( "WebTopicViewTemplate" ); } else { ( $meta, $text ) = &TWiki::Store::readTemplateTopic( "WebTopicNonWikiTemplate" ); } $extra .= " (not exist)"; } if( $TWiki::doLogTopicView ) { # write log entry &TWiki::Store::writeLog( "view", "$webName.$topic", $extra ); } my $ctopicExists = &TWiki::Store::topicExists( $webName, $topic ); writeDebug( "useCache $useCache " ); writeDebug( "cacheSearch $cacheSearch " ); writeDebug( "paramCount $paramCount " ); #my $topicExists = &TWiki::Store::topicExists( $webName, $topic ); #my doesitexists = &TWiki::Store::topicExists( $webName, $topic ) ; writeDebug( "ctopicExists $ctopicExists " ); writeDebug( "adminUseCache $adminUseCache " ); writeDebug( "noCache $noCache " ); # CACHE START if ( ( $paramCount < 0 ) && &TWiki::Store::topicExists( $webName, $topic ) && $adminUseCache && ( $noCache eq "" ) && ( $useCache eq "on" ) ) { writeDebug( "useCache $useCache " ); checkCacheDir( $cacheDir ); my $readCache = 1; #exists cache page?? my @cacheStat = stat( "$cacheDir/$webName/$topic.html" ); writeDebug( "cacheStat = $cacheStat[9]" ); if ( "$cacheStat[9]" ne "" ) { my @topicStat = stat( "$TWiki::dataDir/$webName/$topic.txt" ); #is newer than topic?? if ( $topicStat[9] <= $cacheStat[9] ) { #is newer then include topics?? my ( @includes ) = ( $text =~ /%INCLUDE{\"(.*?)\"}%/g ); writeDebug( "include found @includes $text" ); foreach my $include ( @includes ) { #writeDebug( "include checked : $include " ); my $expText = &TWiki::Func::expandCommonVariables($include, $webName, $topic ); #modified LT : expand variables like %TWIKIWEB% #writeDebug( "include expanded : $expText " ); my ( $tmpWeb, $tmpTopic ) = ( $expText =~ /(.*?)\.(.*)/ ); if ( $tmpTopic eq "" ) { $tmpTopic = $include; $tmpWeb = $webName; } #writeDebug( "readCache: $readCache" ); #writeDebug( "$tmpWeb, $tmpTopic" ); if ( TWiki::Func::topicExists( $tmpWeb, $tmpTopic ) ) {#modified LT : func my @includeStat = stat( "$TWiki::dataDir/$tmpWeb/$tmpTopic.txt" ); if ( $includeStat[9] > $cacheStat[9] ) {#modified LT : was $includeStat[9] > $includeStat[9] $readCache = 0; writeDebug( "includeStat: $includeStat[9] > cacheStat:$cacheStat[9]" ); }; #writeDebug( "readCache: $readCache" ); # does the included topic has SEARCH ? #writeDebug( "reading included topic" ); my $includeText = &TWiki::Func::readTopicText( $tmpWeb, $tmpTopic ); #writeDebug( "reading included topic : $includeText " ); if ( $includeText =~ /%SEARCH{.*?}%/ ) { if ( $cacheSearch ne "on" ) { $readCache = 0; writeDebug( "cacheSearch $cacheSearch" );} }; #include topic ACTIONSEARCH?? if ( $text =~ /%ACTIONSEARCH{.*?}%/ ) { if ( $cacheSearch ne "on" ) { $readCache = 0; } }; #writeDebug( "readCache: $readCache" ); } } #include topic SEARCH?? if ( $text =~ /%SEARCH{.*?}%/ ) { if ( $cacheSearch ne "on" ) { $readCache = 0; writeDebug( "cacheSearch $cacheSearch" );} } #include topic ACTIONSEARCH?? if ( $text =~ /%ACTIONSEARCH{.*?}%/ ) { if ( $cacheSearch ne "on" ) { $readCache = 0; } } #is newer then templates?? my @templateStat = stat( "$TWiki::templateDir/$webName" ); if ( ( $templateStat[9] ne "" ) && ( $templateStat[9] > $cacheStat[9] ) ) { $readCache = 0; } if ( $readCache ) { #&TWiki::Func::writeDebug( "reading from cache $cacheDir/$webName/$topic.html" ); $tmpl = &TWiki::Store::readFile( "$cacheDir/$webName/$topic.html" ); TWiki::writeHeader( $query ); print $tmpl; return; } } } } # CACHE END # get view template, standard view or a view with a different skin $tmpl = &TWiki::Store::readTemplate( "view", $skin ); if( ! $tmpl ) { TWiki::writeHeader( $query ); print "\n" . "

TWiki Installation Error

\n" . "Template file view.tmpl not found or template directory \n" . "$TWiki::templateDir not found.

\n" . "Check the \$templateDir variable in TWiki.cfg.\n" . "\n"; return; } writeDebugTimes( "view - readTemplate" ); if( ! &TWiki::Store::webExists( $webName ) ) { my $url = &TWiki::getOopsUrl( $webName, $topic, "oopsnoweb" ); TWiki::redirect( $query, $url ); return; } writeDebugTimes( "view - webExists" ); if( $unlock eq "on" ) { # unlock topic, user cancelled out of edit &TWiki::Store::lockTopic( $topic, "on" ); } if( $viewRaw ) { my $vtext = "

"; # suppose the CSS has a declaration: .invisibleInNormal {display:none} $tmpl =~ s/[.]invisibleInNormal/.invisibleInRaw/gos; if( $viewRaw !~ /debug/i ) { $text =~ s/%META[\:A-Z]*{[^\}]*}%[\n\r]*//gos; } } writeDebugTimes( "view - get rev info" ); if( ! $viewRaw ) { $text = &TWiki::handleCommonTags( $text, $topic ); writeDebugTimes( "view - handleCommonTags done" ); if ( $noHtml ne "" ) { TWiki::writeHeaderFull( $query, '', "plain/text", 0); print $text; return; } $text = &TWiki::Render::getRenderedVersion( $text ); ## LT addded ::Render writeDebugTimes( "view - getRendereredVersion done" ); } if( $TWiki::doLogTopicView ) { # write log entry &TWiki::Store::writeLog( "view", "$webName.$topic", $extra ); } my( $mirrorSiteName, $mirrorViewURL, $mirrorLink, $mirrorNote ) = &TWiki::readOnlyMirrorWeb( $webName ); if( $mirrorSiteName ) { # disable edit and attach # FIXME: won't work with non-default skins, see %EDITURL% $tmpl =~ s/%EDITTOPIC%/$mirrorLink | Edit<\/strike>/o; $tmpl =~ s/]*?>Attach<\/a>/Attach<\/strike>/oi; if( $topicExists ) { # remove the NOINDEX meta tag $tmpl =~ s/]*>//goi; } else { $text = ""; } $tmpl =~ s/%REVTITLE%//go; } elsif( $rev < $maxrev ) { # disable edit of previous revisions - FIXME consider change to use two templates # FIXME: won't work with non-default skins, see %EDITURL% $tmpl =~ s/%EDITTOPIC%/Edit<\/strike>/o; $tmpl =~ s/]*?>Attach<\/a>/Attach<\/strike>/oi; $tmpl =~ s|]*?>Rename/move<\/a>|Rename/move<\/strike>|oi; $tmpl =~ s/%REVTITLE%/\(r1.$rev\)/go; $tmpl =~ s/%REVARG%/&rev=1.$rev/go; } else { # Remove the NOINDEX meta tag (for robots) from both Edit and # Create pages $tmpl =~ s/]*>//goi; my $editAction = $topicExists ? 'Edit' : 'Create'; if ( $editAction eq 'Create' && &TWiki::Store::_readTemplateFile("oopscreate", $skin ) ) { my $url = &TWiki::getOopsUrl( $webName, $topic, "oopscreate" ); my $warndisplay; # if a template oopscreate exists, use it and set the vars in it: # %PARAM1% to the intended topic name # %PARAM2% to none if the topic is a wiki topic, # (use as a CSS class in the template) if( (&TWiki::isWikiName($topic)) || ($topic =~ /^[A-Z]{3,}$/ ) ) { $warndisplay = "none"; } else { $warndisplay = "warn"; } TWiki::redirect( $query, "$url¶m1=$topic¶m2=$warndisplay"); return; } # Special case for 'view' to handle %EDITTOPIC% and Edit vs. Create. # New %EDITURL% variable is implemented by handleCommonTags, suffixes # '?t=NNNN' to ensure that every Edit link is unique, fixing # Codev.RefreshEditPage bug relating to caching of Edit page. $tmpl =~ s!%EDITTOPIC%!$editAction!go; # FIXME: Implement ColasNahaboo's suggested %EDITLINK% along the # same lines, within handleCommonTags $tmpl =~ s/%REVTITLE%//go; $tmpl =~ s/%REVARG%//go; } my $i = $maxrev; my $j = $maxrev; my $revisions = ""; my $breakRev = 0; if( ( $TWiki::numberOfRevisions > 0 ) && ( $TWiki::numberOfRevisions < $maxrev ) ) { $breakRev = $maxrev - $TWiki::numberOfRevisions + 1; } while( $i > 0 ) { if( $i == $rev) { $revisions = "$revisions | r1.$i"; } else { $revisions = "$revisions | r1.$i"; } if( $i != 1 ) { if( $i == $breakRev ) { $i = 1; } else { $j = $i - 1; $revisions = "$revisions | >"; } } $i = $i - 1; } $tmpl =~ s/%REVISIONS%/$revisions/go; if( $topicExists ) { $revuser = &TWiki::userToWikiName( $revuser ); my $temp = &TWiki::Render::getRenderedVersion( "r1.$rev - $revdate GMT - $revuser" ); $tmpl =~ s/%REVINFO%/$temp$mirrorNote/go; } else { $tmpl =~ s/%REVINFO%/$mirrorNote/go; } $tmpl = &TWiki::handleCommonTags( $tmpl, $topic ); if( $viewRaw ) { $tmpl =~ s/%META{[^}]*}%//go; } else { $tmpl = &TWiki::handleMetaTags( $webName, $topic, $tmpl, $meta, ( $rev == $maxrev ) ); } writeDebugTimes( "view - handleCommonTags for template done" ); $tmpl = &TWiki::Render::getRenderedVersion( $tmpl, "", $meta ); ## better to use meta rendering? $tmpl =~ s/%TEXT%/$text/go; $tmpl =~ s/%MAXREV%/1.$maxrev/go; $tmpl =~ s/%CURRREV%/1.$rev/go; $tmpl =~ s|( ?) *\n?|$1|gois; # remove tags (PTh 06 Nov 2000) # Write header based on "contenttype" parameter, used to produce # MIME types like text/plain or text/xml, e.g. for RSS feeds. my $contentType = $query->param( "contenttype" ); if( $contentType ) { TWiki::writeHeaderFull( $query, 'basic', $contentType, 0); if( $skin =~ /^rss/ ) { $tmpl =~ s/]*>//g; # remove image tags $tmpl =~ s/]*>//g; # remove anchor tags $tmpl =~ s/<\/a>//g; # remove anchor tags } } elsif( $skin =~ /^rss/ ) { TWiki::writeHeaderFull( $query, 'basic', 'text/xml', 0); $tmpl =~ s/]*>//g; # remove image tags $tmpl =~ s/]*>//g; # remove anchor tags $tmpl =~ s/<\/a>//g; # remove anchor tags } else { TWiki::writeHeader( $query ); } # print page content print $tmpl; # CACHE START #&TWiki::Func::writeDebug( "$paramCount - $noCache" ); if ( ( ( $paramCount < 0 ) || ( ( $paramCount == 0 ) && ( $noCache ne "" ) ) ) && $adminUseCache && ( $useCache eq "on" ) ) { #&TWiki::Func::writeDebug( "caching to $cacheDir/$webName/$topic.html" ); my $file = "$cacheDir/$webName/$topic.html"; checkCacheDir( $cacheDir, $webName ); &TWiki::Store::saveFile( $file, $tmpl ); chmod( $cacheFileRights, $file ); if ( $cacheGroup ne "" ) { changeGroup( $file, $cacheGroup ); } } # CACHE END writeDebugTimes( "view - done" ); } sub checkCacheDir { my ( $cacheDir, $web ) = @_; if ( $web ne "" ) { $cacheDir .= "/$web"; } if ( opendir( DIR, $cacheDir ) ) { closedir( DIR ); } else { umask( 0 ); mkdir( $cacheDir, $cacheDirRights ); if ( $cacheGroup ne "" ) { changeGroup( $cacheDir, $cacheGroup ); } } } sub changeGroup { my ( $file, $group ) = @_; my @tmp = stat( $file ); my $gid = getgrnam( $group ) or return; if ( $tmp[5] != $gid ) { chown( $tmp[4], $gid, $file ); } } # EOF