Merge with HEAD, 10472

This commit is contained in:
Colin Kuskie 2009-04-24 17:12:17 +00:00
commit 19f703dc9b
102 changed files with 5700 additions and 2269 deletions

View file

@ -247,7 +247,11 @@ By specifying this method, you activate this feature.
sub getAutoCommitWorkflowId {
my $self = shift;
return $self->getParent->get("submissionApprovalWorkflowId");
if($self->session->form->process("assetId") eq "new"){
return $self->getParent->get("submissionApprovalWorkflowId");
}
return undef;
}
#-------------------------------------------------------------------
@ -312,6 +316,19 @@ sub getEditForm {
hoverHelp =>$i18n->get('maintainer description'),
);
}
else{
my $userId;
if ($func eq "add"){
$userId = $session->user->userId;
}
else{
$userId = $self->get('ownerUserId');
}
$form->hidden(
-name =>'ownerUserId',
-value =>$userId,
);
}
$form->text(
-name =>'version',
-defaultValue =>undef,
@ -492,6 +509,34 @@ sub processPropertiesFromFormPost {
}
$self->update({score => $score});
if ( $self->get('screenshots') ) {
my $fileObject = WebGUI::Form::File->new($self->session,{ value=>$self->get('screenshots') });
my $storage = $fileObject->getStorageLocation;
my @files;
@files = @{ $storage->getFiles } if (defined $storage);
foreach my $file (@files) {
unless ($file =~ m/^thumb-/){
my ($resizeWidth,$resizeHeight);
my ($width, $height) = $storage->getSizeInPixels($file);
my $maxWidth = $self->getParent->get('maxScreenshotWidth');
my $maxHeight = $self->getParent->get('maxScreenshotHeight');
if ($width > $maxWidth){
my $newHeight = $height * ($maxWidth / $width);
if ($newHeight > $maxHeight){
# Heigth requires more resizing so use maxHeight
$storage->resize($file, 0, $maxHeight);
}
else{
$storage->resize($file, $maxWidth);
}
}
elsif($height > $maxHeight){
$storage->resize($file, 0, $maxHeight);
}
}
}
}
$self->requestAutoCommit;
return undef;
}
@ -962,6 +1007,7 @@ sub www_getScreenshots {
@files = @{ $storage->getFiles } if (defined $storage);
foreach my $file (@files) {
unless ($file =~ m/^thumb-/){
my ($width, $height) = $storage->getSizeInPixels($file);
my $thumb = 'thumb-'.$file;
$xml .= "
<slide>
@ -970,6 +1016,8 @@ sub www_getScreenshots {
<image_source>".$storage->getUrl($file)."</image_source>
<duration>5</duration>
<thumb_source>".$storage->getUrl($thumb)."</thumb_source>
<width>".$width."</width>
<height>".$height."</height>
</slide>
";
}

View file

@ -970,6 +970,7 @@ sub postProcess {
my $spamStopWords = $self->session->config->get('spamStopWords');
if (ref $spamStopWords eq 'ARRAY') {
my $spamRegex = join('|',@{$spamStopWords});
$spamRegex =~ s/\s/\\ /g;
if ($data{content} =~ m/$spamRegex/xmsi) {
$data{skipNotification} = 1;
$self->trash;

View file

@ -20,7 +20,7 @@ use base 'WebGUI::Asset';
use WebGUI::International;
use WebGUI::Inbox;
use WebGUI::Shop::Cart;
use JSON qw{ from_json to_json };
=head1 NAME
@ -41,7 +41,6 @@ use WebGUI::Asset::Sku;
$hashRef = $self->getOptions;
$integer = $self->getMaxAllowedInCart;
$float = $self->getPrice;
$float = $self->getTaxRate;
$boolean = $self->isShippingRequired;
$html = $self->processStyle($output);
@ -102,6 +101,7 @@ sub definition {
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, "Asset_Sku");
%properties = (
description => {
@ -125,20 +125,6 @@ sub definition {
label => $i18n->get("display title"),
hoverHelp => $i18n->get("display title help")
},
overrideTaxRate => {
tab => "shop",
fieldType => "yesNo",
defaultValue => 0,
label => $i18n->get("override tax rate"),
hoverHelp => $i18n->get("override tax rate help")
},
taxRateOverride => {
tab => "shop",
fieldType => "float",
defaultValue => 0.00,
label => $i18n->get("tax rate override"),
hoverHelp => $i18n->get("tax rate override help")
},
vendorId => {
tab => "shop",
fieldType => "vendor",
@ -146,6 +132,11 @@ sub definition {
label => $i18n->get("vendor"),
hoverHelp => $i18n->get("vendor help")
},
taxConfiguration => {
noFormPost => 1,
fieldType => 'hidden',
defaultValue => '{}',
},
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
@ -206,6 +197,31 @@ sub getConfiguredTitle {
return $self->getTitle;
}
#-------------------------------------------------------------------
sub getEditForm {
my $self = shift;
my $session = $self->session;
my $tabform = $self->SUPER::getEditForm;
# Let the tax system add the form fields that are required by the active tax plugin for configuring the sku tax.
# WebGUI::Shop::Tax->new( $session )->appendSkuForm( $self->getId, $tabform->getTab('shop') );
my $taxDriver = WebGUI::Shop::Tax->getDriver( $session );
my $definition = $taxDriver->skuFormDefinition;
my $config = $self->getTaxConfiguration( $taxDriver->className );
my $shop = $tabform->getTab( 'shop' );
foreach my $fieldName ( keys %{ $definition } ) {
$shop->dynamicField(
%{ $definition->{ $fieldName } },
name => $fieldName,
value => $config->{ $fieldName },
);
}
return $tabform;
}
#-------------------------------------------------------------------
@ -264,6 +280,22 @@ sub getPrice {
#-------------------------------------------------------------------
=head2 getPostPurchaseActions ( item )
Get a hash reference of LABEL => URL pairs of actions we can do on
this Sku after it is purchased. These will show up in the Transaction
screen. C<item> is the WebGUI::Shop::TransactionItem that was
purchased.
=cut
sub getPostPurchaseActions {
my ( $self, $item ) = @_;
return {};
}
#-------------------------------------------------------------------
=head2 getQuantityAvailable ( )
Returns 99999999. Needs to be overriden by subclasses. Tells the commerce system how many of this item is on hand.
@ -288,16 +320,17 @@ sub getRecurInterval {
}
#-------------------------------------------------------------------
sub getTaxConfiguration {
my $self = shift;
my $namespace = shift;
=head2 getTaxRate ( )
my $configs = eval { from_json( $self->getValue('taxConfiguration') ) };
if ($@) {
$self->session->log->error( 'Tax configuration of asset ' . $self->getId . ' appears to be corrupt. :' . $@ );
return undef;
}
Returns undef unless the "Override tax rate?" switch is set to yes. If it is, then it returns the value of the "Tax Rate Override" field.
=cut
sub getTaxRate {
my $self = shift;
return ($self->get("overrideTaxRate")) ? $self->get("taxRateOverride") : undef;
return $configs->{ $namespace }
}
#-------------------------------------------------------------------
@ -528,6 +561,20 @@ sub onRemoveFromCart {
return undef;
}
#-------------------------------------------------------------------
sub processPropertiesFromFormPost {
my $self = shift;
my $output = $self->SUPER::processPropertiesFromFormPost( @_ );
my $taxDriver = WebGUI::Shop::Tax->new( $self->session )->getDriver;
$self->session->log->fatal( 'Could not instanciate tax driver.' ) unless $taxDriver;
$self->setTaxConfiguration( $taxDriver->className, $taxDriver->processSkuFormPost );
return $output;
}
#-------------------------------------------------------------------
=head2 processStyle ( output )
@ -546,6 +593,28 @@ sub processStyle {
return $self->getParent->processStyle($output);
}
#-------------------------------------------------------------------
sub setTaxConfiguration {
my $self = shift;
my $namespace = shift;
my $configuration = shift;
# Fetch current tax configurations
my $configs = eval { from_json( $self->getValue('taxConfiguration') ) };
if ($@) {
$self->session->log->error( 'Tax configuration of asset ' . $self->getId . ' is corrupt.' );
return undef;
}
# Apply the new configuration for the given driver...
$configs->{ $namespace } = $configuration;
# ...and persist it to the db.
$self->update( {
taxConfiguration => to_json( $configs ),
} );
}
#-------------------------------------------------------------------
=head2 www_view ( )

View file

@ -0,0 +1,708 @@
package WebGUI::Asset::Sku::ThingyRecord;
=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 Tie::IxHash;
use base 'WebGUI::Asset::Sku';
use WebGUI::Utility;
# Collateral data class... very long name. Zoffix eat your heart out.
my $RECORD_CLASS = 'WebGUI::AssetCollateral::Sku::ThingyRecord::Record';
=head1 NAME
Package WebGUI::Asset::Sku::ThingyRecord
=head1 DESCRIPTION
Purchase a record in a thingy.
=head1 SYNOPSIS
use WebGUI::Asset::ThingyRecord;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
=head3 session
=head3 definition
A hash reference passed in from a subclass definition.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
tie my %properties, 'Tie::IxHash', (
templateIdView => {
tab => "display",
fieldType => "template",
namespace => "ThingyRecord/View",
label => $i18n->get('templateIdView label'),
hoverHelp => $i18n->get('templateIdView description'),
},
thingId => {
tab => "properties",
fieldType => "selectBox",
options => $class->getThingOptions($session),
label => $i18n->get('thingId label'),
hoverHelp => $i18n->get('thingId description'),
extras => q{onchange="WebGUI.ThingyRecord.getThingFields(this.options[this.selectedIndex].value,'thingFields_formId')"},
},
thingFields => {
tab => "properties",
fieldType => "selectList",
options => {}, # populated by ajax call
label => $i18n->get('thingFields label'),
hoverHelp => $i18n->get('thingFields description'),
},
thankYouText => {
tab => "properties",
fieldType => "HTMLArea",
defaultValue=> $i18n->get('default thank you message','Asset_Product') . " ^ViewCart;",
label => $i18n->get("thank you message",'Asset_Product'),
hoverHelp => $i18n->get("thank you message help",'Asset_Product'),
},
price => {
tab => "properties",
fieldType => "float",
label => $i18n->get('10',"Asset_Product"), #Price
hoverHelp => $i18n->get('price','Asset_Product'),
},
duration => {
tab => "properties",
fieldType => "interval",
defaultValue=> 60*60*24*7, # One week
label => $i18n->get('duration label'),
hoverHelp => $i18n->get('duration description'),
},
);
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'ThingyRecord.gif',
autoGenerateForms => 1,
tableName => 'ThingyRecord',
className => __PACKAGE__,
properties => \%properties,
};
return $class->SUPER::definition( $session, $definition );
} ## end sub definition
#----------------------------------------------------------------------------
=head2 appendVarsEditRecord ( var, recordId )
Get the template variables for the form to edit the record. Does not include
the header or footer!
=cut
sub appendVarsEditRecord {
my ( $self, $var, $recordId ) = @_;
my $session = $self->session;
my $thingy = $self->getThingy;
my $record = {};
if ( $recordId ) {
# Get an existing record
$record = $self->getThingRecord( $self->get('thingId'), $recordId );
if ( !%$record ) { # Record is hidden
$record = JSON->new->decode(
$RECORD_CLASS->new( $session, $recordId )->get('fields')
);
}
}
my $fields = $self->getThingFields( $self->get('thingId') );
my @allowed = split "\n", $self->get('thingFields');
for my $field ( @{$fields} ) {
next unless grep { $_ eq $field->{fieldId} } @allowed;
$field->{value} = $record->{'field_'.$field->{fieldId}} || $field->{defaultValue};
my %fieldProperties = (
"input" => $thingy->getFormElement($field),
"value" => $thingy->getFieldValue($field->{value}, $field),
"label" => $field->{label},
"isHidden" => ($field->{status} eq 'hidden'),
"isVisible" => ($field->{status} eq "visible"),
"isRequired" => ($field->{status} eq "required"),
"pretext" => $field->{pretext},
"subtext" => $field->{subtext},
);
push @{$var->{form_fields}}, {
map { "field_" . $_ => $fieldProperties{$_} } keys %fieldProperties
};
# Add a way to get the field outside of the loop
# TODO
}
return $var;
}
#-------------------------------------------------------------------
=head2 deleteThingRecord ( thingId, recordId )
Delete a record from a thing
=cut
sub deleteThingRecord {
my ( $self, $thingId, $recordId ) = @_;
my $db = $self->session->db;
my $dbh = $self->session->db->dbh;
my $tableName = $dbh->quote_identifier( 'Thingy_' . $thingId );
$db->write(
"DELETE FROM $tableName WHERE thingDataId=?",
[$recordId]
);
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Add the javascript needed for the edit form
=cut
sub getEditForm {
my ( $self ) = @_;
$self->session->style->setScript(
$self->session->url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'),
{ type => "text/javascript" },
);
$self->session->style->setScript(
$self->session->url->extras('yui/build/connection/connection-min.js'),
{ type => "text/javascript" },
);
$self->session->style->setScript(
$self->session->url->extras('yui/build/json/json-min.js'),
{ type => "text/javascript" },
);
$self->session->style->setScript(
$self->session->url->extras('yui-webgui/build/thingyRecord/thingyRecord.js'),
{ type => "text/javascript" },
);
return $self->SUPER::getEditForm;
}
#----------------------------------------------------------------------------
=head2 getMaxAllowedInCart ( )
One only!
=cut
sub getMaxAllowedInCart {
my ( $self ) = @_;
return 1;
}
#----------------------------------------------------------------------------
=head2 getPostPurchaseActions ( item )
Return a hash reference of "label" => "url" to do things with this item after
it is purchased. C<item> is the WebGUI::Shop::TransactionItem for this item
=cut
sub getPostPurchaseActions {
my ( $self, $item ) = @_;
my $session = $self->session;
my $opts = $self->SUPER::getPostPurchaseActions();
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
my $recordId = $item->get('options')->{recordId};
$opts->{ $i18n->get('renew') }
= $self->getUrl('func=renew;recordId='.$recordId);
$opts->{ $i18n->get('575', 'WebGUI') } # edit
= $self->getUrl('func=editRecord;recordid='.$recordId);
return $opts;
}
#----------------------------------------------------------------------------
=head2 getPrice ( )
Get the price
=cut
sub getPrice {
my ( $self ) = @_;
return $self->get('price');
}
#----------------------------------------------------------------------------
=head2 getTemplateVars ( )
Get common template vars for this asset.
=cut
sub getTemplateVars {
my $self = shift;
my $var = $self->get;
$var->{ url } = $self->getUrl;
return $var;
}
#----------------------------------------------------------------------------
=head2 getThingFields ( thingId )
Get the fields for a thing.
=cut
sub getThingFields {
my ( $self, $thingId ) = @_;
my $fields = $self->session->db->buildArrayRefOfHashRefs(
'SELECT * FROM Thingy_fields WHERE thingId = ? ORDER BY sequenceNumber',
[$thingId]
);
return $fields;
}
#----------------------------------------------------------------------------
=head2 getThingOptions ( session )
Get all the thingys and all the things in them.
=cut
sub getThingOptions {
my ( $class, $session ) = @_;
tie my %options, 'Tie::IxHash', ( "" => "" );
my $thingyIter = WebGUI::Asset->getRoot( $session )
->getLineageIterator( ['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::Wobject::Thingy'],
} );
while ( my $thingy = $thingyIter->() ) {
tie my %things, 'Tie::IxHash', (
$session->db->buildHash(
"SELECT thingId, label FROM Thingy_things WHERE assetId=?",
[$thingy->getId]
)
);
$options{$thingy->get('title')} = \%things;
}
return \%options;
}
#----------------------------------------------------------------------------
=head2 getThingRecord ( thingId, recordId )
Get a row of data from a thing. Returns a hashref
=cut
sub getThingRecord {
my ( $self, $thingId, $recordId ) = @_;
my $table = $self->session->db->dbh->quote_identifier( "Thingy_" . $thingId );
return $self->session->db->quickHashRef(
"SELECT * FROM " . $table . " WHERE thingDataId=?",
[$recordId]
);
}
#----------------------------------------------------------------------------
=head2 getThingy ( )
Get the thingy associated with this ThingyRecord
=cut
sub getThingy {
my ( $self ) = @_;
my $thingyId = $self->session->db->quickScalar(
"SELECT assetId FROM Thingy_things WHERE thingId=?",
[$self->get('thingId')],
);
return WebGUI::Asset->newByDynamicClass( $self->session, $thingyId );
}
#-------------------------------------------------------------------
=head2 onCompletePurchase ( )
Purchase completed, add the record.
=cut
sub onCompletePurchase {
my ( $self, $item ) = @_;
my $option = $self->getOptions;
my $record = $RECORD_CLASS->new( $self->session, $option->{recordId} );
my $now = time;
if ( $option->{action} eq "buy" ) {
# Update record
$record->update({
expires => $now + $self->get('duration'),
transactionId => $item->transaction->getId,
isHidden => 0,
});
# Add to thingy data
my $data = JSON->new->decode( $record->get('fields') );
$self->updateThingRecord( $self->get('thingId'), $record->getId, $data );
}
elsif ( $option->{action} eq "renew" ) {
# Renew a currently active record
if ( $record->get('expires') > $now ) {
$record->update({
expires => $record->get('expires') + $self->get('duration'),
});
}
# Renew an expired but not deleted record
else {
$record->update({
expires => $now + $self->get('duration'),
isHidden => 0,
});
# Add to thingy data
my $data = JSON->new->decode( $record->get('fields') );
$self->updateThingRecord( $self->get('thingId'), $record->getId, $data );
}
}
}
#-------------------------------------------------------------------
=head2 onRemoveFromCart ( )
Removed from cart, remove all knowledge
=cut
sub onRemoveFromCart {
my ( $self, $item ) = @_;
# Remove from cart
my $option = $self->getOptions;
if ( $option->{action} eq "buy" ) {
my $record = $RECORD_CLASS->new($self->session,$option->{recordId});
if ( $record ) {
$record->delete;
}
}
}
#-------------------------------------------------------------------
=head2 prepareView ( )
See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new( $self->session, $self->get("templateIdView") );
$template->prepare($self->getMetaDataAsTemplateVariables);
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 processEditRecordForm ( )
Process the edit record form and return the record
=cut
sub processEditRecordForm {
my ( $self ) = @_;
my $var = {};
my $fields = $self->getThingFields( $self->get('thingId') );
for my $field ( @{$fields} ) {
my $fieldName = 'field_'.$field->{fieldId};
my $fieldType = $field->{fieldType};
$fieldType = "" if ($fieldType =~ m/^otherThing/x);
$var->{ $fieldName }
= $self->session->form->get($fieldName,$fieldType,$field->{defaultValue},$field);
}
return $var;
}
#-------------------------------------------------------------------
=head2 purge ( )
Remove all collateral associated with the ThingyRecord sku
=cut
sub purge {
my $self = shift;
my $options = {
constraints => {
'assetId = ?' => $self->getId,
},
};
my $iter = $RECORD_CLASS->getAllIterator($self->session,$options);
while ( my $item = $iter->() ) {
$item->delete;
}
# Should we also remove the records from the Thingy?
return $self->SUPER::purge;
}
#-------------------------------------------------------------------
=head2 updateThingRecord ( thingId, data )
Update data in a thing
=cut
sub updateThingRecord {
my ( $self, $thingId, $recordId, $data ) = @_;
my $db = $self->session->db;
my $dbh = $self->session->db->dbh;
my $tableName = $dbh->quote_identifier('Thingy_'.$thingId);
$data->{ thingDataId } = $recordId;
my $columns = join ",", map { $dbh->quote_identifier( $_ ) } keys %{$data};
my $values = [ values %{$data} ];
my $places = join ",", ('?') x @{$values};
$self->session->db->write(
"REPLACE INTO $tableName ($columns) VALUES ($places)",
$values,
);
}
#-------------------------------------------------------------------
=head2 view ( options )
method called by the container www_view method.
=cut
sub view {
my ( $self, $options ) = @_;
my $session = $self->session;
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
my $var = $self->getTemplateVars;
$self->appendVarsEditRecord( $var );
$var->{ isNew } = 1;
$var->{ message } = $options->{addedToCart}
? $self->get('thankYouText')
: $options->{message}
;
# Add form header, footer, and submit button
$var->{ form_header }
= WebGUI::Form::formHeader( $session, {
action => $self->getUrl('func=buy'),
} );
$var->{ form_footer }
= WebGUI::Form::formFooter( $session );
$var->{ form_submit }
= WebGUI::Form::submit( $session, {
value => $i18n->get('add to cart','Shop'),
} );
return $self->processTemplate( $var, undef, $self->{_viewTemplate} );
}
#----------------------------------------------------------------------------
=head2 www_buy ( )
Create a new record and add it to the cart
=cut
sub www_buy {
my ( $self ) = @_;
my $session = $self->session;
# Get data for row
my $recordFields = $self->processEditRecordForm;
my $recordData = {
userId => $session->user->userId,
assetId => $self->getId,
fields => JSON->new->encode( $recordFields ),
};
# Add row to cart collateral
my $record = $RECORD_CLASS->create( $session, $recordData );
# Add item to cart with appropriate action and recordId
$self->addToCart({
action => "buy",
recordId => $record->getId,
});
# Return thank you screen
$self->prepareView;
return $self->processStyle(
$self->view({ addedToCart => 1 })
);
}
#----------------------------------------------------------------------------
=head2 www_editRecord ( options )
Edit the record after is has been purchased. Allow the user to show/hide the
record while it is still active.
=cut
sub www_editRecord {
my ( $self, $options ) = @_;
my $session = $self->session;
my $recordId = $session->form->get( 'recordId' );
my $record = $RECORD_CLASS->new( $session, $recordId );
return $self->session->privilege->insufficient
unless $self->session->user->userId eq $record->get('userId');
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
my $var = $self->getTemplateVars;
$self->appendVarsEditRecord( $var, $recordId );
$var->{ message } = $options->{message};
# Add form header, footer, and submit button
$var->{ form_header }
= WebGUI::Form::formHeader( $session, {
action => $self->getUrl('func=editRecordSave;recordId=' . $recordId),
} );
$var->{ form_footer }
= WebGUI::Form::formFooter( $session );
$var->{ form_submit }
= WebGUI::Form::submit( $session, {
value => $i18n->get('save','WebGUI'),
} );
# Add record information
my $recordData = $record->get;
for my $key ( keys %{$recordData} ) {
$var->{ "record_" . $key } = $recordData->{ $key };
}
# Add field to hide/show
# Don't allow user to show expired record
if ( time < $record->get('expires') ) {
$var->{ form_hide }
= WebGUI::Form::yesNo( $session, {
name => "hide",
value => $record->get('isHidden'),
} );
}
return $self->processStyle(
$self->processTemplate( $var, $self->get('templateIdView') )
);
}
#----------------------------------------------------------------------------
=head2 www_editRecordSave ( )
Save the record
=cut
sub www_editRecordSave {
my ( $self ) = @_;
my $session = $self->session;
my $form = $self->session->form;
my $recordId = $form->get('recordId');
my $record = $RECORD_CLASS->new( $session, $recordId );
return $self->session->privilege->insufficient
unless $self->session->user->userId eq $record->get('userId');
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
my $hide = $form->get('hide');
my $recordData = $self->processEditRecordForm;
$record->update({
fields => JSON->new->encode( $recordData ),
isHidden => $hide,
});
if ( $hide ) {
$self->deleteThingRecord( $self->get('thingId'), $recordId );
}
else {
$self->updateThingRecord( $self->get('thingId'), $recordId, $recordData );
}
return $self->www_editRecord({ message => $i18n->get('saved') });
}
#----------------------------------------------------------------------------
=head2 www_renew ( )
Add more time to an existing record.
=cut
sub www_renew {
my ( $self ) = @_;
my $session = $self->session;
my $i18n = WebGUI::International->new( $session, "Asset_ThingyRecord" );
my $recordId = $self->session->form->get('recordId');
my $record = $RECORD_CLASS->new( $session, $recordId );
return $session->privilege->insufficient
unless $session->user->userId eq $record->get('userId');
$self->addToCart({
action => "renew",
recordId => $recordId,
});
return $self->www_editRecord({ message => $i18n->get('renewal added to cart') . ' ^ViewCart;' });
}
1;
#vim:ft=perl

View file

@ -125,6 +125,22 @@ sub getAutoCommitWorkflowId {
my $self = shift;
my $wiki = $self->getWiki;
if ($wiki->hasBeenCommitted) {
# delete spam
my $spamStopWords = $self->session->config->get('spamStopWords');
if (ref $spamStopWords eq 'ARRAY') {
my $spamRegex = join('|',@{$spamStopWords});
$spamRegex =~ s/\s/\\ /g;
if ($self->get('content') =~ m{$spamRegex}xmsi) {
my $tag = WebGUI::VersionTag->new($self->session, $self->get('tagId'));
$self->purgeRevision;
if ($tag->getAssetCount == 0) {
$tag->rollback;
}
return undef;
}
}
return $wiki->get('approvalWorkflow')
|| $self->session->setting->get('defaultVersionTagWorkflow');
}
@ -276,7 +292,6 @@ sub processPropertiesFromFormPost {
}
}
}
}
#-------------------------------------------------------------------

View file

@ -380,10 +380,17 @@ An HTML blob to be parsed into the current style.
=cut
sub processStyle {
my $self = shift;
my $output = shift;
$self->session->style->setRawHeadTags($self->getExtraHeadTags);
return $self->session->style->process($output,$self->get("styleTemplateId"));
my ($self, $output) = @_;
my $session = $self->session;
my $style = $session->style;
$style->setRawHeadTags($self->getExtraHeadTags);
if ($self->get('synopsis')) {
$style->setMeta({
name => 'Description',
content => $self->get('synopsis'),
});
}
return $style->process($output,$self->get("styleTemplateId"));
}

View file

@ -1532,11 +1532,26 @@ sub www_process {
my $default = $field->{defaultValue};
WebGUI::Macro::process($self->session, \$default);
my $value = $entry->field( $field->{name} ) || $default;
# WebGUI::Form::Integer::getValue() returns 0 even if no number is passed in.
# Not really a suitable default if we want to trigger the error message
if ($field->{status} eq "required" || $field->{status} eq "editable") {
# get the raw value (by sending field type as blank)
my $rawValue = $session->form->process($field->{name}, '');
$value = $session->form->process($field->{name}, $field->{type}, undef, {
defaultValue => $default,
value => $value,
});
# this is a hack, but it's better than changing the default getValue() of Integer, which
# could have massive effects downstream in other uses.
if(($field->{type} =~ /integer/i) && defined($rawValue) && ($rawValue eq '') && ($value eq "0")) {
$value = $rawValue;
}
WebGUI::Macro::filter(\$value);
}
if ($field->{status} eq "required" && (! defined($value) || $value =~ /^\s*$/)) {

View file

@ -175,6 +175,7 @@ sub prepareView {
}
my %vars;
$vars{showAdmin} = ($session->var->isAdminOn && $self->canEdit && $self->canEditIfLocked);
my $splitter = $self->{_viewSplitter} = $self->getSeparator;
@ -194,9 +195,12 @@ sub prepareView {
$child->prepareView;
$placeHolder{$assetId} = $child;
push @children, {
id => $assetId,
isUncommitted => $child->get('status') eq 'pending',
content => $splitter . $assetId . '~~',
id => $assetId,
isUncommitted => $child->get('status') eq 'pending',
content => $splitter . $assetId . '~~',
};
if ($vars{showAdmin}) {
$children[-1]->{'dragger.icon'} = sprintf '<div id="td%s_handle" class="dragable"><div class="dragTrigger dragTriggerWrap">%s</div></div>', $assetId, $session->icon->drag('class="dragTrigger"');
};
}
@ -230,7 +234,6 @@ sub prepareView {
unshift @{ $vars{"position1_loop"} }, reverse @children;
}
$vars{showAdmin} = ($session->var->isAdminOn && $self->canEdit && $self->canEditIfLocked);
if ($vars{showAdmin}) {
# under normal circumstances we don't put HTML stuff in our code, but this will make it much easier
# for end users to work with our templates
@ -247,7 +250,6 @@ sub prepareView {
}
</style>
');
$vars{"dragger.icon"} = '<div class="dragTrigger dragTriggerWrap">'.$session->icon->drag('class="dragTrigger"').'</div>';
$vars{"dragger.init"} = '
<iframe id="dragSubmitter" style="display: none;" src="'.$session->url->extras('spacer.gif').'"></iframe>
<script type="text/javascript">

View file

@ -194,6 +194,20 @@ sub definition {
hoverHelp =>$i18n->get('compare color yes description'),
label =>$i18n->get('compare color yes label'),
},
maxScreenshotWidth=>{
fieldType =>"integer",
tab =>"display",
defaultValue =>"800",
hoverHelp =>$i18n->get('max screenshot width description'),
label =>$i18n->get('max screenshot width label'),
},
maxScreenshotHeight=>{
fieldType =>"integer",
tab =>"display",
defaultValue =>"600",
hoverHelp =>$i18n->get('max screenshot height description'),
label =>$i18n->get('max screenshot height label'),
},
categories=>{
fieldType =>"textarea",
tab =>"properties",

View file

@ -197,8 +197,6 @@ sub definition {
fieldType => 'workflow',
label => 'Survey End Workflow',
hoverHelp => 'Workflow to run when user completes the Survey',
# label => $i18n->get('editForm workflowIdAddEntry label'),
# hoverHelp => $i18n->get('editForm workflowIdAddEntry description'),
none => 1,
},
quizModeSummary => {
@ -207,13 +205,16 @@ sub definition {
tab => 'properties',
label => $i18n->get('Quiz mode summaries'),
hoverHelp => $i18n->get('Quiz mode summaries help'),
}
},
allowBackBtn => {
fieldType => 'yesNo',
defaultValue => 0,
tab => 'properties',
label => $i18n->get('Allow back button'),
hoverHelp => $i18n->get('Allow back button help'),
},
);
#my $defaultMC = $session->
#%properties = ();
push @{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'survey.gif',
@ -800,7 +801,7 @@ sub www_loadSurvey {
elsif ( $lastType eq 'question' ) {
$q = 1;
}
$html .= "<li id='$scount' class='section'>S" . ( $scount + 1 ) . ": $_->{text}<\/li><br>\n";
$html .= "<li id='$scount' class='section'>S" . ( $scount + 1 ) . ": $_->{text}<\/li>\n";
push( @ids, $scount );
}
elsif ( $_->{type} eq 'question' ) {
@ -808,7 +809,7 @@ sub www_loadSurvey {
if ( $lastType eq 'answer' ) {
$a = 1;
}
$html .= "<li id='$scount-$qcount' class='question'>Q" . ( $qcount + 1 ) . ": $_->{text}<\/li><br>\n";
$html .= "<li id='$scount-$qcount' class='question'>Q" . ( $qcount + 1 ) . ": $_->{text}<\/li>\n";
push @ids, "$scount-$qcount";
$lastType = 'question';
$acount = -1;
@ -818,12 +819,12 @@ sub www_loadSurvey {
$html
.= "<li id='$scount-$qcount-$acount' class='answer'>A"
. ( $acount + 1 )
. ": $_->{text}<\/li><br>\n";
. ": $_->{text}<\/li>\n";
push @ids, "$scount-$qcount-$acount";
$lastType = 'answer';
}
}
$html = "<ul class='draglist'>$html</ul>";
my $warnings = $self->surveyJSON->validateSurvey();
my $return = {
@ -1154,6 +1155,41 @@ sub www_submitQuestions {
}
#-------------------------------------------------------------------
=head2 www_goBack
Handles the Survey back button
=cut
sub www_goBack {
my $self = shift;
if ( !$self->canTakeSurvey() ) {
$self->session->log->debug('canTakeSurvey false, surveyEnd');
return $self->surveyEnd();
}
my $responseId = $self->responseId();
if ( !$responseId ) {
$self->session->log->debug('No response id, surveyEnd');
return $self->surveyEnd();
}
if ( !$self->get('allowBackBtn') ) {
$self->session->log->debug('allowBackBtn false, delegating to www_loadQuestions');
return $self->www_loadQuestions();
}
$self->responseJSON->pop;
$self->persistResponseJSON;
return $self->www_loadQuestions();
}
#-------------------------------------------------------------------
=head2 getSummary
@ -1305,15 +1341,8 @@ Sends the processed template and questions structure to the client
sub prepareShowSurveyTemplate {
my ( $self, $section, $questions ) = @_;
# my %multipleChoice = (
# 'Multiple Choice', 1, 'Gender', 1, 'Yes/No', 1, 'True/False', 1, 'Ideology', 1,
# 'Race', 1, 'Party', 1, 'Education', 1, 'Scale', 1, 'Agree/Disagree', 1,
# 'Oppose/Support', 1, 'Importance', 1, 'Likelihood', 1, 'Certainty', 1, 'Satisfaction', 1,
# 'Confidence', 1, 'Effectiveness', 1, 'Concern', 1, 'Risk', 1, 'Threat', 1,
# 'Security', 1
# );
my %textArea = ( 'TextArea', 1 );
my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1 );
my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1, 'Number', 1 );
my %slider = ( 'Slider', 1, 'Dual Slider - Range', 1, 'Multi Slider - Allocate', 1 );
my %dateType = ( 'Date', 1, 'Date Range', 1 );
my %dateShort = ( 'Year Month', 1 );
@ -1379,6 +1408,7 @@ sub prepareShowSurveyTemplate {
if(scalar @{$questions} == ($section->{totalQuestions} - $section->{questionsAnswered})){
$section->{isLastPage} = 1
}
$section->{allowBackBtn} = $self->get('allowBackBtn');
my $out = $self->processTemplate( $section, $self->get('surveyQuestionsId') );

View file

@ -54,7 +54,7 @@ sub value {
if (my $other_instance = $other_instances->{$asset_spec}) {
my $values = $other_instance->{values};
my $value = $values->{$key};
$session->log->debug("[$asset_spec, $key] resolves to [$value]");
$session->log->debug("value($asset_spec, $key) resolves to [$value]");
return $value;
} else {
# Throw an exception, triggering run() to resolve the external reference and re-run
@ -63,7 +63,7 @@ sub value {
}
my $key = shift;
my $value = $values->{$key};
$session->log->debug("[$key] resolves to [$value]");
$session->log->debug("value($key) resolves to [$value]");
return $value; # scalar variable, so no need to clone
}
@ -85,7 +85,7 @@ sub score {
if (my $other_instance = $other_instances->{$asset_spec}) {
my $scores = $other_instance->{scores};
my $score = $scores->{$key};
$session->log->debug("[$asset_spec, $key] resolves to [$score]");
$session->log->debug("score($asset_spec, $key) resolves to [$score]");
return $score;
} else {
# Throw an exception, triggering run() to resolve the external reference and re-run
@ -94,7 +94,7 @@ sub score {
}
my $key = shift;
my $score = $scores->{$key};
$session->log->debug("[$key] resolves to [$score]");
$session->log->debug("score($key) resolves to [$score]");
return $score; # scalar variable, so no need to clone
}

View file

@ -39,59 +39,6 @@ number of questions answered (L<"questionsAnswered">) and the Survey start time
This package is not intended to be used by any other Asset in WebGUI.
=head2 surveyOrder
This data strucutre is an array (reference) of Survey addresses (see
L<WebGUI::Asset::Wobject::Survey::SurveyJSON/Address Parameter>), stored in the order
in which items are presented to the user.
By making use of L<WebGUI::Asset::Wobject::Survey::SurveyJSON> methods which expect address params as
arguments, you can access Section/Question/Answer items in order by iterating over surveyOrder.
For example:
# Access sections in order..
for my $address (@{ $self->surveyOrder }) {
my $section = $self->survey->section( $address );
# etc..
}
In general, the surveyOrder data structure looks like:
[ $sectionIndex, $questionIndex, [ $answerIndex1, $answerIndex2, ....]
There is one array element for every section and address in the survey. If there are
no questions, or no addresses, those array elements will not be present.
=head2 responses
This data structure stores a snapshot of all question responses. Both question data and answer data
is stored in this hash reference.
Questions keys are constructed by hypenating the relevant L<"sIndex"> and L<"qIndex">.
Answer keys are constructed by hypenating the relevant L<"sIndex">, L<"qIndex"> and L<aIndex|"aIndexes">.
Question entries only contain a comment field:
{
...
questionId => {
comment => "question comment",
}
...
}
Answers entries contain: value (the recorded value), time and comment fields.
{
...
answerId => {
value => "recorded answer value",
time => time(),
comment => "answer comment",
},
...
}
=cut
use strict;
@ -252,7 +199,7 @@ sub hasTimedOut{
=head2 lastResponse ([ $responseIndex ])
Mutator. The lastResponse property represents the index of the most recent surveyOrder entry shown.
Mutator. The lastResponse property represents the surveyOrder index of the most recent item shown.
This method returns (and optionally sets) the value of lastResponse.
@ -325,8 +272,32 @@ sub startTime {
=head2 surveyOrder
Accessor for surveyOrder (see L<"surveyOrder">).
Initialized on first access via L<"initSurveyOrder">.
Accessor. Initialized on first access via L<"initSurveyOrder">.
This data strucutre represents the list of items that are shown to the user, in the order
that they will be shown (ignoring jumps and jump expressions).
Typically each item will correspond to a question, and contains enough information to look
up both the corresponding section and all contained answers (if any).
Empty sections also appear in the list.
Each element of the array is an address, similar in structure to
L<WebGUI::Asset::Wobject::Survey::SurveyJSON/Address Parameter>,
except that instead of an answerIndex in the third slot, we have a sub-array of all contained answer indicies.
[ $sectionIndex, $questionIndex, [ $answerIndex1, $answerIndex2, ....]
By making use of L<WebGUI::Asset::Wobject::Survey::SurveyJSON> methods which expect address params as
arguments, you can access Section/Question/Answer items in order by iterating over surveyOrder.
For example:
# Access sections in order..
for my $address (@{ $self->surveyOrder }) {
my $section = $self->survey->section( $address );
# etc..
}
=cut
@ -489,7 +460,6 @@ sub recordResponses {
$gotoExpression = $section->{gotoExpression};
}
# Handle empty Section..
if ( !@questions ) {
# No questions to process, so increment lastResponse and return
@ -526,9 +496,22 @@ sub recordResponses {
# Pluck the values out of the responses hash that we want to record..
my $submittedAnswerResponse = $submittedResponses->{ $answer->{id} };
my $submittedAnswerComment = $submittedResponses->{ $answer->{id} . 'comment' };
my $submittedAnswerVerbatim = $submittedResponses->{ $answer->{id} . 'verbatim' };
# Proceed if we're satisfied that the submitted answer response is valid..
if ( defined $submittedAnswerResponse && $submittedAnswerResponse =~ /\S/ ) {
#Validate answers met question criteria
if($question->{questionType} eq 'Number'){
if($answer->{max} =~ /\d/ and $submittedAnswerResponse > $answer->{max}){
next;
}elsif($answer->{min} =~ /\d/ and $submittedAnswerResponse < $answer->{min}){
next;
}elsif($answer->{step} =~ /\d/ and $submittedAnswerResponse % $answer->{step} != 0){
next;
}
}
$aAnswered = 1;
# Now, decide what to record. For multi-choice questions, use recordedAnswer.
@ -537,9 +520,10 @@ sub recordResponses {
= $knownTypes{ $question->{questionType} }
? $submittedAnswerResponse
: $answer->{recordedAnswer};
$self->responses->{ $answer->{id} }->{time} = time;
$self->responses->{ $answer->{id} }->{comment} = $submittedAnswerComment;
$self->responses->{ $answer->{id} }->{verbatim} = $answer->{verbatim} ? $submittedAnswerVerbatim : undef;
$self->responses->{ $answer->{id} }->{time} = time;
$self->responses->{ $answer->{id} }->{comment} = $submittedAnswerComment;
# Handle terminal Answers..
if ( $answer->{terminal} ) {
@ -609,6 +593,23 @@ A variable name to match against all section and question variable names.
sub processGoto {
my $self = shift;
my ($goto) = validate_pos(@_, {type => SCALAR});
if ($goto eq 'NEXT_SECTION') {
$self->session->log->debug("NEXT_SECTION jump target encountered");
my $lastResponseSectionIndex = $self->lastResponseSectionIndex;
# Increment lastRepsonse until nextResponseSectionIndex moves
while ($self->nextResponseSectionIndex == $lastResponseSectionIndex) {
$self->lastResponse( $self->lastResponse + 1);
}
return;
}
if ($goto eq 'END_SURVEY') {
$self->session->log->debug("END_SURVEY jump target encountered");
$self->lastResponse( scalar( @{ $self->surveyOrder} ) - 1 );
return;
}
# Iterate over items in order..
my $itemIndex = 0;
@ -714,17 +715,31 @@ sub recordedResponses{
#-------------------------------------------------------------------
=head2 responseValuesByVariableName
=head2 responseValuesByVariableName ( $options )
Returns a lookup table to question variable names and recorded response values.
Only questions with a defined variable name set are included. Values come from
the L<responses> hash.
=head3 options
The following options are supported:
=over 3
=item * useText
For multiple choice questions, use the answer text instead of the recorded value
(useful for doing [[var]] text substitution
=back
=cut
sub responseValuesByVariableName {
my $self = shift;
my %options = validate(@_, { useText => 0 });
my %lookup;
while (my ($address, $response) = each %{$self->responses}) {
@ -742,14 +757,23 @@ sub responseValuesByVariableName {
# Filter out questions without defined variable names
next if !$question || !defined $question->{variable};
#Test if question is a multiple choice type so we can use the answer text instead
my $answerText;
if($self->survey->getMultiChoiceBundle($question->{questionType})){
$answerText = $self->survey->answer([@address])->{text};
my $value = $response->{value};
if ($options{useText}) {
# Test if question is a multiple choice type so we can use the answer text instead
if($self->survey->getMultiChoiceBundle($question->{questionType})){
my $answer = $self->survey->answer([@address]);
my $answerText = $answer->{text};
# For verbatim mc answers, combine answer text and recorded value
if ($answer->{verbatim}) {
$answerText = "$answerText - \"$response->{verbatim}\"";
}
$value = $answerText ? $answerText : $value;
}
}
# Add variable => value to our hash
$lookup{$question->{variable}} = $answerText ? $answerText : $response->{value};
$lookup{$question->{variable}} = $value;
}
return \%lookup;
}
@ -885,7 +909,7 @@ sub nextQuestions {
my $questionsPerPage = $self->survey->section( [ $self->nextResponseSectionIndex ] )->{questionsPerPage};
# Get all of the existing question responses (so that we can do Section and Question [[var]] replacements
my $responseValuesByVariableName = $self->responseValuesByVariableName();
my $responseValuesByVariableName = $self->responseValuesByVariableName( { useText => 1 } );
# Do text replacement
$section->{text} = $self->getTemplatedText($section->{text}, $responseValuesByVariableName);
@ -1230,11 +1254,32 @@ sub response {
return $self->{_response};
}
#-------------------------------------------------------------------
=head2 responses
Mutator for the L<"responses"> property.
Mutator. Note, this is an unsafe reference.
Note, this is an unsafe reference.
This data structure stores a snapshot of all question responses. Both question data and answer data
is stored in this hash reference.
Questions keys are constructed by hypenating the relevant L<"sIndex"> and L<"qIndex">.
Answer keys are constructed by hypenating the relevant L<"sIndex">, L<"qIndex"> and L<aIndex|"aIndexes">.
{
# Question entries only contain a comment field, e.g.
'0-0' => {
comment => "question comment",
},
# ...
# Answers entries contain: value (the recorded value), time and comment fields.
'0-0-0' => {
value => "recorded answer value",
time => time(),
comment => "answer comment",
},
# ...
}
=cut
@ -1247,6 +1292,62 @@ sub responses {
return $self->response->{responses};
}
=head2 pop
=cut
sub pop {
my $self = shift;
my %responses = %{ $self->responses };
# Iterate over responses first time to determine time of most recent response(s)
my $lastResponseTime;
for my $r ( values %responses ) {
if ( $r->{time} ) {
$lastResponseTime
= !$lastResponseTime || $r->{time} > $lastResponseTime
? $r->{time}
: $lastResponseTime
;
}
}
return unless $lastResponseTime;
my $popped;
my $poppedQuestions;
# Iterate again, removing most recent responses
while (my ($address, $r) = each %responses ) {
if ( $r->{time} == $lastResponseTime) {
$popped->{$address} = $r;
delete $self->responses->{$address};
# Remove associated question/comment entry
my ($sIndex, $qIndex, $aIndex) = split /-/, $address;
my $qAddress = "$sIndex-$qIndex";
$popped->{$qAddress} = $responses{$qAddress};
delete $self->responses->{$qAddress};
# while we're here, build lookup table of popped question ids
$poppedQuestions->{$qAddress} = 1;
}
}
# Now, nextResponse should be set to index of the first popped question we can find in surveyOrder
my $nextResponse = 0;
for my $address (@{ $self->surveyOrder }) {
my $questionId = "$address->[0]-$address->[1]";
if ($poppedQuestions->{$questionId} ) {
$self->session->log->debug("setting nextResponse to $nextResponse");
$self->nextResponse($nextResponse);
last;
}
$nextResponse++;
}
return $popped;
}
#-------------------------------------------------------------------
=head2 survey

View file

@ -117,6 +117,7 @@ sub loadTypes {
'Slider',
'Currency',
'Email',
'Number',
'Phone Number',
'Text',
'Text Date',
@ -413,16 +414,17 @@ Generates the list of valid goto targets
sub getGotoTargets {
my $self = shift;
# Valid goto targets are all of the section variable names..
my @section_vars = map {$_->{variable}} @{$self->sections};
# Valid goto targets are all of the non-empty section variable names..
my @section_vars = grep { $_ ne q{} } map {$_->{variable}} @{$self->sections};
# ..and all of the question variable names..
my @question_vars = map {$_->{variable}} @{$self->questions};
# ..and all of the non-empty question variable names..
my @question_vars = grep { $_ ne q{} } map {$_->{variable}} @{$self->questions};
# ..excluding the ones that are empty
my @grep = grep { $_ ne q{} } (@section_vars, @question_vars);
return \@grep;
#return grep { $_ ne q{} } (@section_vars, @question_vars);
# ..plus some special vars
my @special_vars = qw(NEXT_SECTION END_SURVEY);
# ..all combined
return [ @section_vars, @question_vars, @special_vars ];
}
=head2 getSectionEditVars ( $address )
@ -665,16 +667,32 @@ sub update {
}
}
$self->_handleSpecialAnswerUpdates($address,$properties);
# Update $object with all of the data in $properties
while (my ($key, $value) = each %{$properties}) {
if (defined $value) {
$object->{$key} = $value;
}
}
return;
}
sub _handleSpecialAnswerUpdates{
my $self = shift;
my $address = shift;
my $properties = shift;
my $question = $self->question($address);
if($question->{questionType} =~ /^Slider|Multi Slider - Allocate|Dual Slider - Range$/){
for my $answer(@{$self->answers($address)}){
$answer->{max} = $properties->{max};
$answer->{min} = $properties->{min};
$answer->{step} = $properties->{step};
}
}
}
=head2 insertObject ( $object, $address )
Rearrange existing objects in the current data structure.
@ -1036,10 +1054,9 @@ sub addAnswersToQuestion {
# when updating answer text without causing side-effects for the caller's $address
my @address_copy = @{$address};
for my $answer_index ( 0 .. $#{$answers} ) {
for my $answer (@$answers) {
# Add a new answer to question
push @{ $self->question( \@address_copy )->{answers} }, $answers->[$answer_index];
push @{ $self->question( \@address_copy )->{answers} }, $answer;
}
return;

View file

@ -153,7 +153,7 @@ sub generateFeed {
# care of any encoding specified in the XML prolog
utf8::downgrade($value, 1);
eval {
my $singleFeed = XML::FeedPP->new($value, utf8_flag => 1);
my $singleFeed = XML::FeedPP->new($value, utf8_flag => 1, -type => 'string');
$feed->merge($singleFeed);
};
if ($@) {

View file

@ -292,19 +292,44 @@ sub duplicate {
my $assetId = $self->get("assetId");
my $fields;
my $otherThingFields = $db->buildHashRefOfHashRefs(
"select fieldType, fieldId, right(fieldType,22) as otherThingId, fieldInOtherThingId from Thingy_fields
where fieldType like 'otherThing_%' and assetId = ?",
[$assetId],'fieldInOtherThingId'
);
my $things = $self->getThings;
while ( my $thing = $things->hashRef) {
my $oldThingId = $thing->{thingId};
my $newThingId = $newAsset->addThing($thing,0);
my $oldSortBy = $thing->{sortBy};
my $oldThingId = $thing->{thingId};
my $newThingId = $newAsset->addThing($thing,0);
$fields = $db->buildArrayRefOfHashRefs('select * from Thingy_fields where assetId=? and thingId=?'
,[$assetId,$oldThingId]);
foreach my $field (@$fields) {
# set thingId to newly created thing's id.
$field->{thingId} = $newThingId;
my $originalFieldId = $field->{fieldId};
$newAsset->addField($field,0);
my $newFieldId = $newAsset->addField($field,0);
if ($originalFieldId eq $oldSortBy){
$self->session->db->write( "update Thingy_things set sortBy = ? where thingId = ?",
[ $newFieldId, $newThingId ] );
}
if ($otherThingFields->{$originalFieldId}){
$otherThingFields->{$originalFieldId}->{newFieldType} = 'otherThing_'.$newThingId;
$otherThingFields->{$originalFieldId}->{newFieldId} = $newFieldId;
}
}
}
foreach my $otherThingField (keys %$otherThingFields){
$db->write('update Thingy_fields set fieldType = ?, fieldInOtherThingId = ?
where fieldInOtherThingId = ? and assetId = ?',
[$otherThingFields->{$otherThingField}->{newFieldType},
$otherThingFields->{$otherThingField}->{newFieldId},
$otherThingFields->{$otherThingField}->{fieldInOtherThingId}, $newAsset->get('assetId')]);
}
return $newAsset;
}

View file

@ -61,7 +61,7 @@ sub getAlphabetSearchLoop {
my $htmlEncodedLetter = encode_entities($letter);
my $searchURL = "?searchExact_".$fieldName."=".$letter."%25";
my $hasResults;
my $users = $self->session->db->read("select userId from userProfileData where lastName like '".$letter."%'");
my $users = $self->session->db->read("select userId from userProfileData where `$fieldName` like '".$letter."%'");
while (my $user = $users->hashRef){
my $showGroupId = $self->get("showGroupId");
if ($showGroupId eq '0' || ($showGroupId && $self->isInGroup($showGroupId,$user->{userId}))){
@ -535,7 +535,7 @@ sub view {
my $users = $p->getPageData($paginatePage);
foreach my $user (@$users){
my $userObject = WebGUI::User->new($self->session,$user->{userId});
if ($self->get('overridePublicProfile') || $userObject->profileIsViewable($userObject)){
if ($self->get('overridePublicProfile') || $userObject->profileIsViewable()) {
my (@profileFieldValues);
my %userProperties;
foreach my $profileField (@profileFields){