From e7c6fe6cae5afcc27c5e85108b615967b13ca397 Mon Sep 17 00:00:00 2001 From: JT Smith Date: Sat, 11 Oct 2008 20:33:38 +0000 Subject: [PATCH] - Added Comments asset aspect, which allows comments to be added to any asset easily. - Added comments aspect to wiki. --- docs/changelog/7.x.x.txt | 3 + .../packages-7.6.1/default-wiki-page.wgpkg | Bin 0 -> 1839 bytes docs/upgrades/upgrade_7.6.0-7.6.1.pl | 31 +- lib/WebGUI/Asset/WikiPage.pm | 45 +-- lib/WebGUI/AssetAspect/Comments.pm | 363 ++++++++++++++++++ lib/WebGUI/Form/CommentRating.pm | 144 +++++++ t/Asset/WikiPage.t | 30 +- www/extras/form/CommentRating/0.png | Bin 0 -> 747 bytes www/extras/form/CommentRating/1.png | Bin 0 -> 2113 bytes www/extras/form/CommentRating/2.png | Bin 0 -> 1449 bytes www/extras/form/CommentRating/3.png | Bin 0 -> 1519 bytes www/extras/form/CommentRating/4.png | Bin 0 -> 1534 bytes www/extras/form/CommentRating/5.png | Bin 0 -> 2252 bytes 13 files changed, 590 insertions(+), 26 deletions(-) create mode 100644 docs/upgrades/packages-7.6.1/default-wiki-page.wgpkg create mode 100644 lib/WebGUI/AssetAspect/Comments.pm create mode 100644 lib/WebGUI/Form/CommentRating.pm create mode 100644 www/extras/form/CommentRating/0.png create mode 100644 www/extras/form/CommentRating/1.png create mode 100644 www/extras/form/CommentRating/2.png create mode 100644 www/extras/form/CommentRating/3.png create mode 100644 www/extras/form/CommentRating/4.png create mode 100644 www/extras/form/CommentRating/5.png diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 471e940c7..123f42758 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -11,6 +11,9 @@ - fixed: Thingy list type form elements do not support key/value pairs (SDH Consulting Group) - rfe: enable/disable pagination in asset manager (#756) + - Added Comments asset aspect, which allows comments to be added to any asset + easily. + - Added comments aspect to wiki. - Removed cart icon from ViewCart macro. - Updated WebGUI::Shop::PayDriver::processTransaction() to accept a transaction as a param. diff --git a/docs/upgrades/packages-7.6.1/default-wiki-page.wgpkg b/docs/upgrades/packages-7.6.1/default-wiki-page.wgpkg new file mode 100644 index 0000000000000000000000000000000000000000..d6f6ed5f17e14cec2654d3fe0acbd1d43440bada GIT binary patch literal 1839 zcmV+~2hjK*iwFP!000001MOJrZ`(Ey_vii^*5nGnyN2j;}`bd{xS(~ou(rwua zTB0o`k|>ar9kzB@`X>wcG~TJr(Ywk>-GjU0;CUrWlB>Crd??ja~*C~2xQ~&)>-2m_-CJAA^n!7 zz~*+_UEE#w2lL)*T`7^=lc6B7--Y#t=Z5un-TiK7W&OL|UbjX7&4<4pYW-UeZ~A>` z`0@xgVO?n<>4epZNB;P8&kUtfq)ckg6pk7)r1T}DnguWBI9lJY8)BS@sM1F_sgN-H z-0&n>6fLG<`)DJ-o@S$ntB?hjH(|32o&;iMgtqO3nkOPvd|jNFB}{%$Or8X*hOiqg z=`o|hIi0LCQl*$Km@fD=KLfYV<1{i+k2lw9&tznT2W(8UNH=Fd-%JswgXS zaF`zE^Dsz!k!UF*bvSfj*eUo9FXd%23_BG<&2_|p0tRssf0%=Z0iO~cJiDx;i|FBH zo%pb#3WnwxZzgmKuwB*-7job=%%WAI6$(QY@dcG)5%VEFfNV%fDA^d~KQ48m7c4<7 zLl#$zo^m!jr6U%B;W*(?_(golmO|4eu?&D-lZk_O$s9Uaw4}tqRE5P_Tq22f;I)qE zcG{A@;&MR@e!|Ku*5e0>v(69K%wosziv=d~l0bxv%TM z73;T(utBY~mLSo~Jbu>TDg-Y1oh?W^!m4CZy0lBrWNQS@L-9|n*bn*o;%VB~<>3M`JmI4qwnRw)BdUJpQ zx5zBkNyVu2Lo{?LBJ<%g570yMH`&0AcEm#IGm&Nym1|Neb9ad%lKHSNUWPQ8FtsWP z6)h3mDGsCRSj22q5-BIF1RL?T-0&o1l5608Ek%4rCD^uF$s(SxqBW?SBDtYiv=U8r zd_&{B>RqtWj~`Av&txReJ1?u-qI{aJAV*_EOJS;&vFBA@fyo8 zE@i*h>ss4VDxrHZ;?U31&w?XN8q|DWCZY zGI3dN|2o13Nj0FZJEM$8fddd>jdhH@s{GpmF4%i@;^ceojyy?}^udO8>2n$i;h1|9 zdKdVxK&em!svG*h3TIOYS;yS(Uw9y52Y&Z27&3hVjS!R*$G+ab8tAsL3OmKKIbxdL zzm_t3isx1ZnB9gZMAu`}MA)JcY}uBRlnkd;Rfm-@m(tWC^~j1|lvWnYf@~zT7V$>P+tPrq z8v#8xX_X!GlJC^N#;lhFOdxoz?Sxn`GCnmz0B~UBC;(A6j-5Sa{Bx{n(RlwkN zS$sgsh%G0|QaFn664A`x49#M1ZLs}`wLahqzO(1RoaEuY z;6q@bG+}RPk`n&6Rhz3*D7@wI$I<}@S4ESu+DlY>95d&3I-RHO{obH*C?MIC<2ODu z%0;u%>Ff`BUAKB30f!3U{-PPPW~orjPLkI`#(67J>2wn4#Z*fDTq^cY%k2|Od^u0G z8N?HvicOkMii48Z{pRF2=wAd+&->ZutaI`4_n*Rv)z8)Yxq`EcRo9JIC0fkyL_y;#328I9}006Hxb-@4t literal 0 HcmV?d00001 diff --git a/docs/upgrades/upgrade_7.6.0-7.6.1.pl b/docs/upgrades/upgrade_7.6.0-7.6.1.pl index beeffbfa0..4acd1d045 100644 --- a/docs/upgrades/upgrade_7.6.0-7.6.1.pl +++ b/docs/upgrades/upgrade_7.6.0-7.6.1.pl @@ -32,16 +32,44 @@ addExportExtensionsToConfigFile($session); fixShortAssetIds( $session ); addDataFormDataIndexes($session); addThingyColumns( $session ); +addCommentsAspect( $session ); +addCommentsAspectToWiki( $session ); finish($session); # this line required +#---------------------------------------------------------------------------- +sub addCommentsAspectToWiki { + my $session = shift; + print "\tAdding comments aspect to wiki..." unless $quiet; + my $db = $session->db; + my $pages = $db->read("select assetId,revisionDate from WikiPage"); + while (my ($id, $rev) = $pages->array) { + $db->write("insert into assetAspectComments (assetId, revisionDate, comments, averageCommentRating) values (?,?,'[]',0)",[$id,$rev]); + } + print "Done.\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +sub addCommentsAspect { + my $session = shift; + print "\tAdding comments asset aspect..." unless $quiet; + $session->db->write("create table assetAspectComments ( + assetId char(22) binary not null, + revisionDate bigint not null, + comments mediumtext, + averageCommentRating int, + primary key (assetId, revisionDate) + )"); + print "Done.\n" unless $quiet; +} + #---------------------------------------------------------------------------- # make sure each config file has the extensions to export as-is. however, if # this system received a backport, leave the field as is. sub addExportExtensionsToConfigFile { my $session = shift; - print "Adding binary export extensions to config file... " unless $quiet; + print "\tAdding binary export extensions to config file... " unless $quiet; # skip if the field has been defined already by backporting return if defined $session->config->get('exportBinaryExtensions'); @@ -53,6 +81,7 @@ sub addExportExtensionsToConfigFile { print "Done.\n" unless $quiet; } +#---------------------------------------------------------------------------- sub fixShortAssetIds { print "Fixing assets with short ids... " unless $quiet; my %assetIds = ( diff --git a/lib/WebGUI/Asset/WikiPage.pm b/lib/WebGUI/Asset/WikiPage.pm index 3bcce40cd..906dd7a49 100644 --- a/lib/WebGUI/Asset/WikiPage.pm +++ b/lib/WebGUI/Asset/WikiPage.pm @@ -10,8 +10,9 @@ package WebGUI::Asset::WikiPage; # http://www.plainblack.com info@plainblack.com # ------------------------------------------------------------------- -use base 'WebGUI::Asset'; use strict; +use Class::C3; +use base qw(WebGUI::AssetAspect::Comments WebGUI::Asset); use Tie::IxHash; use WebGUI::International; use WebGUI::Utility; @@ -40,7 +41,7 @@ Override the default method in order to deal with attachments. sub addRevision { my $self = shift; - my $newSelf = $self->SUPER::addRevision(@_); + my $newSelf = $self->next::method(@_); my $now = time(); $newSelf->update({ isHidden => 1, @@ -52,7 +53,7 @@ sub addRevision { sub canAdd { my $class = shift; my $session = shift; - $class->SUPER::canAdd($session, undef, '7'); + $class->next::method($session, undef, '7'); } #------------------------------------------------------------------- @@ -109,16 +110,7 @@ sub definition { properties => \%properties, }; - return $class->SUPER::definition($session, $definition); -} - - -#------------------------------------------------------------------- -# BUGGO: how to handle this? -sub duplicate { - my $self = shift; - my $newAsset = $self->SUPER::duplicate(@_); - return $newAsset; + return $class->next::method($session, $definition); } @@ -192,7 +184,7 @@ sub getWiki { #------------------------------------------------------------------- sub indexContent { my $self = shift; - my $indexer = $self->SUPER::indexContent; + my $indexer = $self->next::method; $indexer->addKeywords($self->get('content')); return $indexer; } @@ -216,7 +208,7 @@ sub preparePageTemplate { #------------------------------------------------------------------- sub prepareView { my $self = shift; - $self->SUPER::prepareView; + $self->next::method; $self->preparePageTemplate; } @@ -224,24 +216,27 @@ sub prepareView { #------------------------------------------------------------------- sub processPropertiesFromFormPost { my $self = shift; - $self->SUPER::processPropertiesFromFormPost(@_); + $self->next::method(@_); my $actionTaken = ($self->session->form->process("assetId") eq "new") ? "Created" : "Edited"; my $wiki = $self->getWiki; - $self->update({ groupIdView => $wiki->get('groupIdView'), - groupIdEdit => $wiki->get('groupToAdminister'), - actionTakenBy => $self->session->user->userId, - actionTaken => $actionTaken, - }); + my $properties = { + groupIdView => $wiki->get('groupIdView'), + groupIdEdit => $wiki->get('groupToAdminister'), + actionTakenBy => $self->session->user->userId, + actionTaken => $actionTaken, + }; if ($wiki->canAdminister) { - $self->update({isProtected => $self->session->form("isProtected")}); + $properties->{isProtected} = $self->session->form->get("isProtected"); } + $self->update($properties); + + # deal with attachments from the attachments form control my $options = { maxImageSize => $wiki->get('maxImageSize'), thumbnailSize => $wiki->get('thumbnailSize'), }; - # deal with attachments from the attachments form control my @attachments = $self->session->form->param("attachments"); my @tags = (); foreach my $assetId (@attachments) { @@ -311,7 +306,8 @@ Wrap update to force isHidden to be on, all the time. sub update { my $self = shift; my $properties = shift; - return $self->SUPER::update({%$properties, isHidden => 1}); + $properties->{isHidden} = 1; + return $self->next::method($properties); } #------------------------------------------------------------------- @@ -346,6 +342,7 @@ sub view { historyUrl => $self->getUrl("func=getHistory"), editContent => $self->getEditForm, allowsAttachments => $self->getWiki->get("allowAttachments"), + comments => $self->getFormattedComments(), content => $self->getWiki->autolinkHtml( $self->scrubContent, {skipTitles => [$self->get('title')]}, diff --git a/lib/WebGUI/AssetAspect/Comments.pm b/lib/WebGUI/AssetAspect/Comments.pm new file mode 100644 index 000000000..9e5cb532a --- /dev/null +++ b/lib/WebGUI/AssetAspect/Comments.pm @@ -0,0 +1,363 @@ +package WebGUI::AssetAspect::Comments; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2008 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 + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'Class::C3'; +use JSON; +use Tie::IxHash; +use WebGUI::Exception; +use WebGUI::Form; +use WebGUI::HTML; +use WebGUI::Utility; + +=head1 NAME + +Package WebGUI::AssetAspect::Comments + +=head1 DESCRIPTION + +This is an aspect which makes adding comments to existing assets trivial. + +=head1 SYNOPSIS + + use Class::C3; + use base qw(WebGUI::AssetAspect::Comments WebGUI::Asset); + +And then where-ever you would call $self->SUPER::someMethodName call $self->next::method instead. + +=head1 METHODS + +These methods are available from this class: + +=cut + +#------------------------------------------------------------------- + +=head2 addComment ( comment [, rating, user ] ) + +Posts a comment. + +=head3 comment + +A string that acts as a comment from a user. + +=head3 rating + +Defaults to 0. An integer between 0 and 5 inclusive. 0 represents N/A, 1 represents a negative rating, 3 represents a neutral rating, and 5 represents a positive rating. + +=head3 user + +Defaults to the current user. A WebGUI::User object. + +=cut + +sub addComment { + my ($self, $comment, $rating, $user) = @_; + my $session = $self->session; + $user ||= $session->user; + $rating ||= 0; + + # add the new comment to the list of comments + my $comments = $self->get('comments'); + push @$comments, { + id => $session->id->generate, + alias => $user->profileField('alias'), + userId => $user->userId, + comment => $comment, + rating => $rating, + date => time(), + ip => $session->var->get('lastIP'), + }; + + # calculate average + my $sum = 0; + my $count = 0; + foreach my $comment (@$comments) { + next unless $comment->{rating} > 0; # skip n/a ratings + $count++; + $sum += $comment->{rating}; + } + my $average = 0; + if ($count > 0) { + $average = $sum/$count; + } + + # update the database + $self->update({comments=>$comments, averageCommentRating=>$average}); + + # add karma + if ($session->setting->get('useKarma')) { + unless ($user->isVisitor) { + $user->karma($self->getKarmaAmountPerComment, $self->getId, 'Left comment for '.$self->getName.' '.$self->getTitle); + } + } +} + +#------------------------------------------------------------------- + +=head2 canComment () + +Returns a boolean indicating whether the current user can post a comment. + +=cut + +sub canComment { + my $self = shift; + return $self->session->user->isInGroup($self->getGroupToComment) || $self->canEdit; +} + + +#------------------------------------------------------------------- + +=head2 definition + +Extends the definition to add the comments and averageCommentRating fields. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my %properties; + tie %properties, 'Tie::IxHash'; + %properties = ( + comments => { + noFormPost => 1, + fieldType => "hidden", + defaultValue => [], + }, + averageCommentRating => { + noFormPost => 1, + fieldType => "hidden", + defaultValue => 0, + }, + ); + push(@{$definition}, { + autoGenerateForms => 1, + tableName => 'assetAspectComments', + className => 'WebGUI::Asset::Sku::BazaarItem', + properties => \%properties + }); + return $class->next::method($session, $definition); +} + +#------------------------------------------------------------------- + +=head2 deleteComment ( id ) + +Deletes a comment. + +=head3 id + +The GUID for the comment to delete. + +=cut + +sub deleteComment { + my ($self, $id) = @_; + my $session = $self->session; + + # remove the comment from the list of comments and calculate the average + my $comments = $self->get('comments'); + my @updatedComments; + my $sum = 0; + my $count = 0; + my $userId; + foreach my $comment (@$comments) { + if ($comment->{id} eq $id) { + $userId = $comment->{userId}; + next; + } + push @updatedComments, $comment; + next unless $comment->{rating} > 0; # skip n/a ratings + $count++; + $sum += $comment->{rating}; + } + + # update the database + my $average = 0; + if ($count > 0) { + $average = $sum/$count; + } + $self->update({comments=>\@updatedComments, averageCommentRating=>$average}); + + # remove karma + if ($session->setting->get('useKarma')) { + if (defined $userId) { + my $user = WebGUI::User->new($session, $userId); + unless ($user->isVisitor) { + $user->karma(($self->getKarmaAmountPerComment * -1), $self->getId, 'Deleted comment for '.$self->getName.' '.$self->getTitle); + } + } + } +} + +#------------------------------------------------------------------- + +=head2 get () + +See SUPER::get(). Extends the get() method to automatically decode the comments field into a Perl hash structure. + +=cut + +sub get { + my $self = shift; + my $param = shift; + if ($param eq 'comments') { + return JSON->new->decode($self->next::method('comments')||'[]'); + } + return $self->next::method($param, @_); +} + +#------------------------------------------------------------------- + +=head2 getAverageCommentRatingIcon () + +Returns the HTML needed to render the average rating icon. + +=cut + +sub getAverageCommentRatingIcon { + my $self = shift; + return q{}.$self->get('averageCommentRating').q{}; + +} + +#------------------------------------------------------------------- + +=head2 getFormattedComments () + +Returns an HTML string listing the comments so far and the leave a comment form if the user canComment(). + +=cut + +sub getFormattedComments { + my $self = shift; + my $session = $self->session; + my $url = $session->url; + my $out = '
'; + my $canEdit = $self->canEdit; + my $comments = $self->get('comments'); + foreach my $comment (@$comments) { + $out .= q{
}.$comment->{rating}.q{}; + if ($canEdit) { + $out .= q{ {id}).q{">[X] }; + } + $out .= q{}.$comment->{alias}.q{: "}.WebGUI::HTML::format($comment->{comment},'text').q{"
}; + } + if ($self->canComment) { + $out .= '
'; + $out .= WebGUI::Form::formHeader($session, {action=>$self->getUrl}); + $out .= WebGUI::Form::hidden($session, {name=>"func",value=>"addComment"}); + $out .= WebGUI::Form::textarea($session, {name=>"comment"}); + $out .= WebGUI::Form::commentRating($session, {name=>"rating"}); + $out .= WebGUI::Form::submit($session); + $out .= WebGUI::Form::formFooter($session); + $out .= '
'; + } + $out .= '
'; + return $out; +} + +#------------------------------------------------------------------- + +=head2 getGroupToComment () + +Returns '2' aka Registered Users. However, should be overridden by subclasses that wish to make this a settable property. + +=cut + +sub getGroupToComment { + return '2'; +} + +#------------------------------------------------------------------- + +=head2 getKarmaAmountPerComment () + +Returns 3. However, should be overridden by subclasses that wish to make this a settable property. + +=cut + +sub getKarmaAmountPerComment { + return 3; +} + + +#------------------------------------------------------------------- + +=head2 update () + +See SUPER::update(). Extends the update() method to encode the comments field into something storable in the database. + +=cut + +sub update { + my $self = shift; + my $properties = shift; + if (exists $properties->{comments}) { + my $comments = $properties->{comments}; + if (ref $comments ne 'ARRAY') { + $comments = eval{JSON->new->decode($comments)}; + if (WebGUI::Error->caught || ref $comments ne 'ARRAY') { + $comments = []; + } + } + $properties->{comments} = JSON->new->encode($comments); + } + $self->next::method($properties, @_); +} + +#------------------------------------------------------------------- + +=head2 www_addComment () + +Posts a comment after verifying the user's privileges. + +=cut + +sub www_addComment { + my $self = shift; + my $session = $self->session; + return $session->privilege->insufficient() unless ($self->canComment); + my $form = $session->form; + my $comment = $form->get('comment','textarea'); + WebGUI::Macro::negate(\$comment); + if ($comment ne '') { + $self->addComment($comment, $form->get('rating','commentRating')); + } + $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_deleteComment () + +Removes a comment. + +=cut + +sub www_deleteComment { + my $self = shift; + my $session = $self->session; + return $session->privilege->insufficient() unless ($self->canEdit); + $self->deleteComment($session->form->get('commentId')); + $self->www_view; +} + +1; + diff --git a/lib/WebGUI/Form/CommentRating.pm b/lib/WebGUI/Form/CommentRating.pm new file mode 100644 index 000000000..c5f56bfdb --- /dev/null +++ b/lib/WebGUI/Form/CommentRating.pm @@ -0,0 +1,144 @@ +package WebGUI::Form::CommentRating; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2008 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 + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Form::RadioList'; + +=head1 NAME + +Package WebGUI::Form::CommentRating + +=head1 DESCRIPTION + +Displays a comment rating field (unhappy to happy). + +=head1 SEE ALSO + +This is a subclass of WebGUI::Form::Control::RadioList. + +=head1 METHODS + +The following methods are specifically available from this class. Check the superclass for additional methods. + +=cut + +#------------------------------------------------------------------- + +=head2 definition ( [ additionalTerms ] ) + +See the super class for additional details. + +=head3 additionalTerms + +The following additional parameters have been added via this sub class. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift || []; + push(@{$definition}, { + }); + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- + +=head2 getDefaultValue ( ) + +Returns 0 + +=cut + +sub getDefaultValue { + return 0; +} + +#------------------------------------------------------------------- + +=head2 getName ( session ) + +Returns the name of the form control. + +=cut + +sub getName { + my ($class, $session) = @_; + return 'Comment Rating'; +} + +#------------------------------------------------------------------- + +=head2 getOptions ( ) + +Options are passed in for many list types. Those options can come in as a hash ref, or a \n separated string, or a key|value\n separated string. This method returns a hash ref regardless of what's passed in. + +=cut + +sub getOptions { + my ($self) = @_; + my %options = (); + tie %options, 'Tie::IxHash'; + my $url = $self->session->url; + %options = ( + 0 => q{0}, + 1 => q{1}, + 2 => q{2}, + 3 => q{3}, + 4 => q{4}, + 5 => q{5}, + ); + return \%options; +} + +#------------------------------------------------------------------- + +=head2 getValue ( [ value ] ) + +Does some special processing. + +=cut + +sub getValue { + my $self = shift; + my $value = $self->SUPER::getValue(@_); + + if ($value !~ m/^\d+$/ || $value < 1 || $value > 5) { + $value = $self->getDefaultValue; + } + + return $value; +} + +#------------------------------------------------------------------- + +=head2 getValueAsHtml ( ) + +Formats as an icon. + +=cut + +sub getValueAsHtml { + my $self = shift; + my $value = $self->getValue; + my $url = $self->session->url; + return q{}.$value.q{}; +} + + +1; + diff --git a/t/Asset/WikiPage.t b/t/Asset/WikiPage.t index 1dd4d727d..2f445b2d9 100644 --- a/t/Asset/WikiPage.t +++ b/t/Asset/WikiPage.t @@ -16,7 +16,7 @@ use lib "$FindBin::Bin/../lib"; use WebGUI::Test; use WebGUI::Session; -use Test::More tests => 5; # increment this value for each test you create +use Test::More tests => 14; # increment this value for each test you create use WebGUI::Asset::Wobject::WikiMaster; use WebGUI::Asset::WikiPage; @@ -46,6 +46,34 @@ my $wikiPageCopy = $wikipage->duplicate(); isa_ok($wikiPageCopy, 'WebGUI::Asset::WikiPage'); my $thirdVersionTag = WebGUI::VersionTag->new($session,$wikiPageCopy->get("tagId")); + +################## +# This section tests the Comments aspect +################## + +is(ref $wikipage->get('comments'), "ARRAY", "Comments Aspect property returns an array ref"); + +my $firstComment = 'what say you fuzzy britches'; +$wikipage->addComment($firstComment,5); +my $secondComment = "i don't have her stuffed down my pants right now, sorry to say"; +$wikipage->addComment($secondComment, 1); + +my $comments = $wikipage->get('comments'); +is(scalar(@{$comments}), 2, "2 comments have been added"); +is($wikipage->get('averageCommentRating'), 3, 'average rating works'); +is($comments->[0]{comment}, $firstComment, "adding initial comment checks out"); +is($comments->[0]{rating}, 5, "adding initial comment rating checks out"); +is($comments->[1]{comment}, $secondComment, "adding additional comments checks out"); +is($comments->[1]{rating}, 1, "adding additional comment rating checks out"); + +$wikipage->deleteComment($comments->[0]{id}); +$comments = $wikipage->get('comments'); +is($comments->[0]{comment}, $secondComment, "you can delete a comment"); +is($wikipage->get('averageCommentRating'), 1, 'average rating is adjusted after deleting a comment'); + + +################## + TODO: { local $TODO = "Tests to make later"; ok(0, 'Lots and lots to do'); diff --git a/www/extras/form/CommentRating/0.png b/www/extras/form/CommentRating/0.png new file mode 100644 index 0000000000000000000000000000000000000000..270e92ff1fb04ded3cfee6769b37f9b04fb526a0 GIT binary patch literal 747 zcmeAS@N?(olHy`uVBq!ia0vp^Hb5-L!3-qb-Fj<*lw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$yAVzlL0;U$&yEp9$mY3ZSC5%H*VaRI(6!uJ9j2cn)Knrhn+ij9z1yP z^5x4ve*Cy~>(-k$Z~p%M`}+0kU%!5R`}Xbp`SY7LZQ8PB%gdK9pFMlFYSpUSw{O3C z^=itLDc`?;zjEcuj2SbgO`CT2?%l6nzb;+6^zh-sbLY+lI^Ijd+YRU`=aL}5U?7)5 zz~Gp~@}7Z#G1JqOcGtMGvak6-Nv#193`3fp5=cu_>`jX}FK*FlKev`1zLt1tjZJKUwURlf zv~5p_cNu5+PeomWi=-rc_rINP8_`74>R&nQcyx@w3U> ycsH0If9xJzadmE4-;3Kn_y0-$@y5G>k>RmuT&nIRv!lQ`WbkzLb6Mw<&;$UrL$3?~ literal 0 HcmV?d00001 diff --git a/www/extras/form/CommentRating/1.png b/www/extras/form/CommentRating/1.png new file mode 100644 index 0000000000000000000000000000000000000000..c478d707fc4442be4be3cbedfe08115c3a7223c8 GIT binary patch literal 2113 zcmd^Bi#OE!79X9XoSGLip3%`EbqslQg`y~r3w4W$QOYaEN#*JsqR5Tn2w{?L zht@fm9{1i(7>`8VlRR=5kIAD{=4p(X-|xHc@2>l2+|OFyz1MfIz4v#&)_3iK;6Pu> zdXx1e5{cq>z$=8{Zp7K7^94bDeTvQ#%sAcqNP6g*W9gZ(sqrLF&Y8G)bHAk6_5XfXQL@_{AT}0DEEC&D$0iZC|YW2#> z3bJ*?sk*?;2CBj8>MF2jtoxKndjbRvSJYCUimpRPmt4LKgTs(_MWs@s&Q6djKqLZ* z9ILIv>+ayZ2B^J@+c@Ba^XWN>WC@6jD0(nGBbF|Kpa;Y<-1HmZ-2p!zY;Xu$QQ*R9 zpc|mO4Vc*`oE;1Yf+b60C@PVPCD8I1x_W?Y0AxcfCKlIVkZ=O|1;WrUR$os{2cpwq zVIJld6*4(mR)Mt>URD7&E8%D)5Z!+~UbWRvuh|iM6m@Am6;F=cwJ#OCO@1*jQ02Jee@F`xR?D+9pF9j)csiW8jDjp) zUTrgu+jet!L{^*~@1q%~Qjc4*i`ic$%6Ok$Gu^j$Ww(3kh*35P1g}U{srZm@1RDF96U%M1`_H>E3mVbQWe9>J& zUy65`-61T}Ch|ehp7PzdLq=D=E||_?n|XTc=$%fS*Sgg5x^OGSl%JBLV@cblHSQ#+ zw`RK7mcPvY%H-Mg(oMg0kL>hzsjy6qI5XVIZ(LY&>H4m)a<{{O_5Zr)WGXY$8f)dI z^gaG|>+DT)^Gl-WodF*vSBD+Wky#$YB#)f--wyp+Tb#<8{AA=d%1fU;HNU~t<-$H2 z-<=?L{1CEbUb%7dMJdnY-sFXbvZ~R#ZH2vit(UTvxX*|)Ed=zowEO_!!Scs`A5G(_HOat?V^DNU2^c= zgd4e<2cdCM>tdfdSEi+Od*3!82W)Nc=+`QYQ1X5scFfw>S8>JR&gRotsMTthoquV6 zn)!$3%r-{UQsLXW{Jf{Tg~m$vfMm|QYo3p1X)kjAw3&tZ52UjCei`3*<;Y0v_QEdn zU2aEa1$s7I-p3}#K$Ew<{Emi+IPI$?<$A4^x-6q^vne0v6L+;UNQK8DjsDTRpjw>! zXOo3Xebiq_BMQIZv0Ll=+x*h{lfoBXO>fU<&rF6J1)jH>APt4nT+ZCzoJRLLeAFT& zHYGQG*p*5>2#{o~%PQHHgaYnX>?+SqH4tTlcq`gyb@ z)v&a;ds{_@=l5^roO#?dXUD4;#vx6OCfVW3wqGSeiy#-W<$cPJ8^!4vjrk2vF9xIV zQ@Q({F5h_cQN(u-GOWHh(6ktDmEyXXv!uLK_Nit$D0x6x*1N94XKRPLrRU?q)W9Lu zAl;mm^QppGxaL)(<+WV}#jgsUhp|7iUnJEy#BB_m!t7K|TPvz|DI<~@;UC6hh3Uhy z>-dH5=bSV@ZrB0ZC@Rc$U#{d}Ii@nNVkOGW-u%FIe`JrJtr0yrnqN3G+N0rb;KuKSP%+^lQwKJ6HYoH8DDcc(NdmSdvY^!%(<_9Q>xZ{Or^C6ZmXgX(0#iPb@ff z41IxUO#-Lz5o9j_L3WQ2K^7n>LilS1 zh9wNA`@B9~C086y79q$Y_-j6#F2k?{aFQ4DLJm+IMs;#cvUr9M^2!rg#s)DUwF%)Q zRcfC!@-1ir<{pb>@$UtQb~{7s;OJdElY{eFl{=`)9g?6JkQ69+V zkiq&+JAH8kP}pq4pM??afXogmtq#tdLtwEo)Y^-q#s-N?VK=wQ4V66gr4a{P3#jZ4 z#*A~!BtW-Em*uF2Y|79V2hipHjKYz3xU7T+b>Mzb+BparnA zXQc-_#9?#?TR&UPl_WA%xIqz;?&905uM{%`&RnBJw3VWWXFx8%V{lu@in)4`AdCb# zJR&4jQ@{a@fGWM$EwH+{#(9l%u2F1i5JPV7yw>R$)&VM~lcTeC%-B0Mo>`Z!dJ<4L zEG_mHR;}gMSMOPTBE4bVF!k9;gMMsHf`9d}xacG_`B2vNe*y8`y`2C6 literal 0 HcmV?d00001 diff --git a/www/extras/form/CommentRating/3.png b/www/extras/form/CommentRating/3.png new file mode 100644 index 0000000000000000000000000000000000000000..f189540259fabc424b4cc3c422b2d892fff2982d GIT binary patch literal 1519 zcmds0i&GP45C>69DO7xdj$*7r5+X0HV5LAoP@#&HhegFvEEEey5HljSl)~_yYPB+2 z?TBC<93fiA$5dpf2uO&Ohgy8(a^XUF6C~tvkMHigyWZc?o!R~E&OT;$cDFJvcDswy z3MV@|JD2Dkk?|saF0zNCgDAIeJ5wOSa#d8aDk1w@RbFatx?Ol$_Th9%v@$g#Jw81( zE&sSSUCfwM7`HPqN{-SpJtap;Z@s7x-g;7wP;x|67(o!^sBJw$%Mc(%3BU_dF%kiC zJtISDxgM7pNGJ%LAZSS_Lm7pR-~}Pk1bp`2^|Dh$aRtY zPgAT6VK)uZUI?u~DX|4F{cJZObWuX~1if#B3CGBXILsKOH6$NCM3zr-r>EHK7B(0I z!A44Eu<00R;f1(i@SNgqS~xv`fd=rv3ilYnYYNKs&<_~rq?r|NSlBD? zfn3Kv#^D>9lcCm9GZ%^x5g0gNf*}~uMsP)wTtCIN*~F>&E7NSs2xVkpi5cL$fOGJy zne8TpFeCl*G*f0~NP*W9d=m+1UdS8;6%&kxgvv4252fp8VdE@&WQ?wy;>O)#;&G~Y_VL=)U%V7h4Ni4ue$ z(4264MjQ=3r z+0tpIdK#Kp{=^trH_MDeCe+B}j?v{TGn_zDzEBkbgN`xlz-F7d9S@!hgI3P?I^xt z>s(WWKCG^wYvfq(s(gOMo%V|{>T_r8n-z;`%5>N%FPpt+zaSeSIo^uXgO~mq`TYhJd8qiluKhs=CQf z>oPahzAip>`NR0S)I&3a_wM%vx(JW6i8C&f4Q{pX3Oq~OG`;Od%I2I7ym#;X4M?#d z43rxh-tBZgc&=$=^f-bfubN+OfA+7mXAb^XYh8j5za3H^SaYeRDox#~k~>D*7{|YI)@oYK?6S7(z6|6N9TUrHy%Sw(%0f&Vm8mA((zV@c(`-95 zS8Yl&-Gwh8il_)`1*CwQc;pdYpdcV1u>2kSnfZL)yf>fs+k5xI!>|ab7Zd~nA@DeK zgaeN_c0G8R<6<#|IS%lo1)rsnQj=*J2~;vDC@D3Oyb*snA%z@4PDr|Ot%K}vIOm2F zBZIL349|pN0Vn~0;{hlZK;T$#91BKZIdlYu1;a2Q7#0){fUztvj)k<_Z3L#{8A;-L znXE>uZ7HWK9Lt8|n2-BVb^;SdU?4C6V##K~G2nwbS&zCs1b}~@ZJ`Jb z;26+;?I)@Tg<(R?RwI^$G+P&6D+>uMNOD^smJPe#eKCaT5KS7>q)aW6X+-zLF?x%Z zzywD&c$t>;#fs~({4F@9OU=+jomo4m?Y7yiXL-JAlWKNxN@W;1&-;jW>AX#m8vASi!Ja?MA2?wm|*Glx}dE?UJf@Qv2BPi~rMR44+yF#^Q&7x74 z)fHMOqoj*4@y*-lAHv@b^?nPf#M5B8M03rMfZ5pp+kG*`Oyg#8Ij%oHIFkGX!8$tni*ED-rD^KA0E9WHK zu}YO;cvSzzX14`1k$v-0>143HMj=@kh`Cv<9J)pZ193v{Zx_JHdW_HOQO&f>`1MG77<2r(Es6p+Ay9n zN9VTV(Ls@!{XLJP3P`8ia*cyk%A)9pC${2$+g#GJOHY0>wQJM&)$yslS=gDxTCicT z&X}Lyo24-wjkP+l@!3+n&3oS^4Ffr5J2@B75|buQn z?LXnKcB+sm2M@IL8t0o&pgix-URj1-LHC$h>9U&d-n8S8;lj%-Tmd#F*VXelw&TIM z3(>=F6xRm~A!d)bJ*>1Ha+G8%TD{fUR~xaf%T=*wQx9Cv>90(vBL3Zrfats$v8O^X zN0#uhnSLGvp5gYza%KK1l<(cvNg1x}z1vE9C(MQQL;SkZliL?t#`ryto^Vf7Q{zMkQve;FVO^RkxPjfL_^Z#E`*TVe7~R1d8X&zc+Tsb_xp9u>-Bz}*LnYR;+-6ARFqdJ69l1R zXKTrov6-w^DbZzUV}0SIjI=_m_JlYG90&>X4DumZ-T_`dx^@CjUmvcIr+0W@t&dDW zC!BWjtn8>|FR*kP#sr9!g)hAUG>E$+5O)CHPr{ffC1*h?6r6}6 zbaN?9ge&>TfGH&=G42J(z%fiIeF~2XFvAqdH{tS1kPZXx1YCjh?&A0_7{)OE3KIhW zk=Qj8opC|>?I3DKmG+cIG}>JT)MpszM9mZ?r5MFiUWGVy2;dW@`T?E6-d<=^H=z4C z(O#PM3@xxA+;vjUr09=UEJ@Fu4S);q?QE17N%4g!T8K{Ff|x5*U>(Rz z{uUXUorVMq0=lGJu;@j@R7q}b zzp=8l?#RQg&4ns%{BG+9k)KlKZ1*2xnnkU4QgwS&Y*StyF@9mC@M=;Hv2@~BzD7aI zp$~y4pY82ibmwB*j_12IwsAA*U8;XIE~pzqjcmBvHgff>y!5Cxb7B8~aAsoSJfR7Du{P zXPzq#Rc5Pgn0&wCUi_*ElVRQ}%Uu5b)8<#M=6NZk3UvZc+(;k`E8Qb~j_~TD>Bk~D zqaEdc`7 zKwO!Bq<(o;D?`1Z^W55(&wqH^d_nHr_3c~36*JR}mt0!6$J^W1o+rLL9v=UMEzvY| z+BPj_m+HBNw>3DN{?`iMUFrg*-wyv)-TJq$)iZ64#|dwrgryv)Y29GJe8sape=R_e z2#!C}Qtqrh6Xm zJa&8B`EB)QidiNa+tw!8Tc`J!wEd@UW$2~F)rq&ewxizc=%lWnVwaz1p6grpmB-X< zZkn*p&40b{@Qtp&S1X-8W~%(Le~Lwmp=CIPrbgar9F4x+HB{9;Qll-dt+lTSn@Sjr zy6GIUHE&^3nCd>ATu!LjO)akcyhhJE{${y+@#tjWC?}M9TTaEhbd73#@rcU&3YY31MgDX!(Hc5%XUKOo#UX literal 0 HcmV?d00001