Added proxying by alternate criteria to WobjectProxy

This commit is contained in:
Len Kranendonk 2004-07-22 18:51:07 +00:00
parent d7171e5d49
commit 02d24c4e4c
13 changed files with 515 additions and 31 deletions

View file

@ -49,3 +49,7 @@ INSERT INTO settings (name, value) values ("metaDataEnabled", 0);
DELETE FROM settings WHERE name = "passiveProfilingEnabled";
INSERT INTO settings (name, value) values ("passiveProfilingEnabled", 0);
alter table WobjectProxy add proxyByCriteria int default 0;
alter table WobjectProxy add resolveMultiples varchar(30) default "mostRecent";
alter table WobjectProxy add proxyCriteria text default NULL;

View file

@ -24,6 +24,7 @@ use WebGUI::SQL;
use WebGUI::Style;
use WebGUI::Template;
use WebGUI::URL;
use WebGUI::Utility;
=head1 NAME
@ -459,7 +460,59 @@ sub dateTime {
</script>';
}
#-------------------------------------------------------------------
=head2 dynamicField ( fieldType , hashRef )
Returns a dynamic configurable field.
=over
=item fieldType
The field type to use. The field name is the name of the method from this forms package.
=item options
The field options. See the documentation for the desired field for more information.
=back
=cut
sub dynamicField {
my $fieldType = shift;
my $param = shift;
# Set options for fields that use a list.
if (isIn($fieldType,qw(selectList checkList radioList))) {
delete $param->{size};
my %options;
tie %options, 'Tie::IxHash';
foreach (split(/\n/, $param->{possibleValues})) {
s/\s+$//; # remove trailing spaces
$options{$_} = $_;
}
if (exists $param->{options} && ref($param->{options}) eq "HASH") {
%options = (%{$param->{options}} , %options);
}
$param->{options} = \%options;
}
# Convert value to list for selectList / checkList
if (isIn($fieldType,qw(selectList checkList)) && ref $param->{value} ne "ARRAY") {
my @defaultValues;
foreach (split(/\n/, $param->{value})) {
s/\s+$//; # remove trailing spaces
push(@defaultValues, $_);
}
$param->{value} = \@defaultValues;
}
# Return the appropriate field.
no strict 'refs';
return &$fieldType($param);
}
#-------------------------------------------------------------------

View file

