webgui/lib/WebGUI/Keyword.pm
JT Smith ab55978f55 fixed some keywords bugs
took beta label away from event manager
added option to shelf to include items below it and items included via keywords
2008-05-17 18:58:22 +00:00

382 lines
9.8 KiB
Perl

package WebGUI::Keyword;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2008 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use Class::InsideOut qw(public register id);
use HTML::TagCloud;
use WebGUI::Paginator;
=head1 NAME
Package WebGUI::Keyword
=head1 DESCRIPTION
This package provides an API to create and modify keywords used by the asset sysetm.
Assets can use the C<keywords> property to set keywords automatically. See
WebGUI::Asset::update() for more details.
=head1 SYNOPSIS
use WebGUI::Keyword;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 session ( )
Returns a reference to the current session.
=cut
public session => my %session;
#-------------------------------------------------------------------
=head2 deleteKeywordsForAsset ( $asset )
Removes all keywords from an asset.
=head3 asset
The asset to delete the keywords from.
=cut
sub deleteKeywordsForAsset {
my $self = shift;
my $asset = shift;
$self->session->db->write("delete from assetKeyword where assetId=?", [$asset->getId]);
}
#-------------------------------------------------------------------
=head2 deleteKeyword ( { keyword => $keyword } )
Removes a particular keyword from the system entirely.
=head3 keyword
The keyword to remove.
=cut
sub deleteKeyword {
my $self = shift;
my $options = shift;
$self->session->db->write("delete from assetKeyword where keyword=?", [$options->{keyword}]);
}
#-------------------------------------------------------------------
=head2 generateCloud ( { startAsset => $asset, displayFunc => "viewKeyword" } )
Generates a block of HTML that represents the prevelence of one keyword compared to another.
=head3 displayAsset
The asset that contains the function to display a list of assets related to a given keyword. If not specified the
startAsset will be used.
=head3 displayFunc
The www func that will be called on the displayAsset to display the list of assets associated to a given keyword.
=head3 cloudLevels
How many levels of keyword sizes should there be displayed in the cloud. Defaults to 24. Range between 2 and 24.
=head3 startAsset
The starting point in the asset tree to search for keywords, so you can show a cloud for just a subsection of the
site.
=head3 maxKeywords
The maximum number of keywords to display in the cloud. Defaults to 50. Valid range between 1 and 50, inclusive.
=cut
sub generateCloud {
my $self = shift;
my $options = shift;
my $display = $options->{displayAsset} || $options->{startAsset};
my $sth = $self->session->db->read("select count(*) as keywordTotal, keyword from assetKeyword
left join asset using (assetId) where lineage like ? group by keyword order by keywordTotal desc limit 50",
[ $options->{startAsset}->get("lineage").'%' ]);
my $cloud = HTML::TagCloud->new(levels=>$options->{cloudLevels} || 24);
while (my ($count, $keyword) = $sth->array) {
$cloud->add($keyword, $display->getUrl("func=".$options->{displayFunc}.";keyword=".$keyword), $count);
}
return $cloud->html_and_css($options->{maxKeywords});
}
#-------------------------------------------------------------------
=head2 getKeywordsForAsset ( { asset => $asset } )
Returns a string of keywords separated by spaces.
=head3 asset
An asset that you want to get the keywords for.
=head3 asArrayRef
A boolean, that if set to 1 will return the keywords as an array reference rather than a string.
=cut
sub getKeywordsForAsset {
my ($self, $options) = @_;
my @keywords = $self->session->db->buildArray("select keyword from assetKeyword where assetId=?",
[$options->{asset}->getId]);
if ($options->{asArrayRef}) {
return \@keywords;
}
else {
return join(" ", map({ (m/\s/) ? '"' . $_ . '"' : $_ } @keywords));
}
}
#-------------------------------------------------------------------
=head2 getMatchingAssets ( { startAsset => $asset, keyword => $keyword } )
Returns an array reference of asset ids matching the params.
=head3 startAsset
An asset object where you'd like to start searching for matching keywords. Doesn't search any particular branch if one isn't specified.
=head3 keyword
The keyword to match.
=head3 keywords
An array reference of keywords to match.
=head3 matchAssetKeywords
A reference to an asset that has a list of keywords to match. This can help locate assets that are similar to another asset.
=head3 isa
A classname pattern to match. For example, if you provide 'WebGUI::Asset::Sku' then everything that has a class name that starts with that including 'WebGUI::Asset::Sku::Product' will be included.
=head3 usePaginator
Instead of returning an array reference of assetId's, return a paginator object.
=cut
sub getMatchingAssets {
my ($self, $options) = @_;
# base query
my @clauses = ();
my @params = ();
# what lineage are we looking for
if (exists $options->{startAsset}) {
push @clauses, 'lineage like ?';
push @params, $options->{startAsset}->get("lineage").'%';
}
# matching keywords against another asset
if (exists $options->{matchAssetKeywords}) {
$options->{keywords} = $self->getKeywordsForAsset({
asset => $options->{matchAssetKeywords},
asArrayRef => 1,
});
}
# looking for a class name match
if (exists $options->{isa}) {
push @clauses, 'className like ?';
push @params, $options->{isa}.'%';
}
# looking for a single keyword
if (exists $options->{keyword}) {
push @clauses, 'keyword=?';
push @params, $options->{keyword};
}
# looking for a list of keywords
if (exists $options->{keywords} && scalar(@{$options->{keywords}})) {
my @placeholders = ();
foreach my $word (@{$options->{keywords}}){
push @placeholders, '?';
push @params, $word;
}
push @clauses, 'keyword in ('.join(',', @placeholders).')';
}
# write the query
my $query = 'select distinct assetKeyword.assetId from assetKeyword left join asset using (assetId)
where '.join(' and ', @clauses).' order by creationDate desc';
# perform the search
if ($options->{usePaginator}) {
my $p = WebGUI::Paginator->new($self->session);
$p->setDataByQuery($query, undef, undef, \@params);
return $p;
}
return $self->session->db->buildArrayRef($query, \@params);
}
#-------------------------------------------------------------------
=head2 new ( $session )
Constructor.
=head3 session
A reference to the current session.
=cut
sub new {
my $class = shift;
my $session = shift;
my $self = bless \do {my $s}, $class;
register($self);
$session{id $self} = $session;
return $self;
}
#-------------------------------------------------------------------
=head2 replaceKeyword ( { currentKeyword => $keyword1, newKeyword => $keyword2 } )
Changes a keyword from one thing to another thing throughout the system.
=head3 currentKeyword
Whatever the keyword is now. Example: "apples"
=head3 newKeyword
Whatever you want it to be. Example; "apple"
=cut
sub replaceKeyword {
my ($self, $options) = @_;
$self->session->db->write("update assetKeyword set keyword=? where keyword=?",
[$options->{newKeyword}, $options->{currentKeyword}]);
}
#-------------------------------------------------------------------
=head2 setKeywordsForAsset ( { asset => $asset, keywords => $keywords } )
Sets the keywords for an asset.
=head3 asset
An asset that you want to set the keywords for.
=head3 keywords
Either a string of space-separated keywords, or an array reference of keywords to assign to the asset.
=cut
sub setKeywordsForAsset {
my $self = shift;
my $options = shift;
my $keywords = [];
if (ref $options->{keywords} eq "ARRAY") {
$keywords = $options->{keywords};
}
else {
$keywords = string2list($options->{keywords});
}
$self->deleteKeywordsForAsset($options->{asset});
my $assetId = $options->{asset}->getId;
if (scalar(@{$keywords})) {
my $sth = $self->session->db->prepare("insert into assetKeyword (assetId, keyword) values (?,?)");
my %found_keywords;
foreach my $keyword (@{$keywords}) {
next if ($keyword eq "");
next
if $found_keywords{$keyword};
$found_keywords{$keyword}++;
$sth->execute([$assetId, lc($keyword)]);
}
}
}
#------------------------------------------------------------------------------
=head2 string2list ( string )
Returns an array reference of phrases.
=head3 string
A scalar containing space separated phrases.
=cut
sub string2list {
my $text = shift;
return if (ref $text);
my @words = ();
my $word = '';
my $errorFlag = 0;
while ( defined $text and length $text and not $errorFlag) {
if ($text =~ s/\A(?: ([^\"\s\\]+) | \\(.) )//mx) {
$word .= $1;
}
elsif ($text =~ s/\A"((?:[^\"\\]|\\.)*)"//mx) {
$word .= $1;
}
elsif ($text =~ s/\A\s+//m){
push(@words, $word);
$word = '';
}
elsif ($text =~ s/\A"//) {
$errorFlag = 1;
}
else {
$errorFlag = 1;
}
}
push(@words, $word);
return \@words;
}
1;