# ConfigManager::ParameterHandler module
#
# Copyright (c) 2006 Erland Isaksson (erland_i@hotmail.com)
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package Plugins::SQLPlayList::ConfigManager::ParameterHandler;
use strict;
use base 'Class::Data::Accessor';
use Slim::Buttons::Home;
use Slim::Utils::Misc;
use Slim::Utils::Strings qw(string);
use DBI qw(:sql_types);
use HTML::Entities;
__PACKAGE__->mk_classaccessors( qw(debugCallback errorCallback parameterPrefix criticalErrorCallback) );
sub new {
my $class = shift;
my $parameters = shift;
my $self = {
'debugCallback' => $parameters->{'debugCallback'},
'errorCallback' => $parameters->{'errorCallback'},
'parameterPrefix' => $parameters->{'parameterPrefix'},
'criticalErrorCallback' => $parameters->{'criticalErrorCallback'},
};
bless $self,$class;
return $self;
}
sub quoteValue {
my $value = shift;
$value =~ s/\\/\\\\/g;
$value =~ s/\'/\\\'/g;
$value =~ s/\"/\\\"/g;
return $value;
}
sub addValuesToTemplateParameter {
my $self = shift;
my $p = shift;
my $currentValues = shift;
if($p->{'type'} =~ '^sql.*') {
my $listValues = $self->getSQLTemplateData($p->{'data'});
if($p->{'type'} =~ /.*optional.*/) {
my %empty = (
'id' => '',
'name' => '',
'value' => ''
);
unshift @$listValues,\%empty;
}
$p->{'values'} = $listValues;
}elsif($p->{'type'} =~ 'function.*') {
my $listValues = $self->getFunctionTemplateData($p->{'data'});
if($p->{'type'} =~ /.*optional.*list$/) {
my %empty = (
'id' => '',
'name' => '',
'value' => ''
);
unshift @$listValues,\%empty;
}
if($p->{'value'}) {
for my $v (@$listValues) {
$v->{'selected'} = 1;
}
}
$p->{'values'} = $listValues;
}elsif($p->{'type'} =~ '.*list$' || $p->{'type'} =~ '.*checkboxes$') {
my @listValues = ();
my @values = split(/,/,$p->{'data'});
for my $value (@values){
my @idName = split(/=/,$value);
my %listValue = (
'id' => @idName->[0],
'name' => @idName->[1]
);
if(scalar(@idName)>2) {
$listValue{'value'} = @idName->[2];
}else {
$listValue{'value'} = @idName->[0];
}
push @listValues, \%listValue;
}
if($p->{'type'} =~ /.*optional.*list$/) {
my %empty = (
'id' => '',
'name' => '',
'value' => ''
);
unshift @listValues,\%empty;
}
$p->{'values'} = \@listValues;
}
if(defined($currentValues)) {
$self->setValueOfTemplateParameter($p,$currentValues);
}
}
sub setValueOfTemplateParameter {
my $self = shift;
my $p = shift;
my $currentValues = shift;
if(defined($currentValues)) {
if($p->{'type'} =~ '^sql.*' || $p->{'type'} =~ 'function.*' || $p->{'type'} =~ '.*list$' || $p->{'type'} =~ '.*checkboxes$') {
my $listValues = $p->{'values'};
for my $v (@$listValues) {
if($currentValues->{$v->{'value'}}) {
$v->{'selected'} = 1;
}else {
$v->{'selected'} = undef;
}
}
}else {
for my $v (keys %$currentValues) {
$p->{'value'} = $v;
}
}
}
}
sub parameterIsSpecified {
my $self = shift;
my $params = shift;
my $parameter = shift;
if($parameter->{'type'} =~ /.*multiplelist$/ || $parameter->{'type'} =~ /.*checkboxes$/) {
my $selectedValues = undef;
if($parameter->{'type'} =~ /.*multiplelist$/) {
$selectedValues = $self->getMultipleListQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}else {
$selectedValues = $self->getCheckBoxesQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}
if(scalar(keys %$selectedValues)>0) {
return 1;
}
}elsif($parameter->{'type'} =~ /.*singlelist$/) {
my $selectedValue = $params->{$self->parameterPrefix.'_'.$parameter->{'id'}};
if(defined($selectedValue)) {
return 1;
}
}else{
if($params->{$self->parameterPrefix.'_'.$parameter->{'id'}}) {
return 1;
}
}
return 0;
}
sub getValueOfTemplateParameter {
my $self = shift;
my $params = shift;
my $parameter = shift;
my $result = undef;
my $dbh = getCurrentDBH();
if($parameter->{'type'} =~ /.*multiplelist$/ || $parameter->{'type'} =~ /.*checkboxes$/) {
my $selectedValues = undef;
if($parameter->{'type'} =~ /.*multiplelist$/) {
$selectedValues = $self->getMultipleListQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}else {
$selectedValues = $self->getCheckBoxesQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}
$self->debugCallback->("Got ".scalar(keys %$selectedValues)." values for ".$parameter->{'id'}."\n");
my $values = $parameter->{'values'};
for my $item (@$values) {
if(defined($selectedValues->{$item->{'id'}})) {
if(defined($result)) {
$result = $result.',';
}
my $thisvalue = $item->{'value'};
if(!defined($parameter->{'rawvalue'}) || !$parameter->{'rawvalue'}) {
$thisvalue = quoteValue($thisvalue);
}
if($parameter->{'quotevalue'}) {
$result .= "'".encode_entities($thisvalue,"&<>\'\"")."'";
}else {
$result .= encode_entities($thisvalue,"&<>\'\"");
}
$self->debugCallback->("Got ".$parameter->{'id'}."=$thisvalue\n");
}
}
if(!defined($result)) {
$result = '';
}
}elsif($parameter->{'type'} =~ /.*singlelist$/) {
my $values = $parameter->{'values'};
my $selectedValue = $params->{$self->parameterPrefix.'_'.$parameter->{'id'}};
$selectedValue = Slim::Utils::Unicode::utf8decode_locale($selectedValue);
for my $item (@$values) {
if($selectedValue eq $item->{'id'}) {
my $thisvalue = $item->{'value'};
if(!defined($parameter->{'rawvalue'}) || !$parameter->{'rawvalue'}) {
$thisvalue = quoteValue($thisvalue);
}
if($parameter->{'quotevalue'}) {
$result = "'".encode_entities($thisvalue,"&<>\'\"")."'";
}else {
$result = encode_entities($thisvalue,"&<>\'\"");
}
$self->debugCallback->("Got ".$parameter->{'id'}."=$thisvalue\n");
last;
}
}
if(!defined($result)) {
$result = '';
}
}else{
if($params->{$self->parameterPrefix.'_'.$parameter->{'id'}}) {
my $thisvalue = $params->{$self->parameterPrefix.'_'.$parameter->{'id'}};
$thisvalue = Slim::Utils::Unicode::utf8decode_locale($thisvalue);
if(!defined($parameter->{'rawvalue'}) || !$parameter->{'rawvalue'}) {
$thisvalue=quoteValue($thisvalue);
}
if($parameter->{'quotevalue'}) {
return "'".encode_entities($thisvalue,"&<>\'\"").".";
}else {
return encode_entities($thisvalue,"&<>\'\"");
}
$self->debugCallback->("Got ".$parameter->{'id'}."=$thisvalue\n");
}else {
if($parameter->{'type'} =~ /.*checkbox$/) {
$result = '0';
}else {
$result = '';
}
$self->debugCallback->("Got ".$parameter->{'id'}."=$result\n");
}
}
return $result;
}
sub getXMLValueOfTemplateParameter {
my $self = shift;
my $params = shift;
my $parameter = shift;
my $dbh = getCurrentDBH();
my $result = undef;
if($parameter->{'type'} =~ /.*multiplelist$/ || $parameter->{'type'} =~ /.*checkboxes$/) {
my $selectedValues = undef;
if($parameter->{'type'} =~ /.*multiplelist$/) {
$selectedValues = $self->getMultipleListQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}else {
$selectedValues = $self->getCheckBoxesQueryParameter($params,$self->parameterPrefix.'_'.$parameter->{'id'});
}
$self->debugCallback->("Got ".scalar(keys %$selectedValues)." values for ".$parameter->{'id'}." to convert to XML\n");
my $values = $parameter->{'values'};
for my $item (@$values) {
if(defined($selectedValues->{$item->{'id'}})) {
$result = $result.'';
$result = $result.encode_entities($item->{'value'},"&<>\'\"");
$result = $result.'';
$self->debugCallback->("Got ".$parameter->{'id'}."=".$item->{'value'}."\n");
}
}
if(!defined($result)) {
$result = '';
}
}elsif($parameter->{'type'} =~ /.*singlelist$/) {
my $values = $parameter->{'values'};
my $selectedValue = $params->{$self->parameterPrefix.'_'.$parameter->{'id'}};
$selectedValue = Slim::Utils::Unicode::utf8decode_locale($selectedValue);
for my $item (@$values) {
if($selectedValue eq $item->{'id'}) {
$result = $result.'';
$result = $result.encode_entities($item->{'value'},"&<>\'\"");
$result = $result.'';
$self->debugCallback->("Got ".$parameter->{'id'}."=".$item->{'value'}."\n");
last;
}
}
if(!defined($result)) {
$result = '';
}
}else{
if(defined($params->{$self->parameterPrefix.'_'.$parameter->{'id'}}) && $params->{$self->parameterPrefix.'_'.$parameter->{'id'}} ne '') {
my $value = Slim::Utils::Unicode::utf8decode_locale($params->{$self->parameterPrefix.'_'.$parameter->{'id'}});
$result = ''.encode_entities($value,"&<>\'\"").'';
$self->debugCallback->("Got ".$parameter->{'id'}."=".$value."\n");
}else {
if($parameter->{'type'} =~ /.*checkbox$/) {
$result = '0';
}else {
$result = '';
}
$self->debugCallback->("Got ".$parameter->{'id'}."=".$result."\n");
}
}
return $result;
}
sub getMultipleListQueryParameter {
my $self = shift;
my $params = shift;
my $parameter = shift;
my $query = $params->{url_query};
my %result = ();
if($query) {
foreach my $param (split /\&/, $query) {
if ($param =~ /^([^=]+)=(.*)$/) {
my $name = unescape($1);
my $value = unescape($2);
if($name eq $parameter) {
# We need to turn perl's internal
# representation of the unescaped
# UTF-8 string into a "real" UTF-8
# string with the appropriate magic set.
if ($value ne '*' && $value ne '') {
$value = Slim::Utils::Unicode::utf8on($value);
$value = Slim::Utils::Unicode::utf8encode_locale($value);
}
$result{$value} = 1;
}
}
}
}
return \%result;
}
sub getCheckBoxesQueryParameter {
my $self = shift;
my $params = shift;
my $parameter = shift;
my %result = ();
foreach my $key (keys %$params) {
my $pattern = '^'.$parameter.'_(.*)';
if ($key =~ /$pattern/) {
my $id = unescape($1);
if ($id ne '*' && $id ne '') {
$id = Slim::Utils::Unicode::utf8on($id);
$id = Slim::Utils::Unicode::utf8encode_locale($id);
}
$result{$id} = 1;
}
}
return \%result;
}
sub getSQLTemplateData {
my $self = shift;
my $sqlstatements = shift;
my @result =();
my $dbh = getCurrentDBH();
my $trackno = 0;
for my $sql (split(/[;]/,$sqlstatements)) {
eval {
$sql =~ s/^\s+//g;
$sql =~ s/\s+$//g;
my $sth = $dbh->prepare( $sql );
$self->debugCallback->("Executing: $sql\n");
$sth->execute() or do {
$self->debugCallback->("Error executing: $sql\n");
$sql = undef;
};
if ($sql =~ /^SELECT+/oi) {
$self->debugCallback->("Executing and collecting: $sql\n");
my $id;
my $name;
my $value;
$sth->bind_col( 1, \$id);
$sth->bind_col( 2, \$name);
$sth->bind_col( 3, \$value);
while( $sth->fetch() ) {
my %item = (
'id' => Slim::Utils::Unicode::utf8on(Slim::Utils::Unicode::utf8decode($id,'utf8')),
'name' => Slim::Utils::Unicode::utf8on(Slim::Utils::Unicode::utf8decode($name,'utf8')),
'value' => Slim::Utils::Unicode::utf8on(Slim::Utils::Unicode::utf8decode($value,'utf8'))
);
push @result, \%item;
}
}
$sth->finish();
};
if( $@ ) {
warn "Database error: $DBI::errstr\n";
$self->criticalErrorCallback->("Running: $sql got error:
".$DBI::errstr);
}
}
return \@result;
}
sub getFunctionTemplateData {
my $self = shift;
my $data = shift;
my @params = split(/\,/,$data);
my @result =();
if(scalar(@params)==2) {
my $object = @params->[0];
my $function = @params->[1];
if(UNIVERSAL::can($object,$function)) {
$self->debugCallback->("Getting values for: $function\n");
no strict 'refs';
my $items = eval { &{$object.'::'.$function}() };
if( $@ ) {
warn "Function call error: $@\n";
}
use strict 'refs';
if(defined($items)) {
@result = @$items;
}
}
}else {
$self->debugCallback->("Error getting values for: $data, incorrect number of parameters ".scalar(@params)."\n");
}
return \@result;
}
sub getCurrentDBH {
return Slim::Schema->storage->dbh();
}
# other people call us externally.
*escape = \&URI::Escape::uri_escape_utf8;
# don't use the external one because it doesn't know about the difference
# between a param and not...
#*unescape = \&URI::Escape::unescape;
sub unescape {
my $in = shift;
my $isParam = shift;
$in =~ s/\+/ /g if $isParam;
$in =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
return $in;
}
1;
__END__