Keywords form control, Keywords API enhancements, Keywords format change
This commit is contained in:
parent
e71a119faa
commit
7064eb1a2a
6 changed files with 254 additions and 38 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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
173
lib/WebGUI/Form/Keywords.pm
Normal 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;
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue