add ad management system

improved performance of cs threads by about 500%
This commit is contained in:
JT Smith 2006-04-05 03:42:14 +00:00
parent c8956ac0c5
commit d26f1bdd8c
11 changed files with 740 additions and 74 deletions

315
lib/WebGUI/AdSpace.pm Normal file
View file

@ -0,0 +1,315 @@
package WebGUI::AdSpace;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2006 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;
=head1 NAME
Package WebGUI::AdSpace
=head1 DESCRIPTION
This class provides a mechanism for controlling advertisements from within WebGUI.
=head1 SYNOPSIS
use WebGUI::AdSpace;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 countClick ( adId )
Increments click counter, and returns the URL to send the user to.
=head3 adId
The unique ID of the ad that was clicked.
=cut
sub countClick {
my $self = shift;
my $id = shift;
my ($url) = $self->session->db->quickArray("select url from advertisement where adId=?",[$id]);
$self->session->db->write("update advertisement set clicks=clicks+1 where adId=?",[$id]);
return $url;
}
#-------------------------------------------------------------------
=head2 create ( session, properties )
=head3 session
A reference to the current session
=head3 properties
The properties used to create this object. See the set() method for details.
=cut
sub create {
my $class = shift;
my $session = shift;
my $properties = shift;
return undef unless $properties->{name};
my $test = $class->newByName($session, $properties->{name});
return undef if defined $test;
my $id = $session->db->setRow("adSpace","adSpaceId",{adSpaceId=>"new"});
my $self = $class->new($session, $id);
$self->set($properties);
return $self;
}
#-------------------------------------------------------------------
=head2 delete
Deletes this ad space.
=cut
sub delete {
my $self = shift;
foreach my $ad (@{$self->getAds}) {
$ad->delete;
}
$self->session->db->deleteRow("adSpace","adSpaceId",$self->getId);
$self = undef;
}
#-------------------------------------------------------------------
=head2 DESTROY ( )
Deconstructor.
=cut
sub DESTROY {
my $self = shift;
undef $self;
}
#-------------------------------------------------------------------
=head2 displayImpression ()
Finds out what the next ad is to display, increments it's impression counter, and returns the HTML to display it.
=cut
sub displayImpression {
my $self = shift;
my ($id, $ad, $priority, $clicks, $clicksBought, $impressions, $impressionsBought) = $self->session->db->quickArray("select adId, renderedAd, priority, clicks, clicksBought, impressions, impressionsBought from advertisement where adSpaceId=? and isActive=1 order by nextInPriority asc limit 1",[$self->getId]);
my $isActive = 1;
if ($clicks > $clicksBought && $impressions > $impressionsBought) {
$isActive = 0;
}
$self->session->db->write("update advertisement set impressions=impressions+1, nextInPriority=?, isActive=? where adId=?", [time()+$priority, $id, $isActive]);
return $ad;
}
#-------------------------------------------------------------------
=head2 get ( name )
Returns the value of a property. See set() for a list of properties.
=head3 name
The name of the property to retrieve the value for.
=cut
sub get {
my $self = shift;
my $name = shift;
return $self->{_properties}{$name};
}
#-------------------------------------------------------------------
=head2 getAds ( )
Returns an array reference containing all the ad objects in this ad space.
=cut
sub getAds {
my $self = shift;
my @ads = ();
my $rs = $self->session->db->read("select adId from advertisement where adSpaceId = ?", [$self->getId]);
while (my ($id) = $rs->array) {
push(@ads, WebGUI::AdSpace::Ad->new($self->session, $id));
}
return \@ads;
}
#-------------------------------------------------------------------
=head2 getId
Returns the id of this object.
=cut
sub getId {
my $self = shift;
return $self->{_properties}{adSpaceId};
}
#-------------------------------------------------------------------
=head2 new ( session, id )
Constructor.
=head3 session
A reference to the current session.
=head3 id
The unqiue ID of an ad space location.
=cut
sub new {
my $class = shift;
my $session = shift;
my $id = shift;
my $properties = $session->db->getRow("adSpace","adSpaceId",$id);
return undef unless $properties->{adSpaceId};
bless {_session=>$session, _properties=>$properties}, $class;
}
#-------------------------------------------------------------------
=head2 newByName ( session, name )
Constructor.
=head3 session
A reference to the current session.
=head3 name
The name of the ad space to retrieve.
=cut
sub newByName {
my $class = shift;
my $session = shift;
my $name = shift;
my $properties = $session->db->getRow("adSpace","name",$name);
return undef unless $properties->{adSpaceId};
bless {_session=>$session, _properties=>$properties}, $class;
}
#-------------------------------------------------------------------
=head2 session
Returns a reference to the current session.
=cut
sub session {
my $self = shift;
return $self->{_session};
}
#-------------------------------------------------------------------
=head2 set ( properties )
Updates the properties of an ad space.
=head3 properties
A hash reference containing the properties to set.
=head4 name
The name that will be used to retrieve this ad space when it's in use one the web site. It should not contain spaces or characters other than alpha-numeric.
=head4 title
A human readable title for this ad space.
=head4 description
A human readable description for this ad space.
=head4 costPerImpression
A float that represents how much it will cost an advertiser every time an ad is viewed in this ad space.
=head4 minimumImpressions
An integer indicating the minimum number of impressions an advertiser is allowed to purchase.
=head4 costPerClick
A float that represents how much it will cost an advertiser everytime someone clicks on an ad in this ad space.
=head4 minimumClicks
An integer indicating the minimum number of clicks an advertiser is allowed to purchase.
=head4 groupToPurchase
A groupId representing the group allowed to purchase ads directly from the web site.
=head4 width
The width, in pixels, of this ad space.
=head4 height
The height, in pixels, of this ad space.
=cut
sub set {
my $self = shift;
my $properties = shift;
$self->{_properties}{name} = $properties->{name} || $self->{_properties}{name} || "Unnamed";
$self->{_properties}{title} = $properties->{title} || $self->{_properties}{title} || "Untitled";
$self->{_properties}{description} = $properties->{description} || $self->{_properties}{description};
$self->{_properties}{costPerImpression} = exists $properties->{costPerImpression} ? $properties->{costPerImpression} : $self->{_properties}{costPerImpression};
$self->{_properties}{costPerClick} = exists $properties->{costPerClick} ? $properties->{costPerClick} : $self->{_properties}{costPerClick};
$self->{_properties}{minimumImpressions} = $properties->{minimumImpressions} || $self->{_properties}{minimumImpressions};
$self->{_properties}{minimumClicks} = $properties->{minimumClicks} || $self->{_properties}{minimumClicks};
$self->{_properties}{groupToPurchase} = $properties->{groupToPurchase} || $self->{_properties}{groupToPurchase} || "3";
$self->{_properties}{width} = $properties->{width} || $self->{_properties}{width} || "468";
$self->{_properties}{height} = $properties->{height} || $self->{_properties}{height} || "60";
$self->session->db->setRow("adSpace","adSpaceId",$self->{_properties});
}
1;

