Skip to content

Commit

Permalink
Item8848: the TOPICINFO fields in the cache cannot be trusted; a new …
Browse files Browse the repository at this point in the history
…rev might be committed without an update to the topicinfo, or the cache topic might be written to without a checkin. In iether case, the topicinfo is wrong. So, consider the head of the RCS history to be the master source of revision info, but only load it when it's actually asked for e.g. for rendering or comparisons. We still might have a cache topic that is ahead of the rev history, but that's unavoidable and will self-repeait the next time that topic is properly committed.

git-svn-id: http://svn.foswiki.org/trunk@7213 0b4bb1d4-4e5a-0410-9cc4-b2b747904278
  • Loading branch information
CrawfordCurrie authored and CrawfordCurrie committed Apr 18, 2010
1 parent 5a87e16 commit aad5888
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 48 deletions.
1 change: 1 addition & 0 deletions UnitTestContrib/test/unit/Fn_IF.pm
Expand Up @@ -1254,6 +1254,7 @@ PONG

$meta =
Foswiki::Meta->load( $this->{session}, $this->{test_web}, $topicName );
$meta->getRevisionInfo();
my $ti = $meta->get('TOPICINFO');

$this->assert_str_equals( $ti->{version}, '1.1' );
Expand Down
26 changes: 10 additions & 16 deletions UnitTestContrib/test/unit/Fn_REVINFO.pm
Expand Up @@ -173,16 +173,13 @@ sub test_compatibility1 {
{
return;
}
$this->assert(
open(
F, '>', "$Foswiki::cfg{DataDir}/$this->{test_web}/CrikeyMoses.txt"
)
);
print F <<'HERE';
my $topicObject =
Foswiki::Meta->new(
$this->{session}, $this->{test_web}, 'CrikeyMoses', <<'HERE');
%META:TOPICINFO{author="ScumBag" date="1120846368" format="1.1" version="$Rev$"}%
HERE
close(F);
my $topicObject =
$topicObject->save();
$topicObject =
Foswiki::Meta->load( $this->{session}, $this->{test_web}, 'CrikeyMoses' );
my $ui =
$topicObject->expandMacros('%REVINFO{format="$username $wikiname"}%');
Expand All @@ -201,16 +198,13 @@ sub test_compatibility2 {
{
return;
}
$this->assert(
open(
F, '>', "$Foswiki::cfg{DataDir}/$this->{test_web}/CrikeyMoses.txt"
)
);
print F <<'HERE';
my $topicObject =
Foswiki::Meta->new(
$this->{session}, $this->{test_web}, 'CrikeyMoses', <<'HERE');
%META:TOPICINFO{author="scum" date="1120846368" format="1.1" version="$Rev$"}%
HERE
close(F);
my $topicObject =
$topicObject->save();
$topicObject =
Foswiki::Meta->load( $this->{session}, $this->{test_web}, 'CrikeyMoses' );
my $ui =
$topicObject->expandMacros('%REVINFO{format="$username $wikiname"}%');
Expand Down
2 changes: 1 addition & 1 deletion UnitTestContrib/test/unit/StoreSmokeTests.pm
Expand Up @@ -272,7 +272,7 @@ sub verify_releaselocksonsave {

# get the date
my $m = Foswiki::Meta->load( $this->{session}, $this->{test_web}, $topic );
my $t1 = $m->get('TOPICINFO')->{date};
my $t1 = $m->getRevisionInfo()->{date};

# create rev 2 as TestUser1
$query = new Unit::Request(
Expand Down
14 changes: 9 additions & 5 deletions UnitTestContrib/test/unit/StoreTests.pm
Expand Up @@ -298,6 +298,7 @@ sub test_beforeSaveHandlerChangeText {

# remove topicinfo, useless for test
$readMeta->remove('TOPICINFO');
$meta->remove('TOPICINFO');

# set expected meta
$meta->putKeyed( 'FIELD', { name => 'fieldname', value => 'text' } );
Expand Down Expand Up @@ -330,7 +331,6 @@ sub test_beforeSaveHandlerChangeMeta {
$meta->putKeyed( "FIELD", $args );
$meta->save( user => $this->{test_user_login} );
$this->assert( $this->{session}->topicExists( $web, $topic ) );

my $readMeta = Foswiki::Meta->load( $this->{session}, $web, $topic );
my $readText = $readMeta->text;

Expand All @@ -341,8 +341,10 @@ sub test_beforeSaveHandlerChangeMeta {

# set expected meta
$meta->putKeyed( 'FIELD', { name => 'fieldname', value => 'meta' } );
delete $meta->get( 'TOPICINFO')->{rev};
delete $readMeta->get( 'TOPICINFO')->{rev};
foreach my $fld qw(rev version date) {
delete $meta->get( 'TOPICINFO')->{$fld};
delete $readMeta->get( 'TOPICINFO')->{$fld};
}
$this->assert_str_equals( $meta->stringify(), $readMeta->stringify() );
my $webObject = Foswiki::Meta->new( $this->{session}, $web );
$webObject->removeFromStore();
Expand Down Expand Up @@ -383,8 +385,10 @@ sub test_beforeSaveHandlerChangeBoth {

# set expected meta
$meta->putKeyed( 'FIELD', { name => 'fieldname', value => 'meta' } );
delete $meta->get( 'TOPICINFO' )->{rev};
delete $readMeta->get( 'TOPICINFO')->{rev};
foreach my $fld qw(rev version date) {
delete $meta->get( 'TOPICINFO')->{$fld};
delete $readMeta->get( 'TOPICINFO')->{$fld};
}
$this->assert_str_equals( $meta->stringify(), $readMeta->stringify() );
my $webObject = Foswiki::Meta->new( $this->{session}, $web );
$webObject->removeFromStore();
Expand Down
2 changes: 2 additions & 0 deletions core/data/System/MetaData.txt
Expand Up @@ -58,6 +58,8 @@ This macro caches some of the information that would normally be derived from th
| format | Format of this topic, will be used for automatic format conversion |
| reprev | Set when a revision is overwritten by the same author within the {ReplaceIfEditedAgainWithin} window (set in [[%SCRIPTURL{configure}%][ =configure= ]]). If =reprev= is the same as =version=, it prevents Foswiki from attempting to do a 3-way merge when merging overlapping edits by two different users. |

Note that the *version and date fields are advisory only* and cannot be trusted. This is because processes outside of Foswiki's control may write topic files without maintaining these fields.

---+++ META:TOPICMOVED

This only exists if the topic has been moved. If a topic is moved more than once, only the most recent META:TOPICMOVED meta datum exists in the topic. Older ones can to be found in the topic history.
Expand Down
66 changes: 40 additions & 26 deletions core/lib/Foswiki/Meta.pm
Expand Up @@ -1062,25 +1062,27 @@ Set TOPICINFO information on the object, as specified by the parameters.
sub setRevisionInfo {
my ( $this, $data ) = @_;

my $ti = $this->get('TOPICINFO') || {};

foreach my $k (keys %$data) {
$ti->{$k} = $data->{$k};
}

# compatibility; older versions of the code use
# RCS rev numbers. Save with them so old code can
# read these topics
my %args = %$data;
$args{version} = 1 if $args{version} < 1;
$args{version} = '1.' . $args{version};
$args{format} = $EMBEDDING_FORMAT_VERSION;
$ti->{version} = 1 if $ti->{version} < 1;
$ti->{rev} = $ti->{version};
$ti->{version} = '1.' . $ti->{version};
$ti->{format} = $EMBEDDING_FORMAT_VERSION;

$this->put( 'TOPICINFO', \%args );
$this->put( 'TOPICINFO', $ti );
}

=begin TML
---++ ObjectMethod getRevisionInfo($fromrev) -> \%info
* =$fromrev= revision number. If 0, undef, or out-of-range, will get info about the most recent revision.
Try and get revision info from the meta information, or, if it is not
present, kick down to the Store module for the same information.
---++ ObjectMethod getRevisionInfo() -> \%info
Return %info with at least:
| date | in epochSec |
Expand All @@ -1094,8 +1096,18 @@ sub getRevisionInfo {
my $this = shift;

my $info;

# This used to try and get revision info from the meta
# information and only kick down to the Store module for the
# same information if it was not present. However there have
# been several cases where the meta information in the cache
# is badly out of step with the store, and the conclusion is
# that it can't be trusted. For this reason, when meta is read
# TOPICINFO version field is automatically undefined, which
# forces this function to re-get it from the store.
my $topicinfo = $this->get('TOPICINFO');
if ($topicinfo) {

if ($topicinfo && defined $topicinfo->{version}) {
$info = {
date => $topicinfo->{date},
author => $topicinfo->{author},
Expand All @@ -1119,12 +1131,13 @@ sub getRevisionInfo {
return $info;
}

# Determins, and caches, the topic revision info of the base version,
#warning: this is a horrid little legacy of the InfoCache object, and should be done away with.
# Determines, and caches, the topic revision info of the base version,
# SMELL: this is a horrid little legacy of the InfoCache object, and
# should be done away with.
sub getRev1Info {
my ( $this, $attr ) = @_;

#my ( $web, $topic ) = Foswiki::Func::normalizeWebTopicName( $this->{_defaultWeb}, $webtopic );
#my ( $web, $topic ) = Foswiki::Func::normalizeWebTopicName( $this->{_defaultWeb}, $webtopic );
my $web = $this->web;
my $topic = $this->topic;

Expand Down Expand Up @@ -1536,6 +1549,10 @@ sub save {

my $plugins = $this->{_session}->{plugins};

# make sure version and date in TOPICINFO are up-to-date
# (side effect of getRevisionInfo)
$this->getRevisionInfo();

# Semantics inherited from Cairo. See
# Foswiki:Codev.BugBeforeSaveHandlerBroken
if ( $plugins->haveHandlerFor('beforeSaveHandler') ) {
Expand Down Expand Up @@ -2958,18 +2975,15 @@ sub setEmbeddedStoreForm {
# Make sure we update the topic format for when we save
$ti->{format} = $EMBEDDING_FORMAT_VERSION;

# add the rev derived from version=''
if ( $ti->{version} ) {
if ( $ti->{version} =~ /(\d+\.)?(\d*)/ ) {
$ti->{rev} = $2;
}
else {
$ti->{rev} = 1;
}
}
else {
$ti->{version} = $ti->{rev} = 0;
}
# The version, date and rev cached in TOPICINFO *cannot be trusted*
# See Tasks.Item8848
# These fields will be populated from the store when
# getRevisionInfo() is called. Important: always do this
# before calling get('TOPICINFO') if you want to use the
# rev info!
delete $ti->{version};
delete $ti->{date};
delete $ti->{rev};
}

# Other meta-data
Expand Down
3 changes: 3 additions & 0 deletions core/lib/Foswiki/Search/InfoCache.pm
Expand Up @@ -396,6 +396,7 @@ sub _getListOfWebs {
}
else {
$web = Foswiki::Sandbox::untaint( $web,\&Foswiki::Sandbox::validateWebName );
ASSERT($web);
push( @tmpWebs, $web );
$webObject = Foswiki::Meta->new( $session, $web );
}
Expand All @@ -406,6 +407,7 @@ sub _getListOfWebs {
unless $Foswiki::WebFilter::user_allowed->ok(
$session, $w );
$w = Foswiki::Sandbox::untaint( $w,\&Foswiki::Sandbox::validateWebName );
ASSERT($web);
push( @tmpWebs, $w );
}
}
Expand Down Expand Up @@ -438,6 +440,7 @@ sub _getListOfWebs {

my @webs;
foreach my $web (@tmpWebs) {
next unless defined $web;
push( @webs, $web ) unless $excludeWeb{$web};
$excludeWeb{$web} = 1; # eliminate duplicates
}
Expand Down
4 changes: 4 additions & 0 deletions core/lib/Foswiki/Store/QueryAlgorithms/BruteForce.pm
Expand Up @@ -164,6 +164,10 @@ sub getField {
if ( $Foswiki::Query::Node::aliases{$field} ) {
$realField = $Foswiki::Query::Node::aliases{$field};
}
if ($realField eq 'META:TOPICINFO') {
# Ensure the revision info is populated from the store
$data->getRevisionInfo();
}
if ( $realField =~ s/^META:// ) {
if ( $Foswiki::Query::Node::isArrayType{$realField} ) {

Expand Down
1 change: 1 addition & 0 deletions core/lib/Foswiki/UI/Save.pm
Expand Up @@ -262,6 +262,7 @@ sub buildNewTopic {

require Foswiki::Merge;

$topicObject->getRevisionInfo();
my $pti = $topicObject->get('TOPICINFO');
if ( $pti->{reprev}
&& $pti->{version}
Expand Down

0 comments on commit aad5888

Please sign in to comment.