diff --git a/docs/upgrades/upgrade_6.1.1-6.2.0.sql b/docs/upgrades/upgrade_6.1.1-6.2.0.sql index 6c9d738a0..b8e77f1b5 100644 --- a/docs/upgrades/upgrade_6.1.1-6.2.0.sql +++ b/docs/upgrades/upgrade_6.1.1-6.2.0.sql @@ -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; + diff --git a/lib/WebGUI/Form.pm b/lib/WebGUI/Form.pm index c84b01f0a..230b6952f 100644 --- a/lib/WebGUI/Form.pm +++ b/lib/WebGUI/Form.pm @@ -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 { '; } +#------------------------------------------------------------------- +=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); + +} #------------------------------------------------------------------- diff --git a/lib/WebGUI/HTMLForm.pm b/lib/WebGUI/HTMLForm.pm index a223fc4f3..525e5ad8c 100644 --- a/lib/WebGUI/HTMLForm.pm +++ b/lib/WebGUI/HTMLForm.pm @@ -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 { diff --git a/lib/WebGUI/MetaData.pm b/lib/WebGUI/MetaData.pm index 5b9034e00..d67313a6a 100644 --- a/lib/WebGUI/MetaData.pm +++ b/lib/WebGUI/MetaData.pm @@ -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/['"][^()|=>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; diff --git a/lib/WebGUI/Page.pm b/lib/WebGUI/Page.pm index 288d44e36..96d20f723 100644 --- a/lib/WebGUI/Page.pm +++ b/lib/WebGUI/Page.pm @@ -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 { diff --git a/lib/WebGUI/Wobject.pm b/lib/WebGUI/Wobject.pm index 377533f8a..72c630843 100644 --- a/lib/WebGUI/Wobject.pm +++ b/lib/WebGUI/Wobject.pm @@ -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")} ); } } diff --git a/lib/WebGUI/Wobject/WobjectProxy.pm b/lib/WebGUI/Wobject/WobjectProxy.pm index a75987c7d..49974ea87 100644 --- a/lib/WebGUI/Wobject/WobjectProxy.pm +++ b/lib/WebGUI/Wobject/WobjectProxy.pm @@ -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=>''.$data[1].' ('.$_[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. + Click here 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 .= ''; + $output .= ''; + + $output .= qq|
| $proxyCriteriaField | +||||
| + | + | + | + | + | +
| + | + | + | + | + $conjunctionField + | +
| + | + | + | + | + |
$fieldLabel |
+ + $opField + | ++ $valueField + | ++ | + | +