270
lib/WebGUI/AdSpace/Ad.pm Normal file
View file

@ -0,0 +1,270 @@
package WebGUI::AdSpace::Ad;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2006 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 WebGUI::AdSpace;
use WebGUI::Storage::Image;
=head1 NAME
Package WebGUI::AdSpace::Ad
=head1 DESCRIPTION
This class provides an mechanism for manipulating an individual ad within an ad space.
=head1 SYNOPSIS
use WebGUI::AdSpace::Ad;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 create ( session, adSpaceId, properties )
=head3 session
A reference to the current session
=head3 adSpaceId
The unique id of an ad space to attach this ad to.
=head3 properties
The properties used to create this object. See the set() method for details.
=cut
sub create {
my $class = shift;
my $session = shift;
my $properties = shift;
my $id = $session->db->setRow("adSpace","adSpaceId",{adSpaceId=>"new"});
my $self = $class->new($session, $id);
$self->set($properties);
return $self;
}
#-------------------------------------------------------------------
=head2 delete
Deletes this ad.
=cut
sub delete {
my $self = shift;
my $storage = WebGUI::Storage::Image->new($self->session, $self->get("storageId"));
$storage->delete;
$self->session->db->deleteRow("advertisement","adId",$self->getId);
$self = undef;
}
#-------------------------------------------------------------------
=head2 DESTROY ( )
Deconstructor.
=cut
sub DESTROY {
my $self = shift;
undef $self;
}
#-------------------------------------------------------------------
=head2 get ( name )
Returns the value of a property.
=head3 name
The name of the property to retrieve the value for.
=cut
sub get {
my $self = shift;
my $name = shift;
return $self->{_properties}{$name};
}
#-------------------------------------------------------------------
=head2 getId
Returns the id of this object.
=cut
sub getId {
my $self = shift;
return $self->{_properties}{adId};
}
#-------------------------------------------------------------------
=head2 new ( session, id )
Constructor.
=head3 session
A reference to the current session.
=head3 id
The unqiue ID of an ad.
=cut
sub new {
my $class = shift;
my $session = shift;
my $id = shift;
my $properties = $session->db->getRow("advertisement","adId",$id);
return undef unless $properties->{adId};
bless {_session=>$session, _properties=>$properties}, $class;
}
#-------------------------------------------------------------------
=head2 session
Returns a reference to the current session.
=cut
sub session {
my $self = shift;
return $self->{_session};
}
#-------------------------------------------------------------------
=head2 set ( properties )
Updates the properties of an ad space.
=head3 properties
A hash reference containing the properties to set.
=head4 title
A human readable name for this ad, which will be displayed in the ad, and in menus.
=head4 adText
A chunk of text, no longer than 255 characters that will be displayed in text ads.
=head4 storageId
The id of the storage location that holds the image for an image style ad.
=head4 filename
The name of the file from the storage location to display for an image ad.
=head4 richMedia
A chunk of HTML that will be inserted into the page for rich media ads.
=head4 ownerUserId
The user that owns this ad, and will be able to view reports for it, etc.
=head4 isActive
A boolean indicating whether the ad is active or not.
=head4 type
The type of ad this is. Defaults to 'text'. Choose from 'text', 'image', or 'rich'.
=head4 borderColor
The hex color to be used to display the border on a text based ad.
=head4 textColor
THe hex color to be used to display the text on a test based ad.
=head4 backgroundColor
The hex color to be used to display the background on a text based ad.
=head4 priority
An integer that will be used to scale the frequency of ad placement based upon traffic to your site. The lower the number, the more frequently it will be displayed. For example, on a site with an average of 1 impression per second, if you have two ads, one with a priority of 0 and another with a priority of 100, the first ad will be displayed 100 times more frequently than the second ad.
=head4 url
The URL that the user will be directed to when clicking on the ad. This is used in text and image based ads.
=head4 clicksBought
The number of clicks that have been purchased for this ad.
=head4 impressionsBought
=cut
sub set {
my $self = shift;
my $properties = shift;
$self->{_properties}{title} = $properties->{title} || $self->{_properties}{title} || "Untitled";
$self->{_properties}{clicksBought} = $properties->{clicksBought} || $self->{_properties}{clicksBought};
$self->{_properties}{impressionsBought} = $properties->{impressionsBought} || $self->{_properties}{impressionsBought};
$self->{_properties}{url} = $properties->{url} || $self->{_properties}{url};
$self->{_properties}{adText} = $properties->{adText} || $self->{_properties}{adText};
$self->{_properties}{filename} = $properties->{filename} || $self->{_properties}{filename};
$self->{_properties}{storageId} = $properties->{storageId} || $self->{_properties}{storageId};
$self->{_properties}{richMedia} = $properties->{richMedia} || $self->{_properties}{richMedia};
$self->{_properties}{ownerUserId} = $properties->{ownerUserId} || $self->{_properties}{ownerUserId} || "3";
$self->{_properties}{isActive} = exists $properties->{isActive} ? $properties->{isActive} : $self->{_properties}{isActive};
$self->{_properties}{type} = $properties->{type} || $self->{_properties}{type} || "text";
$self->{_properties}{borderColor} = $properties->{borderColor} || $self->{_properties}{borderColor} || "#000000";
$self->{_properties}{textColor} = $properties->{textColor} || $self->{_properties}{textColor} || "#000000";
$self->{_properties}{backgroundColor} = $properties->{backgroundColor} || $self->{_properties}{backgroundColor} || "#ffffff";
$self->{_properties}{priority} = $properties->{priority} || $self->{_properties}{priority} || "0";
# prerender the ad for faster display
my $adSpace = WebGUI::AdSpace->new($self->session, $self->get("adSpaceId"));
if ($self->get("type") eq "text") {
$self->{_properties}{renderedAd} = '<a href="'.$self->session->url->gateway(undef, "op=clickAd;id=".$self->getId).'"><div style="overflow: hidden; width: '.$adSpace->get("width").'px; height: '.$adSpace->get("height").'px; color: '.$self->get("textColor").'; background-color: '.$self->get("backgroundColor").'; border: 1px solid '.$self->get("borderColor").';"><b>'.$self->get("title").'</b><br />'.$self->get("adText").'</div></a>';
} elsif ($self->get("type") eq "image") {
my $storage = WebGUI::Storage::Image->new($self->session, $self->get("storageId"));
$self->{_properties}{renderedAd} = '<a href="'.$self->session->url->gateway(undef, "op=clickAd;id=".$self->getId).'"><div style="overflow: hidden; width: '.$adSpace->get("width").'px; height: '.$adSpace->get("height").'px;"><img src="'.$storage->getUrl($self->get("filename")).'" style="border: 0px;" alt="'.$self->get("title").'" /></div></a>';
} elsif ($self->get("type") eq "rich") {
$self->{_properties}{renderedAd} = $self->get("richMedia");
}
$self->session->db->setRow("advertisement","adId",$self->{_properties});
}
1;

