Keywords form control, Keywords API enhancements, Keywords format change

This commit is contained in:
Graham Knop 2009-03-26 20:52:19 +00:00
parent e71a119faa
commit 7064eb1a2a
6 changed files with 254 additions and 38 deletions

View file

@ -5,6 +5,8 @@
- fixed #10011: Wrong spelling of "Descendant" in Navigation Asset Help function (Rob Schipper/NFIA India)
- rfe #9098: Thingy Thing-copy function (SDH Consulting Group)
- rfe #9099: Thingy field-copy function (SDH Consulting Group)
- Keywords are now comma separated rather than space separated.
- added Keywords form control with autocomplete
7.7.0
- fixed #9913: New Content Side Bar missing in Asset window

View file

@ -7,6 +7,14 @@ upgrading from one version to the next, or even between multiple
versions. Be sure to heed the warnings contained herein as they will
save you many hours of grief.
7.7.1
--------------------------------------------------------------------
* Keywords are now comma separated rather than space separated. Quotes are
no longer treated specially and will become part of keywords. Keywords
cannot contain commas. Keyword searching is still case insensitive, but
keyword will preserve the case of what is entered.
7.7.0
--------------------------------------------------------------------
* WebGUI now requires Params::Validate version 0.81 or greater.

View file

@ -930,7 +930,7 @@ sub getEditForm {
label => $i18n->get('keywords'),
hoverHelp => $i18n->get('keywords help'),
value => $self->get('keywords'),
fieldType => 'text',
fieldType => 'keywords',
tab => 'meta',
}
);

View file

@ -317,8 +317,6 @@ sub exportAsHtml {
# tell the user which asset we're exporting.
unless ($quiet) {
my $message = sprintf $i18n->get('exporting page'), $fullPath;
$exportSession->var->end;
$exportSession->close;
$self->session->output->print($message);
}
@ -541,7 +539,7 @@ sub exportGetUrlAsPath {
my $fileTypes = $config->get('exportBinaryExtensions');
# get the asset's URL as a URI::URL object for easy parsing of components
my $url = URI::URL->new($config->get("sitename")->[0] . $self->getUrl);
my $url = URI::URL->new($self->session->url->getSiteURL . $self->getUrl);
my @pathComponents = $url->path_components;
shift @pathComponents; # first item is the empty string
my $filename = pop @pathComponents;

173
lib/WebGUI/Form/Keywords.pm Normal file
View file

@ -0,0 +1,173 @@
package WebGUI::Form::Keywords;
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Copyright 2001-2009 Plain Black Corporation.
-------------------------------------------------------------------
Please read the legal notices (docs/legal.txt) and the license
(docs/license.txt) that came with this distribution before using
this software.
-------------------------------------------------------------------
http://www.plainblack.com info@plainblack.com
-------------------------------------------------------------------
=cut
use strict;
use base 'WebGUI::Form::Text';
use WebGUI::International;
use JSON ();
use WebGUI::Keyword;
=head1 NAME
Package WebGUI::Form::Group
=head1 DESCRIPTION
Creates a group chooser field.
=head1 SEE ALSO
This is a subclass of WebGUI::Form::SelectList.
=head1 METHODS
The following methods are specifically available from this class. Check the superclass for additional methods.
=cut
#-------------------------------------------------------------------
=head2 getDatabaseFieldType ( )
Returns "CHAR(22) BINARY".
=cut
sub getDatabaseFieldType {
return "CHAR(255)";
}
#-------------------------------------------------------------------
=head2 getName ( session )
Returns the human readable name of this control.
=cut
sub getName {
my ($self, $session) = @_;
return WebGUI::International->new($session, 'Asset')->get('keywords');
}
#-------------------------------------------------------------------
=head2 isDynamicCompatible ( )
A class method that returns a boolean indicating whether this control is compatible with the DynamicField control.
=cut
sub isDynamicCompatible {
return 1;
}
#-------------------------------------------------------------------
=head2 toHtml ( )
Returns a group pull-down field. A group pull down provides a select list that provides name value pairs for all the groups in the WebGUI system.
=cut
sub toHtml {
my $self = shift;
my $session = $self->session;
my $style = $session->style;
my $url = $session->url;
$style->setLink($url->extras("yui/build/autocomplete/assets/skins/sam/autocomplete.css"), {rel=>"stylesheet", type=>"text/css"});
$style->setScript($url->extras("yui/build/yahoo-dom-event/yahoo-dom-event.js"), {type=>"text/javascript"});
$style->setScript($url->extras("yui/build/datasource/datasource-min.js"), {type=>"text/javascript"});
$style->setScript($url->extras("yui/build/autocomplete/autocomplete-min.js"), {type=>"text/javascript"});
$style->setRawHeadTags('<style type="text/css">.yui-skin-sam.webgui-keywords-autocomplete .yui-ac-input { position: static; width: auto }</style>');
my $name = $self->generateIdParameter($self->get('name'));
my $autocompleteDiv = $self->privateName('autocomplete');
my $pageUrl = $url->page;
my $output
= '<div class="yui-skin-sam webgui-keywords-autocomplete">' . $self->SUPER::toHtml
. '<div id="' . $autocompleteDiv . '"></div>'
. '<script type="text/javascript">' . <<"END_SCRIPT" . '</script></div>';
(function() {
var oDS = new YAHOO.util.XHRDataSource('$pageUrl');
oDS.responseType = YAHOO.util.XHRDataSource.TYPE_JSON;
oDS.responseSchema = {
resultsList : "keywords",
};
var oAC = new YAHOO.widget.AutoComplete("$name", "$autocompleteDiv", oDS);
oAC.queryDelay = 0.5;
oAC.maxResultsDisplayed = 20;
oAC.minQueryLength = 3;
oAC.delimChar = [','];
oAC.generateRequest = function(sQuery) {
return "?op=formHelper;class=Keywords;sub=searchAsJSON;search=" + sQuery ;
};
})();
END_SCRIPT
return $output;
}
sub www_searchAsJSON {
my $session = shift;
my $search = $session->form->param('search');
my $keyword = WebGUI::Keyword->new($session);
my $keywords = $keyword->findKeywords({search => $search, limit => 20});
$session->http->setMimeType('application/json');
return JSON::to_json({keywords => $keywords});
}
sub getDefaultValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getDefaultValue(@_));
}
sub getOriginalValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getOriginalValue(@_));
}
sub getValue {
my $self = shift;
return _formatKeywordsAsWanted($self->SUPER::getValue(@_));
}
sub _formatKeywordsAsWanted {
my @keywords;
if (@_ == 1 && ref $_[0] eq 'ARRAY') {
@keywords = @{ $_[0] };
}
else {
for my $param (@_) {
for my $keyword (split /,/, $param) {
$keyword =~ s/^\s+//;
$keyword =~ s/\s+$//;
push @keywords, $keyword;
}
}
}
if (wantarray) {
return @keywords;
}
return join(', ', @keywords);
}
1;

