From df7f09c722e98674909d8483957389b7551602be Mon Sep 17 00:00:00 2001 From: JT Smith Date: Thu, 10 Feb 2005 02:50:47 +0000 Subject: [PATCH] started merging uss and forum into one beast --- docs/upgrades/upgrade_6.2.11-6.3.0.pl | 3 + docs/upgrades/upgrade_6.2.11-6.3.0.sql | 76 ++ lib/WebGUI/Asset/Post.pm | 1129 ++++++++++++++++ lib/WebGUI/Asset/Thread.pm | 1038 ++++++++++++++ lib/WebGUI/Asset/Wobject/Collaboration.pm | 1198 +++++++++++++++++ .../assets/{forum.gif => collaboration.gif} | Bin .../small/{forum.gif => collaboration.gif} | Bin .../assets/small/userSubmissionSystem.gif | Bin 680 -> 0 bytes www/extras/assets/userSubmissionSystem.gif | Bin 2068 -> 0 bytes 9 files changed, 3444 insertions(+) create mode 100644 lib/WebGUI/Asset/Post.pm create mode 100644 lib/WebGUI/Asset/Thread.pm create mode 100644 lib/WebGUI/Asset/Wobject/Collaboration.pm rename www/extras/assets/{forum.gif => collaboration.gif} (100%) rename www/extras/assets/small/{forum.gif => collaboration.gif} (100%) delete mode 100644 www/extras/assets/small/userSubmissionSystem.gif delete mode 100644 www/extras/assets/userSubmissionSystem.gif diff --git a/docs/upgrades/upgrade_6.2.11-6.3.0.pl b/docs/upgrades/upgrade_6.2.11-6.3.0.pl index 7ab74792e..cd7587466 100644 --- a/docs/upgrades/upgrade_6.2.11-6.3.0.pl +++ b/docs/upgrades/upgrade_6.2.11-6.3.0.pl @@ -98,6 +98,7 @@ WebGUI::SQL->write("alter table Product_related add assetId varchar(22) not null WebGUI::SQL->write("alter table Product_related add relatedAssetId varchar(22) not null"); WebGUI::SQL->write("alter table Product_related add primary key (assetId,relatedAssetId)"); WebGUI::SQL->write("alter table WobjectProxy add column description mediumtext"); +WebGUI::SQL->write("alter table forum add assetId varchar(22) not null"); @@ -204,6 +205,8 @@ WebGUI::SQL->write("alter table Product_related drop column wobjectId"); WebGUI::SQL->write("alter table Product_specification drop column wobjectId"); WebGUI::SQL->write("alter table Product_related drop column RelatedWobjectId"); WebGUI::SQL->write("alter table Product_accessory drop column AccessoryWobjectId"); +WebGUI::SQL->write("alter table forum drop column forumId"); +WebGUI::SQL->write("alter table forum drop column groupToView"); # start migrating non-wobject stuff into assets diff --git a/docs/upgrades/upgrade_6.2.11-6.3.0.sql b/docs/upgrades/upgrade_6.2.11-6.3.0.sql index 6b01060ca..f8a5d0f2a 100644 --- a/docs/upgrades/upgrade_6.2.11-6.3.0.sql +++ b/docs/upgrades/upgrade_6.2.11-6.3.0.sql @@ -247,5 +247,81 @@ drop table pageStatistics; delete from settings where name='snippetPreviewLength'; delete from incrementer where incrementerId in ('collateralFolderId','themeId','themeComponentId'); +create table Collaboration ( + assetId varchar(22) not null primary key, + postGroupId varchar(22) not null default '2', + moderateGroupId varchar(22) not null default '4', + moderatePosts int not null default 0, + karmaPerPost int not null default 0, + collaborationTemplateId varchar(22) not null, + threadTemplateId varchar(22) not null, + postTemplateId varchar(22) not null, + postFormTemplateId varchar(22) not null, + postPreviewTemplateId varchar(22) not null, + searchTemplateId varchar(22) not null, + notificationTemplateId varchar(22) not null, + sortBy varchar(35) not null default 'dateUpdated', + sortOrder varchar(4) not null default 'desc', + usePreview int not null default 1, + addEditStampToPosts int not null default 0, + editTimeout int not null default 3600, + attachmentsPerPost int not null default 0, + allowRichEdit int not null default 1, + filterCode varchar(30) not null default 'javascript', + useContentFilter int not null default 1, + threads int not null default 0, + views int not null default 0, + replies int not null default 0, + rating int not null default 0, + lastPostId varchar(22), + lastPostDate bigint, + archiveAfter int not null default 31536000, + postsPerPage int not null default 10, + threadsPerPage int not null default 30, + subscriptionGroupId varchar(22) +); +create table Thread ( + assetId varchar(22) not null primary key, + views int not null default 0, + replies int not null default 0, + lastPostId varchar(22) not null default 0, + lastPostDate bigint, + isLocked int not null default 0, + isSticky int not null default 0, + status varchar(30) not null default 'approved', + rating int not null default 0 +); +create table Post ( + assetId varchar(22) not null primary key, + threadId varchar(22) not null, + dateSubmitted bigint, + dateUpdated bigint, + username varchar(30), + content mediumtext, + status varchar(30) not null default 'approved', + views int not null default 0, + contentType varchar(35) not null default 'mixed', + userDefined1 text, + userDefined2 text, + userDefined3 text, + userDefined4 text, + userDefined5 text, + rating int not null default 0 +); + +create table Post_rating ( + assetId varchar(22) not null, + userId varchar(22) not null, + ipAddress varchar(15) not null, + dateOfRating bigint, + rating int not null default 0 +); + +create table Post_read ( + postId varchar(22) not null, + threadId varchar(22) not null, + userId varchar(22) not null, + readDate bigint +); diff --git a/lib/WebGUI/Asset/Post.pm b/lib/WebGUI/Asset/Post.pm new file mode 100644 index 000000000..f68f6b4a4 --- /dev/null +++ b/lib/WebGUI/Asset/Post.pm @@ -0,0 +1,1129 @@ +package WebGUI::Asset::Post; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2005 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 +#------------------------------------------------------------------- + +use strict; +use Tie::CPHash; +use WebGUI::Asset; +use WebGUI::Asset::Template; +use WebGUI::Asset::Thread; +use WebGUI::Asset::Wobject::Collaborate; +use WebGUI::DateTime; +use WebGUI::Grouping; +use WebGUI::HTML; +use WebGUI::HTMLForm; +use WebGUI::HTTP; +use WebGUI::Icon; +use WebGUI::Id; +use WebGUI::International; +use WebGUI::MessageLog; +use WebGUI::Operation; +use WebGUI::Paginator; +use WebGUI::Privilege; +use WebGUI::Session; +use WebGUI::SQL; +use WebGUI::Style; +use WebGUI::URL; +use WebGUI::User; +use WebGUI::Utility; + +our @ISA = qw(WebGUI::Asset); + + + +#------------------------------------------------------------------- +sub canEdit { + my $self = shift; + return ($session{form}{func} eq "add" && $self->getThread->getParent->canPost) || + ($self->isPoster && $self->getThread->getParent->get("editTimeout") > (WebGUI::DateTime::time() - $self->get("dateUpdated"))) || + $self->getThread->getParent->canModerate; + +} + +#------------------------------------------------------------------- + +=head2 canView ( ) + +Returns a boolean indicating whether the user can view the current post. + +=cut + +sub canView { + my $self = shift; + if ($self->get("status") eq "approved" || $self->get("status") eq "archived") { + return 1; + } elsif ($self->get("status") eq "denied" && $self->canEdit) { + return 1; + } else { + return $self->SUPER::canView; + } +} + +#------------------------------------------------------------------- + +=head2 chopSubject ( ) + +Cuts a subject string off at 30 characters. + +=cut + +sub chopSubject { + my $self = shift; + return substr($self->get("title"),0,30); +} + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $definition = shift; + push(@{$definition}, { + tableName=>'Post', + className=>'WebGUI::Asset::Post', + properties=>{ + threadId => { + fieldType=>"hidden", + defaultValue=>undef + }, + dateSubmitted => { + fieldType=>"hidden", + defaultValue=>undef + }, + dateUpdated => { + fieldType=>"hidden", + defaultValue=>time() + }, + username => { + fieldType=>"hidden", + defaultValue=>$session{user}{alias} || $session{user}{username} + }, + status => { + fieldType=>"hidden", + defaultValue=>undef + }, + rating => { + fieldType=>"hidden", + defaultValue=>undef + }, + views => { + fieldType=>"hidden", + defaultValue=>undef + }, + contentType => { + fieldType=>"contentType", + defaultValue=>"mixed" + }, + userDefined1 => { + fieldType=>"HTMLArea", + defaultValue=>undef + }, + userDefined2 => { + fieldType=>"HTMLArea", + defaultValue=>undef + }, + userDefined3 => { + fieldType=>"HTMLArea", + defaultValue=>undef + }, + userDefined4 => { + fieldType=>"HTMLArea", + defaultValue=>undef + }, + userDefined5 => { + fieldType=>"HTMLArea", + defaultValue=>undef + }, + content => { + fieldType=>"HTMLArea", + defaultValue=>undef + } + }, + }); + return $class->SUPER::definition($definition); +} + + +#------------------------------------------------------------------- +sub DESTROY { + my $self = shift; + $self->{_thread}->DESTROY if (exists $self->{_thread}); +} + + +#------------------------------------------------------------------- + +=head2 formatContent ( ) + +Formats post content for display. + +=cut + +sub formatContent { + my $self = shift; + my $msg = WebGUI::HTML::filter($self->get("content"),$self->getThread->getParent->get("filterCode")); + $msg = WebGUI::HTML::format($msg, $self->get("contentType")); + if ($self->getThread->getParent->get("useContentFilter")) { + $msg = WebGUI::HTML::processReplacements($msg); + } + return $msg; +} + +#------------------------------------------------------------------- + +=head2 getApproveUrl ( ) + +Formats the URL to approve a post. + +=cut + +sub getApproveUrl { + my $self = shift; + return $self->getUrl("func=approve&mlog=".$session{form}{mlog}); +} + +#------------------------------------------------------------------- + +=head2 getDeleteUrl ( ) + +Formats the url to delete a post. + +=cut + +sub getDeleteUrl { + my $self = shift; + return $self->getUrl("func=delete"); +} + +#------------------------------------------------------------------- + +=head2 getDenyUrl ( ) + +Formats the url to deny a post. + +=cut + +sub getDenyUrl { + my $self = shift; + return $self->getUrl("func=deny&mlog=".$session{form}{mlog}); +} + +#------------------------------------------------------------------- + +=head2 getEditUrl ( ) + +Formats the url to edit a post. + +=cut + +sub getEditUrl { + my $self = shift; + return $self->getUrl("func=edit"); +} + +#------------------------------------------------------------------- +sub getIcon { + my $self = shift; + my $small = shift; + return $session{config}{extrasURL}.'/assets/small/post.gif' if ($small); + return $session{config}{extrasURL}.'/assets/post.gif'; +} + +#------------------------------------------------------------------- +sub getName { + return "Post"; +} + +#------------------------------------------------------------------- + +=head2 getPosterProfileUrl ( ) + +Formats the url to view a users profile. + +=cut + +sub getPosterProfileUrl { + my $self = shift; + return $self->getUrl("op=viewProfile&uid=".$self->get("ownerUserId")); +} + +#------------------------------------------------------------------- + +=head2 getRateUrl ( rating ) + +Formats the url to rate a post. + +=head3 rating + +An integer between 1 and 5 (5 = best). + +=cut + +sub getRateUrl { + my $self = shift; + my $rating = shift; + return $self->getUrl("func=rate&rating=".$rating."#".$self->getId); +} + +#------------------------------------------------------------------- + +=head2 getReplyUrl ( [ withQuote ] ) + +Formats the url to reply to a post. + +=head3 withQuote + +If specified the reply with automatically quote the parent post. + +=cut + +sub getReplyUrl { + my $self = shift; + my $withQuote = shift || 0; + return $self->getUrl("func=add&class=WebGUI::Asset::Post&withQuote=".$withQuote); +} + +#------------------------------------------------------------------- +sub getTemplateVars { + my $self = shift; + my %var; + #my $callback = WebGUI::URL::gateway($parentsPage->get("urlizedTitle"),"func=viewSubmission&wid=".$self->wid."&sid=".$submission->{USS_submissionId}); +# if ($session{form}{forumOp} ne "" && $session{form}{forumOp} ne "viewForum") { +# return WebGUI::Forum::UI::forumOp({ +# callback=>$callback, +# title=>$submission->{title}, +# forumId=>$submission->{forumId} +# }); +# } + $self->update({views=>$self->get("views")+1}); + $var{content} = WebGUI::HTML::filter($self->get("content"),$self->get("filterContent")); + $var{content} = WebGUI::HTML::format($var{content},"USS"); + $var{"user.label"} = WebGUI::International::get(21,"USS"); + $var{"userProfile.url"} = $self->getUrl('op=viewProfile&uid='.$self->get("ownerUserId")); + $var{"userId"} = $self->get("ownerUserId"); + $var{"username"} = $self->get("username"); + $var{"dateSubmitted.label"} = WebGUI::International::get(13,"USS"); + $var{"dateSubmitted.human"} = epochToHuman($self->get("dateSubmitted")); + $var{"dateUpdated.label"} = WebGUI::International::get(78,"USS"); + $var{"dateUpdated.human"} = epochToHuman($self->get("dateUpdated")); + $var{"status.label"} = WebGUI::International::get(14,"USS"); + $var{"status.status"} = $self->getParent->status($self->get("status")); + $var{"views.label"} = WebGUI::International::get(514); + $var{canPost} = $self->canContribute; + $var{"post.url"} = $self->getUrl("func=edit"); + $var{"post.label"} = WebGUI::International::get(20,"USS"); + my $previous = WebGUI::Asset::Post->newByPropertyHashRef( + WebGUI::SQL->quickHashRef(" + select asset.*,Post.* + from Thread + left join asset on asset.parentId=Thread.assetId + left join Post on Post.assetId=asset.assetId + where Thread.parentId=".quote($self->get("parentId"))." + and asset.state='published' + and ".$self->getParent->getValue("sortBy")."<".quote($self->get($self->getParent->getValue("sortBy")))." + and (userId=".quote($self->get("ownerUserId"))." or Post.status='approved') + order by ".$self->getParent->getValue("sortBy")." desc + ",WebGUI::SQL->getSlave) + ); + $var{"previous.more"} = defined $previous; + $var{"previous.url"} = $previous->getUrl if ($var{"previous.more"}); + $var{"previous.label"} = WebGUI::International::get(58,"USS"); + my $next = WebGUI::Asset::Post->newByPropertyHashRef( + WebGUI::SQL->quickHashRef(" + select asset.*,Post.* + from Thread + left join asset on asset.parentId=Thread.assetId + left join Post on Post.assetId=asset.assetId + where Thread.parentId=".quote($self->get("parentId"))." + and asset.state='published' + and ".$self->getParent->getValue("sortBy").">".quote($self->get($self->getParent->getValue("sortBy")))." + and (userId=".quote($self->get("ownerUserId"))." or Post.status='approved') + order by ".$self->getParent->getValue("sortBy")." asc + ",WebGUI::SQL->getSlave) + ); + $var{"next.more"} = defined $next; + $var{"next.url"} = $next->getUrl if ($var{"next.more"}); + $var{"next.label"} = WebGUI::International::get(59,"USS"); + $var{canEdit} = $self->canEdit; + $var{"delete.url"} = $self->getUrl("func=delete"); + $var{"delete.label"} = WebGUI::International::get(37,"USS"); + $var{"edit.url"} = $self->getUrl("func=edit"); + $var{"edit.label"} = WebGUI::International::get(27,"USS"); + $var{canChangeStatus} = $self->canModerate; + $var{"approve.url"} = $self->getUrl("func=approve&mlog=".$session{form}{mlog}); + $var{"approve.label"} = WebGUI::International::get(572); + $var{"leave.url"} = $self->getUrl('op=viewMessageLog'); + $var{"leave.label"} = WebGUI::International::get(573); + $var{"deny.url"} = $self->getUrl("func=deny&mlog=".$session{form}{mlog}); + $var{"deny.label"} = WebGUI::International::get(574); + $var{"canReply"} = $self->get("allowDiscussion"); +# $var{"reply.url"} = WebGUI::Forum::UI::formatNewThreadURL($callback,$submission->{forumId}); +# $var{"reply.label"} = WebGUI::International::get(47,"USS"); +# if ($submission->{image} ne "") { +# $file = WebGUI::Attachment->new($submission->{image},$self->wid,$submissionId); +# $var{"image.url"} = $file->getURL; +# $var{"image.thumbnail"} = $file->getThumbnail; +# } +# if ($submission->{attachment} ne "") { +# $file = WebGUI::Attachment->new($submission->{attachment},$self->wid,$submissionId); +# $var{"attachment.box"} = $file->box; +# $var{"attachment.url"} = $file->getURL; +# $var{"attachment.icon"} = $file->getIcon; +# $var{"attachment.name"} = $file->getFilename; + # } + if ($self->get("allowDiscussion")) { +# $var{"replies"} = WebGUI::Forum::UI::www_viewForum( +# {callback=>$callback,title=>$submission->{title},forumId=>$submission->{forumId}}, +# $submission->{forumId}); + } +} + +#------------------------------------------------------------------- +sub getThread { + my $self = shift; + unless (exists $self->{_thread}) { + $self->{_thread} = WebGUI::Asset::Thread->new($self->get("threadId")); + } + return $self->{_thread}; +} + + +#------------------------------------------------------------------- + +=head2 hasRated ( ) + +Returns a boolean indicating whether this user has already rated this post. + +=cut + +sub hasRated { + my $self = shift; + return 1 if $self->isPoster; + my ($flag) = WebGUI::SQL->quickArray("select count(*) from Post_rating where assetId=" + .quote($self->getId)." and ((userId=".quote($session{user}{userId})." and userId<>'1') or (userId='1' and + ipAddress=".quote($session{env}{REMOTE_ADDR})."))"); + return $flag; +} + +#------------------------------------------------------------------- + +=head2 incrementViews ( ) + +Increments the views counter for this post. + +=cut + +sub incrementViews { + my ($self) = @_; + $self->update({views=>$self->get("views")+1}); + $self->getThread->incrementViews; +} + +#------------------------------------------------------------------- + +=head2 isMarkedRead ( ) + +Returns a boolean indicating whether this post is marked read for the user. + +=cut + +sub isMarkedRead { + my $self = shift; + return 1 if $self->isPoster; + my ($isRead) = WebGUI::SQL->quickArray("select count(*) from Post_read where userId=".quote($session{user}{userId})." and postId=".quote($self->getId)); + return $isRead; +} + +#------------------------------------------------------------------- + +=head2 isPoster ( ) + +Returns a boolean that is true if the current user created this post and is not a visitor. + +=cut + +sub isPoster { + my $self = shift; + return ($session{user}{userId} ne "1" && $session{user}{userId} eq $self->get("ownerUserId")); +} + + +#------------------------------------------------------------------- + +=head2 isReply ( ) + +Returns a boolean indicating whether this post is a reply. This is the opposite of isRootPost(). + +=cut + +sub isReply { + my $self = shift; + return !$self->isRootPost; +} + + +#------------------------------------------------------------------- + +=head2 isRootPost ( ) + +Returns a boolean indicating that this post is the root post in the thread. This is the opposite of isReply(). + +=cut + +sub isReply { + my $self = shift; + return $self->get("parentId") eq $self->get("threadId"); +} + + +#------------------------------------------------------------------- + +=head2 markRead ( ) + +Marks this post read for this user. + +=cut + +sub markRead { + my $self = shift; + unless ($self->isMarkedRead) { + WebGUI::SQL->write("insert into Post_read (userId, postId, threadId, readDate) values (".quote($session{user}{userId}).", + ".quote($self->getId).", ".quote($self->get("threadId")).", ".WebGUI::DateTime::time().")"); + } +} + +#------------------------------------------------------------------- + +=head2 notifySubscribers ( ) + +Send notifications to the thread and forum subscribers that a new post has been made. + +=cut + +sub notifySubscribers { + my $self = shift; + my %subscribers; + foreach my $userId (@{WebGUI::Grouping::getUsersInGroup($self->getThread->get("subscriptionGroupId"))}) { + $subscribers{$userId} = $userId unless ($userId eq $self->get("ownerUserId")); + } + foreach my $userId (@{WebGUI::Grouping::getUsersInGroup($self->getThread->getParent->get("subscriptionGroupId"))}) { + $subscribers{$userId} = $userId unless ($userId eq $self->get("ownerUserId")); + } + my %lang; + foreach my $userId (keys %subscribers) { + my $u = WebGUI::User->new($userId); + if ($lang{$u->profileField("language")}{message} eq "") { + $lang{$u->profileField("language")}{var} = { + 'notify.subscription.message' => WebGUI::International::get(875,"WebGUI",$u->profileField("language")) + }; + $lang{$u->profileField("language")}{var} = $self->getTemplateVars($lang{$u->profileField("language")}{var}); + $lang{$u->profileField("language")}{subject} = WebGUI::International::get(523,"WebGUI",$u->profileField("language")); + $lang{$u->profileField("language")}{message} = $self->processTemplate($lang{$u->profileField("language")}{var}, $self->getThread->getParent->get("notificationTemplateId"));, + } + WebGUI::MessageLog::addEntry($userId,"",$lang{$u->profileField("language")}{subject},$lang{$u->profileField("language")}{message}); + } +} + + +#------------------------------------------------------------------- +sub processPropertiesFromFormPost { + my $self = shift; + $self->SUPER::processPropertiesFromFormPost; + my %data = ( + ownerUserId => $session{user}{userId}, + groupIdView => $self->getThread->get("groupIdView"), + groupIdEdit => $self->getThread->get("groupIdEdit") + ); + $data{startDate} = $self->getThread->get("startDate") unless ($session{form}{startDate}); + $data{endDate} = $self->getThread->get("endDate") unless ($session{form}{endDate}); + unless ($session{form}{synopsis}) { + my $body = $session{form}{content}; + $body =~ s/\n/\^\-\;/ unless ($body =~ m/\^\-\;/); + my @content = split(/\^\-\;/,$body); + $content[0] = WebGUI::HTML::filter($content[0],"none"); + $data{synopsis} = $content[0]; + $body =~ s/\^\-\;/\n/; + $data{content} = $body; + } + $data{isHidden} = 1; + $self->update(\%data); +} + + +#------------------------------------------------------------------- + +=head2 rate ( rating ) + +Stores a rating against this post. + +=head3 rating + +An integer between 1 and 5 (5 being best) to rate this post with. + +=cut + +sub rate { + my $self = shift; + my $rating = shift; + unless ($self->hasRated) { + WebGUI::SQL->write("insert into Post_rating (assetId,userId,ipAddress,dateOfRating,rating) values (" + .quote($self->getId).", ".quote($session{user}{userId}).", ".quote($session{env}{REMOTE_ADDR}).", + ".WebGUI::DateTime::time().", $rating)"); + $self->recalculateRating; + } +} + +#------------------------------------------------------------------- + +=head2 recalculateRating ( ) + +Recalculates the average rating of the post from all the ratings and stores the result to the database. + +=cut + +sub recalculateRating { + my ($self) = @_; + my ($count) = WebGUI::SQL->quickArray("select count(*) from Post_rating where postId=".quote($self->getId)); + $count = $count || 1; + my ($sum) = WebGUI::SQL->quickArray("select sum(rating) from Post_rating where postId=".quote($self->getId)); + my $average = round($sum/$count); + $self->update({rating=>$average}); + $self->getThread->recalculateRating; +} + + +#------------------------------------------------------------------- + +=head2 setDefaultStatus ( ) + +Sets a new post's status based upon forum settings. + +=cut + +sub setDefaultStatus { + my $self = shift; + if ($self->getThread->getParent->get("moderatePosts")) { + $self->setStatusPending; + } else { + $self->setStatusApproved; + } +} + + +#------------------------------------------------------------------- + +=head2 setStatusApproved ( ) + +Sets the post to approved and sends any necessary notifications. + +=cut + +sub setStatusApproved { + my $self + $self->update({status=>'approved'}); + $self->getThread->setStatusApproved if $self->isRootPost; + $self->getThread->incrementReplies($self->get("dateUpdated"),$self->getId) if $self->isReply; + unless ($self->isPoster) { + WebGUI::MessageLog::addInternationalizedEntry($self->get("ownerUserId"),'',$self->getUrl,579); + } + $self->notifySubscribers; +} + + + +#------------------------------------------------------------------- + +=head2 setStatusArchived ( ) + +Sets the status of this post to archived. + +=cut + + +sub setStatusArchived { + my ($self) = @_; + $self->update({status=>'archived'}); + $self->getThread->setStatusArchived if $self->isRootPost; +} + + +#------------------------------------------------------------------- + +=head2 setStatusDenied ( ) + +Sets the status of this post to denied. + +=cut + +sub setStatusDenied { + my ($self) = @_; + $self->update({status=>'denied'}); + $self->getThread->setStatusDenied if $self->isRootPost; + WebGUI::MessageLog::addInternationalizedEntry($self->get("ownerUserId"),'',$self->getUrl,580); +} + +#------------------------------------------------------------------- + +=head2 setStatusPending ( ) + +Sets the status of this post to pending. + +=cut + +sub setStatusPending { + my ($self) = @_; + $self->update({status=>'pending'}); + $self->getThread->setStatusPending if $self->isRootPost; + WebGUI::MessageLog::addInternationalizedEntry('',$self->getThread->getParent->get("moderateGroupId"), + $self->getUrl,578,'WebGUI','pending'); +} + + +#------------------------------------------------------------------- + +=head2 unmarkRead ( ) + +Negates the markRead method. + +=cut + +sub unmarkRead { + my $self = shift; + WebGUI::SQL->write("delete from forumRead where userId=".quote($session{user}{userId})." and postId=".quote($self->getId)); +} + +#------------------------------------------------------------------- + +=head2 www_approve ( ) + +The web method to approve a post. + +=cut + +sub www_approve { + my $self = shift; + $self->setStatusApproved if $self->getThread->getParent->canModerate; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_deny ( ) + +The web method to deny a post. + +=cut + +sub www_deny { + my $self = shift; + $self->setStatusDenied if $self->getThread->getParent->canModerate; + return $self->www_view; +} + +#------------------------------------------------------------------- +sub www_edit { + my $self = shift; + return WebGUI::Privilege::insufficient() unless ($self->canEdit); + my %var; + if ($session{form}{func} eq "add") { + $self->{_properties}{contentType} = "mixed"; + $var{'isNew'} = 1; + } + $var{'link.header.label'} = WebGUI::International::get(90,"USS"); + $var{'question.header.label'} = WebGUI::International::get(84,"USS"); + $var{'submission.header.label'} = WebGUI::International::get(19,"USS"); + $var{'user.isVisitor'} = ($session{user}{userId} eq '1'); + $var{'visitorName.label'} = WebGUI::International::get(438); + $var{'visitorName.form'} = WebGUI::Form::text({ + name=>"visitorName" + }); + $var{'form.header'} = WebGUI::Form::formHeader() + .WebGUI::Form::hidden({ + name=>"func", + value=>"editSave" + }); + if ($self->getId eq "new") { + $var{'form.header'} .= WebGUI::Form::hidden({ + name=>"assetId", + value=>"new" + }).WebGUI::Form::hidden({ + name=>"class", + value=>$session{form}{class} + }); + } + $var{'url.label'} = WebGUI::International::get(91,"USS"); + $var{'newWindow.label'} = WebGUI::International::get(92,"USS"); + for my $x (1..5) { + $var{'userDefined'.$x.'.form'} = WebGUI::Form::text({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.yesNo'} = WebGUI::Form::yesNo({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.textarea'} = WebGUI::Form::textarea({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.textarea'} = WebGUI::Form::HTMLArea({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + } + $var{'question.label'} = WebGUI::International::get(85,"USS"); + $var{'title.label'} = WebGUI::International::get(35,"USS"); + $var{'title.form'} = WebGUI::Form::text({ + name=>"title", + value=>$self->get("title") + }); + $var{'title.form.textarea'} = WebGUI::Form::textarea({ + name=>"title", + value=>$self->get("title") + }); + $var{'body.label'} = WebGUI::International::get(31,"USS"); + $var{'answer.label'} = WebGUI::International::get(86,"USS"); + $var{'description.label'} = WebGUI::International::get(85); + $var{'contet.form'} = WebGUI::Form::HTMLArea({ + name=>"content", + value=>$self->get("content") + }); + $var{'content.form.textarea'} = WebGUI::Form::textarea({ + name=>"content", + value=>$self->get("content") + }); +# $var{'image.label'} = WebGUI::International::get(32,"USS"); + # if ($submission->{image} ne "") { +# $var{'image.form'} = ''.WebGUI::International::get(391).''; + # } else { +# $var{'image.form'} = WebGUI::Form::file({ +# name=>"image" +# }); + # } +# $var{'attachment.label'} = WebGUI::International::get(33,"USS"); + # if ($submission->{attachment} ne "") { +# $var{'attachment.form'} = ''.WebGUI::International::get(391).''; + # } else { +# $var{'attachment.form'} = WebGUI::Form::file({ +# name=>"attachment" +# }); + # } + $var{'contentType.label'} = WebGUI::International::get(1007); + $var{'contentType.form'} = WebGUI::Form::contentType({ + name=>'contentType', + value=>$self->get("contentType") || "mixed" + }); + $var{'startDate.label'} = WebGUI::International::get(497); + $var{'endDate.label'} = WebGUI::International::get(498); + $var{'startDate.form'} = WebGUI::Form::dateTime({ + name => 'startDate', + value => $self->get("startDate") + }); + $var{'endDate.form'} = WebGUI::Form::dateTime({ + name => 'endDate', + value => $self->get("startDate") + }); + $var{'form.submit'} = WebGUI::Form::submit(); + $var{'form.footer'} = WebGUI::Form::formFooter(); + return $self->getParent->processStyle($self->processTemplate(\%var,$self->getParent->get("submissionFormTemplateId"))); +} + +#------------------------------------------------------------------- + +=head2 www_post ( caller ) + +The web method to display the post form. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=cut + +sub www_post { + my ($caller) = @_; + my ($subject, $message, $forum); + my $var; + $var->{'newpost.header'} = WebGUI::International::get(1064); + $var->{'newpost.isReply'} = ($session{form}{parentId} ne ""); + $var->{'newpost.isEdit'} = ($session{form}{forumPostId} ne ""); + $var->{'newpost.isNewThread'} = ($session{form}{parentId} eq "" && !$var->{'newpost.isEdit'}); + $var->{'user.isVisitor'} = ($session{user}{userId} eq '1'); + $var->{'newpost.isNewMessage'} = ($var->{'newpost.isNewThread'} || $var->{'newpost.isReply'}); + $var->{'form.begin'} = WebGUI::Form::formHeader({ + action=>$caller->{callback} + }); + my $defaultSubscribeValue = 0; + my $contentType = "mixed"; + if ($var->{'newpost.isReply'}) { + my $reply = WebGUI::Forum::Post->new($session{form}{parentId}); + return WebGUI::Privilege::insufficient() unless ($reply->getThread->getForum->canPost); + $var->{'form.begin'} .= WebGUI::Form::hidden({ + name=>'parentId', + value=>$reply->get("forumPostId") + }); + $forum = $reply->getThread->getForum; + $var->{'form.begin'} .= WebGUI::Form::hidden({ + name=>'forumId', + value=>$forum->get("forumId") + }); + $message = "[quote]".$reply->get("message")."[/quote]" if ($session{form}{withQuote}); + $var = getPostTemplateVars($reply, $reply->getThread, $forum, $caller, $var); + + $subject = $reply->get("subject"); + $subject = "Re: ".$subject unless ($subject =~ /^Re:/); + } + if ($var->{'newpost.isNewThread'}) { + $var->{'form.begin'} .= WebGUI::Form::hidden({ + name=>'forumId', + value=>$session{form}{forumId} + }); + $forum = WebGUI::Forum->new($session{form}{forumId}); + if ($forum->isModerator) { + $var->{'sticky.label'} = WebGUI::International::get(1013); + $var->{'sticky.form'} = WebGUI::Form::yesNo({ + name=>'isSticky', + value=>0 + }); + } + $defaultSubscribeValue = 1 unless ($forum->isSubscribed); + } + if ($var->{'newpost.isNewMessage'}) { + $var->{'subscribe.label'} = WebGUI::International::get(873); + return WebGUI::Privilege::insufficient() unless ($forum->canPost); + my $u = WebGUI::User->new($session{user}{userId}); + $u->karma($forum->get("karmaPerPost"),"Forum (".$forum->get("forumId").")","Forum Post") if ($session{setting}{useKarma}); + if ($forum->isModerator) { + $var->{'lock.label'} = WebGUI::International::get(1012); + $var->{'lock.form'} = WebGUI::Form::yesNo({ + name=>'isLocked', + value=>0 + }); + } + $var->{'subscribe.form'} = WebGUI::Form::yesNo({ + name=>'subscribe', + value=>$defaultSubscribeValue + }); + $message .= "\n\n".$session{user}{signature} if ($session{user}{signature}); + } + if ($var->{'newpost.isEdit'}) { + my $post = WebGUI::Forum::Post->new($session{form}{forumPostId}); + return WebGUI::Privilege::insufficient() unless ($post->getThread->getForum->canPost); + $subject = $post->get("subject"); + $message = $post->get("message"); + $forum = $post->getThread->getForum; + $var->{'form.begin'} .= WebGUI::Form::hidden({ + name=>"forumPostId", + value=>$post->get("forumPostId") + }); + $contentType = $post->get("contentType"); + } + $var->{'contentType.label'} = WebGUI::International::get(1007); + $var->{'contentType.form'} = WebGUI::Form::contentType({ + name=>'contentType', + value=>[$contentType] + }); + $var->{'user.isModerator'} = $forum->isModerator; + $var->{allowReplacements} = $forum->get("allowReplacements"); + if ($forum->get("allowRichEdit")) { + $var->{'message.form'} = WebGUI::Form::HTMLArea({ + name=>'message', + value=>$message + }); + } else { + $var->{'message.form'} = WebGUI::Form::textarea({ + name=>'message', + value=>$message + }); + } + $var->{'message.label'} = WebGUI::International::get(230); + if ($var->{'user.isVisitor'}) { + $var->{'visitorName.label'} = WebGUI::International::get(438); + $var->{'visitorName.form'} = WebGUI::Form::text({ + name=>'visitorName' + }); + } + my $forumOp = ($forum->get("usePreview"))? "postPreview" : "postSave"; + $var->{'form.begin'} .= WebGUI::Form::hidden({ + name=>'forumOp', + value=>$forumOp + }); + $var->{'form.submit'} = WebGUI::Form::submit(); + $var->{'subject.label'} = WebGUI::International::get(229); + $var->{'subject.form'} = WebGUI::Form::text({ + name=>'subject', + value=>$subject + }); + $var->{'form.end'} = WebGUI::Form::formFooter(); + return WebGUI::Template::process($forum->get("postformTemplateId"),"Forum/PostForm", $var); +} + +#------------------------------------------------------------------- + +=head2 www_postPreview ( caller ) + +The web method to generate a preview of a posting. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=cut + +sub www_postPreview { + my ($caller) = @_; + + + my $forumId = $session{form}{forumId}; + my $threadId = $session{form}{forumThreadId}; + my $postId = $session{form}{forumPostId}; + my $subject = $session{form}{subject}; + $subject = WebGUI::International::get(232) if ($subject eq ""); + $subject .= ' '.WebGUI::International::get(233) if ($session{form}{message} eq ""); + if ( $subject ne "") { # subjects could never contain anything other than text + $subject = WebGUI::HTML::filter(WebGUI::HTML::processReplacements($subject),"all"); + } + my $newPost = WebGUI::Forum::Post->new(); + + $newPost->{_properties}->{message} = $session{form}{message}; + $newPost->{_properties}->{subject} = $subject; + $newPost->{_properties}->{contentType} = $session{form}{contentType}; + $newPost->{_properties}->{userId} = $session{user}{userId}; + $newPost->{_properties}->{username} = ($session{form}{visitorName} || $session{user}{alias}); + $newPost->{_properties}->{dateUpdated} = WebGUI::DateTime::time(); + my $forum = WebGUI::Forum->new($forumId); + + my $var = getPostTemplateVars($newPost, WebGUI::Forum::Thread->new($threadId), WebGUI::Forum->new($forumId), $caller); + $var->{'newpost.header'} = WebGUI::International::get('Forum, Preview Heading'); + + $var->{'form.begin'} = WebGUI::Form::formHeader({ + action=>$caller->{callback} + }); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'forumId', value=>$forumId}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'forumThreadId', value=>$threadId}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'forumPostId', value=>$postId}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'parentId', value=>$session{form}{parentId}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'subject', value=>$subject}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'message', value=>$session{form}{message}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'contentType', value=>$session{form}{contentType}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'visitorName', value=>$session{form}{visitorName}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'subscribe', value=>$session{form}{subscribe}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'isLocked', value=>$session{form}{isLocked}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'isSticky', value=>$session{form}{isSticky}}); + $var->{'form.begin'} .= WebGUI::Form::hidden({name=>'forumOp', value=>"postSave"}); + $var->{'form.submit'} = WebGUI::Form::submit(); + $var->{'form.end'} = WebGUI::Form::formFooter(); + return WebGUI::Template::process($forum->get("postPreviewTemplateId"),"Forum/PostPreview", $var); +} + + +#------------------------------------------------------------------- + +=head2 www_postSave ( caller ) + +The web method to save the data from the post form. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=cut + +sub www_postSave { + my ($caller) = @_; + my $forumId = $session{form}{forumId}; + my $threadId = $session{form}{forumThreadId}; + my $postId = $session{form}{forumPostId}; + my $subject = $session{form}{subject}; + $subject = WebGUI::International::get(232) if ($subject eq ""); + $subject .= ' '.WebGUI::International::get(233) if ($session{form}{message} eq ""); + if ( $subject ne "") { # subjects could never contain anything other than text + $subject = WebGUI::HTML::filter(WebGUI::HTML::processReplacements($subject),"all"); + } + my %postData = ( + message=>$session{form}{message}, + subject=>$subject, + contentType=>$session{form}{contentType} + ); + my %postDataNew = ( + userId=>$session{user}{userId}, + username=>($session{form}{visitorName} || $session{user}{alias}) + ); + if ($session{form}{parentId} ne '') { # reply + %postData = (%postData, %postDataNew); + my $parentPost = WebGUI::Forum::Post->new($session{form}{parentId}); + return WebGUI::Privilege::insufficient() unless ($parentPost->getThread->getForum->canPost); + $parentPost->getThread->subscribe($session{user}{userId}) if ($session{form}{subscribe}); + $parentPost->getThread->lock if ($session{form}{isLocked}); + $postData{forumThreadId} = $parentPost->getThread->get("forumThreadId"); + $postData{parentId} = $session{form}{parentId}; + my $post = WebGUI::Forum::Post->create(\%postData); + setPostStatus($caller,$post); + WebGUI::HTTP::setRedirect(formatThreadURL($caller->{callback}, $post->get("forumPostId"))); + + return "Redirecting..."; + #return www_viewThread($caller,$post->get("forumPostId")); + } + if ($session{form}{forumPostId} ne '') { # edit + my $post = WebGUI::Forum::Post->new($session{form}{forumPostId}); + return WebGUI::Privilege::insufficient() unless ($post->canEdit); + if ($post->getThread->getForum->get("addEditStampToPosts")) { + $postData{message} .= "\n\n --- (".WebGUI::International::get(1029)." " + .WebGUI::DateTime::epochToHuman(WebGUI::DateTime::time())." ".WebGUI::International::get(1030) + ." $session{user}{username}) --- \n"; + } + $post->set(\%postData); + WebGUI::HTTP::setRedirect(formatThreadURL($caller->{callback}, $post->get("forumPostId"))); + return "Redirecting..."; + #return www_viewThread($caller,$post->get("forumPostId")); + } + if ($forumId) { # new post + %postData = (%postData, %postDataNew); + my $forum = WebGUI::Forum->new($forumId); + return WebGUI::Privilege::insufficient() unless ($forum->canPost); + my $thread = WebGUI::Forum::Thread->create({ + forumId=>$forumId, + isSticky=>$session{form}{isSticky}, + isLocked=>$session{form}{isLocked} + }, \%postData); + $thread->subscribe($session{user}{userId}) if ($session{form}{subscribe}); + setPostStatus($caller,$thread->getPost($thread->get("rootPostId"))); + WebGUI::HTTP::setRedirect(formatForumURL($caller->{callback}, $forumId)); + return "Redirecting..."; + #return www_viewForum($caller,$forumId); + } +} + +#------------------------------------------------------------------- + +=head2 www_ratePost ( ) + +The web method to rate a post. + +=cut + +sub www_rate { + my $self = shift; + $self->rate($session{form}{rating}) if ($self->canView && !$self->hasRated); + $self->www_view; +} + + +#------------------------------------------------------------------- +sub www_view { + my $self = shift; + return $self->getParent->processStyle(WebGUI::Privilege::noAccess()) unless $self->canView; + return $self->getParent->processStyle($self->view); +} + + +1; + diff --git a/lib/WebGUI/Asset/Thread.pm b/lib/WebGUI/Asset/Thread.pm new file mode 100644 index 000000000..cf168c4af --- /dev/null +++ b/lib/WebGUI/Asset/Thread.pm @@ -0,0 +1,1038 @@ +package WebGUI::Asset::Thread; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2005 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 +#------------------------------------------------------------------- + +use strict; +use WebGUI::Asset; +use WebGUI::Asset::Template; +use WebGUI::Asset::Post; +use WebGUI::DateTime; +use WebGUI::Grouping; +use WebGUI::International; +use WebGUI::MessageLog; +use WebGUI::Paginator; +use WebGUI::Privilege; +use WebGUI::Session; +use WebGUI::SQL; + +our @ISA = qw(WebGUI::Asset); + + +#------------------------------------------------------------------- +sub canSubscribe { + my $self = shift; + return ($session{user}{userId} ne "1" && $self->canView); +} + +#------------------------------------------------------------------- +sub createSubscriptionGroup { + my $self = shift; + my $group = WebGUI::Group->new("new"); + $group->name($self->getId); + $group->description("The group to store subscriptions for the collaboration system ".$self->getId); + $group->isEditable(0); + $group->showInForms(0); + $group->deleteGroups([3]); # admins don't want to be auto subscribed to this thing + $self->update({ + subscriptionGroupId=>$group->groupId + }); +} + +#------------------------------------------------------------------- + +=head2 decrementReplies ( ) + +Deccrements this reply counter. + +=cut + +sub decrementReplies { + my $self = shift; + $self->update({replies=>$self->get("replies")-1}); + $self->getParent->decrementReplies; +} + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $definition = shift; + push(@{$definition}, { + tableName=>'Thread', + className=>'WebGUI::Asset::Thread', + properties=>{ + subscriptionGroupId => { + fieldType=>"hidden", + defaultValue=>undef + }, + status => { + fieldType=>"hidden", + defaultValue=>undef + }, + rating => { + fieldType=>"hidden", + defaultValue=>undef + }, + replies => { + fieldType=>"hidden", + defaultValue=>undef + }, + views => { + fieldType=>"hidden", + defaultValue=>undef + }, + isSticky => { + fieldType=>"yesNo", + defaultValue=>0 + }, + isLocked => { + fieldType=>"yesNo", + defaultValue=>0 + }, + lastPostId => { + fieldType=>"hidden", + defaultValue=>undef + }, + lastPostDate => { + fieldType=>"hidden", + defaultValue=>undef + } + }, + }); + return $class->SUPER::definition($definition); +} + +#------------------------------------------------------------------- + +sub DESTROY { + my $self = shift; + $self->{_next}->DESTROY if (exists $self->{_next}); + $self->{_previous}->DESTROY if (exists $self->{_previous}); + $self->SUPER::DESTROY; +} + + +#------------------------------------------------------------------- + +=head2 getFlatThread ( post, thread, forum, caller, currentPost ) + +Returns an array reference with the template variables from all the posts in a thread in flat mode. In flat mode +messages are ordered by submission date, so threading is not maintained. + +=head3 post + +A post object. + +=head3 thread + +A thread object. + +=head3 forum + +A forum object. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=head3 currentPost + +The unique id of the post that was selected by the user in this thread. + +=cut + +sub getFlatThread { + my ($post, $thread, $forum, $caller, $currentPost) = @_; + my (@post_loop, @posts, $OR); + unless ($post->getThread->getForum->isModerator) { + $OR = " and not (status='denied' OR status='pending')"; + } + @posts = WebGUI::SQL->buildArray("SELECT forumPostId FROM forumPost WHERE forumThreadId=".quote($thread->get("forumThreadId"))." $OR ORDER BY dateOfPost"); + foreach my $postId (@posts){ + my $post = WebGUI::Forum::Post->new($postId); + push (@post_loop, getPostTemplateVars($post,$thread, $forum, $caller, { + 'post.isCurrent'=>($currentPost eq $post->get("forumPostId")) + })); + } + return \@post_loop; +} + + +#------------------------------------------------------------------- +sub getIcon { + my $self = shift; + my $small = shift; + return $session{config}{extrasURL}.'/assets/small/thread.gif' if ($small); + return $session{config}{extrasURL}.'/assets/thread.gif'; +} + +#------------------------------------------------------------------- +sub getName { + return "Thread"; +} + +#------------------------------------------------------------------- + +=head2 getLayoutUrl ( layout [, postId] ) + +Formats the url to change the layout of a thread. + +=head3 layout + +A string indicating the type of layout to use. Can be flat, nested, or threaded. + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getLayoutUrl { + my $self = shift; + my $layout = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("layout=".$layout."#".$postId); +} + +#------------------------------------------------------------------- + +=head2 getLockUrl ( [ postId ] ) + +Formats the url to lock a thread. + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getLockUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + $self->getUrl("fucn=lock#">$postId); +} + +#------------------------------------------------------------------- + +=head2 getNextThread ( ) + +Returns a thread object for the next (newer) thread in the same forum. + +=cut + +sub getNextThread { + my $self = shift; + unless (exists $self->{_next}) { + $self->{_next} = WebGUI::Asset::Post->newByPropertyHashRef( + WebGUI::SQL->quickHashRef(" + select asset.*,Post.* + from Thread + left join asset on asset.parentId=Thread.assetId + left join Post on Post.assetId=asset.assetId + where Thread.parentId=".quote($self->get("parentId"))." + and asset.state='published' + and ".$self->getParent->getValue("sortBy").">".quote($self->get($self->getParent->getValue("sortBy")))." + and (userId=".quote($self->get("ownerUserId"))." or Post.status='approved') + order by ".$self->getParent->getValue("sortBy")." asc + ",WebGUI::SQL->getSlave) + ); + }; + return $self->{_next}; +} + + + +#------------------------------------------------------------------- + +=head2 getPreviousThread ( ) + +Returns a thread object for the previous (older) thread in the same forum. + +=cut + +sub getPreviousThread { + my $self = shift; + unless (exists $self->{_previous}) { + $self->{_previous} = WebGUI::Asset::Post->newByPropertyHashRef( + WebGUI::SQL->quickHashRef(" + select asset.*,Post.* + from Thread + left join asset on asset.parentId=Thread.assetId + left join Post on Post.assetId=asset.assetId + where Thread.parentId=".quote($self->get("parentId"))." + and asset.state='published' + and ".$self->getParent->getValue("sortBy")."<".quote($self->get($self->getParent->getValue("sortBy")))." + and (userId=".quote($self->get("ownerUserId"))." or Post.status='approved') + order by ".$self->getParent->getValue("sortBy")." desc + ",WebGUI::SQL->getSlave) + ); + }; + return $self->{_previous}; +} + + +#------------------------------------------------------------------- + +=head2 getStickUrl ( [ postId ] ) + +Formats the url to make a thread sticky. + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getStickUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("func=stick#".$postId); +} + +#------------------------------------------------------------------- + +=head2 getSubscribeUrl ( [ postId ] ) + +Formats the url to subscribe to the thread + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getSubscribeUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("func=subscribe#".$postId); +} + + +#------------------------------------------------------------------- + +=head2 getThreadTemplateVars ( caller, post ) + +Returns a hash reference compatible with WebGUI's template system containing the template variables for the thread. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=head3 post + +A post object. + +=cut + +sub getThreadTemplateVars { + my ($caller, $post) = @_; + my $callback = $caller->{callback}; + $post->markRead($session{user}{userId}); + my $thread = $post->getThread; + unless ($post->canView) { + $post = $thread->getPost($thread->get("rootPostId")); + } + my $forum = $thread->getForum; + my $var = getPostTemplateVars($post, $thread, $forum, $caller); + my $root = WebGUI::Forum::Post->new($thread->get("rootPostId")); + $var->{'callback.url'} = $callback; + $var->{'callback.label'} = WebGUI::International::get(1039); + $var->{'user.canPost'} = $forum->canPost; + $var->{'user.isVisitor'} = ($session{user}{userId} eq '1'); + $var->{'user.isModerator'} = $forum->isModerator; + $var->{'user.isSubscribed'} = $thread->isSubscribed; + $var->{'thread.layout.nested.label'} = WebGUI::International::get(1045); + $var->{'thread.layout.nested.url'} = formatThreadLayoutURL($callback,$post->get("forumPostId"),"nested"); + $var->{'thread.layout.flat.label'} = WebGUI::International::get(510); + $var->{'thread.layout.flat.url'} = formatThreadLayoutURL($callback,$post->get("forumPostId"),"flat"); + $var->{'thread.layout.threaded.label'} = WebGUI::International::get(511); + $var->{'thread.layout.threaded.url'} = formatThreadLayoutURL($callback,$post->get("forumPostId"),"threaded"); + my $layout = $session{scratch}{forumThreadLayout} || $session{user}{discussionLayout}; + $var->{'thread.layout.isFlat'} = ($layout eq "flat"); + $var->{'thread.layout.isNested'} = ($layout eq "nested"); + $var->{'thread.layout.isThreaded'} = ($layout eq "threaded" || !($var->{'thread.layout.isNested'} || $var->{'thread.layout.isFlat'})); + $var->{'thread.subscribe.url'} = formatThreadSubscribeURL($callback,$post->get("forumPostId")); + $var->{'thread.subscribe.label'} = WebGUI::International::get(873); + $var->{'thread.unsubscribe.url'} = formatThreadUnsubscribeURL($callback,$post->get("forumPostId")); + $var->{'thread.unsubscribe.label'} = WebGUI::International::get(874); + $var->{'thread.isSticky'} = $thread->isSticky; + $var->{'thread.stick.url'} = formatThreadStickURL($callback,$post->get("forumPostId")); + $var->{'thread.stick.label'} = WebGUI::International::get(1037); + $var->{'thread.unstick.url'} = formatThreadUnstickURL($callback,$post->get("forumPostId")); + $var->{'thread.unstick.label'} = WebGUI::International::get(1038); + $var->{'thread.isLocked'} = $thread->isLocked; + $var->{'thread.lock.url'} = formatThreadLockURL($callback,$post->get("forumPostId")); + $var->{'thread.lock.label'} = WebGUI::International::get(1040); + $var->{'thread.unlock.url'} = formatThreadUnlockURL($callback,$post->get("forumPostId")); + $var->{'thread.unlock.label'} = WebGUI::International::get(1041); + my $p = WebGUI::Paginator->new(WebGUI::URL::append($callback,"forumOp=viewThread&forumPostId=".$post->get("forumPostId")."&layout=$layout"),$forum->get("postsPerPage")); + if($layout eq "flat"){ + $p->setDataByArrayRef(getFlatThread($root, $thread, $forum, $caller, $post->get("forumPostId"))); + $var->{post_loop} = $p->getPageData(); + }else{ + $p->setDataByArrayRef(recurseThread($root, $thread, $forum, 0, $caller, $post->get("forumPostId"))); + $var->{post_loop} = $p->getPageData(); + } + $var->{firstPage} = $p->getFirstPageLink; + $var->{lastPage} = $p->getLastPageLink; + $var->{nextPage} = $p->getNextPageLink; + $var->{pageList} = $p->getPageLinks; + $var->{previousPage} = $p->getPreviousPageLink; + $var->{multiplePages} = ($p->getNumberOfPages > 1); + $var->{numberOfPages} = $p->getNumberOfPages; + $var->{pageNumber} = $p->getPageNumber; + + $var->{'thread.subject.label'} = WebGUI::International::get(229); + $var->{'thread.date.label'} = WebGUI::International::get(245); + $var->{'thread.user.label'} = WebGUI::International::get(244); + $var->{'thread.new.url'} = formatNewThreadURL($callback,$thread->get("forumId")); + $var->{'thread.new.label'} = WebGUI::International::get(1018); + $var->{'thread.previous.url'} = formatPreviousThreadURL($callback,$thread->get("forumThreadId")); + $var->{'thread.previous.label'} = WebGUI::International::get(513); + $var->{'thread.next.url'} = formatNextThreadURL($callback,$thread->get("forumThreadId")); + $var->{'thread.next.label'} = WebGUI::International::get(512); + $var->{'thread.list.url'} = formatForumURL($callback,$forum->get("forumId")); + $var->{'thread.list.label'} = WebGUI::International::get(1019); + $var->{'forum.title'} = $caller->{title}; + $var->{'forum.description'} = $caller->{description}; + return $var; +} + +#------------------------------------------------------------------- + +=head2 getUnlockUrl ( [ postId ] ) + +Formats the url to unlock the thread + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getUnlockUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("func=unlock#".$postId); +} + + +#------------------------------------------------------------------- + +=head2 getUnstickUrl ( [ postId ] ) + +Formats the url to unstick the thread + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getUnstickUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("func=unstick#".$postId); +} + +#------------------------------------------------------------------- + +=head2 getUnsubscribeUrl ( [ postId ] ) + +Formats the url to unsubscribe from the thread + +=head3 postId + +The asset id of the post to position on. Defaults to the root post of the thread. + +=cut + +sub getUnsubscribeUrl { + my $self = shift; + my $postId = shift || $self->get("rootPostId"); + return $self->getUrl("func=unsubscribe#".$postId); +} + + +#------------------------------------------------------------------- + +=head2 isLocked ( ) + +Returns a boolean indicating whether this thread is locked from new posts and other edits. + +=cut + +sub isLocked { + my ($self) = @_; + return $self->get("isLocked"); +} + + +#------------------------------------------------------------------- + +=head2 incrementReplies ( lastPostDate, lastPostId ) + +Increments the replies counter for this thread. + +=head3 lastPostDate + +The date of the reply that caused the replies counter to be incremented. + +=head3 lastPostId + +The id of the reply that caused the replies counter to be incremented. + +=cut + +sub incrementReplies { + my ($self, $dateOfReply, $replyId) = @_; + $self->update({replies=>$self->get("replies")+1, lastPostId=>$replyId, lastPostDate=>$dateOfReply}); + $self->getParent->incrementReplies($dateOfReply,$replyId); +} + +#------------------------------------------------------------------- + +=head2 incrementViews ( ) + +Increments the views counter for this thread. + +=cut + +sub incrementViews { + my ($self) = @_; + $self->update({views=>$self->get("views")+1}); + $self->getParent->incrementViews; +} + +#------------------------------------------------------------------- + +=head2 isSticky ( ) + +Returns a boolean indicating whether this thread should be "stuck" a the top of the forum and not be sorted with the rest of the threads. + +=cut + +sub isSticky { + my ($self) = @_; + return $self->get("isSticky"); +} + + +#------------------------------------------------------------------- + +=head2 isSubscribed ( ) + +Returns a boolean indicating whether the user is subscribed to this thread. + +=cut + +sub isSubscribed { + my $self = shift; + return WebGUI::Grouping::isInGroup($self->get("subscriptionGroupId")); +} + +#------------------------------------------------------------------- + +=head2 lock ( ) + +Sets this thread to be locked from edits. + +=cut + +sub lock { + my ($self) = @_; + $self->update({isLocked=>1}); +} + + + +#------------------------------------------------------------------- + +=head2 recalculateRating ( ) + +Recalculates the average rating of this thread based upon all of the posts in the thread. + +=cut + +sub recalculateRating { + my ($self) = @_; + my ($count) = WebGUI::SQL->quickArray("select count(*) from Post left join asset on Post.assetId=asset.assetId + where Post.threadId=".quote($self->getId)." and Post.rating>0"); + $count = $count || 1; + my ($sum) = WebGUI::SQL->quickArray("select sum(Post.rating) from Post left join asset on Post.assetId=asset.assetId + where Post.threadId=".quote($self->getId)." and Post.rating>0"); + my $average = round($sum/$count); + $self->update({rating=>$average}); + $self->getParent->recalculateRating; +} + +#------------------------------------------------------------------- + +=head2 setLastPost ( lastPostDate, lastPostId ) + +Sets the pertinent details for the last post. Can also be done directly using the set method. + +=head3 lastPostDate + +The epoch date of the post. + +=head3 lastPostId + +The unique id of the post. + +=cut + +sub setLastPost { + my ($self, $postDate, $postId) = @_; + $self->update({ + lastPostId=>$postId, + lastPostDate=>$postDate + }); + $self->getParent->setLastPost($postDate, $postId); +} + +#------------------------------------------------------------------- + +=head2 setStatusApproved ( ) + +Sets the status of this thread to approved. + +=cut + +sub setStatusApproved { + my ($self) = @_; + $self->update({status=>'approved'}); +} + +#------------------------------------------------------------------- + +=head2 setStatusArchived ( ) + +Sets the status of this thread to archived. + +=cut + +sub setStatusArchived { + my ($self) = @_; + $self->update({status=>'archived'}); +} + + +#------------------------------------------------------------------- + +=head2 setStatusDenied ( ) + +Sets the status of this thread to denied. + +=cut + +sub setStatusDenied { + my ($self) = @_; + $self->update({status=>'denied'}); +} + +#------------------------------------------------------------------- + +=head2 setStatusPending ( ) + +Sets the status of this thread to pending. + +=cut + +sub setStatusPending { + my ($self) = @_; + $self->update({status=>'pending'}); +} + +#------------------------------------------------------------------- + +=head2 stick ( ) + +Makes this thread sticky. + +=cut + +sub stick { + my ($self) = @_; + $self->update({isSticky=>1}); +} + +#------------------------------------------------------------------- + +=head2 subscribe ( ) + +Subscribes the user to this thread. + +=cut + +sub subscribe { + my $self = shift; + unless ($self->isSubscribed) { + WebGUI::Grouping::addUsersToGroups([$session{user}{userId}],[$self->get("subscriptionGroupId")]); + } +} + +#------------------------------------------------------------------- + +=head2 unlock ( ) + +Negates the lock method. + +=cut + +sub unlock { + my ($self) = @_; + $self->update({isLocked=>0}); +} + +#------------------------------------------------------------------- + +=head2 unstick ( ) + +Negates the stick method. + +=cut + +sub unstick { + my ($self) = @_; + $self->update({isSticky=>0}); +} + +#------------------------------------------------------------------- + +=head2 unsubscribe ( ) + +Negates the subscribe method. + +=cut + +sub unsubscribe { + my $self = shift; + if ($self->isSubscribed) { + WebGUI::Grouping::deleteUsersFromGroups([$session{user}{userId}],[$self->get("subscriptionGroupId")]); + } +} + + +#------------------------------------------------------------------- +sub view { + my $self = shift; + my %var; + #my $callback = WebGUI::URL::gateway($parentsPage->get("urlizedTitle"),"func=viewSubmission&wid=".$self->wid."&sid=".$submission->{USS_submissionId}); +# if ($session{form}{forumOp} ne "" && $session{form}{forumOp} ne "viewForum") { +# return WebGUI::Forum::UI::forumOp({ +# callback=>$callback, +# title=>$submission->{title}, +# forumId=>$submission->{forumId} +# }); +# } + $self->update({views=>$self->get("views")+1}); + $var{content} = WebGUI::HTML::filter($self->get("content"),$self->get("filterContent")); + $var{content} = WebGUI::HTML::format($var{content},"USS"); + $var{"user.label"} = WebGUI::International::get(21,"USS"); + $var{"userProfile.url"} = $self->getUrl('op=viewProfile&uid='.$self->get("ownerUserId")); + $var{"userId"} = $self->get("ownerUserId"); + $var{"username"} = $self->get("username"); + $var{"dateSubmitted.label"} = WebGUI::International::get(13,"USS"); + $var{"dateSubmitted.human"} = epochToHuman($self->get("dateSubmitted")); + $var{"dateUpdated.label"} = WebGUI::International::get(78,"USS"); + $var{"dateUpdated.human"} = epochToHuman($self->get("dateUpdated")); + $var{"status.label"} = WebGUI::International::get(14,"USS"); + $var{"status.status"} = $self->getParent->status($self->get("status")); + $var{"views.label"} = WebGUI::International::get(514); + $var{canPost} = $self->canContribute; + $var{"post.url"} = $self->getUrl("func=edit"); + $var{"post.label"} = WebGUI::International::get(20,"USS"); + my $previous = $self->getPreviousThread; + $var{"previous.more"} = defined $previous; + $var{"previous.url"} = $previous->getUrl if ($var{"previous.more"}); + $var{"previous.label"} = WebGUI::International::get(58,"USS"); + my $next = $self->getNextThread; + $var{"next.more"} = defined $next; + $var{"next.url"} = $next->getUrl if ($var{"next.more"}); + $var{"next.label"} = WebGUI::International::get(59,"USS"); + $var{canEdit} = $self->canEdit; + $var{"delete.url"} = $self->getUrl("func=delete"); + $var{"delete.label"} = WebGUI::International::get(37,"USS"); + $var{"edit.url"} = $self->getUrl("func=edit"); + $var{"edit.label"} = WebGUI::International::get(27,"USS"); + $var{canChangeStatus} = $self->canModerate; + $var{"approve.url"} = $self->getUrl("func=approve&mlog=".$session{form}{mlog}); + $var{"approve.label"} = WebGUI::International::get(572); + $var{"leave.url"} = $self->getUrl('op=viewMessageLog'); + $var{"leave.label"} = WebGUI::International::get(573); + $var{"deny.url"} = $self->getUrl("func=deny&mlog=".$session{form}{mlog}); + $var{"deny.label"} = WebGUI::International::get(574); + $var{"canReply"} = $self->get("allowDiscussion"); +# $var{"reply.url"} = WebGUI::Forum::UI::formatNewThreadURL($callback,$submission->{forumId}); +# $var{"reply.label"} = WebGUI::International::get(47,"USS"); + $var{"search.url"} = WebGUI::Search::toggleURL("",$self->getParent->get("url")); + $var{"search.label"} = WebGUI::International::get(364); + $var{"back.url"} = $self->getThread->getParent->getUrl; + $var{"back.label"} = WebGUI::International::get(28,"USS"); + $var{'userDefined1.value'} = $self->get("userDefined1"); + $var{'userDefined2.value'} = $self->get("userDefined2"); + $var{'userDefined3.value'} = $self->get("userDefined3"); + $var{'userDefined4.value'} = $self->get("userDefined4"); + $var{'userDefined5.value'} = $self->get("userDefined5"); +# if ($submission->{image} ne "") { +# $file = WebGUI::Attachment->new($submission->{image},$self->wid,$submissionId); +# $var{"image.url"} = $file->getURL; +# $var{"image.thumbnail"} = $file->getThumbnail; +# } +# if ($submission->{attachment} ne "") { +# $file = WebGUI::Attachment->new($submission->{attachment},$self->wid,$submissionId); +# $var{"attachment.box"} = $file->box; +# $var{"attachment.url"} = $file->getURL; +# $var{"attachment.icon"} = $file->getIcon; +# $var{"attachment.name"} = $file->getFilename; + # } + if ($self->get("allowDiscussion")) { +# $var{"replies"} = WebGUI::Forum::UI::www_viewForum( +# {callback=>$callback,title=>$submission->{title},forumId=>$submission->{forumId}}, +# $submission->{forumId}); + } + return $self->processTemplate(\%var,$self->getParent->get("submissionTemplateId")); +} + +#------------------------------------------------------------------- +sub www_edit { + my $self = shift; + return WebGUI::Privilege::insufficient() unless ($self->canEdit); + my %var; + if ($session{form}{func} eq "add") { + $self->{_properties}{contentType} = "mixed"; + $var{'isNew'} = 1; + } + $var{'link.header.label'} = WebGUI::International::get(90,"USS"); + $var{'question.header.label'} = WebGUI::International::get(84,"USS"); + $var{'submission.header.label'} = WebGUI::International::get(19,"USS"); + $var{'user.isVisitor'} = ($session{user}{userId} eq '1'); + $var{'visitorName.label'} = WebGUI::International::get(438); + $var{'visitorName.form'} = WebGUI::Form::text({ + name=>"visitorName" + }); + $var{'form.header'} = WebGUI::Form::formHeader() + .WebGUI::Form::hidden({ + name=>"func", + value=>"editSave" + }); + if ($self->getId eq "new") { + $var{'form.header'} .= WebGUI::Form::hidden({ + name=>"assetId", + value=>"new" + }).WebGUI::Form::hidden({ + name=>"class", + value=>$session{form}{class} + }); + } + $var{'url.label'} = WebGUI::International::get(91,"USS"); + $var{'newWindow.label'} = WebGUI::International::get(92,"USS"); + for my $x (1..5) { + $var{'userDefined'.$x.'.form'} = WebGUI::Form::text({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.yesNo'} = WebGUI::Form::yesNo({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.textarea'} = WebGUI::Form::textarea({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + $var{'userDefined'.$x.'.form.textarea'} = WebGUI::Form::HTMLArea({ + name=>"userDefined".$x, + value=>$self->get("userDefined".$x) + }); + } + $var{'question.label'} = WebGUI::International::get(85,"USS"); + $var{'title.label'} = WebGUI::International::get(35,"USS"); + $var{'title.form'} = WebGUI::Form::text({ + name=>"title", + value=>$self->get("title") + }); + $var{'title.form.textarea'} = WebGUI::Form::textarea({ + name=>"title", + value=>$self->get("title") + }); + $var{'body.label'} = WebGUI::International::get(31,"USS"); + $var{'answer.label'} = WebGUI::International::get(86,"USS"); + $var{'description.label'} = WebGUI::International::get(85); + $var{'contet.form'} = WebGUI::Form::HTMLArea({ + name=>"content", + value=>$self->get("content") + }); + $var{'content.form.textarea'} = WebGUI::Form::textarea({ + name=>"content", + value=>$self->get("content") + }); +# $var{'image.label'} = WebGUI::International::get(32,"USS"); + # if ($submission->{image} ne "") { +# $var{'image.form'} = ''.WebGUI::International::get(391).''; + # } else { +# $var{'image.form'} = WebGUI::Form::file({ +# name=>"image" +# }); + # } +# $var{'attachment.label'} = WebGUI::International::get(33,"USS"); + # if ($submission->{attachment} ne "") { +# $var{'attachment.form'} = ''.WebGUI::International::get(391).''; + # } else { +# $var{'attachment.form'} = WebGUI::Form::file({ +# name=>"attachment" +# }); + # } + $var{'contentType.label'} = WebGUI::International::get(1007); + $var{'contentType.form'} = WebGUI::Form::contentType({ + name=>'contentType', + value=>$self->get("contentType") || "mixed" + }); + $var{'startDate.label'} = WebGUI::International::get(497); + $var{'endDate.label'} = WebGUI::International::get(498); + $var{'startDate.form'} = WebGUI::Form::dateTime({ + name => 'startDate', + value => $self->get("startDate") + }); + $var{'endDate.form'} = WebGUI::Form::dateTime({ + name => 'endDate', + value => $self->get("startDate") + }); + $var{'form.submit'} = WebGUI::Form::submit(); + $var{'form.footer'} = WebGUI::Form::formFooter(); + return $self->getParent->processStyle($self->processTemplate(\%var,$self->getParent->get("submissionFormTemplateId"))); +} + +#------------------------------------------------------------------- + +=head2 www_lock ( ) + +The web method to lock a thread. + +=cut + +sub www_lock { + my $self = shift; + $self->lock if $self->getParent->canModerate; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_stick ( ) + +The web method to make a thread sticky. + +=cut + +sub www_stick { + my $self = shift; + $self->stick if $self->getParent->canModerate; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_subscribe ( ) + +The web method to subscribe to a thread. + +=cut + +sub www_subscribe { + my $self = shift; + $self->subscribe if $self->canSubscribe; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_unlock ( ) + +The web method to unlock a thread. + +=cut + +sub www_unlock { + my $self = shift; + $self->unlock if $self->getParent->canModerate; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_unstick ( ) + +The web method to make a sticky thread normal again. + +=cut + +sub www_unstick { + my $self = shift; + $self->unstick if $self->getParent->canModerate; + $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_threadUnsubscribe ( ) + +The web method to unsubscribe from a thread. + +=cut + +sub www_unsubscribe { + my $self = shift; + $self->unsubscribe if $self->canSubscribe; + return $self->www_view; +} + +#------------------------------------------------------------------- +sub www_view { + my $self = shift; + return $self->getParent->processStyle(WebGUI::Privilege::noAccess()) unless $self->canView; + return $self->getParent->processStyle($self->view); +} + +#------------------------------------------------------------------- + +=head2 www_viewThread ( caller [ , postId ] ) + +The web method to display a thread. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=head3 postId + +Specify a postId and call this method directly, rather than over the web. + +=cut + +sub www_viewThread { + my ($caller, $postId) = @_; + WebGUI::Session::setScratch("forumThreadLayout",$session{form}{layout}); + $postId = $session{form}{forumPostId} unless ($postId); + my $post = WebGUI::Forum::Post->new($postId); + return WebGUI::Privilege::insufficient() unless ($post->getThread->getForum->canView); + my $var = getThreadTemplateVars($caller, $post); + if ($post->get("forumPostId") eq $post->getThread->get("rootPostId") && !$post->canView) { + return www_viewForum($caller, $post->getThread->getForum->get("forumId")); + } else { + return WebGUI::Template::process($post->getThread->getForum->get("threadTemplateId"),"Forum/Thread", $var); + } +} + + +1; + diff --git a/lib/WebGUI/Asset/Wobject/Collaboration.pm b/lib/WebGUI/Asset/Wobject/Collaboration.pm new file mode 100644 index 000000000..e1b9ddb93 --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/Collaboration.pm @@ -0,0 +1,1198 @@ +package WebGUI::Asset::Wobject::Collaboration; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2005 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 +#------------------------------------------------------------------- + +use strict; +use Tie::CPHash; +use WebGUI::DateTime; +use WebGUI::Grouping; +use WebGUI::HTML; +use WebGUI::HTTP; +use WebGUI::Icon; +use WebGUI::International; +use WebGUI::Paginator; +use WebGUI::Privilege; +use WebGUI::Search; +use WebGUI::Session; +use WebGUI::SQL; +use WebGUI::Style; +use WebGUI::URL; +use WebGUI::Utility; +use WebGUI::Asset::Wobject; +use WebGUI::Asset::USS_submission; + +our @ISA = qw(WebGUI::Asset::Wobject); + +#------------------------------------------------------------------- +sub canModerate { + my $shift; + return WebGUI::Grouping::isInGroup($self->get("moderateGroupId")) || $self->canEdit; +} + +#------------------------------------------------------------------- +sub canPost { + my $shift; + return WebGUI::Grouping::isInGroup($self->get("postGroupId")) || $self->canModerate; +} + + +#------------------------------------------------------------------- +sub canSubscribe { + my $self = shift; + return ($session{user}{userId} ne "1" && $self->canView); +} + +#------------------------------------------------------------------- +sub canView { + my $shift; + return $self->SUPER::canView || $self->canPost; +} + +#------------------------------------------------------------------- +sub createSubscriptionGroup { + my $self = shift; + my $group = WebGUI::Group->new("new"); + $group->name($self->getId); + $group->description("The group to store subscriptions for the collaboration system ".$self->getId); + $group->isEditable(0); + $group->showInForms(0); + $group->deleteGroups([3]); # admins don't want to be auto subscribed to this thing + $self->update({ + subscriptionGroupId=>$group->groupId + }); +} + +#------------------------------------------------------------------- + +=head2 decrementReplies ( ) + +Deccrements this reply counter. + +=cut + +sub decrementReplies { + my $self = shift; + $self->update({replies=>$self->get("replies")-1}); +} + + +#------------------------------------------------------------------- + +=head2 decrementThreads ( ) + +Deccrements this thread counter. + +=cut + +sub decrementThreads { + my $self = shift; + $self->update({threads=>$self->get("threads")-1}); +} + + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $definition = shift; + push(@{$definition}, { + tableName=>'Collaboration', + className=>'WebGUI::Asset::Wobject::Collaboration', + properties=>{ + threadsPerPage =>{ + fieldType=>"integer", + defaultValue=>30 + }, + postsPerPage =>{ + fieldType=>"integer", + defaultValue=>10 + }, + archiveAfter =>{ + fieldType=>"interval", + defaultValue=>31536000 + }, + subscriptionGroupId =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + lastPostDate =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + lastPostId =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + rating =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + replies =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + views =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + threads =>{ + fieldType=>"hidden", + defaultValue=>undef + }, + useContentFilter =>{ + fieldType=>"yesNo", + defaultValue=>1 + }, + filterCode =>{ + fieldType=>"filterContent", + defaultValue=>'javascript' + }, + allowRichEdit =>{ + fieldType=>"yesNo", + defaultValue=>1 + }, + attachmentsPerPost =>{ + fieldType=>"integer", + defaultValue=>0 + }, + editTimeout =>{ + fieldType=>"interval", + defaultValue=>3600 + }, + addEditStampToPosts =>{ + fieldType=>"yesNo", + defaultValue=>0 + }, + usePreview =>{ + fieldType=>"yesNo", + defaultValue=>1 + }, + sortOrder =>{ + fieldType=>"selectList", + defaultValue=>'desc' + }, + sortBy =>{ + fieldType=>"selectList", + defaultValue=>'dateUpdated' + }, + notificationTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + searchTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + postPreviewTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + postFormTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + postTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + threadTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + collaborationTemplateId =>{ + fieldType=>"template", + defaultValue=>undef + }, + karmaPerPost =>{ + fieldType=>"integer", + defaultValue=>0 + }, + moderatePosts =>{ + fieldType=>"yesNo", + defaultValue=>0 + }, + moderateGroupId =>{ + fieldType=>"group", + defaultValue=>'4' + }, + postGroupId =>{ + fieldType=>"group", + defaultValue=>'2' + } + } + }); + return $class->SUPER::definition($definition); +} + +#------------------------------------------------------------------- +sub duplicate { + my $self = shift; + my $newAsset = $self->SUPER::duplicate; + $newAsset->createSubscriptionGroup; + return $newAsset; +} + + + +#------------------------------------------------------------------- +sub getEditForm { + my $self = shift; + my $tabform = $self->SUPER::getEditForm; + $tabform->getTab("display")->template( + -value=>$self->getValue('collaborationTemplateId'), + -namespace=>"Collaboration", + -label=>"Collaboration System Template" + ); + $tabform->getTab("display")->template( + -name=>"threadTemplateId", + -value=>$self->getValue("threadTemplateId"), + -namespace=>"Collaboration/Thread", + -label=>"Thread Template" + ); + $tabform->getTab("display")->template( + -name=>"postTemplateId", + -value=>$self->getValue("postTemplateId"), + -namespace=>"Collaboration/Post", + -label=>"Post Template" + ); + $tabform->getTab("display")->template( + -name=>"postFormTemplateId", + -value=>$self->getValue("postFormTemplateId"), + -namespace=>"Collaboration/PostForm", + -label=>"Post Form Template" + ); + $tabform->getTab("display")->template( + -name=>"postPreviewTemplateId", + -value=>$self->getValue("postPreviewTemplateId"), + -namespace=>"Collaboration/Preview", + -label=>"Post Preview Template" + ); + $tabform->getTab("display")->template( + -name=>"searchTemplateId", + -value=>$self->getValue("SearchTemplateId"), + -namespace=>"Collaboration/Search", + -label=>"Search Template" + ); + $tabform->getTab("display")->template( + -name=>"notificationTemplateId", + -value=>$self->getValue("notificationTemplateId"), + -namespace=>"Collaboration/Notification", + -label=>"Notification Template" + ); + $tabform->getTab("security")->group( + -name=>"moderateGroupId", + -label=>"Who can moderate?", + -value=>[$self->getValue("moderateGroupId")] + ); + $tabform->getTab("security")->group( + -name=>"postGroupId", + -label=>"Who can post?", + -value=>[$self->getValue("postGroupId")] + ); + $tabform->getTab("display")->integer( + -name=>"threadsPerPage", + -label=>"Threads Per Page", + -value=>$self->getValue("threadsPerPage") + ); + $tabform->getTab("display")->integer( + -name=>"postsPerPage", + -label=>"Posts Per Page", + -value=>$self->getValue("postsPerPage") + ); + if ($session{setting}{useKarma}) { + $tabform->getTab("properties")->integer( + -name=>"karmaPerPost", + -label=>"Karma Per Post", + -value=>$self->getValue("karmaPerPost") + ); + } else { + $tabform->getTab("properties")->hidden("karmaPerPost",$self->getValue("karmaPerPost")); + } + $tabform->getTab("security")->filterContent( + -value=>$self->getValue("filterCode"), + -name=>"filterCode", + -label=>"Filter Code" + ); + $tabform->getTab("display")->selectList( + -name=>"sortBy", + -value=>[$self->getValue("sortBy")], + -options=>{ + lineage=>"Sequence", + dateUpdated=>"Date Updated", + dateSubmitted=>"Date Submitted", + title=>"Title" + }, + -label=>"Sort By" + ); + $tabform->getTab("display")->selectList( + -name=>"sortOrder", + -value=>[$self->getValue("sortOrder")], + -options=>{ + asc=>"Ascending" + desc=>"Descending" + }, + -label=>"Sort Order" + ); + $tabform->getTab("properties")->interval( + -name=>"archiveAfter", + -label=>"Archive After", + -value=>$self->getValue("archiveAfter") + ); + $tabform->getTab("properties")->integer( + -name=>"attachmentsPerPost", + -label=>"Attachments Per Post", + -value=>$self->getValue("attachmentsPerPost") + ); + $tabform->getTab("security")->interval( + -name=>"editTimeout", + -label=>"Edit Timeout", + -value=>$self->getValue("editTimeout") + ); + $tabform->getTab("security")->yesNo( + -name=>"addEditStampToPosts", + -label=>"Add edit stamp to posts?", + -value=>$self->getValue("addEditStampToPosts") + ); + $tabform->getTab("display")->yesNo( + -name=>"allowRichEdit", + -label=>"Allow rich edit?", + -value=>$self->getValue("allowRichEdit") + ); + $tabform->getTab("display")->yesNo( + -name=>"useContentFilter", + -label=>"Use content filter?", + -value=>$self->getValue("useContentFilter") + ); + $tabform->getTab("display")->yesNo( + -name=>"usePreview", + -label=>"Use preview?", + -value=>$self->getValue("usePreview") + ); + $tabform->getTab("security")->yesNo( + -name=>"moderatePosts", + -label=>"Moderate posts?", + -value=>$self->getValue("moderatePosts") + ); + return $tabform; +} + +#------------------------------------------------------------------- +sub getIcon { + my $self = shift; + my $small = shift; + return $session{config}{extrasURL}.'/assets/small/collaboration.gif' if ($small); + return $session{config}{extrasURL}.'/assets/collaboration.gif'; +} + + +#------------------------------------------------------------------- +sub getName { + return "Collaboration System"; +} + + +#------------------------------------------------------------------- + +=head2 formatNewThreadURL ( callback, forumId ) + +Formats the url to start a new thread. + +=cut + +sub getNewThreadUrl { + my $self = shift; + $self->getUrl("func=add&class=WebGUI::Asset::Post"); +} + +#------------------------------------------------------------------- + +=head2 getSearchUrl ( ) + +Formats the url to the forum search engine. + +=cut + +sub getSearchUrl { + my $self = shift; + return $self->getUrl("func=search"); +} + +#------------------------------------------------------------------- + +=head2 getSortByUrl ( sortBy ) + +Formats the url to change the default sort. + +=head3 sortBy + +The sort by string. Can be views, rating, date replies, or lastreply. + +=cut + +sub getSortByUrl { + my $self = shift; + my $sortBy = shift; + return $self->getUrl("sortBy=".$sortBy); +} + +#------------------------------------------------------------------- + +=head2 getSubscribeUrl ( ) + +Formats the url to subscribe to the forum. + +=cut + +sub getSubscribeUrl { + my $self = shift; + return $self->getUrl("func=subscribe"); +} + +#------------------------------------------------------------------- + +=head2 getUnsubscribeUrl ( ) + +Formats the url to unsubscribe from the forum. + +=cut + +sub getUnsubscribeUrl { + my $self = shift; + return $self->getUrl("func=unsubscribe"); +} + + +#------------------------------------------------------------------- + +=head2 incrementReplies ( lastPostDate, lastPostId ) + +Increments this forum's reply counter. + +=head3 lastPostDate + +The date of the post being added. + +=head3 lastPostId + +The unique identifier of the post being added. + +=cut + +sub incrementReplies { + my ($self, $lastPostDate, $lastPostId) = @_; + $self->update({replies=>$self->get("replies")+1, lastPostId=>$lastPostId, lastPostDate=>$lastPostDate}); +} + +#------------------------------------------------------------------- + +=head2 incrementThreads ( lastPostDate, lastPostId ) + +Increments the thread counter for this forum. + +=head3 lastPostDate + +The date of the post that was just added. + +=head3 lastPostId + +The unique identifier of the post that was just added. + +=cut + +sub incrementThreads { + my ($self, $lastPostDate, $lastPostId) = @_; + $self->update({threads=>$self->get("threads")+1, lastPostId=>$lastPostId, lastPostDate=>$lastPostDate}); +} + +#------------------------------------------------------------------- + +=head2 incrementViews ( ) + +Increments the views counter on this forum. + +=cut + +sub incrementViews { + my ($self) = @_; + $self->update({views=>$self->get("views")+1}); +} + +#------------------------------------------------------------------- + +=head2 isSubscribed ( ) + +Returns a boolean indicating whether the user is subscribed to the forum. + +=cut + +sub isSubscribed { + my $self = shift; + return WebGUI::Grouping::isInGroup($self->get("subscriptionGroupId")); +} + +#------------------------------------------------------------------- +sub processPropertiesFromFormPost { + my $self = shift; + $self->SUPER::processPropertiesFromFormPost; + if ($session{form}{func} eq "add") { + $self->createSubscriptionGroup; + } +} + + +#------------------------------------------------------------------- +sub purge { + my $self = shift; + my $group = WebGUI::Group->new($self->get("subscriptionGroupId")); + $group->delete; + $self->SUPER::purge; +} + +#------------------------------------------------------------------- + +=head2 recalculateRating ( ) + +Calculates the rating of this forum from its threads and stores the new value in the forum properties. + +=cut + +sub recalculateRating { + my $self = shift; + my ($count) = WebGUI::SQL->quickArray("select count(*) from Thread left join asset on Thread.assetId=asset.assetId + where asset.parentId=".quote($self->getId)." and Thread.rating>0"); + $count = $count || 1; + my ($sum) = WebGUI::SQL->quickArray("select sum(Thread.rating) from Thread left join asset on Thread.assetId=asset.assetId where asset.parentId=".quote($self->getId)." and Thread.rating>0"); + my $average = round($sum/$count); + $self->update({rating=>$average}); +} + + +#------------------------------------------------------------------- + +=head2 setLastPost ( lastPostDate, lastPostId ) + +Sets the pertinent details for the last post. Can also be done directly using the set method. + +=head3 lastPostDate + +The epoch date of the post. + +=head3 lastPostId + +The asset id of the post. + +=cut + +sub setLastPost { + my ($self, $postDate, $postId) = @_; + $self->update({ + lastPostId=>$postId, + lastPostDate=>$postDate + }); +} + + +#------------------------------------------------------------------- +sub status { + my $self = shift; + my $status = shift; + if ($status eq "approved") { + return "Approved"; + } elsif ($status eq "denied") { + return "Denied"; + } elsif ($status eq "pending") { + return "Pending"; + } +} + +#------------------------------------------------------------------- + +=head2 subscribe ( ) + +Subscribes a user to this collaboration system. + +=cut + +sub subscribe { + my $self = shift; + unless ($self->isSubscribed) { + WebGUI::Grouping::addUsersToGroups([$session{user}{userId}],[$self->get("subscriptionGroupId")]); + } +} + +#------------------------------------------------------------------- + +=head2 unsubscribe ( ) + +Unsubscribes a user from this collaboration system + +=cut + +sub unsubscribe { + my $self = shift; + if ($self->isSubscribed) { + WebGUI::Grouping::deleteUsersFromGroups([$session{user}{userId}],[$self->get("subscriptionGroupId")]); + } +} + + +#------------------------------------------------------------------- +sub view { + my $self = shift; + my $numResults = $self->get("threadsPerPage"); + my %var; + $var{"readmore.label"} = "Read More"; + $var{"responses.label"} = "Responses"; + $var{canPost} = $self->canPost; + $var{"post.url"} = $self->getUrl('func=add&class=WebGUI::Asset::Thread'); + $var{"post.label"} = "Add a post."; + $var{"addquestion.label"} = "Add a question."; + $var{"addlink.label"} = "Add a link."; + $var{"search.label"} = "Search"; + $var{"search.Form"} = WebGUI::Search::form({func=>'view',search=>1}); + $var{"search.url"} = WebGUI::Search::toggleURL("func=view"); + $var{"rss.url"} = WebGUI::URL::page('func=viewRSS',1); + $var{canModerate} = $self->canModerate; + $var{"title.label"} = WebGUI::International::get(99); + $var{"thumbnail.label"} = WebGUI::International::get(52,"USS"); + $var{"date.label"} = WebGUI::International::get(13,"USS"); + $var{"date.updated.label"} = WebGUI::International::get(78,"USS"); + $var{"by.label"} = WebGUI::International::get(21,"USS"); + $var{"submission.edit.label"} = WebGUI::International::get(27,"USS"); + WebGUI::Style::setLink($var{"rss.url"},{ rel=>'alternate', type=>'application/rss+xml', title=>'RSS' }); + my $constraints; + if ($session{scratch}{search}) { + $numResults = $session{scratch}{numResults}; + $constraints = WebGUI::Search::buildConstraints([qw(Post.username asset.synopsis asset.title Post.content Post.userDefined1 Post.userDefined2 Post.userDefined3 Post.userDefined4 Post.userDefined5)]); + } + if ($constraints ne "") { + $constraints = "Post.status='approved' and ".$constraints; + } else { + $constraints = "(Post.status='approved' or (asset.ownerUserId=".quote($session{user}{userId})." and asset.ownerUserId<>'1')"; + if ($var{canModerate}) { + $constraints .= " or Post.status='pending'"; + } + $constraints .= ")"; + } + my $p = WebGUI::Paginator->new($self->getUrl,$numResults); + my $sql = "select asset.*,Post.* + from Thread + left join asset on Thread.assetId=asset.parentId + left join Post on Post.assetId=asset.assetId + where Thread.parentId=".quote($self->getId)." and asset.state='published' and asset.className='WebGUI::Asset::Post' and $constraints + order by ".$self->getValue("sortBy")." ".$self->getValue("sortOrder"); + $p->setDataByQuery($sql); + my $page = $p->getPageData; + my $i = 0; + my $imageURL = ""; + foreach my $row (@$page) { + my $post = WebGUI::Asset::USS_submission->newByPropertyHashRef($row); + my $controls = deleteIcon('func=delete',$post->getUrl,"Delete").editIcon('func=edit',$post->getUrl); + if ($self->get("sortBy") eq "lineage") { + if ($self->get("sortOrder") eq "desc") { + $controls .= moveUpIcon('func=demote',$post->get("url")).moveDownIcon('func=promote',$post->get("url")); + } else { + $controls .= moveUpIcon('func=promote',$post->get("url")).moveDownIcon('func=demote',$post->get("url")); + } + } + my $inDateRange; + if ($post->get("startDate") < WebGUI::DateTime::time() && $post->get("endDate") > WebGUI::DateTime::time()) { + $inDateRange = 1; + } else { + $inDateRange = 0; + } + push(@{$var{submissions_loop}}, { + "id"=>$post->getId, + "url"=>$post->getUrl, + "synopsis"=>$post->get("synopsis"), + "content"=>WebGUI::HTML::format(WebGUI::HTML::filter($post->get("content"),$self->get("filterCode")),$post->get("contentType")), + "reply.count"=>$post->getResponseCount, + "title"=>$post->get("title"), + "userDefined1"=>$post->get("userDefined1"), + "userDefined2"=>$post->get("userDefined2"), + "userDefined3"=>$post->get("userDefined3"), + "userDefined4"=>$post->get("userDefined4"), + "userDefined5"=>$post->get("userDefined5"), + "userId"=>$post->get("userId"), + "username"=>$post->get('username'), + "status"=>$self->status($post->get("status")), + # "thumbnail"=>$submission->getThumbnailUrl, + # "submission.image"=>$submission->getImageUrl, + "date.submitted"=>epochToHuman($post->get("dateSubmitted")), + "date.updated"=>epochToHuman($post->get("dateUpdated")), + "userProfile.url"=>$post->getUrl("op=viewProfile&uid=".$post->get("ownerUserId")), + "edit.url"=>$submission->getUrl("func=edit"), + 'controls'=>$submission->getToolbar, + 'inDateRange'=>$inDateRange, + "currentUserIsOwner"=>($session{user}{userId} eq $post->get("ownerUserId") && $session{user}{userId} ne "1") + }); + $i++; + } + $p->appendTemplateVars(\%var); + return $self->processTemplate(\%var,$self->get("collaborationTemplateId")); +} + +#------------------------------------------------------------------- +sub www_edit { + my $self = shift; + return $self->getAdminConsole->render(WebGUI::Privilege::insufficient()) unless $self->canEdit; +# $self->getAdminConsole->setHelp("user submission system add/edit"); + return $self->getAdminConsole->render($self->getEditForm->print,"Edit Collaboration System"); +} + +#------------------------------------------------------------------- + +=head2 www_search ( caller ) + +The web method to display and use the forum search interface. + +=head3 caller + +A hash reference containing information passed from the calling object. + +=cut + +sub www_search { + my ($caller) = @_; + my $forum = WebGUI::Forum->new($session{form}{forumId}); + WebGUI::Session::setScratch("all",$session{form}{all}); + WebGUI::Session::setScratch("atLeastOne",$session{form}{atLeastOne}); + WebGUI::Session::setScratch("exactPhrase",$session{form}{exactPhrase}); + WebGUI::Session::setScratch("without",$session{form}{without}); + WebGUI::Session::setScratch("numResults",$session{form}{numResults}); + my %var; + $var{'callback.url'} = $caller->{callback}; + $var{'callback.label'} = WebGUI::International::get(1039); + $var{'form.begin'} = WebGUI::Form::formHeader({action=>$caller->{callback}}); + $var{'form.begin'} .= WebGUI::Form::hidden({ name=>"forumOp", value=>"search" }); + $var{'form.begin'} .= WebGUI::Form::hidden({ name=>"doit", value=>1 }); + $var{'form.begin'} .= WebGUI::Form::hidden({ name=>"forumId", value=>$session{form}{forumId} }); + $var{'search.label'} = WebGUI::International::get(364); + $var{'all.label'} = WebGUI::International::get(530); + $var{'all.form'} = WebGUI::Form::text({ + name=>'all', + value=>$session{scratch}{all}, + size=>($session{setting}{textBoxSize}-5) + }); + $var{'exactphrase.label'} = WebGUI::International::get(531); + $var{'exactphrase.form'} = WebGUI::Form::text({ + name=>'exactPhrase', + value=>$session{scratch}{exactPhrase}, + size=>($session{setting}{textBoxSize}-5) + }); + $var{'atleastone.label'} = WebGUI::International::get(532); + $var{'atleastone.form'} = WebGUI::Form::text({ + name=>'atLeastOne', + value=>$session{scratch}{atLeastOne}, + size=>($session{setting}{textBoxSize}-5) + }); + $var{'without.label'} = WebGUI::International::get(533); + $var{'without.form'} = WebGUI::Form::text({ + name=>'without', + value=>$session{scratch}{without}, + size=>($session{setting}{textBoxSize}-5) + }); + $var{'results.label'} = WebGUI::International::get(529); + my %results; + tie %results, 'Tie::IxHash'; + %results = (10=>'10', 25=>'25', 50=>'50', 100=>'100'); + my $numResults = $session{scratch}{numResults} || 25; + $var{'results.form'} = WebGUI::Form::selectList({ + name=>"numResults", + options=>\%results, + value=>[$numResults] + }); + $var{'form.search'} = WebGUI::Form::submit({value=>WebGUI::International::get(170)}); + $var{'form.end'} = WebGUI::Form::formFooter(); + $var{'thread.list.url'} = formatForumURL($caller->{callback},$forum->get("forumId")); + $var{'thread.list.label'} = WebGUI::International::get(1019); + $var{doit} = $session{form}{doit}; + if ($session{form}{doit}) { + $var{'post.subject.label'} = WebGUI::International::get(229); + $var{'post.date.label'} = WebGUI::International::get(245); + $var{'post.user.label'} = WebGUI::International::get(244); + my $constraints = WebGUI::Search::buildConstraints([qw(a.subject a.username a.message)]); + my $query = "select a.forumPostId, a.subject, a.userId, a.username, a.dateOfPost from forumPost a left join forumThread b + on a.forumThreadId=b.forumThreadId where b.forumId=".quote($forum->get("forumId"))." and + (a.status='approved' or a.status='archived') and $constraints order by a.dateOfPost desc"; + my $p = WebGUI::Paginator->new(WebGUI::URL::append($caller->{callback},"forumOp=search&doit=1&forumId=".quote($forum->get("forumId"))), $numResults); + $p->setDataByQuery($query) if $constraints; + my @post_loop; + foreach my $row (@{$p->getPageData}) { + push(@post_loop,{ + 'post.subject'=>$row->{subject}, + 'post.url'=>formatThreadURL($caller->{callback},$row->{forumPostId}), + 'post.user.name'=>$row->{username}, + 'post.user.id'=>$row->{userId}, + 'post.user.profile'=>formatUserProfileURL($row->{userId}), + 'post.epoch'=>$row->{dateOfPost}, + 'post.date'=>formatPostDate($row->{dateOfPost}), + 'post.time'=>formatPostTime($row->{dateOfPost}) + }); + } + $var{post_loop} = \@post_loop; + $var{firstPage} = $p->getFirstPageLink; + $var{lastPage} = $p->getLastPageLink; + $var{nextPage} = $p->getNextPageLink; + $var{pageList} = $p->getPageLinks; + $var{previousPage} = $p->getPreviousPageLink; + $var{multiplePages} = ($p->getNumberOfPages > 1); + $var{numberOfPages} = $p->getNumberOfPages; + $var{pageNumber} = $p->getPageNumber; + } + return WebGUI::Template::process($forum->get("searchTemplateId"),"Forum/Search", \%var); +} + +#------------------------------------------------------------------- + +=head2 www_subscribe ( ) + +The web method to subscribe to a collaboration. + +=cut + +sub www_subscribe { + my $self = shift; + $self->subscribe if $self->canSubscribe; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_unsubscribe ( ) + +The web method to unsubscribe from a collaboration. + +=cut + +sub www_unsubscribe { + my $self = shift; + $self->unsubscribe if $self->canSubscribe; + return $self->www_view; +} + +#------------------------------------------------------------------- + +=head2 www_viewForum ( caller [ , forumId ] ) + +The web method to display a forum. + +=head3 caller + +The url to get back to the calling object. + +=head3 forumId + +Specify a forumId and call this method directly, rather than over the web. + +=cut + +sub www_viewForum { + my ($caller, $forumId) = @_; + $forumId = $session{form}{forumId} unless ($forumId); + + if($session{scratch}{forumSortBy} ne $session{form}{sortBy}){ + WebGUI::Session::setScratch("forumSortBy",$session{form}{sortBy}); + WebGUI::Session::setScratch("forumSortDir", "desc"); + }else{ + my $sortDir; + if($session{scratch}{forumSortDir} eq "asc"){ + $sortDir = "desc"; + }else{ + $sortDir = "asc"; + } + WebGUI::Session::setScratch("forumSortDir", $sortDir); + } + + my $forum = WebGUI::Forum->new($forumId); + return WebGUI::Privilege::insufficient() unless ($forum->canView); + my $var = getForumTemplateVars($caller, $forum); + return WebGUI::Template::process($forum->get("forumTemplateId"),"Forum", $var); +} + + +#------------------------------------------------------------------- +# print out RSS 2.0 feed describing the items visible on the first page +sub www_viewRSS { + $_[0]->logView() if ($session{setting}{passiveProfilingEnabled}); + my $wid = $_[0]->get("wobjectId"); + my $numResults = $_[0]->get("submissionsPerPage"); + + my $encTitle = _xml_encode($_[0]->get("title")); + my $encDescription = _xml_encode($_[0]->get("description")); + my $encUrl = _xml_encode(WebGUI::URL::page("wid=$wid")); + + my $xml = qq~ + + +$encTitle +$encUrl +$encDescription +~; + + my $res = WebGUI::SQL->read + ("select USS_submissionId, content, title, " . + "dateSubmitted, username from USS_submission " . + "where USS_id = " .quote($_[0]->get("USS_id")) . " and status='Approved' " . + "order by ".$_[0]->getValue("sortBy")." ".$_[0]->getValue("sortOrder")." limit " . $numResults,WebGUI::SQL->getSlave); + + while (my $row = $res->{_sth}->fetchrow_arrayref()) { + my ($sid, $content, $title, $dateSubmitted, $username) = + @{$row}; + + my $encUrl = _xml_encode + (WebGUI::URL::page + ("wid=$wid&func=viewSubmission&sid=$sid")); + my $encTitle = _xml_encode($title); + my $encPubDate = _xml_encode + (_get_rfc822_date($dateSubmitted)); + my $encDescription = _xml_encode($content); + + $xml .= qq~ + +$encTitle +$encUrl +$encDescription +$encUrl +$encPubDate + +~; + } + + $xml .=qq~ + + +~; + WebGUI::HTTP::setMimeType("text/xml"); + return $xml; +} + + + +1; + +package WebGUI::Search; + +=head1 LEGAL + + ------------------------------------------------------------------- + WebGUI is Copyright 2001-2005 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 Tie::IxHash; +use WebGUI::HTMLForm; +use WebGUI::International; +use WebGUI::Session; +use WebGUI::SQL; + + +=head1 NAME + +Package WebGUI::Search + +=head1 DESCRIPTION + +A package built to take the hassle out of creating advanced search functionality in WebGUI applications. + +=head1 SYNOPSIS + + use WebGUI::Search; + $sql = WebGUI::Search::buildConstraints(\@fields); + $html = WebGUI::Search::form(\%hidden); + +=head1 METHODS + +These methods are available from this package: + +=cut + + +#------------------------------------------------------------------- + +=head2 buildConstraints ( fieldList ) { [ all, atLeastOne, exactPhrase, without ] } + +Generates and returns the constraints to an SQL where clause based upon input from the user. + +=head3 fieldList + +An array reference that contains a list of the fields (table columns) to be considered when searching. + +=head3 all + +A form param with a comma or space separated list of key words to search for in the fields of the fieldList. All the words listed here must be found to be true. + +=head3 atLeastOne + +A form param with a comma or space separated list of key words to search for in the fields of the fieldList. Any of the words may match in any of the fields for this to be true. + +=head3 exactPhrase + +A form param with a phrase to search for in the fields of the fieldList. The exact phrase must be found in one of the fields to be true. + +=head3 without + +A form param with a comma or space separated list of key words to search for in the fields of the fieldList. None of the words may be found in any of the fields for this to be true. + +=cut + +sub buildConstraints { + my ($field, $all, $allSub, $exactPhrase, $atLeastOne, $without, @words, $word, $sql); + if ($session{scratch}{all} ne "") { + $session{scratch}{all} =~ s/,/ /g; + $session{scratch}{all} =~ s/\s+/ /g; + @words = split(/ /,$session{scratch}{all}); + foreach $word (@words) { + $all .= " and " if ($all ne ""); + $all .= "("; + foreach $field (@{$_[0]}) { + $allSub .= " or " if ($allSub ne ""); + $allSub .= " $field like ".quote("%".$word."%"); + } + $all .= $allSub; + $allSub = ""; + $all .= ")"; + } + } + if ($session{scratch}{exactPhrase} ne "") { + foreach $field (@{$_[0]}) { + $exactPhrase .= " or " if ($exactPhrase ne ""); + $exactPhrase .= " $field like ".quote("%".$session{scratch}{exactPhrase}."%"); + } + } + if ($session{scratch}{atLeastOne} ne "") { + $session{scratch}{atLeastOne} =~ s/,/ /g; + $session{scratch}{atLeastOne} =~ s/\s+/ /g; + @words = split(/ /,$session{scratch}{atLeastOne}); + foreach $word (@words) { + foreach $field (@{$_[0]}) { + $atLeastOne .= " or " if ($atLeastOne ne ""); + $atLeastOne .= " $field like ".quote("%".$word."%"); + } + } + } + if ($session{scratch}{without} ne "") { + $session{scratch}{without} =~ s/,/ /g; + $session{scratch}{without} =~ s/\s+/ /g; + @words = split(/ /,$session{scratch}{without}); + foreach $word (@words) { + foreach $field (@{$_[0]}) { + $without .= " and " if ($without ne ""); + $without .= " $field not like ".quote("%".$word."%"); + } + } + } + $sql = "($all) " if ($all ne ""); + $sql .= " and " if ($sql ne "" && $exactPhrase ne ""); + $sql .= " ($exactPhrase) " if ($exactPhrase ne ""); + $sql .= " and " if ($sql ne "" && $atLeastOne ne ""); + $sql .= " ($atLeastOne) " if ($atLeastOne ne ""); + $sql .= " and " if ($sql ne "" && $without ne ""); + $sql .= " ($without) " if ($without ne ""); + return $sql; +} + +#------------------------------------------------------------------- + +=head2 form ( hiddenFields ) { [ numResults ] } + +Generates and returns the advanced search form. + +=head3 hiddenFields + +A hash reference that contains any name/value pairs that should be included as hidden fields in the search form. + +=head3 numResults + +A form param that can optionally specify the number of results to display. Defaults to 25. + +=cut + +sub form { + WebGUI::Session::setScratch("all",$session{form}{all}); + WebGUI::Session::setScratch("atLeastOne",$session{form}{atLeastOne}); + WebGUI::Session::setScratch("exactPhrase",$session{form}{exactPhrase}); + WebGUI::Session::setScratch("without",$session{form}{without}); + WebGUI::Session::setScratch("numResults",$session{form}{numResults}); + my ($key, $numResults, $output, $f, $resultsText, %results); + tie %results, 'Tie::IxHash'; + $numResults = $session{scratch}{numResults} || 25; + $resultsText = WebGUI::International::get(529); + %results = (10=>'10 '.$resultsText, 25=>'25 '.$resultsText, 50=>'50 '.$resultsText, 100=>'100 '.$resultsText); + $f = WebGUI::HTMLForm->new(1); + foreach $key (keys %{$_[0]}) { + $f->hidden($key,${$_[0]}{$key}); + } + $output = ''; + $f->raw(''); + $output .= $f->print; + $output .= '
'; + $output .= '

'.WebGUI::International::get(364).'

'; + $output .= '
'); + $f->raw(''); + $f->raw(''); + $f->raw(''); + $f->raw(''); + $f->raw(''); + $f->raw('
'.WebGUI::International::get(530).''); + $f->text('all','',$session{scratch}{all},'','','',($session{setting}{textBoxSize}-5)); + $f->raw('
'.WebGUI::International::get(531).''); + $f->text('exactPhrase','',$session{scratch}{exactPhrase},'','','',($session{setting}{textBoxSize}-5)); + $f->raw('
'.WebGUI::International::get(532).''); + $f->text('atLeastOne','',$session{scratch}{atLeastOne},'','','',($session{setting}{textBoxSize}-5)); + $f->raw('
'.WebGUI::International::get(533).''); + $f->text('without','',$session{scratch}{without},'','','',($session{setting}{textBoxSize}-5)); + $f->raw('
'); + $f->raw('
'); + $f->selectList("numResults",\%results,'',[$numResults]); + $f->raw('

'); + $f->submit(WebGUI::International::get(170)); + $f->raw('

'; + return $output; +} + +#------------------------------------------------------------------- + +=head2 toggleURL ( [ pairs ] ) + +Returns a URL that toggles the value "search" in the user's scratch +variables on and off. + +=head3 pairs + +URL name value pairs (this=that&foo=bar) to be passed with this toggle. + +=cut + +sub toggleURL { + my $pairs = shift; + my $url = shift || $session{page}{urlizedTitle}; + WebGUI::Session::setScratch("search",$session{form}{search}); + if ($session{scratch}{search}) { + $url = WebGUI::URL::gateway($url,"search=0"); + } else { + $url = WebGUI::URL::gateway($url,"search=1"); + } + $url = WebGUI::URL::append($url,$pairs) if ($pairs); + return $url; +} + +1; + + diff --git a/www/extras/assets/forum.gif b/www/extras/assets/collaboration.gif similarity index 100% rename from www/extras/assets/forum.gif rename to www/extras/assets/collaboration.gif diff --git a/www/extras/assets/small/forum.gif b/www/extras/assets/small/collaboration.gif similarity index 100% rename from www/extras/assets/small/forum.gif rename to www/extras/assets/small/collaboration.gif diff --git a/www/extras/assets/small/userSubmissionSystem.gif b/www/extras/assets/small/userSubmissionSystem.gif deleted file mode 100644 index eecc1d84dda0c5c9bb8a5553b6047a7e431f024c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 680 zcmZ?wbhEHb6krfw_~y%?UZ-nVVeg-76_g#J)vc}5WUDtx+q}!lXpWA_0v-2WuaxQO zC4JR(v+6@v?D5=qDre)N>YYcI$BXYz;oFfZv9wrWVWG|L677xUUMI5oP8W*aDd)Re zC;qgC?@_hD)QR;wTYdMm2ko90ySX>x{7ki*OEq6N^1W&oe7r{O*c_*GOI%v!G_=i_ zHf!UY-j(ZTE?qHg`=0%4X06<^ZPWT?TXt?~+k1M&-ov{O@0oo1=Df>~mYlu6`qG{K zhYu{f`E2{uC;J|~IkvR@$foI+R9&dR|_5X=XPxVg$(n40PE>w)A!z+K-|P=Kw0pU=#< Hk--`OK6b~V diff --git a/www/extras/assets/userSubmissionSystem.gif b/www/extras/assets/userSubmissionSystem.gif deleted file mode 100644 index a099778595870fb51c5fbadb9ac9e8f9475f6363..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2068 zcmV+v2VryH|%DSpt6PI;mshNMGcuc&EBbrqhtJ&qaa~rs`sDi-jXtl;9i78l` ztl4M`YQfhupe%=*hJxGf=6WDMfz?xlk~wb|qC zfe+5=@W5pZ)~}MwZ4Q;g*@1_J#NFuLcMYx76m7) zqQ#2@vCwM3fy09b9v*P8VdGsY9au~I=96egY+opHSa@c6VltY6C-)g9ws*}>jXgQ>XkrwS6v$qr~ z)gZf5(l;BA(u8p)iiIdq&ZMOgcd01dl^~44x*1$_>MZFMCCuvB71QKgGA&v~A@K+HDc*r1#69Y&iVJz!i!i$0iO)&-q+$5JqENM8ApLZA% zG(#7$$S_73$(13_7~JevqCk?|(7_KBKrx0Of;=%sB^J&&&^=vT)4?+`K*7zDH`FkN zkpH-F1|xX4Vw1Ey21r!p* zErncx4mydzKnM!dQky{rvdEGI0f%I<(=-Us*~S}hT)_kcOdxxODTNe5O|(R0kck8m zC_o1;DySev6F%r8Dn>%ga%v&uG5`StwHyoq0}Mk8L%j16qJRSU>gz)jJj@ZrN-mVz z!?;11VlV`=9Gvc{=ydXM2AU`!@x&_7QHKG+$de8na$pp~EIlL}M9Akd@oWL6uzQOw zDicwSCYof@GQ}#~;e!Z17~lgO{}v=C6K#NOE(GY5^2RMcFN-t`USy(nH5pVx2)<%; z!$~*W{Nh8-76okTxVJF>kgh3Z2Y$8}@@8Od%PM>zh8XAY!%sIGv{ZsNr;1$w6AZi& zc-e!?QjHk)EQ1Ov_Smz>9%kUcPbjKHe$gO_h+D|zfN%cu;1X!^K@k;HPzM@!fZ@pn zc7&n`8?IkOjIgH`fR5(22Z6f;UZ7z;8lHHv%mu+Xg2Oz>vy=<-3|v9B?MJIz0UZid zfXXu4tARlyjhLYZJx2VJJVwD*(Q_LFJKxXJ2ZIXaOAL2Npg*Rd134(69`pzXH1xNV zRv-WZ{{tYoZoxrnsH_M{_&^;nfB`L#As)@hMg;!Q3uyR(g5MGo<(db;uI(dQXjt4G zNT`J=WaAGJ$cG{EXrPAx@L+#BiGmPnp$$_ptN;v))*a4q4lQ6H7@;^u1YSS}Lo6W# z0RVz9khqgv2mt}JNTW8+HLGK^pap6`T`>A_gg?aK5Af)P1uQVe%#a`oN|;0lLU4*R z9KdmQaKsk25ez=i-~~&FKn-4D0aC1hjI7zf0UV(SMRI}@s(^+F6p{*ISRfC1xFml_ zaEeVDVGXbhr62qNgje{&5_vem2~_ExX>8ySTbSi6!&u8#Sb`KM$fYhZc^DGNVFaoe yf*$nXhH8c*92W2eCNM#TJW#<0i%?J&