From c3ea6d46835b6f12e226a2f0540af0ebd7afb404 Mon Sep 17 00:00:00 2001 From: Patrick Donelan Date: Tue, 21 Apr 2009 07:12:07 +0000 Subject: [PATCH] Added Survey back button, with option to turn it on/off, tests, and i18n Noticed that some Survey tests are broken, will fix tomorrow --- docs/changelog/7.x.x.txt | 1 + ...root_import_survey_default-questions.wgpkg | Bin 2861 -> 2902 bytes docs/upgrades/upgrade_7.7.3-7.7.4.pl | 8 +- lib/WebGUI/Asset/Wobject/Survey.pm | 58 +++++-- .../Asset/Wobject/Survey/ExpressionEngine.pm | 8 +- .../Asset/Wobject/Survey/ResponseJSON.pm | 161 +++++++++++------- lib/WebGUI/Help/Asset_Survey.pm | 2 + lib/WebGUI/i18n/English/Asset_Survey.pm | 28 +++ t/Asset/Wobject/Survey/ResponseJSON.t | 102 ++++++++++- www/extras/wobject/Survey/administersurvey.js | 7 + 10 files changed, 294 insertions(+), 81 deletions(-) diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 07aa04219..8e85e5170 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -34,6 +34,7 @@ - added: ThingyRecord allows you to sell records in a Thingy (like a classified ad) - fixed: #10109: Matrix 2.0 - Updates to product listing by maintainer account require admin approval - fixed #10146: Thingy duplicate errors + - Added Survey back button 7.7.3 - fixed #10094: double explanation in thread help diff --git a/docs/upgrades/packages-7.7.4/root_import_survey_default-questions.wgpkg b/docs/upgrades/packages-7.7.4/root_import_survey_default-questions.wgpkg index 7aa8955f32511107f774ec9d5df08f106f157f77..374cb3e1486a65eab5c972d9f59c39d2c8af3744 100644 GIT binary patch literal 2902 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 A4FCWD literal 2861 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"alter table Survey add column `allowBackBtn` TINYINT(3)"); + print "Done.\n" unless $quiet; +} # -------------- DO NOT EDIT BELOW THIS LINE -------------------------------- diff --git a/lib/WebGUI/Asset/Wobject/Survey.pm b/lib/WebGUI/Asset/Wobject/Survey.pm index 4223db2dc..b5606fee6 100644 --- a/lib/WebGUI/Asset/Wobject/Survey.pm +++ b/lib/WebGUI/Asset/Wobject/Survey.pm @@ -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', @@ -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,13 +1341,6 @@ 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, 'Number', 1 ); my %slider = ( 'Slider', 1, 'Dual Slider - Range', 1, 'Multi Slider - Allocate', 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') ); diff --git a/lib/WebGUI/Asset/Wobject/Survey/ExpressionEngine.pm b/lib/WebGUI/Asset/Wobject/Survey/ExpressionEngine.pm index e4bfb70f1..7d0742aee 100644 --- a/lib/WebGUI/Asset/Wobject/Survey/ExpressionEngine.pm +++ b/lib/WebGUI/Asset/Wobject/Survey/ExpressionEngine.pm @@ -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 } diff --git a/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm b/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm index 5ddce7f2a..422e423e8 100644 --- a/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm +++ b/lib/WebGUI/Asset/Wobject/Survey/ResponseJSON.pm @@ -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), stored in the order -in which items are presented to the user. - -By making use of L 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. - -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,29 @@ 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 is an array (reference) of Survey addresses (see +L), stored in the order +in which items are presented to the user. + +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. + +By making use of L 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 @@ -1242,11 +1210,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. + + { + # 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 @@ -1259,6 +1248,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 diff --git a/lib/WebGUI/Help/Asset_Survey.pm b/lib/WebGUI/Help/Asset_Survey.pm index 3aa6e4543..6979a5ef4 100644 --- a/lib/WebGUI/Help/Asset_Survey.pm +++ b/lib/WebGUI/Help/Asset_Survey.pm @@ -119,6 +119,8 @@ our $HELP = { { 'name' => 'showProgress' }, { 'name' => 'showTimeLimit' }, { 'name' => 'minutesLeft' }, + { 'name' => 'isLastPage' }, + { 'name' => 'allowBackBtn' }, { 'name' => 'questions', 'variables' => [ { 'name' => 'id' }, diff --git a/lib/WebGUI/i18n/English/Asset_Survey.pm b/lib/WebGUI/i18n/English/Asset_Survey.pm index 992cb3ae4..4288c1dcf 100644 --- a/lib/WebGUI/i18n/English/Asset_Survey.pm +++ b/lib/WebGUI/i18n/English/Asset_Survey.pm @@ -576,6 +576,16 @@ the time limit for completing the survey. This message is in the 'take survey' t message => q|The template used to display the Survey Edit screen.|, lastUpdated => 0, }, + + 'Allow back button' => { + message => q|Allow back button|, + lastUpdated => 0, + }, + + 'Allow back button help' => { + message => q|Allow the user to navigate backwards in a Survey.|, + lastUpdated => 0, + }, 'Max user responses' => { message => q|Max user responses|, @@ -1380,6 +1390,24 @@ section/answer.|, context => q|Sub-label for "Year Month" question type|, lastUpdated => 0, }, + + 'back' => { + message => q|Back|, + context => q|Back button label on Take Survey page|, + lastUpdated => 0, + }, + + 'continue' => { + message => q|Continue|, + context => q|Continue button label on Take Survey page|, + lastUpdated => 0, + }, + + 'finish' => { + message => q|Finish|, + context => q|Finish button label on Take Survey page|, + lastUpdated => 0, + }, }; diff --git a/t/Asset/Wobject/Survey/ResponseJSON.t b/t/Asset/Wobject/Survey/ResponseJSON.t index 5d8913e6f..54535617d 100644 --- a/t/Asset/Wobject/Survey/ResponseJSON.t +++ b/t/Asset/Wobject/Survey/ResponseJSON.t @@ -22,7 +22,7 @@ my $session = WebGUI::Test->session; #---------------------------------------------------------------------------- # Tests -my $tests = 64; +my $tests = 74; plan tests => $tests + 1; #---------------------------------------------------------------------------- @@ -561,9 +561,103 @@ cmp_deeply( 'recordResponses: if the answer is all whitespace, it is skipped over' ); is($rJSON->questionsAnswered, 0, 'question was all whitespace, not answered'); -#delete $rJSON->{_session}; -#delete $rJSON->survey->{_session}; -#diag(Dumper($rJSON)); + +#################################################### +# +# pop +# +#################################################### +$rJSON->responses({}); +$rJSON->lastResponse(2); +is($rJSON->pop, undef, 'pop with no responses returns undef'); +cmp_deeply($rJSON->responses, {}, 'initially no responses'); +$rJSON->recordResponses({ + '1-0comment' => 'Section 1, question 0 comment', + '1-0-0' => 'First answer', + '1-0-0comment' => 'Section 1, question 0, answer 0 comment', + '1-1comment' => 'Section 1, question 1 comment', + '1-1-0' => 'Second answer', + '1-1-0comment' => 'Section 1, question 1, answer 0 comment', + +}); +my $popped = $rJSON->pop; +cmp_deeply($popped, { + # the first q answer + '1-0-0' => { + value => 1, + comment => 'Section 1, question 0, answer 0 comment', + time => num(time(), 3), + }, + # the second q answer + '1-1-0' => { + value => 0, + comment => 'Section 1, question 1, answer 0 comment', + time => num(time(), 3), + }, + # the first question comment + '1-0' => { + comment => 'Section 1, question 0 comment', + }, + # the second question comment + '1-1' => { + comment => 'Section 1, question 1 comment', + } +}, 'pop removes only existing response'); +cmp_deeply($rJSON->responses, {}, 'and now back to no responses'); +is($rJSON->pop, undef, 'additional pop has no effect'); + +$rJSON->responses({}); +$rJSON->lastResponse(2); +$rJSON->recordResponses({ + '1-0comment' => 'Section 1, question 0 comment', + '1-0-0' => 'First answer', + '1-0-0comment' => 'Section 1, question 0, answer 0 comment', + '1-1comment' => 'Section 1, question 1 comment', + '1-1-0' => 'Second answer', + '1-1-0comment' => 'Section 1, question 1, answer 0 comment', +}); + +# fake time so that pop thinks first response happened earlier +$rJSON->responses->{'1-0-0'}->{time} -= 1; +cmp_deeply($rJSON->pop, { + # the second q answer + '1-1-0' => { + value => 0, + comment => 'Section 1, question 1, answer 0 comment', + time => num(time(), 3), + }, + # the second question comment + '1-1' => { + comment => 'Section 1, question 1 comment', + } +}, 'pop now only removes the most recent response'); +cmp_deeply($rJSON->responses, { + # the first q answer + '1-0-0' => { + value => 1, + comment => 'Section 1, question 0, answer 0 comment', + time => num(time(), 3), + }, + # the first question comment + '1-0' => { + comment => 'Section 1, question 0 comment', + }, + }, 'and first response left in tact'); +cmp_deeply($rJSON->pop, { + # the first q answer + '1-0-0' => { + value => 1, + comment => 'Section 1, question 0, answer 0 comment', + time => num(time(), 3), + }, + # the first question comment + '1-0' => { + comment => 'Section 1, question 0 comment', + }, +}, 'second pop removes first response'); +cmp_deeply($rJSON->responses, {}, '..and now responses hash empty again'); + +is($rJSON->pop, undef, 'additional pop has no effect'); } diff --git a/www/extras/wobject/Survey/administersurvey.js b/www/extras/wobject/Survey/administersurvey.js index 8aacfd901..7c71b016a 100644 --- a/www/extras/wobject/Survey/administersurvey.js +++ b/www/extras/wobject/Survey/administersurvey.js @@ -129,6 +129,11 @@ if (typeof Survey === "undefined") { } } + function goBack(event){ + YAHOO.log("Going back"); + Survey.Comm.callServer('', 'goBack'); + } + //an object which creates sliders for allocation type questions and then manages their events and keeps them from overallocating function sliderManager(q, t){ var total = sliderWidth; @@ -527,6 +532,7 @@ if (typeof Survey === "undefined") { span.style.display = 'block'; document.getElementById('survey-header').appendChild(span); + YAHOO.util.Event.addListener("showQuestionsButton", "click", function(){ document.getElementById('showQuestionsButton').style.display = 'none'; if (s.everyPageTitle !== '1') { @@ -696,6 +702,7 @@ if (typeof Survey === "undefined") { butts.push(b); } } + YAHOO.util.Event.addListener("backbutton", "click", goBack); YAHOO.util.Event.addListener("submitbutton", "click", formsubmit); } };