View file

@ -88,6 +88,39 @@ sub deleteKeyword {
$self->session->db->write("delete from assetKeyword where keyword=?", [$options->{keyword}]);
}
sub findKeywords {
my $self = shift;
my $options = shift;
my $sql = 'SELECT keyword FROM assetKeyword';
my @where;
my @placeholders;
my $parentAsset;
if ($options->{asset}) {
$parentAsset = $options->{asset};
}
if ($options->{assetId}) {
$parentAsset = WebGUI::Asset->new($self->session, $options->{assetId});
}
if ($parentAsset) {
$sql .= ' INNER JOIN asset USING (assetId)';
push @where, 'lineage LIKE ?';
push @placeholders, $parentAsset->get('lineage') . '%';
}
if ($options->{search}) {
push @where, 'keyword LIKE ?';
push @placeholders, '%' . $options->{search} . '%';
}
if (@where) {
$sql .= ' WHERE ' . join(' AND ', @where);
}
$sql .= ' GROUP BY keyword';
if ($options->{limit}) {
$sql .= ' LIMIT ' . $options->{limit};
}
my $keywords = $self->session->db->buildArrayRef($sql, \@placeholders);
return $keywords;
}
#-------------------------------------------------------------------
@ -123,14 +156,32 @@ 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 $includeKeywords = $options->{includeOnlyKeywords};
my $maxKeywords = $options->{maxKeywords} || 50;
if ($maxKeywords > 100) {
$maxKeywords = 100;
}
my $urlCallback = $options->{urlCallback};
my $extraWhere = '';
my @extraPlaceholders;
if ($includeKeywords) {
$extraWhere .= ' AND keyword IN (' . join(',', ('?') x @{$includeKeywords}) . ')';
push @extraPlaceholders, @{$includeKeywords};
}
my $sth = $self->session->db->read("SELECT COUNT(*) as keywordTotal, keyword FROM assetKeyword
LEFT JOIN asset USING (assetId) WHERE lineage LIKE ? $extraWhere
GROUP BY keyword ORDER BY keywordTotal DESC LIMIT ?",
[ $options->{startAsset}->get("lineage").'%', @extraPlaceholders, $maxKeywords ]);
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);
my $url
= $urlCallback ? $display->$urlCallback($keyword)
: $options->{displayFunc} ? $display->getUrl("func=".$options->{displayFunc}.";keyword=".$keyword)
: $display->getUrl("keyword=".$keyword)
;
$cloud->add($keyword, $url, $count);
}
return $cloud->html_and_css($options->{maxKeywords});
return $cloud->html_and_css($maxKeywords);
}
#-------------------------------------------------------------------
@ -152,13 +203,14 @@ A boolean, that if set to 1 will return the keywords as an array reference rathe
sub getKeywordsForAsset {
my ($self, $options) = @_;
my @keywords = $self->session->db->buildArray("select keyword from assetKeyword where assetId=?",
[$options->{asset}->getId]);
my $assetId = $options->{asset} ? $options->{asset}->getId : $options->{assetId};
my $keywords = $self->session->db->buildArrayRef("select keyword from assetKeyword where assetId=?",
[$assetId]);
if ($options->{asArrayRef}) {
return \@keywords;
return $keywords;
}
else {
return join(" ", map({ (m/\s/) ? '"' . $_ . '"' : $_ } @keywords));
return join(', ', @$keywords);
}
}
@ -317,9 +369,9 @@ Either a string of space-separated keywords, or an array reference of keywords t
sub setKeywordsForAsset {
my $self = shift;
my $options = shift;
my $keywords = [];
my $keywords;
if (ref $options->{keywords} eq "ARRAY") {
$keywords = $options->{keywords};
$keywords = $options->{keywords};
}
else {
$keywords = string2list($options->{keywords});
@ -335,7 +387,7 @@ sub setKeywordsForAsset {
next
if $found_keywords{$keyword};
$found_keywords{$keyword}++;
$sth->execute([$assetId, lc($keyword)]);
$sth->execute([$assetId, $keyword]);
}
}
}
@ -348,35 +400,18 @@ Returns an array reference of phrases.
=head3 string
A scalar containing space separated phrases.
A scalar containing comma 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;
}
my @words = split /,/, $text;
for my $word (@words) {
$word =~ s/^\s+//;
$word =~ s/\s+$//;
}
push(@words, $word);
return \@words;
}