View file

@ -46,7 +46,8 @@ sub addRevision {
if ($self->get("storageId")) {
my $newStorage = WebGUI::Storage->get($self->session,$self->get("storageId"))->copy;
$newSelf->update({storageId=>$newStorage->getId});
}
}
$self->getThread->unmarkRead;
return $newSelf;
}
@ -412,7 +413,6 @@ sub getTemplateVars {
$var{'rate.url.4'} = $self->getRateUrl(4);
$var{'rate.url.5'} = $self->getRateUrl(5);
$var{'hasRated'} = $self->hasRated;
$var{'isMarkedRead'} = $self->isMarkedRead;
my $gotImage;
my $gotAttachment;
@{$var{'attachment_loop'}} = ();
@ -502,9 +502,12 @@ Returns a boolean indicating whether this user has already rated this post.
sub hasRated {
my $self = shift;
return 1 if $self->isPoster;
my ($flag) = $self->session->db->quickArray("select count(*) from Post_rating where assetId="
.$self->session->db->quote($self->getId)." and ((userId=".$self->session->db->quote($self->session->user->userId)." and userId<>'1') or (userId='1' and
ipAddress=".$self->session->db->quote($self->session->env->get("REMOTE_ADDR"))."))");
my $flag = 0;
if ($self->session->user->userId eq "1") {
($flag) = $self->session->db->quickArray("select count(*) from Post_rating where assetId=? and ipAddress=?",[$self->getId, $self->session->env->get("REMOTE_ADDR")]);
} else {
($flag) = $self->session->db->quickArray("select count(*) from Post_rating where assetId=? and userId=?",[$self->getId, $self->session->user->userId]);
}
return $flag;
}
@ -547,21 +550,6 @@ sub 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) = $self->session->db->quickArray("select count(*) from Post_read where userId=".$self->session->db->quote($self->session->user->userId)." and postId=".$self->session->db->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.
@ -588,22 +576,6 @@ sub isReply {
}
#-------------------------------------------------------------------
=head2 markRead ( )
Marks this post read for this user.
=cut
sub markRead {
my $self = shift;
unless ($self->isMarkedRead) {
$self->session->db->write("insert into Post_read (userId, postId, threadId, readDate) values (".$self->session->db->quote($self->session->user->userId).",
".$self->session->db->quote($self->getId).", ".$self->session->db->quote($self->get("threadId")).", ".$self->session->datetime->time().")");
}
}
#-------------------------------------------------------------------
=head2 notifySubscribers ( )
@ -719,8 +691,6 @@ sub purge {
$storage->delete if defined $storage;
}
$sth->finish;
$self->session->db->write("delete from Post_rating where assetId=".$self->session->db->quote($self->getId));
$self->session->db->write("delete from Post_read where postId=".$self->session->db->quote($self->getId));
return $self->SUPER::purge;
}
@ -838,19 +808,6 @@ sub trash {
#-------------------------------------------------------------------
=head2 unmarkRead ( )
Negates the markRead method.
=cut
sub unmarkRead {
my $self = shift;
$self->session->db->write("delete from forumRead where userId=".$self->session->db->quote($self->session->user->userId)." and postId=".$self->session->db->quote($self->getId));
}
#-------------------------------------------------------------------
=head2 update
We overload the update method from WebGUI::Asset in order to handle file system privileges.
@ -873,7 +830,6 @@ sub update {
#-------------------------------------------------------------------
sub view {
my $self = shift;
$self->markRead;
$self->incrementViews;
return $self->getThread->view;
}
@ -1120,7 +1076,6 @@ sub www_showConfirmation {
#-------------------------------------------------------------------
sub www_view {
my $self = shift;
$self->markRead;
$self->incrementViews;
return $self->getThread->www_view;
}

View file

@ -434,7 +434,7 @@ Returns a boolean indicating whether this thread is marked read for the user.
sub isMarkedRead {
my $self = shift;
return 1 if $self->isPoster;
my ($isRead) = $self->session->db->quickArray("select count(*) from Post_read where userId=".$self->session->db->quote($self->session->user->userId)." and threadId=".$self->session->db->quote($self->getId)." and postId=".$self->session->db->quote($self->get("lastPostId")));
my ($isRead) = $self->session->db->quickArray("select count(*) from Thread_read where threadId=? and userId=?",[$self->getId,$self->session->user->userId]);
return $isRead;
}
@ -479,6 +479,19 @@ sub lock {
}
#-------------------------------------------------------------------
=head2 markRead ( )
Marks this post read for this user.
=cut
sub markRead {
my $self = shift;
$self->session->db->write("replace into Thread_read (threadId, userId) values (?,?)",[$self->getId,$self->session->user->userId]);
}
#-------------------------------------------------------------------
=head2 prepareView ( )
@ -512,6 +525,12 @@ sub processPropertiesFromFormPost {
}
}
#-------------------------------------------------------------------
sub purge {
my $self = shift;
$self->session->db->write("delete from Thread_read where postId=?",[$self->getId]);
return $self->SUPER::purge;
}
#-------------------------------------------------------------------
@ -673,6 +692,19 @@ sub unlock {
#-------------------------------------------------------------------
=head2 unmarkRead ( )
unmarks this post read for all users.
=cut
sub unmarkRead {
my $self = shift;
$self->session->db->write("delete from Thread_read where threadId=?",[$self->getId]);
}
#-------------------------------------------------------------------
=head2 unstick ( )
Negates the stick method.

View file

@ -47,9 +47,10 @@ sub appendPostListTemplateVars {
push(@rating_loop,{'rating_loop.count'=>$i});
}
my %lastReply;
my $hasRead = 0;
if ($post->get("className") =~ /Thread/) {
my $lastPost = $post->getLastPost();
if ($self->get("displayLastReply")) {
my $lastPost = $post->getLastPost();
%lastReply = (
"lastReply.url"=>$lastPost->getUrl.'#'.$lastPost->getId,
"lastReply.title"=>$lastPost->get("title"),
@ -60,6 +61,7 @@ sub appendPostListTemplateVars {
"lastReply.timeSubmitted.human"=>$self->session->datetime->epochToHuman($lastPost->get("dateSubmitted"),"%Z")
);
}
$hasRead = $post->isMarkedRead;
}
my $url;
if ($post->get("status") eq "pending") {
@ -88,6 +90,7 @@ sub appendPostListTemplateVars {
"isThird"=>(($i+1)%3==0),
"isFourth"=>(($i+1)%4==0),
"isFifth"=>(($i+1)%5==0),
"user.hasRead" => $hasRead,
"user.isPoster"=>$post->isPoster,
"avatar.url"=>$post->getAvatarUrl,
%lastReply

View file

@ -77,6 +77,7 @@ Returns a hash reference containing operation and package names.
sub getOperations {
return {
'clickAd' => 'WebGUI::Operation::AdSpace',
'editVersionTag' => 'WebGUI::Operation::VersionTag',
'editVersionTagSave' => 'WebGUI::Operation::VersionTag',
'commitVersionTag' => 'WebGUI::Operation::VersionTag',

View file

@ -0,0 +1,45 @@
package WebGUI::Operation::AdSpace.pm
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2006 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::AdSpace;
=head1 NAME
Package WebGUI::Operation::AdSpace
=head1 DESCRIPTION
Operation handler for advertising functions.
=cut
#-------------------------------------------------------------------
=head2 www_clickAd ( )
Handles a click on an advertisement.
=cut
sub www_clickAd {
my $session = shift;
my $id = $session->form->param("adId");
return undef unless $id;
my $url = WebGUI::AdSpace->countClick($session, $id);
$session->http->setRedirect($url);
return "Redirecting to $url";
}
1;

View file

@ -1049,6 +1049,10 @@ A conditional indicating that is true if this Post is the fifth in this Collabor
<b>user.isPoster</b><br>
A conditional indicating that is true if the current user submitted this Post.
<p>
<b>user.hasRead</b><br>
A conditional indicating whether a user has read this thread.
<p>
<b>avatar.url</b><br>
A URL to the avatar for the owner of the Post, if avatars are enabled and the

View file

@ -224,10 +224,6 @@ URLs that are used to rate this post. N goes from 1 to 5.
A conditional that is true if the user has already rated this Post.
<p>
<b>isMarkedRead</b><br>
A conditional that is true if the post has been marked as read by the user.
<p>
<b>image.url</b><br>
The URL to the first image attached to the Post.
<p>