Merge with HEAD, 10472
This commit is contained in:
commit
19f703dc9b
102 changed files with 5700 additions and 2269 deletions
|
|
@ -1532,11 +1532,26 @@ sub www_process {
|
|||
my $default = $field->{defaultValue};
|
||||
WebGUI::Macro::process($self->session, \$default);
|
||||
my $value = $entry->field( $field->{name} ) || $default;
|
||||
|
||||
# WebGUI::Form::Integer::getValue() returns 0 even if no number is passed in.
|
||||
# Not really a suitable default if we want to trigger the error message
|
||||
|
||||
if ($field->{status} eq "required" || $field->{status} eq "editable") {
|
||||
|
||||
# get the raw value (by sending field type as blank)
|
||||
my $rawValue = $session->form->process($field->{name}, '');
|
||||
|
||||
$value = $session->form->process($field->{name}, $field->{type}, undef, {
|
||||
defaultValue => $default,
|
||||
value => $value,
|
||||
});
|
||||
|
||||
# this is a hack, but it's better than changing the default getValue() of Integer, which
|
||||
# could have massive effects downstream in other uses.
|
||||
if(($field->{type} =~ /integer/i) && defined($rawValue) && ($rawValue eq '') && ($value eq "0")) {
|
||||
$value = $rawValue;
|
||||
}
|
||||
|
||||
WebGUI::Macro::filter(\$value);
|
||||
}
|
||||
if ($field->{status} eq "required" && (! defined($value) || $value =~ /^\s*$/)) {
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ sub prepareView {
|
|||
}
|
||||
|
||||
my %vars;
|
||||
$vars{showAdmin} = ($session->var->isAdminOn && $self->canEdit && $self->canEditIfLocked);
|
||||
|
||||
my $splitter = $self->{_viewSplitter} = $self->getSeparator;
|
||||
|
||||
|
|
@ -194,9 +195,12 @@ sub prepareView {
|
|||
$child->prepareView;
|
||||
$placeHolder{$assetId} = $child;
|
||||
push @children, {
|
||||
id => $assetId,
|
||||
isUncommitted => $child->get('status') eq 'pending',
|
||||
content => $splitter . $assetId . '~~',
|
||||
id => $assetId,
|
||||
isUncommitted => $child->get('status') eq 'pending',
|
||||
content => $splitter . $assetId . '~~',
|
||||
};
|
||||
if ($vars{showAdmin}) {
|
||||
$children[-1]->{'dragger.icon'} = sprintf '<div id="td%s_handle" class="dragable"><div class="dragTrigger dragTriggerWrap">%s</div></div>', $assetId, $session->icon->drag('class="dragTrigger"');
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -230,7 +234,6 @@ sub prepareView {
|
|||
unshift @{ $vars{"position1_loop"} }, reverse @children;
|
||||
}
|
||||
|
||||
$vars{showAdmin} = ($session->var->isAdminOn && $self->canEdit && $self->canEditIfLocked);
|
||||
if ($vars{showAdmin}) {
|
||||
# under normal circumstances we don't put HTML stuff in our code, but this will make it much easier
|
||||
# for end users to work with our templates
|
||||
|
|
@ -247,7 +250,6 @@ sub prepareView {
|
|||
}
|
||||
</style>
|
||||
');
|
||||
$vars{"dragger.icon"} = '<div class="dragTrigger dragTriggerWrap">'.$session->icon->drag('class="dragTrigger"').'</div>';
|
||||
$vars{"dragger.init"} = '
|
||||
<iframe id="dragSubmitter" style="display: none;" src="'.$session->url->extras('spacer.gif').'"></iframe>
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -194,6 +194,20 @@ sub definition {
|
|||
hoverHelp =>$i18n->get('compare color yes description'),
|
||||
label =>$i18n->get('compare color yes label'),
|
||||
},
|
||||
maxScreenshotWidth=>{
|
||||
fieldType =>"integer",
|
||||
tab =>"display",
|
||||
defaultValue =>"800",
|
||||
hoverHelp =>$i18n->get('max screenshot width description'),
|
||||
label =>$i18n->get('max screenshot width label'),
|
||||
},
|
||||
maxScreenshotHeight=>{
|
||||
fieldType =>"integer",
|
||||
tab =>"display",
|
||||
defaultValue =>"600",
|
||||
hoverHelp =>$i18n->get('max screenshot height description'),
|
||||
label =>$i18n->get('max screenshot height label'),
|
||||
},
|
||||
categories=>{
|
||||
fieldType =>"textarea",
|
||||
tab =>"properties",
|
||||
|
|
|
|||
|
|
@ -197,8 +197,6 @@ sub definition {
|
|||
fieldType => 'workflow',
|
||||
label => 'Survey End Workflow',
|
||||
hoverHelp => 'Workflow to run when user completes the Survey',
|
||||
# label => $i18n->get('editForm workflowIdAddEntry label'),
|
||||
# hoverHelp => $i18n->get('editForm workflowIdAddEntry description'),
|
||||
none => 1,
|
||||
},
|
||||
quizModeSummary => {
|
||||
|
|
@ -207,13 +205,16 @@ sub definition {
|
|||
tab => 'properties',
|
||||
label => $i18n->get('Quiz mode summaries'),
|
||||
hoverHelp => $i18n->get('Quiz mode summaries help'),
|
||||
}
|
||||
},
|
||||
allowBackBtn => {
|
||||
fieldType => 'yesNo',
|
||||
defaultValue => 0,
|
||||
tab => 'properties',
|
||||
label => $i18n->get('Allow back button'),
|
||||
hoverHelp => $i18n->get('Allow back button help'),
|
||||
},
|
||||
);
|
||||
|
||||
#my $defaultMC = $session->
|
||||
|
||||
#%properties = ();
|
||||
|
||||
push @{$definition}, {
|
||||
assetName => $i18n->get('assetName'),
|
||||
icon => 'survey.gif',
|
||||
|
|
@ -800,7 +801,7 @@ sub www_loadSurvey {
|
|||
elsif ( $lastType eq 'question' ) {
|
||||
$q = 1;
|
||||
}
|
||||
$html .= "<li id='$scount' class='section'>S" . ( $scount + 1 ) . ": $_->{text}<\/li><br>\n";
|
||||
$html .= "<li id='$scount' class='section'>S" . ( $scount + 1 ) . ": $_->{text}<\/li>\n";
|
||||
push( @ids, $scount );
|
||||
}
|
||||
elsif ( $_->{type} eq 'question' ) {
|
||||
|
|
@ -808,7 +809,7 @@ sub www_loadSurvey {
|
|||
if ( $lastType eq 'answer' ) {
|
||||
$a = 1;
|
||||
}
|
||||
$html .= "<li id='$scount-$qcount' class='question'>Q" . ( $qcount + 1 ) . ": $_->{text}<\/li><br>\n";
|
||||
$html .= "<li id='$scount-$qcount' class='question'>Q" . ( $qcount + 1 ) . ": $_->{text}<\/li>\n";
|
||||
push @ids, "$scount-$qcount";
|
||||
$lastType = 'question';
|
||||
$acount = -1;
|
||||
|
|
@ -818,12 +819,12 @@ sub www_loadSurvey {
|
|||
$html
|
||||
.= "<li id='$scount-$qcount-$acount' class='answer'>A"
|
||||
. ( $acount + 1 )
|
||||
. ": $_->{text}<\/li><br>\n";
|
||||
. ": $_->{text}<\/li>\n";
|
||||
push @ids, "$scount-$qcount-$acount";
|
||||
$lastType = 'answer';
|
||||
}
|
||||
}
|
||||
|
||||
$html = "<ul class='draglist'>$html</ul>";
|
||||
my $warnings = $self->surveyJSON->validateSurvey();
|
||||
|
||||
my $return = {
|
||||
|
|
@ -1154,6 +1155,41 @@ sub www_submitQuestions {
|
|||
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_goBack
|
||||
|
||||
Handles the Survey back button
|
||||
|
||||
=cut
|
||||
|
||||
sub www_goBack {
|
||||
my $self = shift;
|
||||
|
||||
if ( !$self->canTakeSurvey() ) {
|
||||
$self->session->log->debug('canTakeSurvey false, surveyEnd');
|
||||
return $self->surveyEnd();
|
||||
}
|
||||
|
||||
my $responseId = $self->responseId();
|
||||
if ( !$responseId ) {
|
||||
$self->session->log->debug('No response id, surveyEnd');
|
||||
return $self->surveyEnd();
|
||||
}
|
||||
|
||||
if ( !$self->get('allowBackBtn') ) {
|
||||
$self->session->log->debug('allowBackBtn false, delegating to www_loadQuestions');
|
||||
return $self->www_loadQuestions();
|
||||
}
|
||||
|
||||
$self->responseJSON->pop;
|
||||
$self->persistResponseJSON;
|
||||
|
||||
return $self->www_loadQuestions();
|
||||
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getSummary
|
||||
|
|
@ -1305,15 +1341,8 @@ Sends the processed template and questions structure to the client
|
|||
|
||||
sub prepareShowSurveyTemplate {
|
||||
my ( $self, $section, $questions ) = @_;
|
||||
# my %multipleChoice = (
|
||||
# 'Multiple Choice', 1, 'Gender', 1, 'Yes/No', 1, 'True/False', 1, 'Ideology', 1,
|
||||
# 'Race', 1, 'Party', 1, 'Education', 1, 'Scale', 1, 'Agree/Disagree', 1,
|
||||
# 'Oppose/Support', 1, 'Importance', 1, 'Likelihood', 1, 'Certainty', 1, 'Satisfaction', 1,
|
||||
# 'Confidence', 1, 'Effectiveness', 1, 'Concern', 1, 'Risk', 1, 'Threat', 1,
|
||||
# 'Security', 1
|
||||
# );
|
||||
my %textArea = ( 'TextArea', 1 );
|
||||
my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1 );
|
||||
my %text = ( 'Text', 1, 'Email', 1, 'Phone Number', 1, 'Text Date', 1, 'Currency', 1, 'Number', 1 );
|
||||
my %slider = ( 'Slider', 1, 'Dual Slider - Range', 1, 'Multi Slider - Allocate', 1 );
|
||||
my %dateType = ( 'Date', 1, 'Date Range', 1 );
|
||||
my %dateShort = ( 'Year Month', 1 );
|
||||
|
|
@ -1379,6 +1408,7 @@ sub prepareShowSurveyTemplate {
|
|||
if(scalar @{$questions} == ($section->{totalQuestions} - $section->{questionsAnswered})){
|
||||
$section->{isLastPage} = 1
|
||||
}
|
||||
$section->{allowBackBtn} = $self->get('allowBackBtn');
|
||||
|
||||
my $out = $self->processTemplate( $section, $self->get('surveyQuestionsId') );
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,59 +39,6 @@ number of questions answered (L<"questionsAnswered">) and the Survey start time
|
|||
|
||||
This package is not intended to be used by any other Asset in WebGUI.
|
||||
|
||||
=head2 surveyOrder
|
||||
|
||||
This data strucutre is an array (reference) of Survey addresses (see
|
||||
L<WebGUI::Asset::Wobject::Survey::SurveyJSON/Address Parameter>), stored in the order
|
||||
in which items are presented to the user.
|
||||
|
||||
By making use of L<WebGUI::Asset::Wobject::Survey::SurveyJSON> methods which expect address params as
|
||||
arguments, you can access Section/Question/Answer items in order by iterating over surveyOrder.
|
||||
|
||||
For example:
|
||||
|
||||
# Access sections in order..
|
||||
for my $address (@{ $self->surveyOrder }) {
|
||||
my $section = $self->survey->section( $address );
|
||||
# etc..
|
||||
}
|
||||
|
||||
In general, the surveyOrder data structure looks like:
|
||||
|
||||
[ $sectionIndex, $questionIndex, [ $answerIndex1, $answerIndex2, ....]
|
||||
|
||||
There is one array element for every section and address in the survey. If there are
|
||||
no questions, or no addresses, those array elements will not be present.
|
||||
|
||||
=head2 responses
|
||||
|
||||
This data structure stores a snapshot of all question responses. Both question data and answer data
|
||||
is stored in this hash reference.
|
||||
|
||||
Questions keys are constructed by hypenating the relevant L<"sIndex"> and L<"qIndex">.
|
||||
Answer keys are constructed by hypenating the relevant L<"sIndex">, L<"qIndex"> and L<aIndex|"aIndexes">.
|
||||
|
||||
Question entries only contain a comment field:
|
||||
{
|
||||
...
|
||||
questionId => {
|
||||
comment => "question comment",
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
Answers entries contain: value (the recorded value), time and comment fields.
|
||||
|
||||
{
|
||||
...
|
||||
answerId => {
|
||||
value => "recorded answer value",
|
||||
time => time(),
|
||||
comment => "answer comment",
|
||||
},
|
||||
...
|
||||
}
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
|
|
@ -252,7 +199,7 @@ sub hasTimedOut{
|
|||
|
||||
=head2 lastResponse ([ $responseIndex ])
|
||||
|
||||
Mutator. The lastResponse property represents the index of the most recent surveyOrder entry shown.
|
||||
Mutator. The lastResponse property represents the surveyOrder index of the most recent item shown.
|
||||
|
||||
This method returns (and optionally sets) the value of lastResponse.
|
||||
|
||||
|
|
@ -325,8 +272,32 @@ sub startTime {
|
|||
|
||||
=head2 surveyOrder
|
||||
|
||||
Accessor for surveyOrder (see L<"surveyOrder">).
|
||||
Initialized on first access via L<"initSurveyOrder">.
|
||||
Accessor. Initialized on first access via L<"initSurveyOrder">.
|
||||
|
||||
This data strucutre represents the list of items that are shown to the user, in the order
|
||||
that they will be shown (ignoring jumps and jump expressions).
|
||||
|
||||
Typically each item will correspond to a question, and contains enough information to look
|
||||
up both the corresponding section and all contained answers (if any).
|
||||
|
||||
Empty sections also appear in the list.
|
||||
|
||||
Each element of the array is an address, similar in structure to
|
||||
L<WebGUI::Asset::Wobject::Survey::SurveyJSON/Address Parameter>,
|
||||
except that instead of an answerIndex in the third slot, we have a sub-array of all contained answer indicies.
|
||||
|
||||
[ $sectionIndex, $questionIndex, [ $answerIndex1, $answerIndex2, ....]
|
||||
|
||||
By making use of L<WebGUI::Asset::Wobject::Survey::SurveyJSON> methods which expect address params as
|
||||
arguments, you can access Section/Question/Answer items in order by iterating over surveyOrder.
|
||||
|
||||
For example:
|
||||
|
||||
# Access sections in order..
|
||||
for my $address (@{ $self->surveyOrder }) {
|
||||
my $section = $self->survey->section( $address );
|
||||
# etc..
|
||||
}
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -489,7 +460,6 @@ sub recordResponses {
|
|||
$gotoExpression = $section->{gotoExpression};
|
||||
}
|
||||
|
||||
|
||||
# Handle empty Section..
|
||||
if ( !@questions ) {
|
||||
# No questions to process, so increment lastResponse and return
|
||||
|
|
@ -526,9 +496,22 @@ sub recordResponses {
|
|||
# Pluck the values out of the responses hash that we want to record..
|
||||
my $submittedAnswerResponse = $submittedResponses->{ $answer->{id} };
|
||||
my $submittedAnswerComment = $submittedResponses->{ $answer->{id} . 'comment' };
|
||||
my $submittedAnswerVerbatim = $submittedResponses->{ $answer->{id} . 'verbatim' };
|
||||
|
||||
# Proceed if we're satisfied that the submitted answer response is valid..
|
||||
if ( defined $submittedAnswerResponse && $submittedAnswerResponse =~ /\S/ ) {
|
||||
|
||||
#Validate answers met question criteria
|
||||
if($question->{questionType} eq 'Number'){
|
||||
if($answer->{max} =~ /\d/ and $submittedAnswerResponse > $answer->{max}){
|
||||
next;
|
||||
}elsif($answer->{min} =~ /\d/ and $submittedAnswerResponse < $answer->{min}){
|
||||
next;
|
||||
}elsif($answer->{step} =~ /\d/ and $submittedAnswerResponse % $answer->{step} != 0){
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
$aAnswered = 1;
|
||||
|
||||
# Now, decide what to record. For multi-choice questions, use recordedAnswer.
|
||||
|
|
@ -537,9 +520,10 @@ sub recordResponses {
|
|||
= $knownTypes{ $question->{questionType} }
|
||||
? $submittedAnswerResponse
|
||||
: $answer->{recordedAnswer};
|
||||
|
||||
$self->responses->{ $answer->{id} }->{time} = time;
|
||||
$self->responses->{ $answer->{id} }->{comment} = $submittedAnswerComment;
|
||||
|
||||
$self->responses->{ $answer->{id} }->{verbatim} = $answer->{verbatim} ? $submittedAnswerVerbatim : undef;
|
||||
$self->responses->{ $answer->{id} }->{time} = time;
|
||||
$self->responses->{ $answer->{id} }->{comment} = $submittedAnswerComment;
|
||||
|
||||
# Handle terminal Answers..
|
||||
if ( $answer->{terminal} ) {
|
||||
|
|
@ -609,6 +593,23 @@ A variable name to match against all section and question variable names.
|
|||
sub processGoto {
|
||||
my $self = shift;
|
||||
my ($goto) = validate_pos(@_, {type => SCALAR});
|
||||
|
||||
if ($goto eq 'NEXT_SECTION') {
|
||||
$self->session->log->debug("NEXT_SECTION jump target encountered");
|
||||
my $lastResponseSectionIndex = $self->lastResponseSectionIndex;
|
||||
|
||||
# Increment lastRepsonse until nextResponseSectionIndex moves
|
||||
while ($self->nextResponseSectionIndex == $lastResponseSectionIndex) {
|
||||
$self->lastResponse( $self->lastResponse + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ($goto eq 'END_SURVEY') {
|
||||
$self->session->log->debug("END_SURVEY jump target encountered");
|
||||
$self->lastResponse( scalar( @{ $self->surveyOrder} ) - 1 );
|
||||
return;
|
||||
}
|
||||
|
||||
# Iterate over items in order..
|
||||
my $itemIndex = 0;
|
||||
|
|
@ -714,17 +715,31 @@ sub recordedResponses{
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 responseValuesByVariableName
|
||||
=head2 responseValuesByVariableName ( $options )
|
||||
|
||||
Returns a lookup table to question variable names and recorded response values.
|
||||
|
||||
Only questions with a defined variable name set are included. Values come from
|
||||
the L<responses> hash.
|
||||
|
||||
=head3 options
|
||||
|
||||
The following options are supported:
|
||||
|
||||
=over 3
|
||||
|
||||
=item * useText
|
||||
|
||||
For multiple choice questions, use the answer text instead of the recorded value
|
||||
(useful for doing [[var]] text substitution
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
sub responseValuesByVariableName {
|
||||
my $self = shift;
|
||||
my %options = validate(@_, { useText => 0 });
|
||||
|
||||
my %lookup;
|
||||
while (my ($address, $response) = each %{$self->responses}) {
|
||||
|
|
@ -742,14 +757,23 @@ sub responseValuesByVariableName {
|
|||
# Filter out questions without defined variable names
|
||||
next if !$question || !defined $question->{variable};
|
||||
|
||||
#Test if question is a multiple choice type so we can use the answer text instead
|
||||
my $answerText;
|
||||
if($self->survey->getMultiChoiceBundle($question->{questionType})){
|
||||
$answerText = $self->survey->answer([@address])->{text};
|
||||
my $value = $response->{value};
|
||||
if ($options{useText}) {
|
||||
# Test if question is a multiple choice type so we can use the answer text instead
|
||||
if($self->survey->getMultiChoiceBundle($question->{questionType})){
|
||||
my $answer = $self->survey->answer([@address]);
|
||||
my $answerText = $answer->{text};
|
||||
|
||||
# For verbatim mc answers, combine answer text and recorded value
|
||||
if ($answer->{verbatim}) {
|
||||
$answerText = "$answerText - \"$response->{verbatim}\"";
|
||||
}
|
||||
$value = $answerText ? $answerText : $value;
|
||||
}
|
||||
}
|
||||
|
||||
# Add variable => value to our hash
|
||||
$lookup{$question->{variable}} = $answerText ? $answerText : $response->{value};
|
||||
$lookup{$question->{variable}} = $value;
|
||||
}
|
||||
return \%lookup;
|
||||
}
|
||||
|
|
@ -885,7 +909,7 @@ sub nextQuestions {
|
|||
my $questionsPerPage = $self->survey->section( [ $self->nextResponseSectionIndex ] )->{questionsPerPage};
|
||||
|
||||
# Get all of the existing question responses (so that we can do Section and Question [[var]] replacements
|
||||
my $responseValuesByVariableName = $self->responseValuesByVariableName();
|
||||
my $responseValuesByVariableName = $self->responseValuesByVariableName( { useText => 1 } );
|
||||
|
||||
# Do text replacement
|
||||
$section->{text} = $self->getTemplatedText($section->{text}, $responseValuesByVariableName);
|
||||
|
|
@ -1230,11 +1254,32 @@ sub response {
|
|||
return $self->{_response};
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 responses
|
||||
|
||||
Mutator for the L<"responses"> property.
|
||||
Mutator. Note, this is an unsafe reference.
|
||||
|
||||
Note, this is an unsafe reference.
|
||||
This data structure stores a snapshot of all question responses. Both question data and answer data
|
||||
is stored in this hash reference.
|
||||
|
||||
Questions keys are constructed by hypenating the relevant L<"sIndex"> and L<"qIndex">.
|
||||
Answer keys are constructed by hypenating the relevant L<"sIndex">, L<"qIndex"> and L<aIndex|"aIndexes">.
|
||||
|
||||
{
|
||||
# Question entries only contain a comment field, e.g.
|
||||
'0-0' => {
|
||||
comment => "question comment",
|
||||
},
|
||||
# ...
|
||||
# Answers entries contain: value (the recorded value), time and comment fields.
|
||||
'0-0-0' => {
|
||||
value => "recorded answer value",
|
||||
time => time(),
|
||||
comment => "answer comment",
|
||||
},
|
||||
# ...
|
||||
}
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -1247,6 +1292,62 @@ sub responses {
|
|||
return $self->response->{responses};
|
||||
}
|
||||
|
||||
=head2 pop
|
||||
|
||||
=cut
|
||||
|
||||
sub pop {
|
||||
my $self = shift;
|
||||
my %responses = %{ $self->responses };
|
||||
|
||||
# Iterate over responses first time to determine time of most recent response(s)
|
||||
my $lastResponseTime;
|
||||
for my $r ( values %responses ) {
|
||||
if ( $r->{time} ) {
|
||||
$lastResponseTime
|
||||
= !$lastResponseTime || $r->{time} > $lastResponseTime
|
||||
? $r->{time}
|
||||
: $lastResponseTime
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
return unless $lastResponseTime;
|
||||
|
||||
my $popped;
|
||||
my $poppedQuestions;
|
||||
# Iterate again, removing most recent responses
|
||||
while (my ($address, $r) = each %responses ) {
|
||||
if ( $r->{time} == $lastResponseTime) {
|
||||
$popped->{$address} = $r;
|
||||
delete $self->responses->{$address};
|
||||
|
||||
# Remove associated question/comment entry
|
||||
my ($sIndex, $qIndex, $aIndex) = split /-/, $address;
|
||||
my $qAddress = "$sIndex-$qIndex";
|
||||
$popped->{$qAddress} = $responses{$qAddress};
|
||||
delete $self->responses->{$qAddress};
|
||||
|
||||
# while we're here, build lookup table of popped question ids
|
||||
$poppedQuestions->{$qAddress} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# Now, nextResponse should be set to index of the first popped question we can find in surveyOrder
|
||||
my $nextResponse = 0;
|
||||
for my $address (@{ $self->surveyOrder }) {
|
||||
my $questionId = "$address->[0]-$address->[1]";
|
||||
if ($poppedQuestions->{$questionId} ) {
|
||||
$self->session->log->debug("setting nextResponse to $nextResponse");
|
||||
$self->nextResponse($nextResponse);
|
||||
last;
|
||||
}
|
||||
$nextResponse++;
|
||||
}
|
||||
|
||||
return $popped;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 survey
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@ sub loadTypes {
|
|||
'Slider',
|
||||
'Currency',
|
||||
'Email',
|
||||
'Number',
|
||||
'Phone Number',
|
||||
'Text',
|
||||
'Text Date',
|
||||
|
|
@ -413,16 +414,17 @@ Generates the list of valid goto targets
|
|||
sub getGotoTargets {
|
||||
my $self = shift;
|
||||
|
||||
# Valid goto targets are all of the section variable names..
|
||||
my @section_vars = map {$_->{variable}} @{$self->sections};
|
||||
# Valid goto targets are all of the non-empty section variable names..
|
||||
my @section_vars = grep { $_ ne q{} } map {$_->{variable}} @{$self->sections};
|
||||
|
||||
# ..and all of the question variable names..
|
||||
my @question_vars = map {$_->{variable}} @{$self->questions};
|
||||
# ..and all of the non-empty question variable names..
|
||||
my @question_vars = grep { $_ ne q{} } map {$_->{variable}} @{$self->questions};
|
||||
|
||||
# ..excluding the ones that are empty
|
||||
my @grep = grep { $_ ne q{} } (@section_vars, @question_vars);
|
||||
return \@grep;
|
||||
#return grep { $_ ne q{} } (@section_vars, @question_vars);
|
||||
# ..plus some special vars
|
||||
my @special_vars = qw(NEXT_SECTION END_SURVEY);
|
||||
|
||||
# ..all combined
|
||||
return [ @section_vars, @question_vars, @special_vars ];
|
||||
}
|
||||
|
||||
=head2 getSectionEditVars ( $address )
|
||||
|
|
@ -665,16 +667,32 @@ sub update {
|
|||
}
|
||||
}
|
||||
|
||||
$self->_handleSpecialAnswerUpdates($address,$properties);
|
||||
|
||||
# Update $object with all of the data in $properties
|
||||
while (my ($key, $value) = each %{$properties}) {
|
||||
if (defined $value) {
|
||||
$object->{$key} = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub _handleSpecialAnswerUpdates{
|
||||
my $self = shift;
|
||||
my $address = shift;
|
||||
my $properties = shift;
|
||||
my $question = $self->question($address);
|
||||
if($question->{questionType} =~ /^Slider|Multi Slider - Allocate|Dual Slider - Range$/){
|
||||
for my $answer(@{$self->answers($address)}){
|
||||
$answer->{max} = $properties->{max};
|
||||
$answer->{min} = $properties->{min};
|
||||
$answer->{step} = $properties->{step};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
=head2 insertObject ( $object, $address )
|
||||
|
||||
Rearrange existing objects in the current data structure.
|
||||
|
|
@ -1036,10 +1054,9 @@ sub addAnswersToQuestion {
|
|||
# when updating answer text without causing side-effects for the caller's $address
|
||||
my @address_copy = @{$address};
|
||||
|
||||
for my $answer_index ( 0 .. $#{$answers} ) {
|
||||
|
||||
for my $answer (@$answers) {
|
||||
# Add a new answer to question
|
||||
push @{ $self->question( \@address_copy )->{answers} }, $answers->[$answer_index];
|
||||
push @{ $self->question( \@address_copy )->{answers} }, $answer;
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ sub generateFeed {
|
|||
# care of any encoding specified in the XML prolog
|
||||
utf8::downgrade($value, 1);
|
||||
eval {
|
||||
my $singleFeed = XML::FeedPP->new($value, utf8_flag => 1);
|
||||
my $singleFeed = XML::FeedPP->new($value, utf8_flag => 1, -type => 'string');
|
||||
$feed->merge($singleFeed);
|
||||
};
|
||||
if ($@) {
|
||||
|
|
|
|||
|
|
@ -292,19 +292,44 @@ sub duplicate {
|
|||
my $assetId = $self->get("assetId");
|
||||
my $fields;
|
||||
|
||||
my $otherThingFields = $db->buildHashRefOfHashRefs(
|
||||
"select fieldType, fieldId, right(fieldType,22) as otherThingId, fieldInOtherThingId from Thingy_fields
|
||||
where fieldType like 'otherThing_%' and assetId = ?",
|
||||
[$assetId],'fieldInOtherThingId'
|
||||
);
|
||||
|
||||
my $things = $self->getThings;
|
||||
while ( my $thing = $things->hashRef) {
|
||||
my $oldThingId = $thing->{thingId};
|
||||
my $newThingId = $newAsset->addThing($thing,0);
|
||||
my $oldSortBy = $thing->{sortBy};
|
||||
my $oldThingId = $thing->{thingId};
|
||||
my $newThingId = $newAsset->addThing($thing,0);
|
||||
$fields = $db->buildArrayRefOfHashRefs('select * from Thingy_fields where assetId=? and thingId=?'
|
||||
,[$assetId,$oldThingId]);
|
||||
foreach my $field (@$fields) {
|
||||
# set thingId to newly created thing's id.
|
||||
$field->{thingId} = $newThingId;
|
||||
|
||||
my $originalFieldId = $field->{fieldId};
|
||||
|
||||
$newAsset->addField($field,0);
|
||||
my $newFieldId = $newAsset->addField($field,0);
|
||||
if ($originalFieldId eq $oldSortBy){
|
||||
$self->session->db->write( "update Thingy_things set sortBy = ? where thingId = ?",
|
||||
[ $newFieldId, $newThingId ] );
|
||||
}
|
||||
|
||||
if ($otherThingFields->{$originalFieldId}){
|
||||
$otherThingFields->{$originalFieldId}->{newFieldType} = 'otherThing_'.$newThingId;
|
||||
$otherThingFields->{$originalFieldId}->{newFieldId} = $newFieldId;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach my $otherThingField (keys %$otherThingFields){
|
||||
$db->write('update Thingy_fields set fieldType = ?, fieldInOtherThingId = ?
|
||||
where fieldInOtherThingId = ? and assetId = ?',
|
||||
[$otherThingFields->{$otherThingField}->{newFieldType},
|
||||
$otherThingFields->{$otherThingField}->{newFieldId},
|
||||
$otherThingFields->{$otherThingField}->{fieldInOtherThingId}, $newAsset->get('assetId')]);
|
||||
}
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ sub getAlphabetSearchLoop {
|
|||
my $htmlEncodedLetter = encode_entities($letter);
|
||||
my $searchURL = "?searchExact_".$fieldName."=".$letter."%25";
|
||||
my $hasResults;
|
||||
my $users = $self->session->db->read("select userId from userProfileData where lastName like '".$letter."%'");
|
||||
my $users = $self->session->db->read("select userId from userProfileData where `$fieldName` like '".$letter."%'");
|
||||
while (my $user = $users->hashRef){
|
||||
my $showGroupId = $self->get("showGroupId");
|
||||
if ($showGroupId eq '0' || ($showGroupId && $self->isInGroup($showGroupId,$user->{userId}))){
|
||||
|
|
@ -535,7 +535,7 @@ sub view {
|
|||
my $users = $p->getPageData($paginatePage);
|
||||
foreach my $user (@$users){
|
||||
my $userObject = WebGUI::User->new($self->session,$user->{userId});
|
||||
if ($self->get('overridePublicProfile') || $userObject->profileIsViewable($userObject)){
|
||||
if ($self->get('overridePublicProfile') || $userObject->profileIsViewable()) {
|
||||
my (@profileFieldValues);
|
||||
my %userProperties;
|
||||
foreach my $profileField (@profileFields){
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue