Merge up to 10305

This commit is contained in:
Colin Kuskie 2009-04-13 17:04:23 +00:00
parent fa2e5c2c90
commit 1edaca4ed2
65 changed files with 1300 additions and 477 deletions

View file

@ -1,7 +1,23 @@
7.7.4
- fixed #9989: thing-search template won't include data-save params in the url for pagination
- fixed #10122: fixed date object to not change the value in 'toHtml' function.
- fixed #9764: drag drop now uses the handle for 'pickup' rather than the whole object.
- Default Survey Question bundles now store full answer information in json. Everything configured in an answer will be saved in a default configuration.
- Survey [[question variable]] now returns the shown answer text for multiple choice questions, and the recorded value for non-multiple choice questions.
- fixed #10142: Matrix 2.0 - Search screen compare button not functional
- fixed #10141: Matrix 2.0 - Search does not check matching products
- fixed #10077: after matrix sort can't return to alphanumeric sort
- fixed #10138: Matrix 2.0 Links in Product Listing are broken
7.7.3
- fixed #10094: double explanation in thread help
- rfe #9612: Carousel Wobject (was Widget Wobject) (SDH Consulting Group)
- Survey summaries now added. In the Survey edit, select quiz mode, and a summary will be shown to the user at the end of the survey.
- fixed #10110: Matrix 2.0 - "Data Error" on Pending Product Listing Updates
- rfe #9965: matrix/pls reverse dropped event variables
- fixed install of Passive Analytics settings for new sites (SDH Consulting Group)
- Survey Expression Engine upgraded (SDH Consulting Group)
7.7.2
- fixed #10056: YUI javascripts included while adminOff (BNC)
- fixed a bug that required you to hit "update cart" before the checkout

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View file

@ -23,7 +23,8 @@ use WebGUI::Session;
use WebGUI::Storage;
use WebGUI::Asset;
use WebGUI::Utility;
use WebGUI::PassiveAnalytics::Rule;
use WebGUI::Utility;
my $toVersion = '7.7.3';
my $quiet; # this line required
@ -35,11 +36,8 @@ my $session = start(); # this line required
addSurveyQuizModeColumns($session);
addSurveyExpressionEngineConfigFlag($session);
# Story Manager
installStoryManagerTables($session);
sm_upgradeConfigFiles($session);
sm_updateDailyWorkflow($session);
addCarouselWobject($session);
reInstallPassiveAnalyticsConfig($session);
finish($session); # this line required
@ -53,6 +51,22 @@ finish($session); # this line required
# print "DONE!\n" unless $quiet;
#}
sub addCarouselWobject{
my $session = shift;
print "\tAdding Carousel wobject... " unless $quiet;
$session->db->write("create table Carousel (
assetId char(22) binary not null,
revisionDate bigint not null,
items mediumtext,
templateId char(22),
primary key (assetId, revisionDate)
)");
my $assets = $session->config->get( "assets" );
$assets->{ "WebGUI::Asset::Wobject::Carousel" } = { category => "utilities" };
$session->config->set( "assets", $assets );
print "Done.\n" unless $quiet;
}
sub addSurveyQuizModeColumns{
my $session = shift;
print "\tAdding columns to Survey table... " unless $quiet;
@ -68,97 +82,52 @@ sub addSurveyExpressionEngineConfigFlag{
print "Done.\n" unless $quiet;
}
sub installStoryManagerTables {
my ($session) = @_;
print "\tAdding Story Manager tables... " unless $quiet;
my $db = $session->db;
$db->write(<<EOSTORY);
CREATE TABLE Story (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
headline CHAR(255),
subtitle CHAR(255),
byline CHAR(255),
location CHAR(255),
highlights TEXT,
story MEDIUMTEXT,
photo LONGTEXT,
PRIMARY KEY ( assetId, revisionDate )
)
EOSTORY
$db->write(<<EOARCHIVE);
CREATE TABLE StoryArchive (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPerPage INTEGER,
groupToPost CHAR(22) BINARY,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
editStoryTemplateId CHAR(22) BINARY,
archiveAfter INT(11),
richEditorId CHAR(22) BINARY,
approvalWorkflowId CHAR(22) BINARY DEFAULT 'pbworkflow000000000003',
PRIMARY KEY ( assetId, revisionDate )
)
EOARCHIVE
$db->write(<<EOTOPIC);
CREATE TABLE StoryTopic (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPer INTEGER,
storiesShort INTEGER,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
PRIMARY KEY ( assetId, revisionDate )
)
EOTOPIC
print "DONE!\n" unless $quiet;
}
sub sm_upgradeConfigFiles {
my ($session) = @_;
print "\tAdding Story Manager to config file... " unless $quiet;
my $config = $session->config;
$config->addToHash(
'assets',
'WebGUI::Asset::Wobject::StoryTopic' => {
'category' => 'community'
},
);
$config->addToHash(
'assets',
"WebGUI::Asset::Wobject::StoryArchive" => {
"isContainer" => 1,
"category" => "community"
},
);
my $activities = $config->get('workflowActivities');
my $none = $activities->{None};
if (!isIn('WebGUI::Workflow::Activity::ArchiveOldStories', @{ $none })) {
unshift @{ $none }, 'WebGUI::Workflow::Activity::ArchiveOldStories';
#----------------------------------------------------------------------------
# Conditionally re-add passive analytics config because it wasn't added to WebGUI.conf.original
# in version 7.7.0.
sub reInstallPassiveAnalyticsConfig {
my $session = shift;
print "\tAdd Passive Analytics entry to the config file... " unless $quiet;
# Admin Bar/Console
my $adminConsole = $session->config->get('adminConsole');
if (!exists $adminConsole->{'passiveAnalytics'}) {
$adminConsole->{'passiveAnalytics'} = {
"icon" => "passiveAnalytics.png",
"uiLevel" => 1,
"url" => "^PageUrl(\"\",op=passiveAnalytics;func=editRuleflow);",
"title" => "^International(Passive Analytics,PassiveAnalytics);",
"groupSetting" => "3",
};
$session->config->set('adminConsole', $adminConsole);
}
$config->set('workflowActivities', $activities);
print "DONE!\n" unless $quiet;
}
sub sm_updateDailyWorkflow {
my ($session) = @_;
print "\tAdding Archive Old Stories to Daily Workflow... " unless $quiet;
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
foreach my $activity (@{ $workflow->getActivities }) {
return if $activity->getName() eq 'WebGUI::Workflow::Activity::ArchiveOldStories';
# Content Handler
my $contentHandlers = $session->config->get('contentHandlers');
if (!isIn('WebGUI::Content::PassiveAnalytics',@{ $contentHandlers} ) ) {
my $contentIndex = 0;
HANDLER: while ($contentIndex <= $#{ $contentHandlers } ) {
##Insert before Operation
if($contentHandlers->[$contentIndex] eq 'WebGUI::Content::Operation') {
splice @{ $contentHandlers }, $contentIndex, 0, 'WebGUI::Content::PassiveAnalytics';
last HANDLER;
}
++$contentIndex;
}
$session->config->set('contentHandlers', $contentHandlers);
}
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
$activity->set('title', 'Archive Old Stories');
$activity->set('description', 'Archive old stories, based on the settings of the Story Archives that own them');
# Workflow Activities
my $workflowActivities = $session->config->get('workflowActivities');
my @none = @{ $workflowActivities->{'None'} };
if (!isIn('WebGUI::Workflow::Activity::SummarizePassiveAnalytics', @none)) {
push @none, 'WebGUI::Workflow::Activity::SummarizePassiveAnalytics';
}
if (!isIn('WebGUI::Workflow::Activity::BucketPassiveAnalytics', @none)) {
push @none, 'WebGUI::Workflow::Activity::BucketPassiveAnalytics';
}
$workflowActivities->{'None'} = [ @none ];
$session->config->set('workflowActivities', $workflowActivities);
print "DONE!\n" unless $quiet;
}
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
#----------------------------------------------------------------------------

View file

@ -0,0 +1,280 @@
#!/usr/bin/env perl
#-------------------------------------------------------------------
# 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
#-------------------------------------------------------------------
our ($webguiRoot);
BEGIN {
$webguiRoot = "../..";
unshift (@INC, $webguiRoot."/lib");
}
use strict;
use Getopt::Long;
use WebGUI::Session;
use WebGUI::Storage;
use WebGUI::Asset;
use WebGUI::Utility;
use JSON;
my $toVersion = '7.7.4';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
updateSurveyQuestionTypes($session);
# Story Manager
installStoryManagerTables($session);
sm_upgradeConfigFiles($session);
sm_updateDailyWorkflow($session);
finish($session); # this line required
#----------------------------------------------------------------------------
# Describe what our function does
#sub exampleFunction {
# my $session = shift;
# print "\tWe're doing some stuff here that you should know about... " unless $quiet;
# # and here's our code
# print "DONE!\n" unless $quiet;
#}
sub updateSurveyQuestionTypes{
my $session = shift;
my $refs = $session->db->buildArrayRefOfHashRefs("SELECT * FROM Survey_questionTypes");
for my $ref(@$refs){
my $name = $ref->{questionType};
my $params;
my @texts = split/,/,$ref->{answers};
#next if(@texts == 0);
my $count = 0;
for my $text(@texts){
my $verbatim = 0;
$verbatim = 1 if($text =~ /verbatim/);
push(@$params,[$text,$count++,$verbatim]);
}
_loadValues($name,$params,$session);
}
}
sub _loadValues{
my $name = shift;
my $values = shift;
my $session = shift;
my $answers = [];
for my $value(@$values){
my $answer = _getAnswer();
$answer->{text} = $value->[0];
$answer->{recordedAnswer} = $value->[1];
$answer->{verbatim} = $value->[2];
push @$answers,$answer;
}
my $json = to_json($answers);
$session->db->write("UPDATE Survey_questionTypes SET answers = ? WHERE questionType = ?",[$json,$name]);
}
sub _getAnswer{
my $answer = {
text => q{},
verbatim => 0,
textCols => 10,
textRows => 5,
goto => q{},
gotoExpression => q{},
recordedAnswer => q{},
isCorrect => 1,
min => 1,
max => 10,
step => 1,
value => 1,
terminal => 0,
terminalUrl => q{},
type => 'answer'
};
return $answer;
}
sub installStoryManagerTables {
my ($session) = @_;
print "\tAdding Story Manager tables... " unless $quiet;
my $db = $session->db;
$db->write(<<EOSTORY);
CREATE TABLE Story (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
headline CHAR(255),
subtitle CHAR(255),
byline CHAR(255),
location CHAR(255),
highlights TEXT,
story MEDIUMTEXT,
photo LONGTEXT,
PRIMARY KEY ( assetId, revisionDate )
)
EOSTORY
$db->write(<<EOARCHIVE);
CREATE TABLE StoryArchive (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPerPage INTEGER,
groupToPost CHAR(22) BINARY,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
editStoryTemplateId CHAR(22) BINARY,
archiveAfter INT(11),
richEditorId CHAR(22) BINARY,
approvalWorkflowId CHAR(22) BINARY DEFAULT 'pbworkflow000000000003',
PRIMARY KEY ( assetId, revisionDate )
)
EOARCHIVE
$db->write(<<EOTOPIC);
CREATE TABLE StoryTopic (
assetId CHAR(22) BINARY NOT NULL,
revisionDate BIGINT NOT NULL,
storiesPer INTEGER,
storiesShort INTEGER,
templateId CHAR(22) BINARY,
storyTemplateId CHAR(22) BINARY,
PRIMARY KEY ( assetId, revisionDate )
)
EOTOPIC
print "DONE!\n" unless $quiet;
}
sub sm_upgradeConfigFiles {
my ($session) = @_;
print "\tAdding Story Manager to config file... " unless $quiet;
my $config = $session->config;
$config->addToHash(
'assets',
'WebGUI::Asset::Wobject::StoryTopic' => {
'category' => 'community'
},
);
$config->addToHash(
'assets',
"WebGUI::Asset::Wobject::StoryArchive" => {
"isContainer" => 1,
"category" => "community"
},
);
my $activities = $config->get('workflowActivities');
my $none = $activities->{None};
if (!isIn('WebGUI::Workflow::Activity::ArchiveOldStories', @{ $none })) {
unshift @{ $none }, 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
$config->set('workflowActivities', $activities);
print "DONE!\n" unless $quiet;
}
sub sm_updateDailyWorkflow {
my ($session) = @_;
print "\tAdding Archive Old Stories to Daily Workflow... " unless $quiet;
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
foreach my $activity (@{ $workflow->getActivities }) {
return if $activity->getName() eq 'WebGUI::Workflow::Activity::ArchiveOldStories';
}
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
$activity->set('title', 'Archive Old Stories');
$activity->set('description', 'Archive old stories, based on the settings of the Story Archives that own them');
print "DONE!\n" unless $quiet;
}
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
#----------------------------------------------------------------------------
# Add a package to the import node
sub addPackage {
my $session = shift;
my $file = shift;
# Make a storage location for the package
my $storage = WebGUI::Storage->createTemp( $session );
$storage->addFileFromFilesystem( $file );
# Import the package into the import node
my $package = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
# Turn off the package flag, and set the default flag for templates added
my $assetIds = $package->getLineage( ['self','descendants'] );
for my $assetId ( @{ $assetIds } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
if ( !$asset ) {
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
next;
}
my $properties = { isPackage => 0 };
if ($asset->isa('WebGUI::Asset::Template')) {
$properties->{isDefault} = 1;
}
$asset->update( $properties );
}
return;
}
#-------------------------------------------------
sub start {
my $configFile;
$|=1; #disable output buffering
GetOptions(
'configFile=s'=>\$configFile,
'quiet'=>\$quiet
);
my $session = WebGUI::Session->open($webguiRoot,$configFile);
$session->user({userId=>3});
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->set({name=>"Upgrade to ".$toVersion});
return $session;
}
#-------------------------------------------------
sub finish {
my $session = shift;
updateTemplates($session);
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->commit;
$session->db->write("insert into webguiVersion values (".$session->db->quote($toVersion).",'upgrade',".$session->datetime->time().")");
$session->close();
}
#-------------------------------------------------
sub updateTemplates {
my $session = shift;
return undef unless (-d "packages-".$toVersion);
print "\tUpdating packages.\n" unless ($quiet);
opendir(DIR,"packages-".$toVersion);
my @files = readdir(DIR);
closedir(DIR);
my $newFolder = undef;
foreach my $file (@files) {
next unless ($file =~ /\.wgpkg$/);
# Fix the filename to include a path
$file = "packages-" . $toVersion . "/" . $file;
addPackage( $session, $file );
}
}
#vim:ft=perl

View file

@ -345,6 +345,13 @@
"title" : "^International(manage graphics,Graphics);",
"groupSetting" : "groupIdAdminGraphics"
},
"passiveAnalytics" : {
"icon" : "passiveAnalytics.png",
"uiLevel" : 1,
"url" : "^PageUrl(\"\",op=passiveAnalytics;func=editRuleflow);",
"title" : "^International(Passive Analytics,PassiveAnalytics);",
"groupSetting" : "3"
},
"shop" : {
"icon" : "shop.gif",
"uiLevel" : 5,
@ -827,7 +834,9 @@
"WebGUI::Workflow::Activity::SummarizePassiveProfileLog",
"WebGUI::Workflow::Activity::SyncProfilesToLdap",
"WebGUI::Workflow::Activity::TrashClipboard",
"WebGUI::Workflow::Activity::TrashExpiredEvents"
"WebGUI::Workflow::Activity::TrashExpiredEvents",
"WebGUI::Workflow::Activity::SummarizePassiveAnalytics",
"WebGUI::Workflow::Activity::BucketPassiveAnalytics"
],
"WebGUI::Asset::Wobject::Thingy" : [
"WebGUI::Workflow::Activity::NotifyAboutThing"
@ -912,6 +921,7 @@
"WebGUI::Content::Referral",
"WebGUI::Content::AssetManager",
"WebGUI::Content::AssetDiscovery",
"WebGUI::Content::PassiveAnalytics",
"WebGUI::Content::AjaxI18N",
"WebGUI::Content::Account",
"WebGUI::Content::AssetHistory",

View file

@ -1,7 +1,7 @@
package WebGUI;
our $VERSION = '7.7.3';
our $VERSION = '7.7.4';
our $STATUS = 'beta';

View file

@ -41,6 +41,14 @@ use WebGUI::DateTime;
=head1 Methods
=cut
####################################################################
=head2 addRevision ( )
Extent the method from the super class to handle iCalSequenceNumbers.
=cut
sub addRevision {
@ -1698,6 +1706,12 @@ sub processPropertiesFromFormPost {
#-------------------------------------------------------------------
=head2 purge ( )
Extent the method from the super class to delete all storage locations.
=cut
sub purge {
my $self = shift;
my $sth = $self->session->db->read("select storageId from Event where assetId=?",[$self->getId]);
@ -1711,6 +1725,12 @@ sub purge {
#-------------------------------------------------------------------
=head2 purgeRevision ( )
Extent the method from the super class to delete the storage location for this revision.
=cut
sub purgeRevision {
my $self = shift;
$self->getStorageLocation->delete;
@ -1908,6 +1928,13 @@ sub view {
#-------------------------------------------------------------------
=head2 www_deleteFile ( )
Delete a file given in the form variable "filename" from the storage location.
=cut
sub www_deleteFile {
my $self = shift;
$self->getStorageLocation->deleteFile($self->session->form->process("filename")) if $self->canEdit;

View file

@ -640,6 +640,13 @@ sub view {
$var->{manufacturerUrl_click} = $self->getUrl("func=click;manufacturer=1");
$var->{productUrl_click} = $self->getUrl("func=click");
if($self->get('status') eq 'pending'){
my $revisionDate = $self->get('revisionDate');
$var->{revision} = $revisionDate;
$var->{manufacturerUrl_click} .= ';revision='.$revisionDate;
$var->{productUrl_click} .= ';revision='.$revisionDate;
}
$self->session->style->setScript($self->session->url->extras('yui/build/yahoo/yahoo-min.js'),
{type => 'text/javascript'});
$self->session->style->setScript($self->session->url->extras('yui/build/dom/dom-min.js'),

View file

@ -144,6 +144,12 @@ sub definition {
#-------------------------------------------------------------------
=head2 duplicate ( )
Extend the super class to duplicate the storage location.
=cut
sub duplicate {
my $self = shift;
my $newAsset = $self->SUPER::duplicate(@_);
@ -169,6 +175,14 @@ sub exportAssetData {
#-------------------------------------------------------------------
=head2 getStorageLocation ( )
Fetches the storage location for this asset. If it does not have one,
then make one. Build an internal cache of the storage object.
=cut
sub getStorageLocation {
my $self = shift;
unless (exists $self->{_storageLocation}) {
@ -223,6 +237,13 @@ sub prepareView {
#-------------------------------------------------------------------
=head2 processPropertiesFromFormPost ( )
Extend the super class to calculate total asset size from
any files stored in the storage location.
=cut
sub processPropertiesFromFormPost {
my $self = shift;
$self->SUPER::processPropertiesFromFormPost(@_);
@ -235,6 +256,15 @@ sub processPropertiesFromFormPost {
}
#-------------------------------------------------------------------
=head2 update ( )
Extend the super class to handle the storage location. Sets
the correct privileges and deletes the internally cached
Storage object.
=cut
sub update {
my $self = shift;
my $previousStorageId = $self->get('storageId');
@ -253,6 +283,12 @@ sub update {
#-------------------------------------------------------------------
=head2 purge ( )
Extend the super class to delete all storage locations.
=cut
sub purge {
my $self = shift;
my $sth = $self->session->db->read("select storageId from Article where assetId=?",[$self->getId]);
@ -280,6 +316,12 @@ sub purgeCache {
#-------------------------------------------------------------------
=head2 purgeRevision ( )
Extend the super class to delete the storage location for this revision.
=cut
sub purgeRevision {
my $self = shift;
$self->getStorageLocation->delete;

View file

@ -40,7 +40,7 @@ sub definition {
%properties = (
templateId =>{
fieldType =>"template",
defaultValue =>'CarouselTmpl0000000002',
defaultValue =>'CarouselTmpl0000000001',
tab =>"display",
noFormPost =>0,
namespace =>"Carousel",
@ -273,66 +273,5 @@ adminConsole views.
# return $self->getAdminConsole->render($self->getEditForm->print, $i18n->get("edit title"));
#}
#-------------------------------------------------------------------
# Everything below here is to make it easier to install your custom
# wobject, but has nothing to do with wobjects in general
#-------------------------------------------------------------------
# cd /data/WebGUI/lib
# perl -MWebGUI::Asset::Wobject::Carousel -e install www.example.com.conf [ /path/to/WebGUI ]
# - or -
# perl -MWebGUI::Asset::Wobject::Carousel -e uninstall www.example.com.conf [ /path/to/WebGUI ]
#-------------------------------------------------------------------
use base 'Exporter';
our @EXPORT = qw(install uninstall);
use WebGUI::Session;
#-------------------------------------------------------------------
sub install {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::Carousel -e install www.example.com.conf\n" unless ($home && $config);
print "Installing asset.\n";
my $session = WebGUI::Session->open($home, $config);
my $assets = $session->config->get( "assets" );
$assets->{ "WebGUI::Asset::Wobject::Carousel" } = { category => "utilities" };
$session->config->set( "assets", $assets );
#$session->config->addToArray("assets","WebGUI::Asset::Wobject::Carousel");
$session->db->write("create table Carousel (
assetId char(22) binary not null,
revisionDate bigint not null,
items mediumtext,
templateId char(22),
primary key (assetId, revisionDate)
)");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
#-------------------------------------------------------------------
sub uninstall {
my $config = $ARGV[0];
my $home = $ARGV[1] || "/data/WebGUI";
die "usage: perl -MWebGUI::Asset::Wobject::Carousel -e uninstall www.example.com.conf\n" unless ($home && $config);
print "Uninstalling asset.\n";
my $session = WebGUI::Session->open($home, $config);
$session->config->deleteFromArray("assets","WebGUI::Asset::Wobject::Carousel");
my $rs = $session->db->read("select assetId from asset where className='WebGUI::Asset::Wobject::Carousel'");
while (my ($id) = $rs->array) {
my $asset = WebGUI::Asset->new($session, $id, "WebGUI::Asset::Wobject::Carousel");
$asset->purge if defined $asset;
}
$session->db->write("drop table Carousel");
$session->var->end;
$session->close;
print "Done. Please restart Apache.\n";
}
1;
#vim:ft=perl

View file

@ -1452,5 +1452,18 @@ sub www_view {
return $self->next::method(@_);
}
#-------------------------------------------------------------------
=head2 www_viewRSS ( )
Deprecated. Use www_viewRss() instead.
=cut
sub www_viewRSS {
my $self = shift;
return $self->www_viewRss;
}
1;

View file

@ -567,6 +567,8 @@ sub view {
$varStatistics = JSON->new->decode($varStatisticsEncoded);
}
else{
$varStatistics->{alphanumeric_sortButton} = "<span id='sortByName'><button type='button'>Sort by name</button></span><br />";
# Get the MatrixListing with the most views as an object using getLineage.
my ($bestViews_listing) = @{ $self->getLineage(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
@ -629,7 +631,7 @@ sub view {
lastUpdated => $self->session->datetime->epochToHuman($lastUpdatedListing->get('lastUpdated'),"%z")
});
}
$var->{lastUpdated_sortButton} = "<span id='sortByUpdated'><button type='button'>Sort by updated</button></span><br />";
$varStatistics->{lastUpdated_sortButton} = "<span id='sortByUpdated'><button type='button'>Sort by updated</button></span><br />";
# For each category, get the MatrixListings with the best ratings.
@ -1302,7 +1304,7 @@ sub www_search {
$options{'blank'} = 'blank';
$attribute->{options} = \%options;
$attribute->{value} = 'blank';
$attribute->{extras} = "style='width:120px'";
$attribute->{extras} .= " style='width:120px'";
}
$attribute->{form} = WebGUI::Form::DynamicField->new($self->session,%{$attribute})->toHtml;
push(@attribute_loop,$attribute);

View file

@ -84,6 +84,13 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Manually build the edit form due to javascript elements.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm;
@ -340,7 +347,7 @@ sub getToolbar {
=head2 prepareView ( )
See WebGUI::Asset::prepareView() for details.
Extend the superclass to add metadata and to preprocess the template.
=cut
@ -354,6 +361,13 @@ sub prepareView {
#-------------------------------------------------------------------
=head2 view ( )
See WebGUI::Asset::view() for details.
=cut
sub view {
my $self = shift;
# we've got to determine what our start point is based upon user conditions
@ -512,6 +526,13 @@ sub view {
}
#-------------------------------------------------------------------
=head2 www_goBackToPage ( )
Do a redirect to the form parameter returnUrl if it exists.
=cut
sub www_goBackToPage {
my $self = shift;
$self->session->http->setRedirect($self->session->form->process("returnUrl")) if ($self->session->form->process("returnUrl"));

View file

@ -220,6 +220,13 @@ sub definition {
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Manually make the edit form due to javascript for adding more queries.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm();
@ -495,6 +502,14 @@ sub purgeCache {
}
#-------------------------------------------------------------------
=head2 view ( )
See WebGUI::Asset::view() for details. This method also performs content caching
if the user is not in Admin Mode.
=cut
sub view {
my $self = shift;
if (!$self->session->var->isAdminOn && $self->get("cacheTimeout") > 10) {

View file

@ -396,6 +396,7 @@ Loads the initial edit survey page. All other edit actions are ajax calls from t
sub www_editSurvey {
my $self = shift;
return $self->session->privilege->insufficient()
if !$self->session->user->isInGroup( $self->get('groupToEditSurvey') );
@ -676,7 +677,7 @@ sub www_dragDrop {
#If target is being moved down, then before has just moved up do to the target being deleted
$bid[0]-- if($tid[0] < $bid[0]);
$self->surveyJSON->insertObject( $target, [ $bid[0] ] );
$address = $self->surveyJSON->insertObject( $target, [ $bid[0] ] );
}
elsif ( @tid == 2 ) { #questions can be moved to any section, but a pushed to the end of a new section.
if ( $bid[0] !~ /\d/ ) {
@ -700,21 +701,21 @@ sub www_dragDrop {
else{ #Moved within the same section
$bid[1]-- if($tid[1] < $bid[1]);
}
$self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1] ] );
$address = $self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1] ] );
} ## end elsif ( @tid == 2 )
elsif ( @tid == 3 ) { #answers can only be rearranged in the same question
if ( @bid == 2 and $bid[1] == $tid[1] ) {#moved to the top of the question
$bid[2] = -1;
$self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1], $bid[2] ] );
$address = $self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1], $bid[2] ] );
}
elsif ( @bid == 3 ) {
#If target is being moved down, then before has just moved up do to the target being deleted
$bid[2]-- if($tid[2] < $bid[2]);
$self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1], $bid[2] ] );
$address = $self->surveyJSON->insertObject( $target, [ $bid[0], $bid[1], $bid[2] ] );
}
else {
#else put it back where it was
$self->surveyJSON->insertObject( $target, \@tid );
$address = $self->surveyJSON->insertObject( $target, \@tid );
}
}
@ -742,6 +743,7 @@ sub www_loadSurvey {
my ( $self, $options ) = @_;
my $editflag = 1;
my $address = defined $options->{address} ? $options->{address} : undef;
if ( !defined $address ) {
if ( my $inAddress = $self->session->form->process('data') ) {
if ( $inAddress eq q{-} ) {
@ -760,7 +762,7 @@ sub www_loadSurvey {
= defined $options->{var}
? $options->{var}
: $self->surveyJSON->getEditVars($address);
my $editHtml;
if ( $var->{type} eq 'section' ) {
$editHtml = $self->processTemplate( $var, $self->get('sectionEditTemplateId') );
@ -903,7 +905,7 @@ returns the output.
sub view {
my $self = shift;
my $var = $self->getMenuVars;
my ( $code, $overTakeLimit ) = $self->getResponseInfoForView();
$var->{lastResponseCompleted} = $code;
@ -1153,13 +1155,23 @@ sub www_submitQuestions {
}
#-------------------------------------------------------------------
sub getSummary{
=head2 getSummary
Returns a copy of the summary stored in JSON, and the output of
the survey summary template.
=cut
sub getSummary {
my $self = shift;
my $summary = $self->responseJSON->showSummary();
my $out = $self->processTemplate( $summary, $self->get('surveySummaryTemplateId') );
return $out;
return ($summary,$out);
# return $self->session->style->process( $out, $self->get('styleTemplateId') );
}
#-------------------------------------------------------------------
=head2 www_loadQuestions
@ -1171,7 +1183,6 @@ Determines which questions to display to the survey taker next, loads and return
sub www_loadQuestions {
my $self = shift;
my $wasRestarted = shift;
if ( !$self->canTakeSurvey() ) {
$self->session->log->debug('canTakeSurvey false, surveyEnd');
return $self->surveyEnd();
@ -1191,7 +1202,8 @@ sub www_loadQuestions {
$self->session->log->debug('Response surveyEnd, so calling surveyEnd');
if ( $self->get('quizModeSummary') ) {
if(! $self->session->form->param('shownsummary')){
my $json = to_json( { type => 'summary', summary => $self->getSummary() });
my ($summary,$html) = $self->getSummary();
my $json = to_json( { type => 'summary', summary => $summary, html => $html });
return $json;
}
}
@ -1750,7 +1762,7 @@ sub www_viewStatisticalOverview {
#-------------------------------------------------------------------
=head2 www_exportTransposedResults ()
=head2 www_exportSimpleResults ()
Exports transposed results in a tab deliniated file.
@ -1908,4 +1920,21 @@ sub www_editDefaultQuestions{
}
#-------------------------------------------------------------------
=head2 www_downloadDefaulQuestions
Sends the user a json file of the default question types, which can be imported to other WebGUI instances.
=cut
sub www_downloadDefaultQuestionTypes{
my $self = shift;
return $self->session->privilege->insufficient()
if !$self->session->user->isInGroup( $self->get('groupToViewReports') );
my $content = to_json($self->surveyJSON->{multipleChoiceTypes});
return $self->export( "WebGUI-Survey-DefaultQuestionTypes.json", $content );
}
1;

View file

@ -8,6 +8,10 @@ Package WebGUI::Asset::Wobject::Survey::ExpressionEngine
This class is used to process Survey gotoExpressions.
If you want to allow the expression engine to run you need to turn on the enableSurveyExpressionEngine flag
in your site config file. This is because no matter how 'Safe' the Safe.pm compartment is, it still has
caveats. For example, it doesn't protect you from infinite loops.
See L<run> for more details.
=cut
@ -17,6 +21,7 @@ use Params::Validate qw(:all);
use Safe;
use Data::Dumper;
use List::Util qw/sum/;
use WebGUI::Asset;
Params::Validate::validation_options( on_fail => sub { WebGUI::Error::InvalidParam->throw( error => shift ) } );
# We need these as semi-globals so that utility subs (which are shared with the safe compartment)
@ -27,20 +32,39 @@ my $scores;
my $jump_count;
my $validate;
my $validTargets;
my $other_instances;
=head2 value
Utility sub that gives expressions access to recorded response values
value(question_variable) returns the recorded response value for the answer to question_variable
value(asset_spec, question_variable) returns value(question_variable) on the most recent completed response
for the user on the survey instance given by asset_spec (either an assetId or a url)
=cut
sub value($) {
sub value {
# Two arguments implies the first arg is an asset_spec
if ( @_ == 2 ) {
my ( $asset_spec, $key ) = @_;
# See if $other_instances already contains the external survey
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]");
return $value;
} else {
# Throw an exception, triggering run() to resolve the external reference and re-run
die( { other_instance => $asset_spec } );
}
}
my $key = shift;
my $value = $values->{$key};
$session->log->debug("[$key] resolves to [$value]");
return $value; # scalar variable, so no need to clone
return $value; # scalar variable, so no need to clone
}
=head2 score
@ -52,11 +76,26 @@ score(section_variable) returns the summed score for the answers to all the ques
=cut
sub score($) {
sub score {
# Two arguments implies the first arg is an asset_spec
if ( @_ == 2 ) {
my ( $asset_spec, $key ) = @_;
# See if $other_instances already contains the external survey
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]");
return $score;
} else {
# Throw an exception, triggering run() to resolve the external reference and re-run
die( { other_instance => $asset_spec } );
}
}
my $key = shift;
my $score = $scores->{$key};
$session->log->debug("[$key] resolves to [$score]");
return $score; # scalar variable, so no need to clone
return $score; # scalar variable, so no need to clone
}
=head2 jump
@ -71,17 +110,18 @@ catch the first successful jump.
sub jump(&$) {
my ( $sub, $target ) = @_;
$jump_count++;
# If $validTargets known, make sure target is valid
if ($validTargets && !exists $validTargets->{$target}) {
if ( $validTargets && !exists $validTargets->{$target} ) {
$session->log->debug("Invalid target [$target]");
if ($validate) {
die("Invalid jump target \"$target\""); # bail and report error
} else {
return; # skip jump but continue with expression
die("Invalid jump target \"$target\""); # bail and report error
}
else {
return; # skip jump but continue with expression
}
}
if ( $sub->() ) {
$session->log->debug("jump call #$jump_count is truthy");
die( { jump => $target } );
@ -120,8 +160,12 @@ A gotoExpression is essentially a perl expression that gets evaluated in a Safe
To access Section/Question recorded response values, the expression calls L<value>.
To access Section/Question recorded response scores, the expression calls L<score>.
Both L<value> and L<score> allow you to resolve values and scores from other completed survey
instances.
To trigger a jump, the expression calls L<jump>. The first truthy jump succeeds.
We also give expressions access to some useful utility subs such as avg(), and all of the
Expressions also have access to some useful utility subs such as avg(), and all of the
handy subs from List::Util (min, max, sum, etc..).
A very simple expression that checks if the response to s1q1 is 0 might look like:
@ -131,7 +175,7 @@ A very simple expression that checks if the response to s1q1 is 0 might look lik
A more complicated gotoExpression with two possible jumps might look like:
jump { value(q1) > 5 and value(s2q1) =~ m/textmatch/ } target1;
jump { avg(value(q1), value(q2), value(q3)) > 10 } target2;
jump { avg(value(q1), value(q2), value(home/anotherSurvey, q3)) > 10 } target2;
=head3 opts (optional)
@ -147,7 +191,7 @@ Hashref of values to make available to the expression via the L<value> utility s
Hashref of scores to make available to the expression via the L<score> utility sub
=item* validTargets
=item * validTargets
A hashref of valid jump targets. If this is provided, all L<jump> calls will fail unless
the specified target is a key in the hashref.
@ -166,52 +210,113 @@ sub run {
= validate_pos( @_, { isa => 'WebGUI::Session' }, { type => SCALAR }, { type => HASHREF, default => {} } );
# Init package globals
( $session, $values, $scores, $jump_count, $validate, $validTargets ) = ( $s, $opts->{values}, $opts->{scores}, 0, $opts->{validate}, $opts->{validTargets} );
if (!$session->config->get('enableSurveyExpressionEngine')) {
( $session, $values, $scores, $jump_count, $validate, $validTargets )
= ( $s, $opts->{values}, $opts->{scores}, 0, $opts->{validate}, $opts->{validTargets} );
if ( !$session->config->get('enableSurveyExpressionEngine') ) {
$session->log->debug('enableSurveyExpressionEngine config option disabled, skipping');
return;
}
# Create the Safe compartment
my $compartment = Safe->new();
REVAL: {
# Share our utility subs with the compartment
$compartment->share('&value');
$compartment->share('&score');
$compartment->share('&jump');
$compartment->share('&avg');
# Give them all of List::Util too
$compartment->share_from('List::Util', ['&first', '&max', '&maxstr', '&min', '&minstr', '&reduce', '&shuffle', '&sum',]);
# Create the Safe compartment
my $compartment = Safe->new();
$session->log->debug("Expression is: \"$expression\"");
$compartment->reval($expression);
# See if we ran the engine just to check for errors
if ($opts->{validate}) {
if ($@ && ref $@ ne 'HASH') {
my $error = $@;
$error =~ s/(.*?) at .*/$1/s; # don't reveal too much
return $error;
# Share our utility subs with the compartment
$compartment->share('&value');
$compartment->share('&score');
$compartment->share('&jump');
$compartment->share('&avg');
# Give them all of List::Util too
$compartment->share_from( 'List::Util',
[ '&first', '&max', '&maxstr', '&min', '&minstr', '&reduce', '&shuffle', '&sum', ] );
$session->log->debug("Expression is: \"$expression\"");
$compartment->reval($expression);
# See if we ran the engine just to check for errors
if ( $opts->{validate} ) {
if ( $@ && ref $@ ne 'HASH' ) {
my $error = $@;
$error =~ s/(.*?) at .*/$1/s; # don't reveal too much
return $error;
}
return; # no validation errors
}
return; # no validation errors
}
# A successful jump triggers a hashref containing the jump target to be thrown
if ( ref $@ && ref $@ eq 'HASH' && $@->{jump} ) {
my $jump = $@->{jump};
$session->log->debug("Returning [$jump]");
return $jump;
}
# A successful jump triggers a hashref containing the jump target to be thrown
if ( ref $@ && ref $@ eq 'HASH' && $@->{jump} ) {
my $jump = $@->{jump};
$session->log->debug("Returning [$jump]");
return $jump;
}
# Log all other errors (for example compile errors from bad expressions)
if ($@) {
$session->log->error($@);
}
# See if an unresolved external reference was encountered
if ( ref $@ && ref $@ eq 'HASH' && $@->{other_instance} ) {
my $asset_spec = $@->{other_instance};
$session->log->debug("Resolving external reference: $asset_spec");
my $asset;
# Return undef on failure
return;
# Instantiate the asset to check it is a Survey instance, and to grab its assetId
if ( $session->id->valid($asset_spec) ) {
$asset = WebGUI::Asset->new( $session, $asset_spec );
}
if ( !$asset ) {
$asset = WebGUI::Asset->newByUrl( $session, $asset_spec );
}
if ( ref $asset ne 'WebGUI::Asset::Wobject::Survey' ) {
$session->log->warn("Not a survey instance: $asset_spec");
return;
}
if ( !$asset ) {
$session->log->warn("Unable to find asset: $asset_spec");
return;
}
my $assetId = $asset->getId;
# Get the responseId of the most recently completed survey response for the user
my $userId = $opts->{userId} || $session->user->userId;
my $mostRecentlyCompletedResponseId = $session->db->quickScalar(
"select Survey_responseId from Survey_response where userId = ? and assetId = ? and isComplete = 1",
[ $userId, $assetId ]
);
if ( !$mostRecentlyCompletedResponseId ) {
$session->log->debug("User $userId has not completed Survey");
return;
}
$session->log->debug("Using responseId: $mostRecentlyCompletedResponseId");
# (re)Instantiate the survey instance using the responseId
use WebGUI::Asset::Wobject::Survey;
$asset = WebGUI::Asset::Wobject::Survey->newByResponseId( $session, $mostRecentlyCompletedResponseId );
$asset->responseIdCookies(0);
if ( !$asset ) {
$session->log->warn("Unable to instantiate asset by responseId: $mostRecentlyCompletedResponseId");
return;
}
$other_instances->{$asset_spec} = {
values =>
$asset->responseJSON( undef, $mostRecentlyCompletedResponseId )->responseValuesByVariableName,
scores =>
$asset->responseJSON( undef, $mostRecentlyCompletedResponseId )->responseScoresByVariableName,
};
$session->log->debug("Successfully looked up asset: $assetId. Repeating reval.");
redo REVAL;
}
# Log all other errors (for example compile errors from bad expressions)
if ($@) {
$session->log->error($@);
}
# Return undef on failure
return;
}
}
1;

View file

@ -85,7 +85,7 @@ Answers entries contain: value (the recorded value), time and comment fields.
{
...
answerId => {
value => "answer value",
value => "recorded answer value",
time => time(),
comment => "answer comment",
},
@ -738,12 +738,18 @@ sub responseValuesByVariableName {
# Grab the corresponding question
my $question = $self->survey->question([@address]);
# 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};
}
# Add variable => value to our hash
$lookup{$question->{variable}} = $response->{value};
$lookup{$question->{variable}} = $answerText ? $answerText : $response->{value};
}
return \%lookup;
}
@ -781,8 +787,11 @@ sub responseScoresByVariableName {
# Grab the corresponding answer
my $answer = $self->survey->answer([@address]);
# Use question score if answer score undefined
my $score = (exists $answer->{value} && length $answer->{value} > 0) ? $answer->{value} : $question->{value};
# Add variable => score to our hash
$lookup{$question->{variable}} = $answer->{value};
$lookup{$question->{variable}} = $score;
}
# Add section score totals
@ -1074,9 +1083,8 @@ sub showSummary{
return if(! $responses);
my ($sectionIndex, $questionIndex, $answerIndex) = (-1, -1, -1);
my ($currentSection,$currentQuestion) = (-1, -1);
my ($sectionIndex, $responseIndex) = (-1, 1);
my ($currentSection,$currentQuestion) = (-1,-1);
($summaries->{totalCorrect},$summaries->{totalIncorrect}) = (0,0);
for my $response (@$responses){
@ -1090,62 +1098,54 @@ sub showSummary{
if($currentSection != $response->{address}->[0]){
$summaries->{totalSections}++;
$sectionIndex++;
$questionIndex = -1;
$answerIndex = -1;
$currentQuestion = -1;
$responseIndex = -1;
$currentSection = $response->{address}->[0];
_loadSectionIntoSummary(\%{$summaries->{sections}->[$sectionIndex]},$response);
}
if($currentQuestion != $response->{address}->[1]){
$summaries->{totalQuestions}++;
$questionIndex++;
$answerIndex = -1;
$currentQuestion = $response->{address}->[1];
_loadQuestionIntoSummary(\%{$summaries->{sections}->[$sectionIndex]->{questions}->[$questionIndex]},$response);
}
$answerIndex++;
_loadAnswerIntoSummary(\%{$summaries->{sections}->[$sectionIndex]->{questions}->[$questionIndex]->{answers}->[$answerIndex]},
_loadSectionIntoSummary(\%{$summaries->{sections}->[$sectionIndex]}, $response);
$responseIndex++;
_loadResponseIntoSummary(\%{$summaries->{sections}->[$sectionIndex]->{responses}->[$responseIndex]},
$response,
$self->survey->{multipleChoiceTypes});
}
return $summaries;
}
sub _loadAnswerIntoSummary{
sub _loadResponseIntoSummary{
my $node = shift;
my $response = shift;
my $types = shift;
$node->{id} = $response->{address}->[2] + 1;
$node->{"Question ID"} = $response->{address}->[1] + 1;
$node->{"Question Text"} = $response->{questionText};
$node->{"Answer ID"} = $response->{address}->[2] + 1;
if($response->{isCorrect}){
$node->{iscorrect} = 1;
$node->{score} = $response->{value};
$node->{Correct} = "Y";
$node->{Score} = $response->{value};
}else{
$node->{iscorrect} = 0;
$node->{score} = 0;
$node->{Correct} = "N";
$node->{Score} = 0;
}
$node->{text} = $response->{answerText};
$node->{"Answer Text"} = $response->{answerText};
#test if it is a multiple choide type
if($types->{$response->{questionType}}){
$node->{value} = $response->{value};
$node->{Value} = $response->{value};
}else{
$node->{value} = $response->{recordedValue};
$node->{Value} = $response->{recordedValue};
}
}
sub _loadQuestionIntoSummary{
my $node = shift;
my $response = shift;
$node->{id} = $response->{address}->[1] + 1;
$node->{text} = $response->{questionText};
}
sub _loadSectionIntoSummary{
my $node = shift;
my $response = shift;
$node->{id} = $response->{address}->[0] + 1;
$node->{inCorrect} = 0 if(!defined $node->{section}->{inCorrect});
$node->{score} = 0 if(!defined $node->{section}->{score});
$node->{correct} = 0 if(!defined $node->{section}->{correct});
if($response->{isCorrect}){
$node->{inCorrect} = 0 if(!defined $node->{inCorrect});
$node->{score} = 0 if(!defined $node->{score});
$node->{correct} = 0 if(!defined $node->{correct});
$node->{total} = 0 if(!defined $node->{total});
$node->{total}++;
if($response->{isCorrect} == 1){
$node->{score} += $response->{value};
$node->{correct}++;
}else{

View file

@ -110,6 +110,7 @@ Loads the Multiple Choice and Special Question types
sub loadTypes {
my $self = shift;
@{$self->{specialQuestionTypes}} = (
'Dual Slider - Range',
'Multi Slider - Allocate',
@ -125,9 +126,11 @@ sub loadTypes {
'Date Range',
'Year Month',
'Hidden',
);
my $refs = $self->session->db->buildArrayRefOfHashRefs("SELECT questionType, answers FROM Survey_questionTypes");
map($self->{multipleChoiceTypes}->{$_->{questionType}} = [split/,/,$_->{answers}], @$refs);
) if(! defined $self->{specialQuestionTypes});
if(! defined $self->{multipleChoiceTypes}){
my $refs = $self->session->db->buildArrayRefOfHashRefs("SELECT questionType, answers FROM Survey_questionTypes");
map($self->{multipleChoiceTypes}->{$_->{questionType}} = $_->{answers} ? from_json($_->{answers}) : {}, @$refs);
}
}
sub addType {
@ -135,11 +138,7 @@ sub addType {
my $name = shift;
my $address = shift;
my $obj = $self->getObject($address);
my @answers;
for my $ans(@{$obj->{answers}}){
push(@answers,$ans->{text});
}
my $ansString = join(',',@answers);
my $ansString = $obj->{answers} ? to_json $obj->{answers} : {};
$self->session->db->write("INSERT INTO Survey_questionTypes VALUES(?,?) ON DUPLICATE KEY UPDATE answers = ?",[$name,$ansString,$ansString]);
$self->question($address)->{questionType} = $name;
}
@ -391,7 +390,6 @@ sections, questions, or answers.
sub getEditVars {
my $self = shift;
my ($address) = validate_pos(@_, { type => ARRAYREF });
# Figure out what to do by counting the number of elements in the $address array ref
my $count = @{$address};
@ -727,15 +725,18 @@ sub insertObject {
# Use splice to rearrange the relevant array of objects..
if ( $count == 1 ) {
splice @{ $self->sections($address) }, sIndex($address) +1, 0, $object;
$address->[0]++;
}
elsif ( $count == 2 ) {
splice @{ $self->questions($address) }, qIndex($address) + 1, 0, $object;
$address->[1]++;
}
elsif ( $count == 3 ) {
splice @{ $self->answers($address) }, aIndex($address) + 1, 0, $object;
$address->[2]++;
}
return;
return $address;
}
=head2 copy ( $address )
@ -988,16 +989,8 @@ sub updateQuestionAnswers {
}
elsif ( my $answerBundle = $self->getMultiChoiceBundle($type) ) {
# We found a known multi-choice bundle.
# Mark any answer containing the string "verbatim" as verbatim
my $verbatims = {};
for my $answerIndex (0 .. $#$answerBundle) {
if ($answerBundle->[$answerIndex] =~ /\(verbatim\)/) {
$verbatims->{$answerIndex} = 1;
}
}
# Add the bundle of multi-choice answers, along with the verbatims hash
$self->addAnswersToQuestion( \@address_copy, $answerBundle, $verbatims );
# Add the bundle of multi-choice answers
$self->addAnswersToQuestion( \@address_copy, $answerBundle );
} else {
# Default action is to add a single, default answer to the question
push @{ $question->{answers} }, $self->newAnswer();
@ -1008,9 +1001,7 @@ sub updateQuestionAnswers {
=head2 getMultiChoiceBundle
Returns a list of answers for each multi-choice bundle.
Currently these are hard-coded but soon they will live in the database.
Returns a list of answer objects for each multi-choice bundle.
=cut
@ -1021,7 +1012,7 @@ sub getMultiChoiceBundle {
return $self->{multipleChoiceTypes}->{$type};
}
=head2 addAnswersToQuestion ($address, $answers, $verbatims)
=head2 addAnswersToQuestion ($address, $answers)
Helper routine for updateQuestionAnswers. Adds an array of answers to a question.
@ -1034,39 +1025,21 @@ See L<"Address Parameter">. The address of the question to add answers to.
An array reference of answers to add. Each element will be assigned to the text field of
the answer that is created.
=head3 $verbatims
An hash reference. Each key is an index into the answers array. The value is a placeholder
for doing existance lookups. For each requested index, the verbatim flag in the answer is
set to true.
=cut
sub addAnswersToQuestion {
my $self = shift;
my ( $address, $answers, $verbatims )
= validate_pos( @_, { type => ARRAYREF }, { type => ARRAYREF }, { type => HASHREF } );
my ( $address, $answers )
= validate_pos( @_, { type => ARRAYREF }, { type => ARRAYREF } );
# Make a private copy of the $address arrayref that we can use locally
# when updating answer text without causing side-effects for the caller's $address
my @address_copy = @{$address};
for my $answer_index ( 0 .. $#{$answers} ) {
# Add a new answer to question
push @{ $self->question( \@address_copy )->{answers} }, $self->newAnswer();
# Update address to point at newly created answer (so that we can update it)
$address_copy[2] = $answer_index;
# Update the answer appropriately
$self->update(
\@address_copy,
{ text => $answers->[$answer_index],
recordedAnswer => $answer_index + 1, # 1-indexed
verbatim => $verbatims->{$answer_index},
}
);
push @{ $self->question( \@address_copy )->{answers} }, $answers->[$answer_index];
}
return;
@ -1200,15 +1173,12 @@ Returns an array of messages to inform a user what is logically wrong with the S
sub validateSurvey{
my $self = shift;
#check all goto's
#bad goto expressions
#check that all survey is able to be seen
my @messages;
#set up valid goto targets
my $gotoTargets = $self->getGotoTargets();
my $goodTargets;
my $goodTargets = {};
my $duplicateTargets;
for my $g (@{$gotoTargets}) {
$goodTargets->{$g}++;
@ -1302,7 +1272,11 @@ sub validateGotoExpression{
my $self = shift;
my $object = shift;
my $goodTargets = shift;
return unless $object->{gotoExpression};
return unless $object->{gotoExpression};
if (!$self->session->config->get('enableSurveyExpressionEngine')) {
return 'enableSurveyExpressionEngine is disabled in your site config!';
}
use WebGUI::Asset::Wobject::Survey::ExpressionEngine;
my $engine = "WebGUI::Asset::Wobject::Survey::ExpressionEngine";

View file

@ -403,20 +403,6 @@ sub www_viewRSS10 {
return $self->www_viewRdf;
}
#-------------------------------------------------------------------
=head2 www_viewRSS ( )
Deprecated. Use www_viewRss() instead.
=cut
sub www_viewRSS {
my $self = shift;
return $self->www_viewRss;
}
#-------------------------------------------------------------------
=head2 www_viewRSS20 ( )

View file

@ -3152,6 +3152,9 @@ sub getSearchTemplateVars {
$currentUrl = $self->getUrl();
foreach ($self->session->form->param) {
# if we just saved data from an edit, we do not want to keep any of the params
last if $_ eq 'func' and $self->session->form->process($_) eq 'editThingDataSave';
unless ($_ eq "pn" || $_ eq "op" || $_ =~ /identifier/xi || $_ =~ /password/xi || $_ eq "orderBy" ||
$self->session->form->process($_) eq "") {
$currentUrl = $self->session->url->append($currentUrl,$self->session->url->escape($_)
@ -3260,10 +3263,16 @@ sequenceNumber');
$templateVars{canEditThingData} = 1;
$templateVars{searchResult_delete_icon} = $session->icon->delete('func=deleteThingDataConfirm;thingId='
.$thingId.';thingDataId='.$thingDataId,$self->get("url"),$i18n->get('delete thing data warning'));
$templateVars{searchResult_delete_url} = $session->url->append($url,
'func=deleteThingDataConfirm;thingId='.$thingId.';thingDataId='.$thingDataId);
$templateVars{searchResult_edit_icon} = $session->icon->edit('func=editThingData;thingId='
.$thingId.';thingDataId='.$thingDataId,$self->get("url"));
$templateVars{searchResult_edit_url} = $session->url->append($url,
'func=editThingData;thingId='.$thingId.';thingDataId='.$thingDataId);
$templateVars{searchResult_copy_icon} = $session->icon->copy('func=copyThingData;thingId='
.$thingId.';thingDataId='.$thingDataId,$self->get("url"));
$templateVars{searchResult_copy_url} = $session->url->append($url,
'func=copyThingData;thingId='.$thingId.';thingDataId='.$thingDataId,);
}
push(@searchResult_loop,\%templateVars);
}

View file

@ -252,12 +252,35 @@ sub exportAssetCollateral {
=head2 getRssFeedItems ()
This method should throw an exception if it's not overridden. Its intention is
to be overridden by whatever class is using it and should return an array
reference of hash references. Each hash reference should contain at minimum a title,
description, link, and date field. The date field can be either an epoch date, an RFC 1123
date, or a ISO date in the format of YYYY-MM-DD HH:MM::SS. Optionally specify an
author, and a guid field.
This method needs to be overridden by any class that is using it. To ensure
this, it will throw an exception.
It returns an array reference of hash references. The list below shows
which ones are required, along with some common keys which are optional.
Other keys may be added, as well.
=head3 Hash reference keys
=head4 title
=head4 description
=head4 link
This is a url to the item.
=head4 date
An epoch date, an RFC 1123 date, or a date in ISO format (referred to as MySQL format
inside WebGUI)
=head4 author
This is optional.
=head4 guid
This is optional. A unique descriptor for this item.
=cut
@ -405,12 +428,29 @@ sub getFeed {
return $feed;
}
#-------------------------------------------------------------------
=head2 prepareView ()
Extend the master class to insert head links via addHeaderLinks.
=cut
sub prepareView {
my $self = shift;
$self->addHeaderLinks;
return $self->next::method(@_);
}
#-------------------------------------------------------------------
=head2 addHeaderLinks ()
Add RSS, Atom, or RDF links in the HEAD block of the Asset, depending
on how the Asset has configured feedHeaderLinks.
=cut
sub addHeaderLinks {
my $self = shift;
my $style = $self->session->style;

View file

@ -221,7 +221,7 @@ sub toHtml {
|| !$self->get("value")
|| $self->get("value") =~ m/^\d+$/) {
# Epoch format
$value = $self->set("value",$self->session->datetime->epochToSet($self->getOriginalValue));
$value = $self->session->datetime->epochToSet($self->getOriginalValue);
}
else {
# MySQL format

View file

@ -132,6 +132,14 @@ sub getValue {
return WebGUI::HTML::cleanSegment($self->SUPER::getValue(@_));
}
#-------------------------------------------------------------------
=head2 getValueAsHtml ( )
Calls getValueAsHtml from WebGUI::Form::Control
=cut
sub getValueAsHtml {
my $self = shift;
return $self->WebGUI::Form::Control::getValueAsHtml(@_);

View file

@ -256,7 +256,7 @@ sub dateCreated {
=head2 delete ( )
Deletes this group and all references to it.
Deletes this group from the group related tables in the database and calls clearCaches.
=cut

View file

@ -221,7 +221,11 @@ our $HELP = {
{ 'name' => 'searchResult_id' },
{ 'name' => 'searchResult_view_url' },
{ 'name' => 'searchResult_edit_icon' },
{ 'name' => 'searchResult_edit_url' },
{ 'name' => 'searchResult_delete_icon' },
{ 'name' => 'searchResult_delete_url' },
{ 'name' => 'searchResult_copy_icon' },
{ 'name' => 'searchResult_copy_url' },
{ 'name' => 'searchResult_field_loop',
'variables' => [
{ 'name' => 'field_id' },

View file

@ -503,9 +503,11 @@ Disconnects from the database. And destroys the object.
=cut
sub disconnect {
my $self = shift;
$self->dbh->disconnect;
undef $self;
my $self = shift;
my $dbh = delete $self->{_dbh};
if ($dbh) {
$dbh->disconnect;
}
}

View file

@ -807,6 +807,17 @@ selectBox.</p>|
message => q|Matrix Fieldtype|,
},
'too many message' => {
lastUpdated => 0,
message => q|You tried to compare more than your maximum number of listings.|,
context => q|A message shown to the user when they have selected too many listings to compare.|,
},
'too few message' => {
lastUpdated => 0,
message => q|You tried to compare only one listing. If you want to view just one listing, click on its name.|,
context => q|A message shown to the user when they have selected only one listing to compare.|,
}
};
1;

View file

@ -67,6 +67,10 @@ our $I18N = {
message => q|Delete|,
lastUpdated => 1224686319
},
'warnings' => {
message => q|Warnings|,
lastUpdated => 0
},
'section number' => {
message => q|Section Number:|,
lastUpdated => 1224686319
@ -251,9 +255,9 @@ our $I18N = {
lastUpdated => 1224686319
},
'show text in button description' => {
message => q|Select if the buttons of a multiple choice question display the answer values or not.|,
message => q|By default multiple choice answer buttons show the answer text above each button. Change this to have the text appear inside of the buttons.|,
context => q|Description of the 'show text in button' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 0
lastUpdated => 1239251986
},
'allow comment' => {
message => q|Allow comment:|,
@ -300,14 +304,14 @@ our $I18N = {
context => q|Description of the 'required' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 0
},
'question value' => {
message => q|Question value:|,
'question score' => {
message => q|Question score:|,
lastUpdated => 1224686319
},
'question value description' => {
message => q|Enter a value for this question.|,
'question score description' => {
message => q|Default score to use for answers in this question that don't have an answer score value set.|,
context => q|Description of the 'question value' field, used as hoverhelp in the edit question dialog.|,
lastUpdated => 0
lastUpdated => 1239255403
},
'please enter answer information' => {
message => q|Please enter answer information:|,
@ -333,13 +337,13 @@ our $I18N = {
lastUpdated => 0
},
'recorded answer' => {
message => q|Answer title:|,
message => q|Recorded Answer:|,
lastUpdated => 1224686319
},
'recorded answer description' => {
message => q|Text to display inside multiple-choice answer buttons (only if 'Show text in button' is enabled for this question).|,
message => q|Determines what gets recorded as the response value if this answer is selected. Allows you to 'recode' recorded responses, e.g. 'Yes' could be recorded as '1' and 'No' as '0'. Relevant only for Multiple Choice questions (other question types record the input actually entered by the user: free text, selected date, etc..).|,
context => q|Description of the 'recorded answer' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
lastUpdated => 1239251436
},
'jump to' => {
message => q|Jump to:|,
@ -355,9 +359,9 @@ our $I18N = {
lastUpdated => 0
},
'jump expression description' => {
message => q|An expression used to control complex branching based user responses to previous questions. A branch expression is made up of a list of rules, one per line, along with a branch target for each rule. |,
message => q|An expression used to control complex branching based user responses to previous questions. Ignored unless enableSurveyExpressionEngine enabled in your site config file.|,
context => q|Description of the 'jump expression' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
lastUpdated => 1239259550
},
'text answer' => {
message => q|TextArea|,
@ -416,14 +420,14 @@ our $I18N = {
context => q|Description of the 'verbatim' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
},
'answer value' => {
message => q|Answer value:|,
lastUpdated => 1224686319
'answer score' => {
message => q|Answer score:|,
lastUpdated => 1239251986
},
'answer value description' => {
message => q|Assign a numeric scores to this answers. Used in question scoring and jump expressions.|,
context => q|Description of the 'answer value' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 0
'answer score description' => {
message => q|Assign a numeric score to this answer. If blank, the question score value will used instead. Used in question scoring and jump expressions.|,
context => q|Description of the 'answer score' field, used as hoverhelp in the edit answer dialog.|,
lastUpdated => 1239251986
},
'checked' => {
message => q|Checked|,
@ -1282,7 +1286,7 @@ section/answer.|,
},
'textInButton' => {
message => q|A boolean indicating whether the buttons for answers to multiple choice questions should display the answer's text.|,
message => q|A boolean indicating whether the buttons for answers to multiple choice questions should display the answer's text inside or above.|,
context => q|Description of a template variable for a template Help page.|,
lastUpdated => 0,
},
@ -1318,7 +1322,7 @@ section/answer.|,
},
'recordedAnswer' => {
message => q|The value that gets recorded for this answer in the database.|,
message => q|Determines what gets recorded as the response value if this answer is selected. Allows you to 'recode' recorded responses, e.g. 'Yes' could be recorded as '1' and 'No' as '0'. Relevant only for Multiple Choice questions (other question types record the input actually entered by the user: free text, selected date, etc..).|,
context => q|Description of a template variable for a template Help page.|,
lastUpdated => 0,
},

View file

@ -1013,12 +1013,35 @@ search has been done.|,
context => q|Description of a tmpl_var for the template help.|,
},
'searchResult_edit_url' => {
message => q|Url to the edit screen of this search result.|,
lastUpdated => 1104630516,
context => q|Description of a tmpl_var for the template help.|,
},
'searchResult_delete_icon' => {
message => q|Delete icon to delete this search result.|,
lastUpdated => 1104630516,
context => q|Description of a tmpl_var for the template help.|,
},
'searchResult_delete_url' => {
message => q|Url to delete this search result.|,
lastUpdated => 1104630516,
context => q|Description of a tmpl_var for the template help.|,
},
'searchResult_copy_icon' => {
message => q|Copy icon to copy this search result.|,
lastUpdated => 1104630516,
context => q|Description of a tmpl_var for the template help.|,
},
'searchResult_copy_url' => {
message => q|Url to copy this search result.|,
lastUpdated => 1104630516,
},
'searchResult_field_loop' => {
message => q|A loop containing the fields that are to be displayed for this search result.|,
lastUpdated => 1104630516,

View file

@ -63,12 +63,14 @@ $testGroups{'canEdit asset'} = WebGUI::Group->new($session, 'new');
$testUsers{'canEdit group user'} = WebGUI::User->new($session, 'new');
$testUsers{'canEdit group user'}->addToGroups([$testGroups{'canEdit asset'}->getId]);
$testUsers{'canEdit group user'}->username('Edit Group User');
WebGUI::Test->groupsToDelete($testGroups{'canEdit asset'});
##A group and user for groupIdEdit
$testGroups{'canAdd asset'} = WebGUI::Group->new($session, 'new');
$testUsers{'canAdd group user'} = WebGUI::User->new($session, 'new');
$testUsers{'canAdd group user'}->addToGroups([$testGroups{'canAdd asset'}->getId]);
$testUsers{'canEdit group user'}->username('Can Add Group User');
WebGUI::Test->groupsToDelete($testGroups{'canAdd asset'});
my $canAddMaker = WebGUI::Test::Maker::Permission->new();
$canAddMaker->prepare({
@ -819,9 +821,6 @@ END {
foreach my $user (values %testUsers) {
$user->delete;
}
foreach my $group (values %testUsers) {
$group->delete;
}
}
##Return an array of hashrefs. Each hashref describes a test

View file

@ -17,7 +17,7 @@ use WebGUI::Session;
use WebGUI::User;
use WebGUI::Asset;
use Test::More tests => 92; # increment this value for each test you create
use Test::More tests => 90; # increment this value for each test you create
use Test::Deep;
# Test the methods in WebGUI::AssetLineage
@ -195,12 +195,10 @@ is(
#
####################################################
ok(!$snippet2->setParent($folder), 'setParent: user must be in group 4 to do this');
$session->user({userId => $editor->userId});
ok(!$snippet2->setParent(), 'setParent: new parent must be passed in');
ok(!$snippet2->setParent($snippet2), 'setParent: cannot be your own parent');
ok(!$snippet2->setParent($folder2), 'setParent: will not move self to current parent');
ok(!$snippet2->setParent($folder), 'setParent: user cannot edit parent');
$session->user({userId => 3});
ok(!$folder2->setParent($snippet2), 'setParent: will not move self to my child');

View file

@ -60,6 +60,7 @@ my $otherUser = WebGUI::User->new($session, 'new');
my $groupIdEditUser = WebGUI::User->new($session, 'new');
my $groupToEditPost = WebGUI::Group->new($session, $collab->get('groupToEditPost'));
my $groupIdEditGroup = WebGUI::Group->new($session, $collab->get('groupIdEdit'));
WebGUI::Test->groupsToDelete($groupToEditPost, $groupIdEditGroup);
$postingUser->username('userForPosting');
$otherUser->username('otherUser');

View file

@ -0,0 +1,49 @@
#-------------------------------------------------------------------
# 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
#-------------------------------------------------------------------
use FindBin;
use strict;
use lib "$FindBin::Bin/../../lib";
##The goal of this test is to test the creation of Carousel Wobjects.
use WebGUI::Test;
use WebGUI::Session;
use Test::More tests => 2; # increment this value for each test you create
use WebGUI::Asset::Wobject::Carousel;
my $session = WebGUI::Test->session;
# Do our work in the import node
my $node = WebGUI::Asset->getImportNode($session);
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->set({name=>"Search Test"});
my $carousel = $node->addChild({className=>'WebGUI::Asset::Wobject::Carousel'});
# Test for a sane object type
isa_ok($carousel, 'WebGUI::Asset::Wobject::Carousel');
# Test to see if we can set new values
my $newSettings = {
templateId=>'testingtestingtesting1',
};
$carousel->update($newSettings);
foreach my $newSetting (keys %{$newSettings}) {
is ($carousel->get($newSetting), $newSettings->{$newSetting}, "updated $newSetting is ".$newSettings->{$newSetting});
}
END {
# Clean up after thy self
$versionTag->rollback();
}

View file

@ -32,7 +32,7 @@ use WebGUI::Asset::Wobject::Collaboration;
use WebGUI::Asset::Post;
use WebGUI::Asset::Wobject::Layout;
use Data::Dumper;
use Test::More tests => 7; # increment this value for each test you create
use Test::More tests => 10; # increment this value for each test you create
my $session = WebGUI::Test->session;
@ -50,7 +50,10 @@ my $layout = $node->addChild({className => 'WebGUI::Asset::Wobject::Layout'});
$session->asset($layout);
# finally, add the collab
my $collab = $layout->addChild({className => 'WebGUI::Asset::Wobject::Collaboration'});
my $collab = $layout->addChild({
className => 'WebGUI::Asset::Wobject::Collaboration',
url => 'collab',
});
# Test for a sane object type
isa_ok($collab, 'WebGUI::Asset::Wobject::Collaboration');
@ -91,6 +94,11 @@ $post = $collab->addChild($props,
my $rssitems = $collab->getRssFeedItems();
is(scalar @{ $rssitems }, 2, 'rssitems set to number of posts added');
diag "AssetAspect tests";
is($collab->getRssFeedUrl, '/collab?func=viewRss', 'getRssFeedUrl');
is($collab->getRdfFeedUrl, '/collab?func=viewRdf', 'getRdfFeedUrl');
is($collab->getAtomFeedUrl, '/collab?func=viewAtom', 'getAtomFeedUrl');
TODO: {
local $TODO = "Tests to make later";
ok(0, 'A whole lot more work to do here');

View file

@ -22,23 +22,24 @@ my $session = WebGUI::Test->session;
#----------------------------------------------------------------------------
# Tests
my $tests = 36;
my $tests = 41;
plan tests => $tests + 1;
#----------------------------------------------------------------------------
# put your tests here
my $usedOk = use_ok('WebGUI::Asset::Wobject::Survey::ExpressionEngine');
my ($user, $survey, $versionTag);
SKIP: {
skip $tests, "Unable to load ExpressionEngine" unless $usedOk;
my $e = "WebGUI::Asset::Wobject::Survey::ExpressionEngine";
WebGUI::Test->originalConfig('enableSurveyExpressionEngine');
$session->config->set( 'enableSurveyExpressionEngine', 0 );
is( $e->run( $session, 'jump { 1 } target' ),
undef, "Nothing happens unless we turn on enableSurveyExpressionEngine in config" );
WebGUI::Test->originalConfig('enableSurveyExpressionEngine');
$session->config->set( 'enableSurveyExpressionEngine', 1 );
is( $e->run( $session, 'jump { 1 } target' ), 'target', "..now we're in business!" );
@ -109,8 +110,63 @@ SKIP: {
undef, 'target is not valid' );
is( $e->run( $session, q{jump {1} target}, { values => \%values, validTargets => { target => 1 } } ),
'target', '..whereas now it is ok' );
# Create a test user
$user = WebGUI::User->new( $session, 'new' );
# Create a Survey
$versionTag = WebGUI::VersionTag->getWorking($session);
$survey = WebGUI::Asset->getImportNode($session)->addChild(
{ className => 'WebGUI::Asset::Wobject::Survey',
},
);
isa_ok($survey, 'WebGUI::Asset::Wobject::Survey');
my $url = $survey->get('url');
my $id = $survey->getId;
$survey->surveyJSON->newObject([]); # s0
$survey->surveyJSON->newObject([0]); # s0q0
$survey->surveyJSON->newObject([0,0]); # s0q0a0
$survey->surveyJSON->newObject([0]); # s0q1
$survey->surveyJSON->newObject([0,1]); # s0q1a0
$survey->surveyJSON->section([0])->{variable} = 'ext_s0';
$survey->surveyJSON->question([0,0])->{variable} = 'ext_s0q0';
$survey->surveyJSON->question([0,1])->{variable} = 'ext_s0q1';
$survey->surveyJSON->answer([0,0,0])->{recordedAnswer} = 'ext_s0q0a0';
$survey->surveyJSON->answer([0,0,0])->{value} = 150; # worth 150 points
$survey->surveyJSON->answer([0,1,0])->{recordedAnswer} = 'ext_s0q1a0';
$survey->surveyJSON->answer([0,1,0])->{value} = 50; # worth 50 points
$survey->responseIdCookies(0); # disable cookies so that test code doesn't die
my $responseId = $survey->responseId($user->userId);
my $rJSON = $survey->responseJSON(undef, $responseId);
$rJSON->recordResponses({
'0-0-0' => 'My ext_s0q0a0 answer',
'0-1-0' => 'My ext_s0q1a0 answer',
});
# Remember to persist our changes..
$survey->persistSurveyJSON();
$survey->persistResponseJSON();
$survey->surveyEnd;
is( $e->run( $session, qq{jump {value('$id', ext_s0q0) eq 'ext_s0q0a0'} target}, {userId => $user->userId} ),
'target', 'external value resolves ok when id used' );
is( $e->run( $session, qq{jump {value('$url', ext_s0q0) eq 'ext_s0q0a0'} target}, {userId => $user->userId} ),
'target', 'external value resolves ok when url used' );
is( $e->run( $session, qq{jump {score('$url', ext_s0q0) == 150} target}, {userId => $user->userId} ),
'target', 'external score resolves ok too' );
is( $e->run( $session, qq{jump {score('$url', ext_s0) == 200} target}, {userId => $user->userId} ),
'target', 'external score section totals work too' );
}
#----------------------------------------------------------------------------
# Cleanup
END { }
END {
$user->delete if $user;
$survey->purge if $survey;
$versionTag->rollback if $versionTag;
}

View file

@ -233,6 +233,7 @@ my $gB = WebGUI::Group->new($session, "new");
$gA->name('Group A');
$gB->name('Group B');
ok( ($gA->name eq 'Group A' and $gB->name eq 'Group B'), 'object name assignment, multiple objects');
WebGUI::Test->groupsToDelete($gA, $gB);
$gB->addGroups([$gA->getId]);
@ -253,6 +254,7 @@ cmp_bag($gA->getGroupsIn(), [3], 'Not allowed to add myself to my group');
my $gC = WebGUI::Group->new($session, "new");
$gC->name('Group C');
$gA->addGroups([$gC->getId]);
WebGUI::Test->groupsToDelete($gC);
cmp_bag($gC->getGroupsFor(), [$gA->getId], 'Group A contains Group C');
cmp_bag($gA->getGroupsIn(), [$gC->getId, 3], 'Group C is a member of Group A, cached');
@ -279,6 +281,7 @@ my $gZ = WebGUI::Group->new($session, "new");
$gX->name('Group X');
$gY->name('Group Y');
$gZ->name('Group Z');
WebGUI::Test->groupsToDelete($gX, $gY, $gZ);
$gZ->addGroups([$gX->getId, $gY->getId]);
@ -439,6 +442,7 @@ ok( isIn($mob[0]->userId, @{ $gZ->getAllUsers() }), 'mob[0] in list of group Z u
my $gK = WebGUI::Group->new($session, "new");
$gK->name('Group K');
$gC->addGroups([$gK->getId]);
WebGUI::Test->groupsToDelete($gK);
# B
# / \
@ -498,6 +502,7 @@ $session->setting->set('useKarma', $defaultKarmaSetting);
my $gS = WebGUI::Group->new($session, "new");
$gS->name('Group S');
$gC->addGroups([$gS->getId]);
WebGUI::Test->groupsToDelete($gS);
# B
# / \
@ -579,6 +584,7 @@ foreach my $idx (0..$#ipTests) {
}
my $gI = WebGUI::Group->new($session, "new");
WebGUI::Test->groupsToDelete($gI);
$gI->name('Group I');
$gI->ipFilter('194.168.0.0/24');
@ -614,6 +620,7 @@ ok( !$cacheDude->isInGroup($gY->getId), "Cache dude removed from group Y");
ok( !$cacheDude->isInGroup($gZ->getId), "Cache dude removed from group Z too");
my $gCache = WebGUI::Group->new($session, "new");
WebGUI::Test->groupsToDelete($gCache);
$gCache->addUsers([$cacheDude->userId]);
@ -642,10 +649,13 @@ SKIP: {
ok(undef, "expiration date in groupings for getUser");
}
################################################################
#
# getUserList
#
################################################################
END {
foreach my $testGroup ($gX, $gY, $gZ, $gA, $gB, $gI, $gC, $g, $gK, $gS, $gCache) {
$testGroup->delete if (defined $testGroup and ref $testGroup eq 'WebGUI::Group');
}
foreach my $dude ($user, @crowd, @mob, @chameleons, @itchies, @tcps, $cacheDude) {
$dude->delete if (defined $dude and ref $dude eq 'WebGUI::User');
}

View file

@ -53,12 +53,6 @@ my @testArray = (
output => 0,
comment => "s.also Privoxy/3.0 (Anonymous)"
},
{
agent => "*/Nutch-0.9-dev",
address => "123.113.184.232",
output => 1,
comment => "Unknown Yahoo robot"
},
{
agent => "123spider-Bot (Version: 1.02&#44; powered by www.123spider.de",
output => 1,

View file

@ -51,6 +51,8 @@ our $logger_error;
my %originalConfig;
my $originalSetting;
my @groupsToDelete;
BEGIN {
STDERR->autoflush(1);
@ -129,12 +131,15 @@ BEGIN {
$SESSION = WebGUI::Session->open( $WEBGUI_ROOT, $CONFIG_FILE );
$SESSION->{_request} = $pseudoRequest;
$originalSetting = clone $SESSION->setting;
$originalSetting = clone $SESSION->setting->get;
}
END {
my $Test = Test::Builder->new;
foreach my $group (@groupsToDelete) {
$group->delete;
}
if ($ENV{WEBGUI_TEST_DEBUG}) {
$Test->diag('Sessions: '.$SESSION->db->quickScalar('select count(*) from userSession'));
$Test->diag('Scratch : '.$SESSION->db->quickScalar('select count(*) from userSessionScratch'));
@ -149,8 +154,7 @@ END {
$SESSION->config->delete($key);
}
}
my $settings = $originalSetting->get();
while (my ($param, $value) = each %{ $settings }) {
while (my ($param, $value) = each %{ $originalSetting }) {
$SESSION->setting->set($param, $value);
}
$SESSION->var->end;
@ -384,6 +388,20 @@ sub originalConfig {
#----------------------------------------------------------------------------
=head2 groupsToDelete ( $group, [$group ] )
Push a list of group objects onto the stack of groups to be automatically deleted
at the end of the test.
=cut
sub groupsToDelete {
my $class = shift;
push @groupsToDelete, @_;
}
#----------------------------------------------------------------------------
=head1 BUGS
When trying to load the APR module, perl invariably throws an Out Of Memory

View file

@ -8,7 +8,7 @@ YAHOO.util.Event.addListener(window, "load", function() {
if(typeof(oRecord.getData("checked")) != 'undefined' && oRecord.getData("checked") == 'checked'){
innerHTML = innerHTML + " checked='checked'";
}
innerHTML = innerHTML + " onchange='javascript:compareFormButton()' class='compareCheckBox'>";
innerHTML = innerHTML + " class='compareCheckBox'>";
elCell.innerHTML = innerHTML;
};
@ -77,12 +77,21 @@ YAHOO.util.Event.addListener(window, "load", function() {
},this,true);
}
if(document.getElementById("sortByName")){
var btnSortByName = new YAHOO.widget.Button("sortByName");
btnSortByName.on("click", function(e) {
this.myDataTable.sortColumn(this.myDataTable.getColumn(1));
var request = YAHOO.util.Connect.asyncRequest('POST', matrixUrl + "?func=setSort;sort=lastUpdated");
},this,true);
}
var myCallback = function() {
this.set("sortedBy", null);
this.onDataReturnAppendRows.apply(this,arguments);
};
window.compareFormButton = function() {
var compareFormButton = function() {
var compareCheckBoxes = YAHOO.util.Dom.getElementsByClassName('compareCheckBox','input');
var checked = 0;
var checkedCompareBoxes = new Object();
@ -92,12 +101,12 @@ YAHOO.util.Event.addListener(window, "load", function() {
checkedCompareBoxes[compareCheckBoxes[i].value] = true;
}
}
if (checked > 1 && checked < maxComparisons){
btnCompare.set("disabled",false);
btnCompare2.set("disabled",false);
if (checked < 2){
alert(tooFewMessage);
}else if (checked > maxComparisons){
alert(tooManyMessage);
}else{
btnCompare.set("disabled",true);
btnCompare2.set("disabled",true);
window.document.forms['doCompare'].submit();
}
var elements = window.compareDataTable.getRecordSet().getRecords();
for(j=0; j<elements.length; j++){
@ -109,21 +118,17 @@ YAHOO.util.Event.addListener(window, "load", function() {
}
}
}
if(document.getElementById("compare")){
var btnCompare = new YAHOO.widget.Button("compare",{disabled:true,id:"compareButton"});
btnCompare.on("click", function(e) {
window.document.forms['doCompare'].submit();
},this,true);
var btnCompare = new YAHOO.widget.Button("compare",{id:"compareButton"});
btnCompare.on("click", compareFormButton);
}
if(document.getElementById("compare2")){
var btnCompare2 = new YAHOO.widget.Button("compare2",{disabled:true,id:"compareButton2"});
btnCompare2.on("click", function(e) {
window.document.forms['doCompare'].submit();
},this,true);
var btnCompare2 = new YAHOO.widget.Button("compare2",{id:"compareButton2"});
btnCompare2.on("click", compareFormButton);
}
if(document.getElementById("search")){
var btnSearch = new YAHOO.widget.Button("search");
btnSearch.on("click", function(e) {

View file

@ -104,58 +104,40 @@ YAHOO.util.Event.addListener(window, "load", function() {
scope : myDataTable
};
var btnCompare = new YAHOO.widget.Button("compare",{disabled:true,id:"compareButton"});
btnCompare.on("click", function(e) {
var compareCheckBoxes = YAHOO.util.Dom.getElementsByClassName('compareCheckBox','input');
var uri = "func=getCompareListData";
for (var i = compareCheckBoxes.length; i--; ) {
if(compareCheckBoxes[i].checked == true){
uri = uri+';listingId='+compareCheckBoxes[i].value;
}
}
myDataTable.getRecordSet().reset();
myDataTable.refreshView();
myDataTable.showTableMessage('Loading...');
this.myDataSource.sendRequest(uri,callback2);
},this,true);
var btnCompare2 = new YAHOO.widget.Button("compare2",{disabled:true,id:"compareButton2"});
btnCompare2.on("click", function(e) {
var compareCheckBoxes = YAHOO.util.Dom.getElementsByClassName('compareCheckBox','input');
var uri = "func=getCompareListData";
for (var i = compareCheckBoxes.length; i--; ) {
if(compareCheckBoxes[i].checked == true){
uri = uri+';listingId='+compareCheckBoxes[i].value;
}
}
myDataTable.getRecordSet().reset();
myDataTable.refreshView();
myDataTable.showTableMessage('Loading...');
this.myDataSource.sendRequest(uri,callback2);
},this,true);
var btnSearch = new YAHOO.widget.Button("search");
btnSearch.on("click", function(e) {
window.location.href = matrixUrl + '?func=search';
},this,true);
window.compareFormButton = function() {
if(document.getElementById("compare3")){
var btnCompare3 = new YAHOO.widget.Button("compare3",{id:"compareButton3"});
btnCompare3.on("click", function(e) {
var compareCheckBoxes = YAHOO.util.Dom.getElementsByClassName('compareCheckBox','input');
var checked = 0;
var checkedCompareBoxes = new Object();
for (var i = compareCheckBoxes.length; i--; ) {
if(compareCheckBoxes[i].checked){
checked++;
checkedCompareBoxes[compareCheckBoxes[i].value] = true;
}
}
if (checked > 1 && checked < maxComparisons){
btnCompare.set("disabled",false);
btnCompare2.set("disabled",false);
if (checked < 2){
alert(tooFewMessage);
}else if (checked > maxComparisons){
alert(tooManyMessage);
}else{
btnCompare.set("disabled",true);
btnCompare2.set("disabled",true);
//window.document.forms['doCompare'].submit();
var uri = "func=getCompareListData";
for (var i = compareCheckBoxes.length; i--; ) {
if(compareCheckBoxes[i].checked == true){
uri = uri+';listingId='+compareCheckBoxes[i].value;
}
}
myDataTable.getRecordSet().reset();
myDataTable.refreshView();
myDataTable.showTableMessage('Loading...');
this.myDataSource.sendRequest(uri,callback2);
}
},this,true);
}
if(document.getElementById("stickied")){
var btnStickied = new YAHOO.widget.Button("stickied");
btnStickied.on("click", function(e) {
var elements = myDataTable.getRecordSet().getRecords();
@ -187,6 +169,7 @@ YAHOO.util.Event.addListener(window, "load", function() {
hideStickies = 0;
}
},this,true);
}
};
});

View file

@ -44,6 +44,9 @@ YAHOO.util.Event.addListener(window, "load", function() {
};
var uri = "func=getAttributes";
if(typeof(revision) != 'undefined'){
uri = uri + ';revision=' + revision;
}
var initAttributeHoverHelp = function() {
initHoverHelp('attributes');

View file

@ -10,7 +10,7 @@ YAHOO.util.Event.addListener(window, "load", function() {
if(typeof(oRecord.getData("checked")) != 'undefined' && oRecord.getData("checked") == 'checked'){
innerHTML = innerHTML + " checked='checked'";
}
innerHTML = innerHTML + " onchange='javascript:compareFormButton()' class='compareCheckBox'>";
innerHTML = innerHTML + " class='compareCheckBox'>";
elCell.innerHTML = innerHTML;
};
@ -42,7 +42,6 @@ YAHOO.util.Event.addListener(window, "load", function() {
var myCallback = function() {
this.set("sortedBy", null);
this.onDataReturnAppendRows.apply(this,arguments);
compareFormButton();
};
var callback2 = {
@ -72,12 +71,8 @@ YAHOO.util.Event.addListener(window, "load", function() {
attributeSelects[i].onchange = reloadCompareForm;
}
var btnCompare = new YAHOO.widget.Button("compare",{disabled:true,id:"compareButton"});
var btnCompare = new YAHOO.widget.Button("compare",{id:"compareButton"});
btnCompare.on("click", function(e) {
window.document.forms['doCompare'].submit();
},this,true);
window.compareFormButton = function() {
var compareCheckBoxes = YAHOO.util.Dom.getElementsByClassName('compareCheckBox','input');
var checked = 0;
for (var i = compareCheckBoxes.length; i--; ) {
@ -85,12 +80,15 @@ YAHOO.util.Event.addListener(window, "load", function() {
checked++;
}
}
if (checked > 1 && checked < maxComparisons){
btnCompare.set("disabled",false);
if (checked < 2){
alert(tooFewMessage);
}else if (checked > maxComparisons){
alert(tooManyMessage);
}else{
btnCompare.set("disabled",true);
window.document.forms['doCompare'].submit();
}
}
},this,true);
};
});

View file

@ -324,14 +324,127 @@ if (typeof Survey === "undefined") {
}
*/
}
YAHOO.widget.Chart.SWFURL = "/extras/yui/build/charts/assets/charts.swf";
// Public API
Survey.Form = {
showSummary: function(html){
Survey.Summary = {
globalSummaryDataTip: function(item, index, series){
var toolTipText = "hello";
//var toolTipText = series.displayName + " for " + item.section;
//toolTipText += "\n" + item[series.yField];
return toolTipText;
},
showSummary: function(summary,html){
var html = html;
document.getElementById('survey').innerHTML = html;
//Add totoal summary pie chart
totalSummary =
[
{ correct: "Correct", count: summary.totalCorrect },
{ correct: "Incorrect", count: summary.totalIncorrect }
]
var totalSummaryDS = new YAHOO.util.DataSource( totalSummary );
totalSummaryDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
totalSummaryDS.responseSchema = { fields: [ "correct", "count" ] };
new YAHOO.widget.PieChart( "chart", totalSummaryDS,
{
dataField: "count",
categoryField: "correct",
style:
{
padding: 10,
legend:
{
display: "left",
padding: 10,
spacing: 2,
font:
{
family: "Arial",
size: 13
}
}
},
//only needed for flash player express install
expressInstall: "/extras/yui/build/charts/assets/charts.swf"
});
//define section datatable columns
var myColumnDefs = [
{key:"Question ID", sortable:true, resizeable:true},
{key:"Question Text", formatter: YAHOO.widget.DataTable.formatText, sortable:true, resizeable:true},
{key:"Answer ID", sortable:true, resizeable:true},
{key:"Correct", sortable:true, resizeable:true},
{key:"Answer Text", formatter: YAHOO.widget.DataTable.formatText, sortable:true, resizeable:true},
{key:"Score", sortable:true, resizeable:true},
{key:"Value", formatter: YAHOO.widget.DataTable.formatText, sortable:true, resizeable:true}
];
var sectionSummary = [];
//Load up datatables and create section data for bar chart
for(var i = 0; i < summary.sections.length; i++){
var temp = summary.sections[i];
sectionSummary[sectionSummary.length] = {"Total Responses": temp.total, "Correct": temp.correct, "Incorrect": temp.inCorrect, "section": (i+1)};
var myDataSource = new YAHOO.util.DataSource(summary.sections[i].responses);
//These needs to be put in a destroy call list for when the html dom is recreated, if summaries are going to be uses with page reloads, else memory leak.
myDataSource.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
myDataSource.responseSchema = {
fields: ["Question ID","Question Text","Answer ID","Correct","Answer Text","Score","Value"]
};
var tempText = "section"+ (i+1) + "datatable";
new YAHOO.widget.DataTable(tempText, myColumnDefs, myDataSource, {caption:"Section "+(i+1)});
}
//Now create section summary bar charts
var sectionSummaryDS = new YAHOO.util.DataSource( sectionSummary );
sectionSummaryDS.responseType = YAHOO.util.DataSource.TYPE_JSARRAY;
sectionSummaryDS.responseSchema =
{
fields: [ "Total Responses", "Correct", "Incorrect", "section" ]
};
var sectionSummarySeriesDef =
[
{
displayName: "Total Responses",
yField: "Total Responses",
style:{size:10}
},
{
displayName: "Correct",
yField: "Correct",
style:{size:10}
},
{
displayName: "Incorrect",
yField: "Incorrect",
style:{size:10}
}
];
//create a Numeric Axis for displaying dollars
var responseAxis = new YAHOO.widget.NumericAxis();
responseAxis.title = "Responses";
//create Category Axis to specify a title for the months
var sectionAxis = new YAHOO.widget.CategoryAxis();
sectionAxis.title = "Sections";
//create the Chart
var mychart = new YAHOO.widget.ColumnChart( "summarychart", sectionSummaryDS,
{
series: sectionSummarySeriesDef,
xField: "section",
xAxis: sectionAxis,
yAxis: responseAxis,
// dataTipFunction: Survey.Form.globalSummaryDataTip, //try again in 2.7
expressInstall: "/extras/yui/build/charts/assets/charts.swf"
});
YAHOO.util.Event.addListener("submitbutton", "click", function(){ Survey.Comm.submitSummary(); });
},
}
};
Survey.Form = {
displayQuestions: function(params){
toValidate = [];
var qs = params.questions;
@ -530,3 +643,11 @@ YAHOO.util.Event.onDOMReady(function(){
// Survey.Comm.setUrl('/' + document.getElementById('assetPath').value);
Survey.Comm.callServer('', 'loadQuestions');
});
YAHOO.example.getDataTipText = function( item, index, series )
{
var toolTipText = series.displayName + " for " + item.month;
// toolTipText += "\n" + YAHOO.example.formatCurrencyAxisLabel( item[series.yField] );
return toolTipText;
}

View file

@ -55,7 +55,7 @@ if (typeof Survey === "undefined") {
window.location = url;
}
else if(response.type === 'summary'){
Survey.Form.showSummary(response.summary);
Survey.Summary.showSummary(response.summary,response.html);
}
else {
alert("bad response");

View file

@ -56,9 +56,17 @@ Survey.Data = (function(){
focus = d.address;//What is the current highlighted item.
var warnings = "";
for(var w in d.warnings){
warnings = warnings + "<br>" + d.warnings[w];
warnings += "<div class='warning'>" + d.warnings[w] + "</div>";
}
if (document.getElementById('warnings')) {
if (warnings !== "") {
document.getElementById('warnings').innerHTML = warnings;
YAHOO.util.Dom.setStyle('warnings-outer', 'display', 'block');
}
else {
YAHOO.util.Dom.setStyle('warnings-outer', 'display', 'none');
}
}
document.getElementById('warnings').innerHTML = warnings;
var showEdit = 1;
if (lastId.toString() === d.address.toString()) {
showEdit = 0;

View file

@ -171,3 +171,13 @@ li.newAnswer {
width:15em;
margin-top:0.5em;
}
#warnings-outer {
margin: 5px;
padding: 0 5px;
border: 1px solid;
color: red;
}
#warnings {
color: red;
}

View file

@ -223,8 +223,10 @@ function dragable_init(url) {
}else {
for (i = 0; i< children.length;i++) {
draggableObjectList[draggableObjectList.length] = children[i];
new YAHOO.webgui.DDList(document.getElementById(children[i].id + "_div"));
new YAHOO.util.DDTarget(document.getElementById(children[i].id + "_div"));
dragDropElement = document.getElementById(children[i].id + "_div");
dragDrop = new YAHOO.webgui.DDList(dragDropElement);
new YAHOO.util.DDTarget(dragDropElement);
dragDrop.setHandleElId(children[i].id + "_handle");
}
}
obj = document.getElementById("position" + contentCount);