%s ",$message));
}
//returns an sql timestamp value from a formatted date/time string
// 14 Feb 2005 - currently not used in questionnaire
function dt2timestamp($date_time) {
$year = strtok($date_time," -:");
$month = strtok(" -:");
$day = strtok(" -:");
$hour = strtok(" -:");
$min = strtok(" -:");
$sec = strtok(" -:");
$timestamp = mktime($hour,$min,$sec,$month,$day,$year);
return $timestamp;
}
function GetSQLValueString($theValue, $theType, $theDefinedValue = "", $theNotDefinedValue = "")
{
$theValue = (!get_magic_quotes_gpc()) ? addslashes($theValue) : $theValue;
switch ($theType) {
case "blob":
$theValue = "COMPRESS('" . $theValue . "')";
case "file": //if $theType == "file", replace the hostname in addition to regular text processing
$theValue = substr($theValue,56);
case "text":
$theValue = ($theValue != "") ? "'" . preg_replace("|%20|"," ",$theValue) . "'" : "NULL"; //replacing urlencoded spaces in addition to regular sql processing
break;
case "long":
case "int":
$theValue = ($theValue != "") ? intval($theValue) : "NULL";
break;
case "double":
$theValue = ($theValue != "") ? "'" . doubleval($theValue) . "'" : "NULL";
break;
case "date":
$theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
break;
case "time":
$theValue = ($theValue != "") ? "'" . $theValue . "'" : "NULL";
break;
case "defined":
$theValue = ($theValue != "") ? $theDefinedValue : $theNotDefinedValue;
break;
}
return $theValue;
}
//performs a select query (given as $sql_string) and returns the first record of the query
//useful when a select is needed to get just one record.
function mysql_select($sql_string) {
$query = mysql_query($sql_string) or report_error_form("mysql_select error ($sql_string): " . mysql_error());
$result_query = mysql_fetch_assoc($query);
return ($result_query);
}
//performs an insert query. If query unsuccessful, returns 0, otherwise, returns id inserted.
function mysql_insert($sql_string) {
$query = mysql_query($sql_string) or report_error_form("mysql_insert error ($sql_string): " . mysql_error());
if (!query)
return($query);
else
return(mysql_insert_id());
}
function mysql_update($sql_string) {
mysql_query($sql_string) or report_error_form("mysql_update error ($sql_string): " . mysql_error());
}
//parses a comma-delimited enum string with enum fields in double-quotes
//returns the string array of enum fields with double-quotes removed
function enumstr2array($enum_str) {
$enum_vals = array();
$str_rem = $enum_str;
while(preg_match("|\"([^\"]*)\",?|",$str_rem,$matches)) {
$enum_vals[] = $matches[1];
$str_rem_start = strpos($str_rem,$matches[0])+strlen($matches[0]);
$str_rem_len = strlen($str_rem) - $str_rem_start;
$str_rem = substr($str_rem,$str_rem_start,$str_rem_len);
}
return $enum_vals;
}
//
// I think this one is self-explanatory.
// it checks against the subject table, not the response
// table for this specific experiment.
// Return value is the string containing the subject_id
// which is constructed in the form_subject_id or
// form_subject_id_with_validation form handlers.
// Or, returns NULL if subject_id for this name and DOB
// combination doesn't exist in the subject table. - JG
//
// 29 Sept. 2009 - Added code to decrypt subject Info - S.T.
//
function get_subject_id_if_it_exists( $name_first, $name_last, $dob,$find_inc_session = FALSE )
{
// these are created in /var/www/private/conn_XXXX.php
// the "$subject" variable is actually the mysql connection
global $database_subject;
global $subject;
if( !$name_first || !$name_last || !$dob )
return( NULL );
$enc_key = subinfo_encryption_key();
$sql_query = sprintf( "SELECT *,session.end_datetime as edt, session.date_time as sdt, session.end_datetime is not null as session_is_finished ".
" FROM subject left join session using (subject_id) ".
"WHERE aes_decrypt(`name_last`,'%s')=%s ".
"AND aes_decrypt(`name_first`,'%s')=%s AND aes_decrypt(`dob`,'%s')=%s ".
"order by ".
(($find_inc_session)? "session_is_finished asc, ": "").
"session.date_time desc",
$enc_key,
GetSQLValueString($name_last,"text"),
$enc_key,
GetSQLValueString($name_first,"text"),
$enc_key,
GetSQLValueString($dob,"date"));
$query_result = mysql_query( $sql_query, $subject) or report_error_form("get_subject_id_if_it_exists error ($sql_query): " . mysql_error());
// if there's anything in the result, it will be the
// record corresponding to this subject's id, so return the value
// of the subject_id field in the first row in the result set.
// there should never be 2 rows in the result set because
// subject_id is a unique field in the subject table
if( mysql_num_rows( $query_result ) )
{
$record_array = mysql_fetch_assoc( $query_result );
return( $record_array[ 'subject_id' ] );
}
return( NULL );
}
//
// play_video_with_quicktime( $player, $videoURL, $height, $width, $bAutoPlay, $bAllowControl, $numPlays )
//
// player not used here. artifact of the design of present_stimuli()
//
// url full path to video file
//
// height, width required
//
// bAutoPlay true-> movie plays automatically right after loading
// optional: default = true
// bAllowControl true-> basic QT controls are visible to the user which allow pausing,
// repeating, etc. but the movie will still auto play on page load if
// bAutoPlay is true, regardless of bAllowControl's value.
// optional: default = false
// numPlays only applies if bAutoPlay is set to true. can repeat the same movie
// any number of times. after that, if the user clicks the play button
// again (if bAllowControl is true), the original movie is only played once.
// QT actually allows any number of different movies to be auto-played in
// sequence on page load using the QTnext tag. We will assume that the
// numPlays applies only to the original movie, but we could easily modify
// to support multiple movies if necessary.
// optional: default = 1
//
function play_video_with_quicktime()
{
$numArgs = func_num_args();
$argList = func_get_args();
if( $numArgs < 4 || $argList[1] == NULL )
report_error_form( "NULL or missing video URL, height, or width parameter(s) passed to include/functions.php: play_video_with_quicktime()." );
if( $argList[2] < 1 || $argList[3] < 1 )
report_error_form( "invalid height or width values passed to include/functions.php: play_video_with_quicktime()." );
// Check for optional arguments and assign defaults
$videoURL = $argList[1];
if( $videoURL == NULL )
report_error_form( "NULL video URL passed to play_video_with_quicktime() in include/functions.php" );
$height = $argList[2];
$width = $argList[3];
if( $numArgs > 4 )
$autoPlayStr = ( $argList[4] == true )? "true" : "false";
else
$autoPlayStr = "true"; // default
if( $numArgs > 5 )
$allowControlStr = ( $argList[5] == true )? "true" : "false";
else
$allowControlStr = "false"; // default
if( $numArgs > 6 )
$numReps = $argList[6];
else
$numReps = 1; // default
if( $numReps < 1 )
$numReps = 1;
// Note: for movies with controll=true, displaying the
// movie controls to the user, you need to add 16 to the movie's actual
// height to account for the controls. for audio only movies, set height to 16.
if( $allowControlStr == "true" )
$height = $height + 16;
// -- Output HTML object tag for QT player --------------------------------------
// this clsid is always the same for anyone using QT plugin
// This should be compatible with most browsers and gracefully degrade for others
printf( " \n");
printf( "\n" );
printf( " \n", $videoURL );
printf( " \n", $autoPlayStr );
printf( " \n", $allowControlStr );
printf( " \n" ); // prevent reloading movie on every play
printf( " \n" ); // don't sacrifice quality for speed
// QTnext tags enumerate a list of videos (may all be the same or different) to play in sequence
// the T part says to use the same browser window to play each movie in the QTnext list.
// If numReps == 1, then no QTnext tag will be included at all.
for( $x = 1; $x < $numReps; $x++ )
{
printf( " T\">\n", $x, $videoURL );
}
// safari and firefox seem to follow the tag
printf( "\n", $videoURL, $width, $height );
printf( " \n", $allowControlStr );
printf( " \n", $autoPlayStr );
printf( " \n" ); // prevent reloading movie on every play
printf( " \n" ); // don't sacrifice quality for speed
printf( "Download video: sign.mov \n", $videoURL );
for( $x = 1; $x < $numReps; $x++ )
{
printf( " T\">\n", $x, $videoURL );
}
printf( " \n" );
printf( " \n" );
printf( " \n" );
// -------------------------------------------------------------------------
$_SESSION[ 'stimulus_completed' ] = true;
}
function play_video_with_html5()
{
$numArgs = func_num_args();
$argList = func_get_args();
if( $numArgs < 4 || $argList[1] == NULL )
report_error_form( "NULL or missing video URL, height, or width parameter(s) passed to include/functions.php: play_video_with_quicktime()." );
if( $argList[2] < 1 || $argList[3] < 1 )
report_error_form( "invalid height or width values passed to include/functions.php: play_video_with_quicktime()." );
// Check for optional arguments and assign defaults
$videoURL = $argList[1];
if( $videoURL == NULL )
report_error_form( "NULL video URL passed to play_video_with_quicktime() in include/functions.php" );
$height = $argList[2];
$width = $argList[3];
if( $numArgs > 4 )
$autoPlayStr = ( $argList[4] == true )? "autoplay" : "";
else
$autoPlayStr = "autoplay"; // default
if( $numArgs > 5 )
$controlStr = ( $argList[5] == true )? "controls" : "";
else
$controlStr = ""; // default
if( $numArgs > 6 )
$numReps = $argList[6];
else
$numReps = 1; // default
if( $numReps < 1 )
$numReps = 1;
printf( " \n");
printf(" ", $videoURL, $controlStr, $autoPlayStr, $height, $width);
printf( " \n" );
// -------------------------------------------------------------------------
$_SESSION[ 'stimulus_completed' ] = true;
} // play_video_with_html5()
// java applet audio player that supports wav, aiff, au
// can play any number of audio stimuli in succession
// Should never be called directly by a form handler (use present_stimuli or present_trial_stimuli)
// Expects play_audio_stimuli_applet($player,$audioURL1,$playLength1,$pause1,$audioURL2,$playLength2,$pause2,$audioURL3,...)
// Expects a single argument ($options), which is an associative array with the following possible values:
// $options['urls'] - an array of urls of audio files. At least one url should be set.
// $options['playLengths'] an array of play lengths corresponding to the 'urls' in order. A value of 0 indicates to play the entire stim.
// $options['pauses'] - an array of pause values (pause before stim) corresponding to the 'urls' in order. Uses $default_pause if ommitted.
// $options['loadWhenDone'] - URL to load when finished playing.
// where $audioURL# is the url of the audio file, $playLength# is the duration of the audio file to play, and
// $pause# is the number of seconds to pause before playing the audio file.
function play_audio_stimuli_applet($options) {
global $questionnaire_location;
global $stimuli_location;
global $stimuli_dir;
$default_audio_player="JavaSamplePlayer.class";
$this_file = "include/functions.php";
$this_fn = "play_audio_stimuli_applet";
$loadWhenDone=$options['loadWhenDone'];
$default_pause = 1; //default pause value to use between stims if pause isn't specified.
$applet_player_loc = $stimuli_location."/applet/classes/";
$audioURLs_list = (isset($options['urls']))? $options['urls'] : array();
$playLengths_list = (isset($options['playLengths']))? $options['playLengths'] : array();
$pauses_list = (isset($options['pauses']))? $options['pauses'] : array();
$audio_player = $flash_player_loc . (isset($options['audio_player']))? $options['audio_player'] : $default_audio_player;
$nAudio = count($audioURLs_list);
$nPL = count($playLengths_list);
$nPauses = count($pauses_list);
if($nAudio < 1)
report_error_form("No Audio URLs specified.",$this_file,$this_fn);
//applet player expects playLengths to be set for each audio file
//also, we need to take care of NULLs that are propogated down to this function.
//when playLength = 0, the entire audio file is played.
for ($iAudio = 0; $iAudio < $nAudio; $iAudio++) {
if( ($iAudio > $nPauses - 1) || ($pauses_list[$iAudio] == NULL) )
$pauses_list[$iAudio] = $default_pause;
if ( ($iAudio > $nPL - 1) || ($playLengths_list[$iAudio] == NULL) )
$playLengths_list[$iAudio] = 0;
}
//if the stim is greater than a certain file size, preloading will fail
//This threshold was empirically found at approximately 4Mb by trying different file sizes
//Investigation into the Java player code will be necessary to find the
//real limitation. Currently setting the threshold to 3500000 bytes.
//This is essentially a temporary fix. S.T.
$soundFileSize = 0;
foreach($options['urls'] as $audPath) {
//remove $stimuli_location from the url and use local dir path
$audPath = substr($audPath,(strlen($stimuli_location)-1));
$audPath = $stimuli_dir . $audPath;
$soundFileSize = $soundFileSize + filesize($audPath);
}
if($soundFileSize < 3500000)
$preloadVal = "true";
else
$preloadVal = "false";
printf("\n");
printf(" \n",$audio_player);
printf(" \n",$applet_player_loc);
printf(" \n");
printf(" \n",$loadWhenDone);
if($nAudio == 1)
printf(" \n",$preloadVal);
for($audioIdx = 0; $audioIdx < $nAudio; $audioIdx++)
printf(" \n",$audioIdx+1, $audioURLs_list[$audioIdx]);
for($plIdx= 0; $plIdx < $nPL; $plIdx++)
printf(" \n",$playLengthIdx+1, $playLengths_list[$plIdx]);
for($pauseIdx = 0; $pauseIdx < $nPauses; $pauseIdx++)
printf(" \n",$pauseIdx+1, $pauses_list[$pauseIdx]);
printf("\n");
printf("\n");
printf("\n");
printf("No Java 2 SDK, Standard Edition v 1.5 support for APPLET!!\n");
printf(" \n");
printf(" \n");
printf(" \n");
printf(" \n");
$_SESSION['stimulus_completed'] = true;
}
// Flash player that supports 1 or 2 mp3 stims.
// Should never be called directly by a form handler (use present_stimuli or present_trial_stimuli)
// This function is currently used for trials which present
// play_audio_stimuli_flash($player,$audioURL1,$playLength1,$pause1,$audioURL2,$playLength2,$pause2)
//edited 3/19/2010 to accept param, value pairs
//this will allow for unlimited numbers of audioURLs, playLengths, and pauses. Also allows easy exclusion of unneeded parameters.
function play_audio_stimuli_flash($options) {
global $questionnaire_location;
global $stimuli_location;
$this_file = "include/functions.php";
$this_fn = "play_audio_stimuli_flash";
$flash_player_loc = $stimuli_location."flash/";
$default_audio_player = "present_audio_stimuli.swf";
$getvars = "";
$audioURLs_list = (isset($options['urls']))? $options['urls'] : array();
$playLengths_list = (isset($options['playLengths']))? $options['playLengths'] : array();
$pauses_list = (isset($options['pauses']))? $options['pauses'] : array();
$audio_player = $flash_player_loc . ((isset($options['audio_player']))? $options['audio_player'] : $default_audio_player);
$nAudio = count($audioURLs_list);
if($nAudio < 1)
report_error_form("No Audio URLs specified.",$this_file,$this_fn);
for($idx = 0; $idx < $nAudio; $idx++) {
$getvars = $getvars . (($idx == 0)? "":"&") . "audioURL" . ($idx+1) . "=" . urlencode($audioURLs_list[$idx]);
}
$nPL = count($playLengths_list);
for($idx = 0; $idx < $nPL ; $idx++) {
if(!is_null($playLengths_list[$idx]))
$getvars = $getvars . ( ($playLengths_list[$idx] != NULL)? ("&playLength" . ($idx+1) . "=" . $playLengths_list[$idx]) : "" );
}
$nPauses = count($pauses_list);
for($idx = 0; $idx < $nPauses ; $idx++) {
if(!is_null($pauses_list[$idx]))
$getvars = $getvars . "&pause" . ($idx+1) . "=" . $pauses_list[$idx];
}
if(isset($options['loadWhenDone']))
$getvars = sprintf("loadWhenDone=%s",urlencode($options['loadWhenDone']))."&".$getvars;
printf("\n");
printf(" \n");
printf(" \n",$audio_player,$getvars);
printf(" \n");
printf(" \n");
printf(" \n\n");
}
//Uses HTML5 to play audio stimuli. In the case of IE6-9, it uses Google Chrome Frames, which imparts (nearly) full HTML5 functionality from Google Chrome.
//It uses Modernizr (www.modernizr.com) to check for the ability of a particular browser to play a given stimulus type. When a browser is found that does not
//comply to HTML5 standard or does not support a given format, jPlayer is used as a fall-back to play the stimulus. Currently, a flash fallback is included for
//mp3 files in Firefox, through jPlayer. Since all current browsers support .wav playback (well, IE6+ has issues with .wav, but we cover that by using Google
//Chrome Frames), we do not have a .wav fallback lined up at the moment.
//play lengths and pauses are defined in MILLISECONDS
function play_audio_stimuli($options) {
$this_file = "include/functions.php";
$this_fn = "play_audio_stimuli";
$audioURLs_list = (isset($options['urls'])) ? $options['urls'] : array();
$playLengths_list = (isset($options['playLengths']) && $options['playLengths'][0] != NULL) ? $options['playLengths'] : array();
$pauses_list = (isset($options['pauses']) && $options['pauses'][0] != NULL) ? $options['pauses'] : array();
$skips_list = (isset($options['skips']) && $options['skips'][0] != NULL) ? $options['skips'] : array();
if (count($audioURLs_list) < 1) report_error_form("No Audio URLs specified.",$this_file,$this_fn);
// fill each of the optional arrays with zeros if they don't have the correct length
$playLengths_list = fill_array_if_needed($playLengths_list, $audioURLs_list, 0);
$pauses_list = fill_array_if_needed($pauses_list, $audioURLs_list, 0);
$skips_list = fill_array_if_needed($skips_list, $audioURLs_list, 0);
printf("\n");
include("stim_player_html5.php");
}
//Unsupported function. It is currently here only as a placeholder
//This was replaced by play_audio_stimuli_flash() which plays one or two stims using the flash player
//This is still included here since some form handlers call this
function play_audio_no_control($audio_url,$length = NULL,$pause = NULL) {
report_error_form('play_audio_no_control is no longer supported. Use play_audio_stimuli_flash instead.','functions.php','play_audio_no_control');
play_audio_stimuli_flash(NULL,$audio_url,$length,$pause);
}
//presents a text stimulus. $player should simply be NULL. Other stim functions can specify a stim player but there is currently only one for text stimuli
//the $player variable also serves as a placeholder variable for future Ensemble versions, which may possibly have more than one text stim player.
function display_text_stimulus() {
global $questionnaire_location;
global $QPI_HOSTNAME;
global $default_text_stim_dur;
$this_file = "include/functions.php";
$this_fn = "display_text_stimulus";
$numargs = func_num_args();
$arglist = func_get_args();
if($numargs < 2)
report_error_form("Not enough arguments passed. Need at least display_text_stimulus($player,$text_url).",$this_file,$this_fn);
$player = $arglist[0];
$text_url = $arglist[1];
$display_time_sec = ($numargs > 2)? $arglist[2] : $default_text_stim_dur;
$fid = fopen($questioinnaire_dir.$text_url,"r");
$file_contents = "";
while(!feof($fid))
$file_contents .= fread($fid,8192);
fclose($fid);
printf("%s",$file_contents);
$_SESSION['stimulus_completed'] = true;
printf(' ',$display_time_sec,$questionnaire_location);
}
//Unsupported function. It is currently here only as a placeholder.
//Use present_stimuli() instead
function present_stimulus() {
report_error_form('present_stimulus is no longer supported. Use present_stimuli instead.','functions.php','present_stimulus');
global $default_text_stim_dur;
global $stimuli_location;
$numargs = func_num_args();
$arglist = func_get_args();
$stimulus_loc = $stimuli_location.$arglist[0];
preg_match("|.+\.([[:alpha:][:digit:]]{3})|",$stimulus_loc,$matches);
$stim_ext = $matches[1];
($numargs > 2)? $pause = $arglist[2] : $pause = 0;
switch ($stim_ext) {
case "txt" :
($numargs > 1)? $duration = $arglist[1] : $duration = $default_text_stim_dur;
display_text_stimulus($stimulus_loc,$duration,$pause);
break;
case "mp3" :
($numargs > 1)? $duration = $arglist[1] : $duration = -1;
play_audio_no_control($stimulus_loc,$duration,$pause);
break;
}
}
//
// Takes a mysql database ID for the stimulus table and returns
// an array of strings describing the type of the stimulus, whose
// elements are indexed by string tags rather than integers.
// I think the input arg can be either a string or an int.
//
// Elements of the return array are:
// 'type': broad stimulus type. as of 01/10/07 possible return
// values for type are: audio, video, document, image, unknown
// Unknown document types are considered valid here so the
// caller would want to check for that. This will be the case
// if the file_format field of the stimulus record is unspecified.
// 'format': returns whatever is stored in the file_format field of the
// stimulus record. So for everyone has been using the convention
// of 3-letter formats corresponding to the file extenstion. The stimulus
// file name is NOT parsed to verify that it matches file_format.
// Values for format may be anything like: mp3, wav, jpg, mov, etc.
// 'url': This is the complete www path to the stimulus file itself.
// Formed by concatenating the global $stimuli_location, and the
// relative file path stored in the stimulus record. for example:
// "https://atonal.ucdavis.edu/ensemble_dev/stimuli/audio/filename.mp3"
// $stimuli_location is "https://atonal.ucdavis.edu/ensemble_dev/" and
// the part from the stimulus record is "stimuli/audio/filename.mp3".
// 'height': this will be 0 for audio stimuli. thid is mostly for videos
// 'width': "
//
function get_stim_file_info_from_ID( $stimID )
{
// ### TODO: add some error checking on input value ###
global $stimuli_location;
global $stimuli_dir;
$this_file = "include/functions.php";
$this_fn = "get_stim_file_info_from_ID";
$sqlQuery = sprintf( "select location, file_format, height, width from stimulus where stimulus_id = %d", $stimID );
$dbRecord = mysql_select( $sqlQuery );
if(!$dbRecord)
report_error_form("Could not retrieve stimulus info for stimulus ID ".$stimID,$this_file,$this_fn);
if(!file_exists($stimuli_dir.$dbRecord['location']) )
report_error_form( "Bad file path for stimulus ID ".$stimID.": ".$stimuli_dir.$dbRecord['location'],$this_file,$this_fn);
$url = $stimuli_location . $dbRecord['location'];
$file_format = $dbRecord['file_format'];
switch( $file_format )
{
case "aif":
case "wav":
case "mp3":
case "snd":
$type = "audio";
break;
case "jpg":
case "gif":
case "tif":
case "png":
case "bmp":
$type = "image";
break;
case "mov":
case "mpg":
case "mp4":
$type = "video";
break;
case "txt":
$type = "document";
break;
default:
$type = "unknown";
break;
}
$returnVar = array();
$returnVar['type'] = $type;
$returnVar['format'] = $file_format;
$returnVar['url'] = $url;
$returnVar['height'] = $dbRecord['height'];
$returnVar['width'] = $dbRecord['width'];
return( $returnVar );
}
// -------------------- present_stimuli() --------------------------
// General stimulus presentation function designed to handle all
// types of stimuli. If the type of player is not specified by the caller,
// we will determine the correct file type based on the file
// extension of the 1st stimulus's URL.
//
// Infrastructure is in place to support an arbitrary number of stimuli,
// but right now support is only really available for: one audio stim,
// one video stim, or one text stim.
//
// Prototypes
// --------------------------
// If the caller doesn't know the stim type, the only safe prototype is:
// present_stimuli( NULL, stimulus1_id );
//
// For audio:
// present_stimuli( $player, $stimulus1_id, $duration1, $pause1, $skip1[, $stimulus2_id, $duration2, $pause2, $skip2]);
// The only required variables are $player and $stimulus1_id.
// If $player is NULL, the default audio player will be used.
// The pauses and lengths specify the pause time (in ms) before each auditory stim is presented,
// and the duration of the stim to present. If not specified, the defaults are pause=0 and
// length=(full sound file). The skip parameters identify whether an option will be presented
// to the participant to skip the given stimulus. If this is presented for a stimulus, the skip
// time will be reported for that stimulus using the question id identified by $response_time_id
// in includes/variables.php. Specify NULL to omit the pause, duration, or skip parameters.
//
//
// For video:
// present_stimuli( $player, $stimulus1_id, $bAutoPlay, $bAllowControl, $numPlays )
// The only required variables are $player and $stimulus1_id.
// See: play_video_with_quicktime() for info on the optional video arguments.
// If $player is NULL, the default video player will be used.
//
function present_stimuli()
{
global $default_text_stim_dur;
global $stimuli_location;
global $default_audio_player;
global $default_video_player;
global $questionnaire_location;
$this_file = "include/functions.php";
$this_fn = "present_stimuli";
$numargs = func_num_args();
$arglist = func_get_args();
if( $numargs < 2 )
report_error_form( "Not enough arguments passed to %s - %s. Need at least %s($player,$stimulus_id).", $this_file, $this_fn, $this_fn );
if( !is_numeric( $arglist[1] ) )
report_error_form( "String argument 2 is NULL or non-integer. Need a valid stimulus ID.", $this_file, $this_fn );
// Set up some variables to be passed into the presentation function calls below.
// For the most part we're just forwarding the same parameter list we received here.
$forwardArgs = $arglist;
$usePlayer = $arglist[0]; // assume whatever player type the user passed in for now. may be NULL.
$stim1FileInfo = get_stim_file_info_from_ID( $arglist[1] );
$forwardArgs[1] = $stim1FileInfo['url']; // This is important: replace the stimID with the full URL
// because all the play functions require input arg [1] to be a URL
if( $stim1FileInfo['type'] == "unknown" )
report_error_form( "Unknown stimulus type passed in.", $this_file, $this_fn );
// As of Jan 2007 support for 2 or more stims is only provided
// for audio stims. the parameters are required to come in an order
// such that stims IDs are found in arglist[1], [4], [7], [10], [13], etc.
// We've already replaced the db ID in forwardArgs[1] with the URL. now
// we need to do the same for any additional audio stims.
//
// Don't do this for video stims or it will break play_video_XXXX()
//
// as of Feb 2012, we have adopted HTML5 and a flash fallback for presentation
// of audio stimuli, and we have also built in functionality to allow participants
// to skip stimuli if they choose to, and if we allow it, but for the moment that
// has come at the expense of multiple stimuli explicitly played in succession in
// one call to present_stimuli(). For now, please use separate calls to present_stimuli()
// or use a custom form handler to handle this functionality.
//
// 01Sep2015 PJ - added total hack to bypass loading of goto_current_form.php
// A calling form can set this session variable to be TRUE to bypass loading on a one time
// basis only, after which it switches to the default behavior of being set to FALSE.
if( $stim1FileInfo['type'] == "audio" )
{
if (isset($_SESSION['stayOnPageWhenDone']) && $_SESSION['stayOnPageWhenDone'])
{
$loadWhenDone = "";
$_SESSION['stayOnPageWhenDone'] = FALSE;
}
else
{
$loadWhenDone = $questionnaire_location."form_processing/goto_current_form.php";
}
$options['loadWhenDone'] = $loadWhenDone;
$options['urls'][0] = $stim1FileInfo['url'];
for( $iarg = 2; $iarg < $numargs; $iarg ++ )
{
switch((($iarg-1) % 4)+1) {
case 1:
$subsequentStimFileInfo = get_stim_file_info_from_ID( $arglist[$iarg] );
$options['urls'][] = $subsequentStimFileInfo['url'];
break;
case 2:
$options['playLengths'][] = $arglist[$iarg];
break;
case 3:
$options['pauses'][] = $arglist[$iarg];
break;
case 4:
$options['skips'][] = $arglist[$iarg];
break;
}
}
}
// for video stimuli, we need to shift the args around a little.
// height and width are required so they will be inserted as args [2] and [3].
// then the 3 optional args autoplay allowControl numReps will be tacked on
// to the end so the arguments to play_video_XXX will be:
// [0] player (unused by the video play function)
// [1] URL
// [2] height
// [3] width
// [4] auto play (optional)
// [5] allow control (optional)
// [6] num reps (optional)
if( $stim1FileInfo['type'] == 'video' )
{
$forwardArgs[2] = $stim1FileInfo['height'];
$forwardArgs[3] = $stim1FileInfo['width'];
for( $x = 4; ($x-2) < $numargs; $x += 1 )
$forwardArgs[$x] = $arglist[$x-2];
}
//no support for two different stim types yet.
switch( $stim1FileInfo['format'] )
{
case "txt" :
call_user_func_array('display_text_stimulus',$forwardArgs);
break;
// -----------------------------------------------------
case "mp3" :
/*
//
// We are now using play_audio_stimuli() instead of breaking out separate
// flash and java applets for different audio stimulus types
//
if( $usePlayer == NULL )
$usePlayer = "flash";
// would be deleted if we were actually supporting a choice in mp3 players.
// because the value of $usePlayer right here would be correct.
$usePlayer = "flash";
if( $usePlayer == "flash" ) {
play_audio_stimuli_flash($options);
}
else
report_error_form( 'Invalid mp3 audio player specified.', $this_file, $this_fn );
break;
*/
// -----------------------------------------------------
case "wav" :
/*
//
// We are now using play_audio_stimuli() instead of breaking out separate
// flash and java applets for different audio stimulus types
//
if( $usePlayer == NULL )
$usePlayer = $default_audio_player;
// no support for multiple audio players here- we're using
// the java applet player regardless of what the caller passes
// in as the $player argument
$usePlayer = "java";
if( $usePlayer == "java" )
play_audio_stimuli_applet($options);
else
report_error_form( 'Invalid wav file audio player specified.', $this_file, $this_fn );
*/
play_audio_stimuli($options);
break;
// -----------------------------------------------------
case "mp4" :
case "mov" :
if( $usePlayer == NULL )
$usePlayer = $default_video_player;
if( $usePlayer == "quicktime" )
call_user_func_array( 'play_video_with_quicktime', $forwardArgs );
elseif ($usePlayer == "html5")
call_user_func_array('play_video_with_html5', $forwardArgs);
else
report_error_form( 'Invalid video player specified.', $this_file, $this_fn );
break;
// -----------------------------------------------------
default:
report_error_form( 'Unknown stimulus file type.', $this_file, $this_fn );
break;
}
}
function present_trial_stimuli() {
//expects at least present_trial_stimuli($trial_id)
//possible parameters are present_trial_stimuli( $trial_id, $player, $duration1, $pause1,$duration2, $pause2);
//Obtains stimulus_id1 and stimulus_id2 from the trial table and calls present_stimuli to present the stims
//see present_stimuli for a description of $pause1,$duration1,$pause2,$duration2,$player, and $load_when_done
global $stimuli_location;
$this_file = "include/functions.php";
$this_fn = "present_trial_stimuli";
$numargs = func_num_args();
$arglist = func_get_args();
if($numargs == 0 || $arglist[0] == NULL)
report_error_form("Not enough arguments passed. Need at least present_trial_stimuli($trial_id).",$this_file,$this_fn);
$trial_id = $arglist[0];
$player= ($numargs < 2)? NULL : $arglist[1];
$duration1 = ($numargs < 3)? NULL : $arglist[2];
$pause1 = ($numargs < 4)? NULL : $arglist[3];
$duration2 = ($numargs < 5)? NULL : $arglist[4];
$pause2 = ($numargs < 6)? NULL : $arglist[5];
$trial_stimuli = mysql_select(sprintf("select stimulus_id1,stimulus_id2 from trial where trial_id = %d",$trial_id));
$stimulus_id1 = ($trial_stimuli['stimulus_id1'] != NULL && $trial_stimuli['stimulus_id1'] != "")? $trial_stimuli['stimulus_id1'] : NULL;
$stimulus_id2 = ($trial_stimuli['stimulus_id2'] != NULL && $trial_stimuli['stimulus_id2'] != "")? $trial_stimuli['stimulus_id2'] : NULL;
if($stimulus_id1 == NULL)
report_error_form("$stimulus_id1 was NULL.",$this_file,$this_fn);
if($stimulus_id2 != NULL)
//present_stimuli($player,$stimulus_id1,$duration1,$pause1,$stimulus_id2,$duration2,$pause2);
present_stimuli($player,$stimulus_id1,$duration1,$pause1,NULL,$stimulus_id2,$duration2,$pause2,NULL);
else
present_stimuli($player,$stimulus_id1,$duration1,$pause1);
}
function socket_establish($hostname,$port,$external_app) {
global $questionnaire_location;
$this_file = "include/functions.php";
$this_fn = "socket_establish";
$host_ip = gethostbyname($hostname);
$time = time();
$socket_rsc = socket_create_and_set_nonblock();
//keep attempting socket connects through non-blocking requests until timeout occurs.
//non-blocking requests allows socket_connect to continue retrying.
//when the socket is non-blocking, socket_connect should return false, and provide
//an error message (this behavior seems inconsistent across platforms).
$connected = FALSE;
$timeout = FALSE;
while( !$timeout && !$connected) {
@socket_connect($socket_rsc, $host_ip,$port);
$err = socket_last_error($socket_rsc);
$socket_error_list[] = $err;
switch($err) {
case SOCKET_EINPROGRESS:
usleep(ENS_SOCKET_WAIT_BETWEEN_REQUESTS_MS*1000);
break;
case SOCKET_EALREADY:
case SOCKET_EISCONN:
$connected = TRUE;
break;
default:
//if the socket connection errored out, reset block on socket, close it, sleep, then recreate it
socket_set_block($socket_rsc) or report_error_form(sprintf("Unable to reset block on socket after connection failure code %d, %s",
$thisErr,socket_strerror($thisErr)),$this_file,$this_fn);
socket_close($socket_rsc);
$socket_rsc = NULL;
usleep(ENS_SOCKET_WAIT_AFTER_FAIL_MS*1000);
$socket_rsc = socket_create_and_set_nonblock();
}
$timeout = ( (time() -$time) >= ENS_SOCKET_CONNECT_TIMEOUT );
} //while
if( $timeout && !$connected ) {
socket_set_block($socket_rsc) or report_error_form("Unable to reset block on socket after connection timeout",$this_file,$this_fn);
socket_close($socket_rsc);
$socket_error_message = sprintf("Timeout occurred while attempting to connect to %s socket\n".
"Socket Connection Error Stack (in order of most recent to least recent):\n",$external_app);
$socket_error_list = array_reverse($socket_error_list);
foreach ($socket_error_list as $thisErr) {
$socket_error_message = $socket_error_message.sprintf("Error code: %d, Error string: %s\n",$thisErr,socket_strerror($thisErr));
}
report_error_form("socket_establish error: " . $socket_error_message,$this_file,$this_fn);
}
//reset block on socket
socket_set_block($socket_rsc) or report_error_form("Unable to set block on socket",$this_file,$this_fn);
return($socket_rsc);
}
function socket_create_and_set_nonblock() {
$this_file = "include/functions.php";
$this_fn = "socket_create_and_set_nonblock";
$socket_rsc = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if(!$socket_rsc) {
$socket_error_code = socket_last_error($socket_rsc);
$error_message = sprintf("socket_create failed: Error code %d; Error string: %s",
$socket_error_code, socket_strerror($socket_error_code));
report_error_form("socket_create_and_set_nonblock: " . $error_message,$this_file,$this_fn);
}
socket_set_nonblock($socket_rsc) or report_error_form("socket_set_nonblock failed",$this_file,$this_fn);
return $socket_rsc;
}
function socket_message($socket_rsc,$message_send,$receive = true) {
socket_write($socket_rsc, $message_send, strlen($message_send)+1);
if($receive)
$message_receive = socket_read($socket_rsc, 1024);
else
$message_receive = NULL;
return($message_receive);
}
//sends a message to matrpc via a socket and receives a message via socket
function matrpc_message($message_send) {
global $MATRPC_HOST;
global $MATRPC_PORT;
$_SESSION['uses_matrpc'] = true;
$this_file = "include/functions.php";
$this_fn = "matrpc_message";
//prefix the session ID to the message for matrpc
$message_send = $_SESSION['session_id']."|".$message_send."\0";
$socket_rsc = socket_establish($MATRPC_HOST,$MATRPC_PORT,"MatRPC");
$message_receive = socket_message($socket_rsc,$message_send,true);
socket_close($socket_rsc);
if(strncasecmp($message_receive,"error",5) == 0)
report_error_form("Received the following message from MatRPC:\n".$message_receive,$this_file,$this_fn);
elseif($message_receive == "\0")
report_error_form("Received empty string from MatRPC. This does not mean that the Matlab function returned an empty string. This could have resulted from an unhandled error condition. Check the MatRPC log.",$this_file,$this_fn);
return(chop($message_receive));
}
//sends a message to labview over a socket
function labview_message($message_send) {
global $LABVIEW_HOST;
global $LABVIEW_PORT;
$this_file = "include/functions.php";
$this_fn = "labview_message";
$message_send = $_SESSION['session_id']."|".$message_send."\0";
$socket_rsc = socket_establish($LABVIEW_HOST,$LABVIEW_PORT,"LabView");
$labview_status = socket_message($socket_rsc,$message_send,true);
if(!$labview_status || is_null($labview_status))
report_error_form("Labview did not pass a positive status flag back to Ensemble.",$this_file,$this_fn);
socket_close($socket_rsc);
return;
}
function report_error_form($message,$location = "",$function = "") {
global $questionnaire_location;
global $LOG_FILE;
$timeStr = strftime ("%a, %b %d, %Y %H:%M:%S");
if(!headers_sent())
include('../form_processing/htmlhead.php');
printf("%s. ",LANGUAGE_TEXT_ERROR_REPORT1);
printf("%s ",LANGUAGE_TEXT_ERROR_REPORT2);
printf("%s
",LANGUAGE_TEXT_ERROR_REPORT3);
printf("%s
",LANGUAGE_TEXT_ERROR_REPORT4);
//construct an error log message.
$error_log_message = "\n" . $timeStr . ((!empty($location))? "\tLocation: " . $location : "") .
( (!empty($function))? "\tFunction: " . $function : "") . "\n" . $message . "\n";
$fid = fopen($LOG_FILE,'a');
if($fid !== FALSE) {
fwrite($fid,$error_log_message);
}
else {
print "" . LANGUAGE_TEXT_ERROR_CANT_OPEN_LOG . "
";
}
printf("%s:
%s
",LANGUAGE_TEXT_QUOTE_TIMESTAMP,$timeStr);
printf("");
printf("");
die();
}
//writes the message to the ensemble log file, along with a timestamp
function write2log($message) {
global $LOG_FILE;
$fid = fopen($LOG_FILE,'a');
if($fid !== FALSE) {
$timeStr = strftime ("%a, %b %d, %Y %H:%M:%S");
fwrite($fid,"\n".$timeStr."\n");
fwrite($fid,$message);
}
}
function parse_matlab_arguments($matlab_string) {
$matched = preg_match("|([^\(\)]+)\(?([^\)]*)\)?|",$matlab_string,$matches);
if(!($matched))
return false;
$matlab_function = $matches[1];
if((sizeof($matches) < 2) || ($matches[2] == NULL))
return($matlab_function);
else
$matlab_args = $matches[2];
$arg_array = explode(",",$matlab_args);
$pass_args = NULL;
foreach($arg_array as $arg) {
switch (trim($arg)) {
case "'subject_id'":
$argval = "'".$_SESSION['subject_id']."'";
break;
case "'session_id'":
$argval = $_SESSION['session_id'];
break;
case "'stimulus_id'":
$argval = $_SESSION['stimulus_id'];
break;
case "'form_id'":
$argval = $_SESSION['form_id'];
break;
case "'last_visited'":
$argval = $_SESSION['last_visited'];
break;
case "'response_table'":
$argval = "'".$_SESSION['response_table']."'";
break;
case "'trial_id'":
$argval = $_SESSION['trial_id'];
break;
default:
$argval = $arg; //just pass through arguments not recognized as tags
break;
}
if($pass_args == NULL)
$pass_args = sprintf("%s",$argval);
else
$pass_args = $pass_args.sprintf(",%s",$argval);
}
return($matlab_function."(".$pass_args.")");
}
//submits the question text and response text for a security question to the
//appropriate fields in the subject table rather than to the response table
function submit_security_question($question_id,$subquestion,$response_text) {
$enc_key = subinfo_encryption_key();
$response_text = '"' . $response_text . '"';
$sql_get_question_text = sprintf("select `question_text`, `heading` from question left join question_x_data_format using (question_id) " .
" where question.question_id = %d and subquestion = %d",$question_id,$subquestion);
$result_get_question_text = mysql_query($sql_get_question_text) or report_error_form("submit_security_question error ($sql_get_question_text): " .mysql_error());
$row_get_question_text = mysql_fetch_assoc($result_get_question_text);
if($subquestion == 1)
$qtext = '"' .$row_get_question_text['question_text'] . '"';
else
$qtext = '"' . $row_get_question_text['heading'] . '"';
$sql_current_security_questions = sprintf("select security_questions,aes_decrypt(`security_responses`,'%s') as security_responses from subject where ".
"subject_id = '%s'",$enc_key,$_SESSION['subject_id']);
$row_current_security_questions = mysql_select($sql_current_security_questions);
$security_quest_set = (is_null($row_current_security_questions['security_questions']))? $qtext :
$row_current_security_questions['security_questions'] . "," . $qtext;
$security_resp_set = (is_null($row_current_security_questions['security_responses']))? $response_text :
$row_current_security_questions['security_responses'] . "," . $response_text;
//escape any quotes or special characters in the question and response text
$security_quest_set = mysql_real_escape_string($security_quest_set);
$security_resp_set = mysql_real_escape_string(strtolower($security_resp_set));
$sql_submit_security_questions = sprintf("update subject set security_questions = '%s',security_responses = aes_encrypt('%s','%s') ".
"where subject_id = '%s'",$security_quest_set,$security_resp_set,$enc_key,$_SESSION['subject_id']);
mysql_query($sql_submit_security_questions) or report_error_form("submit_security_question: " . mysql_error());
}
function submit_demographics_field($question_id,$response_enum,$sub_id) {
//317-319
//77-79
switch ($question_id) {
case 317:
$fieldname = "gender";
switch($response_enum) {
case 0:
$value = ""; //No Answer
break;
case 1:
$value = "F";
break;
case 2:
$value = "M";
break;
}
break;
case 318:
$fieldname = "ethnic_category";
switch($response_enum) {
case 0:
$value = "unknown";
break;
case 1:
$value = "HL";
break;
case 2:
$value = "not_HL";
break;
}
break;
case 319:
$fieldname = "racial_category";
switch($response_enum) {
case 0:
$value = "Unknown";
break;
case 1:
$value = "NatAm";
break;
case 2:
$value = "Asian";
break;
case 3:
$value = "NatHaw";
break;
case 4:
$value = "AfrAm";
break;
case 5:
$value = "Cauc";
break;
case 6:
$value = "More";
break;
case 7:
$value = "Other";
break;
}
break;
}//switch($question_id)
$sql_submit_demo_val = sprintf("update subject set %s = %s where subject_id = '%s'",$fieldname,GetSQLValueString($value,"text"),$sub_id);
mysql_update($sql_submit_demo_val);
}
function convert_temp2anon_id() {
if(preg_match("|tmp_(.*)|",$_SESSION['subject_id'],$matches)) {
$old_subid = $_SESSION['subject_id'];
$_SESSION['subject_id'] = "anon_".$matches[1];
}
mysql_update(sprintf("update %s set subject_id = '%s' where subject_id = '%s'",$_SESSION['response_table'],$_SESSION['subject_id'],$old_subid));
mysql_update(sprintf("update session set subject_id = '%s' where subject_id = '%s'",$_SESSION['subject_id'],$old_subid));
}
function test_url_file($url) {
$res = (($ftest = @fopen($url, ‘r’)) === false) ? false : @fclose($ftest);
return ($res == TRUE) ? 1:0 ;
}
//outputs a header for downloading text files
function text_download_header($filename) {
$mime_type = (PMA_USR_BROWSER_AGENT == 'IE' || PMA_USR_BROWSER_AGENT == 'OPERA')
? 'application/octetstream'
: 'application/octet-stream';
header('Content-Type: ' . $mime_type);
if (PMA_USR_BROWSER_AGENT == 'IE')
{
header('Content-Disposition: inline; filename=' . $filename );
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
} else {
header('Content-Disposition: attachment; filename=' . $filename );
header("Content-Transfer-Encoding: binary");
header('Expires: 0');
header('Pragma: no-cache');
}
}
//Whether or not the given subject ID exists in the subject table
//this may be used for a number of reasons. Subject ID form handlers
//may use this to check whether a valid subject ID has already been assigned
//to the current session
// S.T. 5/10/2010
function subID_in_subject_table($subID) {
$this_file = "functions.php";
$this_fn = "subID_in_subject_table";
$sql_subID_exists = sprintf("select subject_id from subject where subject_id = '%s'",$subID);
$result_subID_exists = mysql_query($sql_subID_exists) or report_error_form("subID_in_subject_table error ($sql_subID_exists): " . mysql_error());
switch(mysql_num_rows($result_subID_exists)) {
case 0:
return FALSE;
case 1:
return TRUE;
default:
report_error_form("Multiple subject records exists for this subject",$this_file,$this_fn);
}
}
//checks if responses exist for a given subject in a response table
//this can be used by one of the subidgen routines to see if responses
//exist before assigning the subject ID to that session. If the responses
//exist, a call to find_old_session will recover the previous session.
//the current (newer) session will then be orphaned (the tmp_ subject ID will remain assigned
//to the newer session and remain associated with it in the session table).
//S.T. 5/7/2010
function responses_exist_for_sub($subID,$respTable) {
$sql_get_resps = sprintf("select subject_id from %s where subject_id = '%s'",$respTable,$subID);
$result_get_resps = mysql_query($sql_get_resps) or report_error_form("responses_exist_for_sub error ($sql_get_resps): " . mysql_error());
if(mysql_num_rows($result_get_resps) > 0)
return TRUE;
else
return FALSE;
}
//
// USAGE:
// $subID argument is required (it could be the $_SESSION['subject_id'] variable however)
// $resume_if_complete is optional. If it is true (default), then if a session is found, but it is completed, the last form will be visited (should be end_session form handler)
// This allows an admin to create a custom message at the end of the experiment/survey that will simply be revisited if the same person returns
// If this is false, it is up to the form handler to handle any logic for determining the behavior for a returning participant who has already
// completed the survey.
//routines for picking up a session that was not completed
//if a particular experiment should only be completed one time by a particular subject,
//and if that participant leaves the experiment half-way through (closes their browser, clears cookies, etc)
//and then logs in again, you can call this function to end the current session, and pick up where they left
//off in the previous session. //FB 2009.08.26
// NOTE: now returns three different statuses:
// "found" if an old session was successfully found and instantiated
// "not_found" if an old session was not found (this shouldn't normally occur, since responses_exist_for_sub should have been called)
// "completed" if an old session was found, but is completed.
//
//
// 5/10/2010, S.T. - only session IDs that have responses associated with them are now returned
// If a subject enters the sub ID form, exits immediately, then tries again, a new session is created for them.
// If subsequently, they exit mid-session, start experiment again, the previous behavior was returning the first session ID
// with no responses associated. This then caused Ensemble to error out since find_old_session wasn't updating variables appropriately.
// The cleaner solution would be to return the first session ID (with no responses associated) and update variables appropriately based
// on this.
// Also, returning max(session_id) instead of min(session_id). This seemed more appropriate since one should be completing the last
// visited session. However, in any event, the subject should only really have one session associated with them if this function is being
// called.
// 7/26/2010, S.T. - Fixed "group by" syntax on sql query that checks if the session was completed. This seemed to error out only with certain versions of MySQL (5.0.45).
// Version 5.0.77 did not error out with this query.
// 8/11/2010, S.T. - Added the three return statuses ("found","not_found", and "completed")
// Now expects $subID to be passed in. This allows for checking for past sessions on subID before assigning it to the $_SESSION variable
// 8/12/2010, S.T. - Added $resume_if_complete optional argument
function find_old_session($subID,$resume_if_complete = TRUE) {
global $QPI_SESSION_NAME;
// record the session end time in the session table, for the current session
$date = getdate();
//$formatted_dt = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$date['year'],$date['mon'],$date['mday'],$date['hours'],
// $date['minutes'],$date['seconds']);
//$sql_set_session_endtime = sprintf("update session set end_datetime = %s where session_id = %d",
// GetSQLValueString($formatted_dt,"date"),
// $_SESSION['session_id']);
//mysql_update($sql_set_session_endtime);
//see if the subject completed this experiment (there is a completed session). If so, simply set the current form to the last form of the
//experiment and return. This allows one to set up a form at the end of the experiment that provides a custom message to the participant
$sql_session_is_complete = sprintf("select max(session_id) as max_session, max(form_order) as max_form,experiment_id ".
"from session left join experiment_x_form using (experiment_id) " .
"where session.end_datetime is not null and session.subject_id = %s and session.experiment_id = %s group by session_id",
GetSQLValueString($subID,'text'),
GetSQLValueString($_SESSION['experiment_id'],'int'));
$result_session_is_complete = mysql_query($sql_session_is_complete) or report_error_form("find_old_session error ($sql_session): " . mysql_error());
$row_session_is_complete = mysql_fetch_assoc($result_session_is_complete);
if(!is_null($row_session_is_complete['max_form'])) {
//allowing the handler to decide what to do with a completed session now
if($resume_if_complete) {
$_SESSION['last_visited'] = $row_session_is_complete['max_form'] - 1;
$_SESSION['session_id'] = $row_session_is_complete['max_session'];
}
return "completed";
}
// get the old session id
$sql_get_old_sessid = sprintf("SELECT max(session_id) FROM session left join %s using (session_id) WHERE session.subject_id=%s AND session.experiment_id=%d " .
"AND end_datetime IS NULL AND response_id IS NOT NULL ORDER BY session.session_id DESC",
$_SESSION['response_table'],
GetSQLValueString($subID,'text'),
GetSQLValueString($_SESSION['experiment_id'],'int'));
$result_get_old_sessid = mysql_query($sql_get_old_sessid) or report_error_form("find_old_session error ($sql_get_old_sessid): " . mysql_error());
$row_get_old_sessid = mysql_fetch_assoc($result_get_old_sessid);
$oldsessid = $row_get_old_sessid['max(session_id)'];
if (!$oldsessid) {
return "not_found";
} else {
$_SESSION['session_id'] = $oldsessid;
}
//at this stage, we are certain that a previous session exists, and it is not completed.
//So, set the subject_id session variable (if it wasn't already set when calling this function)
$_SESSION['subject_id'] = $subID;
// find last form completed
// form order is the order of forms as prescribed in the experiment definition
// this will not increment continuously through a loop ... every time you hit a particular form in a
// loop, it will have the same form order, therefore max(form_order) can help you to figure out if
// you are in a loop
$sql_last_visited = sprintf("SELECT max(form_order) FROM %s WHERE subject_id=%s AND experiment_id=%d " .
"AND `session_id`=%d",$_SESSION['response_table'],
GetSQLValueString($_SESSION['subject_id'],'text'),
GetSQLValueString($_SESSION['experiment_id'],'int'),
GetSQLValueString($_SESSION['session_id'],'int'));
$result_last_visited = mysql_query($sql_last_visited) or report_error_form("find_old_session error ($sql_last_visited): " . mysql_error());
$row_get_last_visited = mysql_fetch_assoc($result_last_visited);
$_SESSION['last_visited'] = $row_get_last_visited['max(form_order)'];
// set response_order and loop_iter, just incase this is a question within a loop
// response order is incremented every time a new form is submitted
// therefore, response order should tell you how many forms have been submitted thus far
$sql_response_order = sprintf("SELECT distinct(response_order) FROM %s WHERE subject_id=%s AND " .
"experiment_id=%d AND session_id=%d AND form_order=%d",$_SESSION['response_table'],
GetSQLValueString($_SESSION['subject_id'],'text'),
GetSQLValueString($_SESSION['experiment_id'],'int'),
GetSQLValueString($_SESSION['session_id'],'int'),
GetSQLValueString($_SESSION['last_visited'],'int'));
$result_response_order = mysql_query($sql_response_order) or report_error_form("find_old_session error ($sql_last_visited): " . mysql_error());
$num_rows_resporder = mysql_num_rows($result_response_order);
$mds_status = mysql_data_seek($result_response_order,$num_rows_resporder-1);
$row_response_order = mysql_fetch_assoc($result_response_order);
$_SESSION['response_order'] = $row_response_order['response_order'] + 1;
// check if in a loop
$sql_check_loop = sprintf("SELECT * FROM experiment_x_form WHERE experiment_id=%d ".
"AND form_order>=%d AND goto<=%d AND goto IS NOT NULL",
GetSQLValueString($_SESSION['experiment_id'],'int'),
GetSQLValueString($_SESSION['last_visited'],'int'),
GetSQLValueString($_SESSION['last_visited'],'int'));
$return_check_loop = mysql_query($sql_check_loop) or report_error_form("find_old_session error ($sql_last_visited): " . mysql_error());
$num_rows_check_loop = mysql_num_rows($return_check_loop);
if ($num_rows_check_loop > 0) {
$row_check_loop = mysql_fetch_assoc($return_check_loop);
$_SESSION['last_visited'] = $row_check_loop['goto']-1;
$_SESSION['looping'] = TRUE;
$_SESSION['loop_incr_trial'] = FALSE;
$_SESSION['loop_end'] = $row_check_loop['form_order'];
$_SESSION['loop_repeat'] = $row_check_loop['repeat'];
$_SESSION['loop_goto'] = $row_check_loop['goto'];
$_SESSION['num_skip_forms'] = 0;
// get number of loop iterations
$sql_get_iter = sprintf("SELECT * FROM %s WHERE experiment_id=%d AND subject_id=%s ".
"AND form_order=%d",$_SESSION['response_table'],
GetSQLValueString($_SESSION['experiment_id'],'int'),
GetSQLValueString($_SESSION['subject_id'],'text'),
GetSQLValueString($row_check_loop['goto'],'int'));
$result_get_iter = mysql_query($sql_get_iter) or report_error_form("find_old_session error ($sql_last_visited): " . mysql_error());
$num_get_iter = mysql_num_rows($result_get_iter);
$_SESSION['loop_iter'] = $num_get_iter;
}
return "found";
}
function subinfo_encryption_key() {
global $ENCRYPTION_KEY_PATH;
return(file_get_contents($ENCRYPTION_KEY_PATH));
}
// compares two ensemble version strings in the format major.minor.maintenance
// although, technically, this function supports as many levels of subversions
// as one likes. If the versions are equal, 0 is returned.
// If $vstring1 is an earlier version than $vstring2, -1 is returned.
// If $vstring1 is a later version than $vstring2, 1 is returned.
// Oct 2, 2009 - Stefan Tomic
function compareVersionStrings($vstring1,$vstring2) {
$varray1 = explode(".",$vstring1);
$varray2 = explode(".",$vstring2);
//if there are less sub-versions in one string, use zeros for the missing subversions
//we will assume that the subversioning was added at a later release
while(count($varray1) < count($varray2))
$varray1[] = '0';
while(count($varray2) < count($varray1))
$varray2[] = '0';
$nSubVersions = count($varray1);
for($isub = 0; $isub < $nSubVersions; $isub++) {
if(intval($varray1[$isub]) > intval($varray2[$isub]))
return 1;
elseif(intval($varray1[$isub]) < intval($varray2[$isub]))
return -1;
elseif(intval($varray1[$isub]) == intval($varray2[$isub]))
continue;
else
report_error_form("compareVersionStrings failed.");
}
return 0;
}
/*
Function to encrypt a string using aes encryption routine in mysql
Although PHP has its own set of encryption functions, they are not easily
compatible with the MySQL encryption functions used by Ensemble.
By encrypting any PHP strings using the MySQL encryption functions,
all encrypted data use the same encryption algorithm.
The encrypted string is converted to hex representation for easy transport.
It needs to be "unhexed" before decrypting it.
June 17, 2010 - Stefan Tomic, original version
*/
function mysql_aes_encrypt($str,$key) {
$sql_encrypt = sprintf("select hex(aes_encrypt('%s','%s')) as enc_str",$str,$key);
$encrypt = mysql_query($sql_encrypt) or report_error_form("mysql_aes_encrypt error ($sql_enrcypt): " . mysql_error());
$row_encrypt = mysql_fetch_assoc($encrypt);
$enc_str = $row_encrypt['enc_str'];
return $enc_str;
}
/*
Function to decrypt a string using MySQL's aes_decrypt.
See mysql_aes_encrypt for more details (above).
June 17, 2010 - Stefan Tomic, original version
*/
function mysql_aes_decrypt($str,$key) {
$sql_decrypt = sprintf("select aes_decrypt(unhex('%s'),'%s') as dec_str",$str,$key);
$decrypt = mysql_query($sql_decrypt) or report_error_form("mysql_aes_decrypt error ($sql_decrypt): " . mysql_error());
$row_decrypt = mysql_fetch_assoc($decrypt);
$dec_str = $row_decrypt['dec_str'];
return $dec_str;
}
// Returns subject first name and last name as an associative array with keys 'name_first','name_last'
// July 13, 2010 - Stefan Tomic, original version
function get_subject_name($subjectID) {
$enc_key = subinfo_encryption_key();
$sql_get_sub_name = sprintf("select aes_decrypt(`name_last`,'%s') as name_last, aes_decrypt(`name_first`,'%s') as name_first from subject where subject_id = '%s'",$enc_key,$enc_key,$subjectID);
$get_sub_name = mysql_query($sql_get_sub_name) or report_error_form("get_subject_name error ($sql_get_sub_name): " . mysql_error());
$row_get_sub_name = mysql_fetch_assoc($get_sub_name);
return $row_get_sub_name;
}
//sets a parameter for an experiment in the params field (experiment table).
//params affect minor behavior differences (primarily views) of experiments/surveys.
function set_experiment_params($params_assoc_array) {
global $EXPERIMENT_PARAMS;
foreach($params_assoc_array as $thisParamName=>$thisParamVal) {
if(array_key_exists($thisParamName,$EXPERIMENT_PARAMS))
$paramStrings[] = sprintf('%s=%s',$thisParamName,$thisParamVal);
}
$paramCSV = implode(',',$paramStrings);
$sql_set_params = sprintf("update experiment set params = '%s' where experiment_id = %d",
$paramCSV,$_SESSION['editor_experiment_id']);
mysql_update($sql_set_params);
}
//gets the parameter list for an experiment.
//returns the parameters as an associative array, where key is the param name
function get_experiment_params($experiment_id) {
$sql_get_current_params = sprintf('select params from experiment where experiment_id = %d',$experiment_id);
$params_string = mysql_select($sql_get_current_params);
$params_list = explode(',',$params_string['params']);
foreach($params_list as $param) {
preg_match('/^([^=]+)\=([^=]+)$/',$param,$matches);
$thisname = $matches[1];
$thisval = $matches[2];
$params_assoc_array[$thisname] = $thisval;
}
return $params_assoc_array;
}
//add an entry not defined in variables.php into the global experiment parameter options
function add_experiment_params($param_name, $vals, $dflt, $disp_name, $disp_type) {
global $EXPERIMENT_PARAMS;
// only add this if it doesn't already exist
if(!array_key_exists($param_name, $EXPERIMENT_PARAMS)) {
$EXPERIMENT_PARAMS[$param_name] = array();
if(is_array($vals)) {
$EXPERIMENT_PARAMS[$param_name]['values'] = $vals;
} else {
$EXPERIMENT_PARAMS[$param_name]['values'] = array($vals);
}
$EXPERIMENT_PARAMS[$param_name]['default'] = $dflt;
$EXPERIMENT_PARAMS[$param_name]['display_name'] = $disp_name;
$EXPERIMENT_PARAMS[$param_name]['display_type'] = $disp_type;
}
}
function fill_array_if_needed($array_to_fill, $array_to_match, $fill_value) {
// check the lengths of the arrays; if the array to fill is shorter than the
// array to match, then add enough of the fill value to bring the size
// of the array to fill up to the target
if (count($array_to_fill) < count($array_to_match)) {
$zeros_needed = count($array_to_match) - count($array_to_fill);
$zeros_list = array_fill(count($array_to_fill), $zeros_needed, 0);
$array_to_fill = array_merge($array_to_fill, $zeros_list);
}
return $array_to_fill;
}
?>