@ -726,33 +726,9 @@ sub dynamicField {
$param{$1} = $param{$key};
delete $param{$key};
}
# Set options for fields that use a list.
if (isIn($fieldType,qw(selectList checkList radioList))) {
delete $param{size};
my %options;
tie %options, 'Tie::IxHash';
foreach (split(/\n/, $param{possibleValues})) {
s/\s+$//; # remove trailing spaces
$options{$_} = $_;
}
$param{options} = \%options;
}
# Convert value to list for selectList / checkList
if (isIn($fieldType,qw(selectList checkList)) && ref $param{value} ne "ARRAY") {
my @defaultValues;
foreach (split(/\n/, $param{value})) {
s/\s+$//; # remove trailing spaces
push(@defaultValues, $_);
}
$param{value} = \@defaultValues;
}
my $cmd = "WebGUI::Form::".$fieldType;
my $output;
if (_uiLevelChecksOut($param{uiLevel})) {
no strict "refs";
$output = &$cmd(\%param);
$output = WebGUI::Form::dynamicField($fieldType, \%param);
$output .= _subtext($param{subtext});
$output = $self->_tableFormRow($param{label},$output);
} else {

View file

@ -18,6 +18,7 @@ package WebGUI::MetaData;
use strict;
use WebGUI::Session;
use WebGUI::SQL;
use WebGUI::Macro;
use Tie::IxHash;
=head1 NAME
@ -42,8 +43,9 @@ These functions/methods are available from this package:
#-------------------------------------------------------------------
=head2 getFieldTypes ( )
=head2 getFieldTypes ()
Returns an array ref with supported metadata field types.
@ -164,6 +166,8 @@ sub getMetaDataFields {
Saves posted metadata for requested wobjectId
=over
=item wobjectId
The Id from the wobject you want to save metadata for.
@ -203,6 +207,8 @@ sub metaDataSave {
Deletes the metadata for requested wobjectId
=over
=item wobjectId
The Id from the wobject you want to delete metadata for.
@ -222,6 +228,8 @@ sub metaDataDelete {
Duplicates Metadata
=over
=item fromWobjectId
The original wobject Id
@ -246,5 +254,128 @@ sub MetaDataDuplicate {
}
#-------------------------------------------------------------------
=head2 getWobjectByCriteria ( hashRef )
This function will search for a wobject that match a metadata criteria set.
If no wobject is found, undef will be returned.
=over
=item hashRef
A typical hashRef for this function will look like:
{
proxiedNamespace => "Article",
resolveMultiples => "random",
proxyCriteria => "State = Wisconsin AND Country != Sauk"
}
Most of the time this will be a:
WebGUI::SQL->quickHashRef("select * from WobjectProxy where wobjectId=$proxiedId");
=back
=cut
sub getWobjectByCriteria {
my $wobjectProxy = shift;
my $criteria = $wobjectProxy->{proxyCriteria};
my $order = $wobjectProxy->{resolveMultiples};
my $namespace = $wobjectProxy->{proxiedNamespace};
my $wobjectId = $wobjectProxy->{wobjectId};
# Once a wobject is found, we will stick to that wobject,
# to prevent the proxying of multiple- depth wobjects like Surveys and USS.
my $scratchId;
if ($wobjectId) {
$scratchId = "WobjectProxy_" . $wobjectId;
if($session{scratch}{$scratchId}) {
return $session{scratch}{$scratchId};
}
}
# $criteria = "State = Wisconsin AND Country != Sauk";
#
# State = Wisconsin AND Country != Sauk
# | | |
# |- $field |_ $operator |- $value
# |_ $attribute |_ $attribute
my $operator = qr/<>|!=|=|>=|<=|>|<|like/i;
my $attribute = qr/['"][^()|=><!]+['"]|[^()|=><!\s]+/i;
my $constraint = $criteria;
# Get each expression from $criteria
foreach my $expression ($criteria =~ /($attribute\s*$operator\s*$attribute)/gi) {
# $expression will match "State = Wisconsin"
my $replacement = $expression; # We don't want to modify $expression.
# We need it later.
# Get the field (State) and the value (Wisconsin) from the $expression.
$expression =~ /($attribute)\s*$operator\s*($attribute)/gi;
my $field = $1;
my $value = $2;
# quote the field / value variables.
my $quotedField = $field;
unless ($field =~ /^\s*['"].*['"]\s*/) {
$quotedField = quote($field);
}
my $quotedValue = WebGUI::Macro::process($value);
unless ($value =~ /^\s*['"].*['"]\s*/) {
$quotedValue = quote($value);
}
# transform replacement from "State = Wisconsin" to
# "(fieldname=State and value = Wisconsin)"
$replacement =~ s/\Q$field/(fieldname=$quotedField and value /;
$replacement =~ s/\Q$value/$quotedValue )/i;
# replace $expression with the new $replacement in $constraint.
$constraint =~ s/\Q$expression/$replacement/;
}
my $sql = " select w.wobjectId
from metaData_data d, metaData_fields f, wobject w
where f.fieldId = d.fieldId
and w.wobjectId = d.wobjectId
and w.namespace = ".quote($namespace);
# Add constraint only if it has been modified.
$sql .= " and ".$constraint if (($constraint ne $criteria) && $constraint ne "");
$sql .= " order by w.lastEdited desc";
# Execute the query with an unconditional read
my @wids;
my $sth = WebGUI::SQL->unconditionalRead($sql);
while (my ($data) = $sth->array) {
push (@wids, $data);
}
$sth->finish;
# No matching wobjects found.
if (scalar(@wids) == 0) {
return undef;
}
my $wid;
# Grab a wid from the results
if ($order = 'random') {
$wid = $wids[ rand @wids ];
} else {
#default order is mostRecent
$wid = $wids[0]; # 1st element in list is most recent.
}
# Store the matching wobjectId in user scratch.
WebGUI::Session::setScratch($scratchId,$wid) if ($scratchId);
return $wid;
}
1;

View file

@ -31,6 +31,7 @@ use WebGUI::Style;
use WebGUI::Template;
use WebGUI::Utility;
use DBIx::Tree::NestedSet;
use WebGUI::MetaData;
our @ISA = qw(DBIx::Tree::NestedSet);
@ -478,7 +479,10 @@ sub generate {
if (${$wobject}{namespace} eq "WobjectProxy") {
my $originalWobject = $wobject;
my ($wobjectProxy) = WebGUI::SQL->quickHashRef("select * from WobjectProxy where wobjectId=".${$wobject}{wobjectId},WebGUI::SQL->getSlave);
$wobject = WebGUI::SQL->quickHashRef("select * from wobject where wobject.wobjectId=".$wobjectProxy->{proxiedWobjectId},WebGUI::SQL->getSlave);
if($wobjectProxy->{proxyByCriteria}) {
$wobjectProxy->{proxiedWobjectId} = WebGUI::MetaData::getWobjectByCriteria($wobjectProxy) || $wobjectProxy->{proxiedWobjectId};
}
$wobject = WebGUI::SQL->quickHashRef("select * from wobject where wobject.wobjectId=".$wobjectProxy->{proxiedWobjectId},WebGUI::SQL->getSlave);
if (${$wobject}{namespace} eq "") {
$wobject = $originalWobject;
} else {

View file

@ -1476,7 +1476,8 @@ sub www_edit {
-uiLevel=>5,
-value=>$meta->{$field}{value},
-extras=>qq/title="$meta->{$field}{description}"/,
-possibleValues=>$meta->{$field}{possibleValues}
-possibleValues=>$meta->{$field}{possibleValues},
-options=>{"", WebGUI::International::get("Select...","MetaData")}
);
}
}

View file

@ -22,6 +22,7 @@ use WebGUI::Page;
use WebGUI::TabForm;
use WebGUI::Template;
use WebGUI::Wobject;
use WebGUI::MetaData;
our @ISA = qw(WebGUI::Wobject);
@ -64,7 +65,19 @@ sub new {
proxiedTemplateId=>{
fieldType=>"template",
defaultValue=>1
}
},
proxyByCriteria=>{
fieldType=>"yesNo",
defaultValue=>0,
},
resolveMultiples=>{
fieldType=>"selectList",
defaultValue=>"mostRecent",
},
proxyCriteria=>{
fieldType=>"textarea",
defaultValue=>"",
},
}
);
bless $self, $class;
@ -111,13 +124,171 @@ sub www_edit {
-label=>WebGUI::International::get(1,$_[0]->get("namespace")),
-value=>'<a href="'.WebGUI::URL::gateway($data[0]).'">'.$data[1].'</a> ('.$_[0]->get("proxiedWobjectId").')'
);
$properties->yesNo(
-name=>"proxyByCriteria",
-value=>$_[0]->getValue("proxyByCriteria"),
-label=>WebGUI::International::get("Proxy by alternate criteria?",$_[0]->get("namespace")),
-extras=>q|Onchange="
if (this.form.proxyByCriteria[0].checked) {
this.form.resolveMultiples.disabled=false;
this.form.proxyCriteria.disabled=false;
} else {
this.form.resolveMultiples.disabled=true;
this.form.proxyCriteria.disabled=true;
}"|
);
my $extras;
if ($_[0]->getValue("proxyByCriteria") == 0) {
$extras = 'disabled=true';
}
$properties->selectList(
-name=>"resolveMultiples",
-value=>[ $_[0]->getValue("resolveMultiples") ],
-label=>WebGUI::International::get("Resolve Multiples?",$_[0]->get("namespace")),
-options=>{
mostRecent=>WebGUI::International::get("Most Recent",$_[0]->get("namespace")),
random=>WebGUI::International::get("Random",$_[0]->get("namespace")),
},
-extras=>$extras
);
$properties->readOnly(
-value=>$_[0]->_drawQueryBuilder(),
-label=>WebGUI::International::get("Criteria",$_[0]->get("namespace")),
);
return $_[0]->SUPER::www_edit(
-properties=>$properties->printRowsOnly,
-layout=>$layout->printRowsOnly,
-headingId=>2,
-helpId=>"wobject proxy add/edit"
);
}
#-------------------------------------------------------------------
sub _drawQueryBuilder {
# Initialize operators
my @textFields = qw|text yesNo selectList radioList|;
my %operator;
foreach (@textFields) {
$operator{$_} = {
"=" => WebGUI::International::get("is",$_[0]->get("namespace")),
"!=" => WebGUI::International::get("isnt",$_[0]->get("namespace"))
};
}
$operator{integer} = {
"=" => WebGUI::International::get("equal to",$_[0]->get("namespace")),
"!=" => WebGUI::International::get("not equal to",$_[0]->get("namespace")),
"<" => WebGUI::International::get("less than",$_[0]->get("namespace")),
">" => WebGUI::International::get("greater than",$_[0]->get("namespace"))
};
# Get the fields and count them
my $fields = WebGUI::MetaData::getMetaDataFields();
my $fieldCount = scalar(keys %$fields);
unless ($fieldCount) { # No fields found....
return 'No metadata defined yet.
<a href="'.WebGUI::URL::page('op=manageMetaData').
'">Click here</a> to define metadata attributes.';
}
# Static form fields
my $proxyCriteriaField = WebGUI::Form::textarea({
name=>"proxyCriteria",
value=>$_[0]->getValue("proxyCriteria"),
});
my $conjunctionField = WebGUI::Form::selectList({
name=>"conjunction",
options=>{
"AND" => WebGUI::International::get("AND",$_[0]->get("namespace")),
"OR" => WebGUI::International::get("OR",$_[0]->get("namespace"))},
value=>["OR"],
extras=>'class="qbselect"',
});
# html
my $output;
$output .= '<script type="text/javascript" language="javascript" src="'.
$session{config}{extrasURL}.'/wobject/WobjectProxy/querybuilder.js"></script>';
$output .= '<link href="'.$session{config}{extrasURL}.
'/wobject/WobjectProxy/querybuilder.css" type="text/css" rel="stylesheet">';
$output .= qq|<table cellspacing="0" cellpadding=0 border=0 >
<tr>
<td colspan="5">$proxyCriteriaField</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="qbtdright">
</td>
</tr>
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="qbtdright">
$conjunctionField
</td>
</tr>
|;
# Here starts the field loop
foreach my $field (keys %$fields) {
my $fieldLabel = $fields->{$field}{fieldName};
my $fieldType = $fields->{$field}{fieldType} || "text";
# The operator select field
my $opFieldName = "op_field".$fields->{$field}{fieldId};
my $opField = WebGUI::Form::selectList({
name=>$opFieldName,
uiLevel=>5,
options=>$operator{$fieldType},
extras=>'class="qbselect"'
});
# The value select field
my $valFieldName = "val_field".$fields->{$field}{fieldId};
my $valueField = WebGUI::Form::dynamicField($fieldType, {
name=>$valFieldName,
uiLevel=>5,
extras=>qq/title="$fields->{$field}{description}" class="qbselect"/,
possibleValues=>$fields->{$field}{possibleValues},
});
# An empty row
$output .= qq|
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="qbtdright"></td>
</tr>
|;
# Table row with field info
$output .= qq|
<tr>
<td class="qbtdleft"><p class="qbfieldLabel">$fieldLabel</p></td>
<td class="qbtd">
$opField
</td>
<td class="qbtd">
<span class="qbText">$valueField</span>
</td>
<td class="qbtd"></td>
<td class="qbtdright">
<input class="qbselect" type=button value=Add onclick="addCriteria('$fieldLabel', this.form.$opFieldName, this.form.$valFieldName)"></td>
</tr>
|;
}
# Close the table
$output .= "</table>";
return $output;
}
#-------------------------------------------------------------------

View file

@ -50,8 +50,10 @@ our $I18N = {
message => q|Enable passive profiling ?|,
lastUpdated=> 1089039511,
},
'Select...' => {
message => q|Select...|,
lastUpdated=> 1089039511,
},
};
1;

View file

@ -79,6 +79,71 @@ Set to "yes" to use the template of the wobject proxy instead of the original te
lastUpdated => 1053183804,
context => q|Asking the user if s/he would like to use the description specified in the wobject proxy or the original description of the original wobject.|
},
'Proxy by alternate criteria?' => {
message => q|Proxy by alternate criteria?|,
lastUpdated => 1053183804,
context => q|Asking the user if s/he would like to use alternate criteria to find a matching a wobject to proxy.|
},
'Resolve Multiples?' => {
message => q|Resolve Multiples?|,
lastUpdated => 1053183804,
context => q|Asking the user what sort order (random / most recent) to use if multiple hits are found.|
},
'Most Recent' => {
message => q|Most Recent|,
lastUpdated => 1053183804,
context => q|Selectlist item for "Resolve Multiples?"|
},
'Random' => {
message => q|Random|,
lastUpdated => 1053183804,
context => q|Selectlist item for "Resolve Multiples?"|
},
'Criteria' => {
message => q|Criteria|,
lastUpdated => 1053183804,
context => q|Label for the criteria textarea|
},
'AND' => {
message => q|AND|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
'OR' => {
message => q|OR|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
'is' => {
message => q|is|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
'isnt' => {
message => q|isn't|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
"less than" => {
message => q|less than|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
"equal to" => {
message => q|equal to|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
"greater than" => {
message => q|greater than|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
"not equal to" => {
message => q|not equal to|,
lastUpdated => 1053183804,
context => q|Part of the WobjectProxy Query Builder|
},
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

View file

@ -0,0 +1,42 @@
.qbtd {
height: 20px;
background-image: url(/extras/wobject/WobjectProxy/hline.gif);
padding-left: 5px;
padding-right: 5px;
}
.qbtdleft {
height: 20px;
background-image: url(/extras/wobject/WobjectProxy/hline.gif);
padding-right: 5px;
}
.qbtdright {
height: 20px;
background-image: url(/extras/wobject/WobjectProxy/vline.gif);
background-repeat: repeat-y;
align: right;
}
.qbfieldLabel {
text-align: left;
font-size: 12px;
border-style: solid;
border-width: 1px;
padding-right: 4px;
padding-left: 4px;
background-color: white;
}
.qbselect {
height: 20px;
font-size: 12px;
}
.qbText {
background-color: white;
text-align: left;
font-size: 12px;
}

View file

@ -0,0 +1,35 @@
function addCriteria ( fieldname, opform, valform ) {
var form = opform.form;
var operator = getValue(opform);
var value = getValue(valform);
var criteria = form.proxyCriteria.value;
var conjunction = "";
var re = /^\s*$/;
if(! re.test(criteria)) {
conjunction = " " + getValue(form.conjunction) + " ";
}
if(/\s+/.test(fieldname)) {
fieldname = '"' + fieldname + '"';
}
var statement = fieldname + " " + operator + " " + '"' + value + '"';
form.proxyCriteria.value = criteria + conjunction + statement;
}
function getValue(sel) {
if(sel.type == "text") {
return sel.value;
}
for(i=0;i<sel.length;i++) {
if(sel[i].type == "radio") {
if(sel[i].checked) {
return sel[i].value;
}
} else {
if(sel.options[i].selected) {
return sel.options[i].value;
}
}
}
return "";
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B