From 54c56019d1cac35da1031db5aa269d97f526e185 Mon Sep 17 00:00:00 2001 From: Colin Kuskie Date: Mon, 21 Jun 2010 14:38:46 -0700 Subject: [PATCH] Update lastPost information in the Thread and CS when a Post is archived. Made a separate method so it can be used by other actions, and in the upgrade script. Fixes bug #11628. --- docs/changelog/7.x.x.txt | 1 + docs/upgrades/upgrade_7.9.7-7.9.8.pl | 42 ++++++++- lib/WebGUI/Asset/Post.pm | 87 +++++++++++++++++-- t/Asset/Post/archiving.t | 122 +++++++++++++++++++++++++++ 4 files changed, 245 insertions(+), 7 deletions(-) create mode 100644 t/Asset/Post/archiving.t diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 9016c4e30..e3850bd76 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -3,6 +3,7 @@ - fixed #11656: Thingy: Select list fields are not sorted properly - fixed #11662: yahooapis.com sourced links - fixed #11658: tmpl var message missing in template help for the cart + - fixed #11628: Message Board: Last Post doesn't show up in CS Thread List 7.9.7 - added #11571: Allow return from photo edit view to gallery edit view diff --git a/docs/upgrades/upgrade_7.9.7-7.9.8.pl b/docs/upgrades/upgrade_7.9.7-7.9.8.pl index 7c91d5648..b5cea7794 100644 --- a/docs/upgrades/upgrade_7.9.7-7.9.8.pl +++ b/docs/upgrades/upgrade_7.9.7-7.9.8.pl @@ -22,17 +22,19 @@ use Getopt::Long; use WebGUI::Session; use WebGUI::Storage; use WebGUI::Asset; +use WebGUI::Asset::Wobject::Collaboration; +use WebGUI::Asset::Post::Thread; use WebGUI::ProfileField; - my $toVersion = '7.9.8'; my $quiet; # this line required - my $session = start(); # this line required # upgrade functions go here changeFirstDayOfWeekDefault($session); +updateLastPostCS($session); +updateLastPostThread($session); finish($session); # this line required @@ -51,11 +53,47 @@ finish($session); # this line required sub changeFirstDayOfWeekDefault { my $session = shift; print "\tMake the default for firstDayOfWeek a number instead of a string... " unless $quiet; + # and here's our code my $profileField = WebGUI::ProfileField->new($session, 'firstDayOfWeek'); my $properties = $profileField->get(); $properties->{dataDefault} = 0; $profileField->set($properties); + print "DONE!\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +# Describe what our function does +sub updateLastPostCS { + my $session = shift; + print "\tUpdating last post information in every Collaboration System. This could take a very long time... " unless $quiet; # and here's our code + my $getCs = WebGUI::Asset::Wobject::Collaboration->getIsa($session); + CS: while (my $cs = eval { $getCs->() } ) { + next CS if Exception::Class->caught(); + last CS if ! $cs; + next CS unless $cs->get('lastPostId'); + my $lastPost = WebGUI::Asset->newByDynamicClass($session, $cs->get('lastPostId')); + next CS unless $lastPost && $lastPost->get('status') eq 'archived'; + $lastPost->disqualifyAsLastPost; + } + print "DONE!\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +# Describe what our function does +sub updateLastPostThread { + my $session = shift; + print "\tUpdating last post information in every Thread. This could also take a very long time... " unless $quiet; + # and here's our code + my $getThread = WebGUI::Asset::Wobject::Collaboration->getIsa($session); + THREAD: while (my $thread = eval { $getThread->() } ) { + next THREAD if Exception::Class->caught(); + last THREAD if ! $thread; + next THREAD unless $thread->get('lastPostId'); + my $lastPost = WebGUI::Asset->newByDynamicClass($session, $thread->get('lastPostId')); + next THREAD unless $lastPost && $lastPost->get('status') eq 'archived'; + $lastPost->disqualifyAsLastPost; + } print "DONE!\n" unless $quiet; } diff --git a/lib/WebGUI/Asset/Post.pm b/lib/WebGUI/Asset/Post.pm index 3f1a5b8c6..e5a8b2b72 100644 --- a/lib/WebGUI/Asset/Post.pm +++ b/lib/WebGUI/Asset/Post.pm @@ -62,6 +62,7 @@ sub _fixReplyCount { my $lastPostId = $asset->getLineage( [ qw{ self descendants } ], { isa => 'WebGUI::Asset::Post', orderByClause => 'assetData.revisionDate desc', + limit => 1, } )->[0]; if (my $lastPost = WebGUI::Asset->newByDynamicClass( $self->session, $lastPostId ) ) { @@ -353,6 +354,53 @@ sub definition { } +#------------------------------------------------------------------- + +=head2 disqualifyAsLastPost ( ) + +This method should be called whenever something happens to the Post or Thread that would disqualify +it as being the last post in a Thread, or Collaboration System. Good examples are cutting to the +clipboard, trashing, or archiving. + +If the Post was the last post, it will find the second to last post for each kind of parent asset, +and update that asset with that Post's information. + +=cut + +sub disqualifyAsLastPost { + my $self = shift; + my $thread = $self->getThread; + if ($thread->get('lastPostId') eq $self->getId) { + my $secondary_post = $thread->getLineage(['descendants'], { + returnObjects => 1, + includeOnlyClasses => ["WebGUI::Asset::Post", ], + limit => 1, + orderByClause => 'revisionDate,lineage DESC', + })->[0]; + if ($secondary_post) { ##Handle edge case for no other + $thread->update({ lastPostId => $secondary_post->getId, lastPostDate => $secondary_post->get('creationDate'), }); + } + else { + $thread->update({ lastPostId => '', lastPostDate => '', }); + } + } + my $cs = $thread->getParent; + if ($cs->get('lastPostId') eq $self->getId) { + my $secondary_post = $cs->getLineage(['descendants'], { + returnObjects => 1, + includeOnlyClasses => ["WebGUI::Asset::Post","WebGUI::Asset::Post::Thread"], + limit => 1, + orderByClause => 'revisionDate DESC', + })->[0]; + if ($secondary_post) { ##Handle edge case for no other + $cs->update({ lastPostId => $secondary_post->getId, lastPostDate => $secondary_post->get('creationDate'), }); + } + else { + $cs->update({ lastPostId => '', lastPostDate => '', }); + } + } +} + #------------------------------------------------------------------- =head2 DESTROY @@ -1188,6 +1236,31 @@ sub purgeRevision { +#------------------------------------------------------------------- + +=head2 qualifyAsLastPost ( ) + +This method should be called whenever something happens to the Post or Thread that would qualify +it as being the last post in a Thread, or Collaboration System. Good examples are pasting from +the clipboard, restoring from the trash, or changing the state from archiving. + +It checks the parent Thread and CS to see if it is now the last Post, and updates that asset with +its information. + +=cut + +sub qualifyAsLastPost { + my ($self) = @_; + my $thread = $self->getThread(); + if ($self->get('creationDate') > $thread->get('lastPostDate')) { + $thread->update({ lastPostId => $self->getId, lastPostDate => $self->get('creationDate'), }); + } + my $cs = $thread->getParent; + if ($self->get('creationDate') > $cs->get('lastPostDate')) { + $cs->update({ lastPostId => $self->getId, lastPostDate => $self->get('creationDate'), }); + } +} + #------------------------------------------------------------------- =head2 rate ( rating ) @@ -1292,14 +1365,16 @@ sub setParent { =head2 setStatusArchived ( ) -Sets the status of this post to archived. +Sets the status of this post to archived. Updates the parent thread and CS to remove +the lastPost, if this post is the last post. =cut sub setStatusArchived { - my ($self) = @_; - $self->update({status=>'archived'}); + my ($self) = @_; + $self->update({status=>'archived'}); + $self->disqualifyAsLastPost; } @@ -1308,13 +1383,15 @@ sub setStatusArchived { =head2 setStatusUnarchived ( ) Sets the status of this post to approved, but does so without any of the normal notifications and other stuff. +Updates the last post information in the parent Thread and CS if applicable. =cut sub setStatusUnarchived { - my ($self) = @_; - $self->update({status=>'approved'}) if ($self->get("status") eq "archived"); + my ($self) = @_; + $self->update({status=>'approved'}) if ($self->get("status") eq "archived"); + $self->qualifyAsLastPost; } #------------------------------------------------------------------- diff --git a/t/Asset/Post/archiving.t b/t/Asset/Post/archiving.t new file mode 100644 index 000000000..013a4c25b --- /dev/null +++ b/t/Asset/Post/archiving.t @@ -0,0 +1,122 @@ +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2009 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------- + +# 1. The basic framework for a test suite for the Post Asset. +# Includes setup, cleanup, boilerplate, etc. Basically the really boring, +# repetitive parts of the test that you don't want to write yourself. +# 2. The tests for the features I've implemented; namely, functionality and +# general access controls on who can edit a post. + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../lib"; +use WebGUI::Test; +use WebGUI::Session; +use Test::More tests => 13; # increment this value for each test you create +use WebGUI::Asset::Wobject::Collaboration; +use WebGUI::Asset::Post; +use WebGUI::Asset::Post::Thread; + +my $session = WebGUI::Test->session; + +# Do our work in the import node +my $node = WebGUI::Asset->getImportNode($session); + +# Grab a named version tag +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Collab setup"}); + +# Need to create a Collaboration system in which the post lives. +my @addArgs = ( undef, undef, { skipAutoCommitWorkflows => 1, skipNotification => 1 } ); + +my $collab = $node->addChild({className => 'WebGUI::Asset::Wobject::Collaboration'}, @addArgs); + +# finally, add posts and threads to the collaboration system + +my $first_thread = $collab->addChild( + { className => 'WebGUI::Asset::Post::Thread', }, + undef, + WebGUI::Test->webguiBirthday, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +my $second_thread = $collab->addChild( + { className => 'WebGUI::Asset::Post::Thread', }, + undef, + WebGUI::Test->webguiBirthday, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +##Thread 1, Post 1 => t1p1 +my $t1p1 = $first_thread->addChild( + { className => 'WebGUI::Asset::Post', }, + undef, + WebGUI::Test->webguiBirthday, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +my $t1p2 = $first_thread->addChild( + { className => 'WebGUI::Asset::Post', }, + undef, + WebGUI::Test->webguiBirthday + 1, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +my $past = time()-15; + +my $t2p1 = $second_thread->addChild( + { className => 'WebGUI::Asset::Post', }, + undef, + $past, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +my $t2p2 = $second_thread->addChild( + { className => 'WebGUI::Asset::Post', }, + undef, + undef, + { skipAutoCommitWorkflows => 1, skipNotification => 1 } +); + +$versionTag->commit(); +WebGUI::Test->addToCleanup($versionTag); + +foreach my $asset ($collab, $t1p1, $t1p2, $t2p1, $t2p2, $first_thread, $second_thread, ) { + $asset = $asset->cloneFromDb; +} + +is $collab->getChildCount, 2, 'collab has correct number of children'; + +is $collab->get('lastPostId'), $t2p2->getId, 'lastPostId set in collab'; +is $collab->get('lastPostDate'), $t2p2->get('creationDate'), 'lastPostDate, too'; + +$t2p2->setStatusArchived; +is $t2p2->get('status'), 'archived', 'setStatusArchived set the post to be archived'; + +$second_thread = $second_thread->cloneFromDb; +is $second_thread->get('lastPostId'), $t2p1->getId, '.. updated lastPostId in the thread'; +is $second_thread->get('lastPostDate'), $t2p1->get('creationDate'), '... lastPostDate, too'; + +$collab = $collab->cloneFromDb; +is $collab->get('lastPostId'), $t2p1->getId, '.. updated lastPostId in the CS'; +is $collab->get('lastPostDate'), $t2p1->get('creationDate'), '... lastPostDate, too'; + +$t2p2->setStatusUnarchived; +is $t2p2->get('status'), 'approved', 'setStatusUnarchived sets the post back to approved'; + +$second_thread = $second_thread->cloneFromDb; +is $second_thread->get('lastPostId'), $t2p2->getId, '.. updated lastPostId in the thread'; +is $second_thread->get('lastPostDate'), $t2p2->get('creationDate'), '... lastPostDate, too'; + +$collab = $collab->cloneFromDb; +is $collab->get('lastPostId'), $t2p2->getId, '.. updated lastPostId in the CS'; +is $collab->get('lastPostDate'), $t2p2->get('creationDate'), '... lastPostDate, too'; + +#vim:ft=perl