diff -urp --strip-trailing-cr webform623/components/date.inc webform_arian_latest/components/date.inc --- webform623/components/date.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/components/date.inc 2008-10-23 15:45:46.000000000 -0400 @@ -319,11 +319,13 @@ function _webform_theme_date() { * submissions. */ function _webform_analysis_rows_date($component) { - $query = 'SELECT no,data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d '. - ' ORDER BY sid,no ASC '; + $query = 'SELECT sd.no,sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + ' AND s.is_draft != 1'. + ' ORDER BY sd.sid,sd.no ASC'; $result = db_query($query, $component['nid'], $component['cid']); // build an array of timestamps from entered values. diff -urp --strip-trailing-cr webform623/components/email.inc webform_arian_latest/components/email.inc --- webform623/components/email.inc 2008-10-06 01:45:10.000000000 -0400 +++ webform_arian_latest/components/email.inc 2008-10-23 14:56:10.000000000 -0400 @@ -179,10 +179,12 @@ function _webform_help_email($section) { * submissions. */ function _webform_analysis_rows_email($component) { - $query = 'SELECT data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d'; + $query = 'SELECT sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND s.is_draft != 1'. + ' AND sd.cid = %d'; $nonblanks = 0; $submissions = 0; $wordcount = 0; diff -urp --strip-trailing-cr webform623/components/file.inc webform_arian_latest/components/file.inc --- webform623/components/file.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/components/file.inc 2008-10-31 11:36:11.790625000 -0400 @@ -274,7 +274,7 @@ function _webform_render_file($component '#prefix' => '
', '#suffix' => '
', '#element_validate' => array( - '_webform_validate_file', + '_webform_CHECKALWAYS_validate_file', '_webform_required_file', // Custom required routine. ), '#webform_component' => $component, @@ -287,30 +287,65 @@ function _webform_render_file($component return $form_item; } +/** + * If an error occurs on the file field, FF3 dont seem to allow red border on the file form element (FF2 has it very light border for some reason). so this sets another error on the field componant holder + * NOTE: if dont care about all browsers, then comment out set_file_componant_error calls + */ +function set_file_componant_error($form_key) { + drupal_add_js("$(document).ready(function(){ + $('#webform-component-". $form_key ."').addClass('error'); + });", "inline"); // adds error class so highlights input field +} + function _webform_required_file($form_element, $form_state) { $component = $form_element['#webform_component']; $form_key = $component['form_key']; + $fieldname = $component['name']; + $required = $component['mandatory']; - if (empty($_FILES['files']['name'][$form_key]) && $component['mandatory']) { - form_set_error($form_key, t('%field field is required.', array('%field' => $component['name']))); + if ($required) { // make sure if required file that user uploaded a file + if ( empty($_FILES['files']['name'][$form_key]) ) { // if didnt upload anything for this field currently, search to see if saved from earlier draft submission + $qargs = array( $component['nid'], $form_element['#post']['details']['sid'], $component['cid'] ); + $result = db_query("SELECT `data` FROM {webform_submitted_data} d WHERE nid=%d and sid=%d and cid=%d", $qargs ); + if ($row = db_fetch_object($result)) { + $current_file = unserialize( $row->data ); + if ( trim($current_file['filepath']) == "" ) { // no filepath define before, user just saved form before, and hence no filepath values, and since didnt upload anything new, error + form_set_error($form_key, t('%field field is required.', array('%field' => $fieldname))); + set_file_componant_error($form_key); //set * errors on file field, needed to manually add this before i thought for some reason in drupal5 but i am unsure of this now, Dont think I need this anymore, but just in case leaving it in commented out + } + else { // filepath is defined/saved before, but the file itself isnt found, admin possibly manually deleted file + if (!file_exists($current_file['filepath']) ) { + form_set_error($form_key, t('Your previously uploaded file %filename is not found/lost. The %field field is required so please reupload file.', array('%field' => $fieldname, '%filename' => $current_file['filename'])) ); + set_file_componant_error($form_key); + } + } + } + else { // no old data but no new data and since field is required, error + form_set_error($form_key, t('%field field is required.', array('%field' => $fieldname))); + set_file_componant_error($form_key); + } + } } } -function _webform_validate_file($form_element, &$form_state) { +function _webform_CHECKALWAYS_validate_file($form_element, &$form_state) { + // Set the current file as the default. $component = $form_element['#webform_component']; $form_key = $component['form_key']; + $filtering = $component['extra']['filtering']; // Set the previous file as the default. if (isset($form_element['#webform_current_file'])) { form_set_value(array('#parents' => $form_element['#array_parents']), serialize($form_element['#webform_current_file']), $form_state); } - if (empty($_FILES['files']['name'][$form_key])) { + // think if nothing uploaded for this componant dont check for requiredness + if ( empty($_FILES['files']['name'][ $form_key ]) ) { return; } // Build a human readable list of extensions: - $extensions = $component['extra']['filtering']['types']; + $extensions = $filtering['types']; if (count($extensions) > 1) { for ($n = 0; $n < count($extensions) - 1; $n++) { $extension_list .= $extensions[$n] .", "; @@ -329,11 +364,23 @@ function _webform_validate_file($form_el $extension = strtolower(substr($_FILES['files']['name'][$form_key], $dot+1)); if (!in_array($extension, $extensions)) { form_set_error($form_key, t("Files with the '%ext' extension are not allowed, please upload a file with a %exts extension.", array('%ext' => $extension, '%exts' => $extension_list))); + set_file_componant_error($form_key); } - // Now let's check the file size (limit is set in KB). - if ($_FILES['files']['size'][$form_key] > $component['extra']['filtering']['size'] * 1024) { - form_set_error($form_key, t("The file '%filename' is too large (%filesize KB). Please upload a file %maxsize KB or smaller.", array('%filename' => $_FILES['files']['name'][$form_key], '%filesize' => (int)($_FILES['files']['size'][$form_key]/1024), '%maxsize' => $component['extra']['filtering']['size']))); + if ( $php_default_file_validation_msg = _webform_php_file_errors($form_key) ) { // noticed the file size check didnt work in a certain case but php reported the error silently, so using new function to report php's file error info. + form_set_error($form_key, t($php_default_file_validation_msg, array('%filename' => $_FILES['files']['name'][$form_key]) )); + set_file_componant_error($form_key); + } + else { + // Now let's check the file size (limit is set in KB) + if ($_FILES['files']['size'][$form_key] > $filtering['size']*1024) { + form_set_error($form_key, t("The file '%filename' is too large (%filesize KB). Please upload a file %maxsize KB or smaller.", array('%filename' => $_FILES['files']['name'][$form_key], '%filesize' => (int)($_FILES['files']['size'][$form_key]/1024), '%maxsize' => $filtering['size']))); + set_file_componant_error($form_key); + } + else if ($_FILES['files']['size'][$form_key] <= 0) { // a 0byte 'fake' file or nonexistant file on server, if posting with IE6, the ajax 'file does not exist error' wont occur, however FF works. This test case is for IE6, possibly 7 + form_set_error($form_key, t("The file '%filename' does not seem to exist on your computer (or is an empty file). Please click the Browse button and repick a file, and make sure to not modify the text that auto-fills the textfield.", array('%filename' => $_FILES['files']['name'][$form_key]) )); + set_file_componant_error($form_key); + } } } @@ -348,23 +395,37 @@ function _webform_validate_file($form_el * @return * Nothing. */ -function _webform_submit_file(&$data, $component) { +function _webform_submit_file(&$data, $component, $nid, $form_values = NULL ) { $current_file = unserialize($data); - $upload_dir = file_directory_path() ."/webform/". $component['extra']['savelocation']; + $current_data = $data; // to pass into delete function + if ( trim($component['extra']['savelocation']) == '' ) { + $upload_dir_pre = file_directory_path() ."/webform/". $nid; // standardized structure to find files easily + // $upload_dir_append = "/". $sid; // makes sense to put file in directory structore of nid/sid i think so easier to find on server... not sure how to make this database safe, so left out, leave this if statement in case someone can help, iniatially checked db for next sid available and added to $upload_dir, but not multi-user safe + $upload_dir = $upload_dir_pre; + $dodetailedpath = TRUE; + } + else { + $upload_dir = file_directory_path() ."/webform/". $component['extra']['savelocation']; + } + if (!empty($_FILES['files']['name'][$component['form_key']])) { - if (file_check_directory($upload_dir, FILE_CREATE_DIRECTORY)) { - $file_saved = file_save_upload($component['form_key'], array(), $upload_dir); + if ( ($path = file_create_path($upload_dir)) && file_check_directory($path, FILE_CREATE_DIRECTORY) ) { // creates the directory if not already there + $file_saved = file_save_upload($component['form_key'], array(), $upload_dir, FILE_EXISTS_RENAME); if (!$file_saved) { drupal_set_message(t("The uploaded file %filename was unable to be saved. The destination directory may not be writable.", array('%filename' => $file_saved['filename'])), "error"); } else { file_set_status($file_saved, FILE_STATUS_PERMANENT); $data = serialize((array)$file_saved); - file_delete($current_file['filepath']); + _webform_delete_file($current_data, $componant, TRUE); + if (isset($current_file['fid'])) + drupal_set_message(t("Uploaded new file '%filename_new' and Deleted old file: '%filename_old'", array('%filename_new' => $file_saved->filename, '%filename_old' => basename($current_file['filepath']))) ); + else + drupal_set_message(t("Uploaded new file '%filename_new' successfully", array('%filename_new' => $file_saved->filename)) ); } } else { - drupal_set_message(t("The uploaded file was unable to be saved. The destination directory does not exist."), "error"); + drupal_set_message(t("The uploaded file was unable to be saved. The destination directory '%path' does not exist.", array('%path' => $path)), "error"); } } } @@ -398,7 +459,7 @@ function theme_webform_mail_file($data, * @return * Textual output formatted for human reading. */ -function _webform_submission_display_file($data, $component, $enabled = false) { +function _webform_submission_display_file($data, $component, $enabled = false, $sid) { // RECHECK: should pass whole $submission instead?, or sending to every display_$componant will be bad usage $filedata = unserialize($data['value'][0]); $form_item = _webform_render_file($component); if (!$enabled) { @@ -408,10 +469,22 @@ function _webform_submission_display_fil $form_item['#default_value'] = empty($filedata['filepath']) ? $filedata['error'] : $filedata['filepath']; } if ($filedata['filename']) { - $form_item['#suffix'] = ' Download '. $filedata['filename'] .'' . $form_item['#suffix']; + $suffix_link = 'Download '. $filedata['filename'] .''; + $node = node_load($form_item['#webform_component']['nid']); + $submission = webform_menu_submission_load($sid, $node->nid); + // drupal_set_message($node->nid .' '. $sid); + if ( $enabled && webform_submission_access($node, $submission, 'edit') ) { // if can update node, then give link to delete, maybe change to user_access? + $suffix_link .= '            Delete '. $filedata['filename'] .''; + } + $form_item['#suffix'] .= $suffix_link . $form_item['#suffix']; if ($enabled) { $form_item['#description'] = t('Uploading a new file will replace the current file.'); $form_item['#webform_current_file'] = $filedata; + if ( $form_item['#webform_component']['mandatory'] == 1 ) { + drupal_add_js("$(document).ready(function(){ + $('#webform-component-". $component['form_key'] ." label .form-required').append(' (Dont submit new file unless want to replace current file.)'); // some clarification, as when I see the * character, i tend to think i didnt upload something previously. the description is small, and user may not notice hyperlink... Hence why I put this here; You may remove if dont like. + });", "inline"); + } } } return $form_item; @@ -427,12 +500,17 @@ function _webform_submission_display_fil * An array of information describing the component, directly correlating to * the webform_component database schema. */ -function _webform_delete_file($data, $component) { +function _webform_delete_file($data, $component, $custom = FALSE) { // Delete an individual submission file. - $filedata = unserialize($data['value']['0']); + if ($custom) { + $filedata = unserialize($data); //for reusing this function for deleting the file componant. like if uploading new file, deletes old file + } + else { + $filedata = unserialize($data['value']['0']); + } + db_query("DELETE FROM {files} WHERE fid = %d", $filedata['fid']); if (isset($filedata['filepath']) && is_file($filedata['filepath'])) { unlink($filedata['filepath']); - db_query("DELETE FROM {files} WHERE filepath = '%s'", $filedata['filepath']); } } @@ -472,10 +550,12 @@ function _webform_theme_file() { * submissions. */ function _webform_analysis_rows_file($component) { - $query = 'SELECT data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d'; + $query = 'SELECT sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND s.is_draft != 1'. + ' AND sd.cid = %d'; $nonblanks = 0; $submissions = 0; $wordcount = 0; @@ -565,3 +645,109 @@ function webform_file_url($filepath) { } return isset($file_url) ? $file_url : ''; } +function _webform_delete_submitted_file_data($nid, $sid, $cid, $filename = NULL) { // dont make copy of node as could be wasteful possibly + $result2 = db_query("DELETE FROM {webform_submitted_data} WHERE nid = %d AND sid = %d AND cid = %d", $nid, $sid, $cid); + if ($result2) { + drupal_set_message("File form data '". $filename ."' deleted successfully."); + } + else { + drupal_set_message("File form data '". $filename ."' not deleted successfully.", "error"); + watchdog(WATCHDOG_WARNING, "Form form data '". $filename ."' not deleted successfully."); + } +} + +/* + * Reports back php file errors: I ran into a case where php error log reported error when trying to validate file but webform doesn't check php file errors? + */ +function _webform_php_file_errors($form_key) { + //drupal_set_message( 'fileerrors='. print_r($_FILES["files"]["error"],true)); + $error = $_FILES["files"]["error"][$form_key]; + + if ($error == UPLOAD_ERR_INI_SIZE)//1 + return "The uploaded file '%filename' exceeds the upload_max_filesize directive in php.ini."; + else if ($error == UPLOAD_ERR_FORM_SIZE)//2 + return "The uploaded file '%filename' exceeds the MAX_FILE_SIZE directive that was specified in the HTML form."; + else if ($error == UPLOAD_ERR_PARTIAL)//3 + return "The uploaded file '%filename' was only partially uploaded. Please reupload."; + else if ($error == UPLOAD_ERR_NO_TMP_DIR)//6 + return "Cannot save '%filename'. Missing temporary folder."; + else if ($error == UPLOAD_ERR_CANT_WRITE) //7 + return "Failed to write file '%filename' to disk."; +} + +/* + * Confirmation page for deleting a file from the webform + */ +function _webform_delete_file_confirm(&$form_state, $node, $submission, $cid, $fid) { + $nid = $node->nid; + $sid = $submission->sid; + $form['nid'] = array('#type' => 'value', '#value' => $nid); + $form['sid'] = array('#type' => 'value', '#value' => $sid); + $form['cid'] = array('#type' => 'value', '#value' => $cid); + $form['fid'] = array('#type' => 'value', '#value' => $fid); + $form['#submit'][] = '_webform_delete_file_confirm_submit'; +//drupal_set_message($nid.'-'.$sid.'-'.$cid.'-'.$fid); + $result = db_query("SELECT filename,filepath FROM {files} WHERE fid = %d", $fid); + $fileobject = db_fetch_object($result); + $filename = $fileobject->filename; + + return confirm_form($form, + t('Are you sure you want to delete the file') . (!empty($filename) ? (' '. $filename ) : '' ) .'?', + ('node/'. $nid .'/'. intval($sid)), + t('This action cannot be undone.'), + t('Delete'), t('Cancel')); + +} + +/** + * Deletes the file from system/webform + */ +function _webform_delete_file_confirm_submit($form, &$form_state) { //since changed _webform_delete_file to use fid, maybe just call that instead? + $nid = $form_state['values']['nid']; + $sid = $form_state['values']['sid']; + $cid = $form_state['values']['cid']; + $fid = $form_state['values']['fid']; + + $result = db_query("SELECT filename,filepath FROM {files} WHERE fid = %d", $fid); + // delete the file + if ($result && ($fileobject = db_fetch_object($result)) ) { + // delete file from drupal database and file itself + db_query("DELETE FROM {files} WHERE fid = %d", $fid); + $filename = $fileobject->filename; + $filepath = $fileobject->filepath; //relative or full filepath depending on public/private filesystem + // delete data from webform_submission_data, possible case if file already deleted or not in database, so just get rid now from form data + _webform_delete_submitted_file_data($nid, $sid, $cid, $filename ); + if (isset($filepath) && is_file($filepath)) { + unlink($filepath); + } + else { + drupal_set_message("File '". $filename ."' could not be deleted since it was not found. Contact Administrator", "error"); + watchdog(WATCHDOG_WARNING, "File '". $filename ."' could not be deleted since it was not found. Contact Administrator"); + } + drupal_goto('node/'. $nid .'/submission/'. intval($sid) .'/edit' ); // probably want to redirect to the edit submission page + } + else { //Edge case: if file still there just not in database for some reason + _webform_delete_submitted_file_data($nid, $sid, $cid, $filename ); + // unlink() would be nice to delete the file also but... Don't unlink as, if uploaded a file called file.doc, and someone deleted off server. someone else (or same user for different question) could have uploaded resume.doc and hence it will have same filename as the one you want to delete + drupal_set_message("File database information could not be deleted for file id: '". $fid .".", "error"); + watchdog(WATCHDOG_WARNING, "File database information not found for file id: ". $fid ."."); + drupal_goto('node/'. $nid .'/submission/'. intval($sid) .'/edit' ); + } +} + +/** + * Checks if another file was uploaded when an error occured. User wont know his file didnt upload, if field wasnt required, or if saved a draft with another file, this is a problem, as submitting will work after previous error is fixed. This just informs user on that error page to make sure to resubmit the file. + */ +function _webform_check_error_and_newfile($form_state, $node) { + $form_errors = form_get_errors(); + if ( count($_FILES['files']['size']) > 0 ) { //need to check if no files upload in this form, dont check, otherwise foreach warns FILES not array/invalid arg + foreach ( $_FILES['files']['size'] as $form_key => $size) { // see if submitted new file + if (($size > 0)) { //&& there was an error + if ( $form_errors && (!array_key_exists($form_key, $form_errors)) ) { // error occured on a required textfield for example. but file isnt uploaded yet... need some info saying this to the user, so he remembers to reupload his file + form_set_error( $form_key, t('An error occured on another question so unfortunately your file upload was lost. Please correct the other field and also re-upload %filename.', array('%filename' => $_FILES['files']['name'][$form_key]) ) ); + set_file_componant_error($form_key); + } + } + } + } +} diff -urp --strip-trailing-cr webform623/components/grid.inc webform_arian_latest/components/grid.inc --- webform623/components/grid.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/components/grid.inc 2008-10-30 16:00:24.978125000 -0400 @@ -241,12 +241,14 @@ function _webform_analysis_rows_grid($co $questions = array_values(_webform_grid_options($component['extra']['questions'])); // Generate a lookup table of results. - $query = 'SELECT no, data, count(data) as datacount '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d '. - " AND data != '0' AND data != '' ". - ' GROUP BY no, data'; + $query = 'SELECT sd.no, sd.data, count(sd.data) as datacount '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + " AND sd.data != '0' AND sd.data != ''". + ' AND s.is_draft != 1'. + ' GROUP BY sd.no, sd.data'; $result = db_query($query, $component['nid'], $component['cid']); $counts = array(); while ($data = db_fetch_object($result)) { diff -urp --strip-trailing-cr webform623/components/hidden.inc webform_arian_latest/components/hidden.inc --- webform623/components/hidden.inc 2008-09-06 16:51:40.000000000 -0400 +++ webform_arian_latest/components/hidden.inc 2008-10-23 14:52:38.000000000 -0400 @@ -117,10 +117,12 @@ function _webform_help_hidden($section) * submissions. */ function _webform_analysis_rows_hidden($component) { - $query = 'SELECT data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d'; + $query = 'SELECT sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND s.is_draft != 1'. + ' AND sd.cid = %d'; $nonblanks = 0; $submissions = 0; $wordcount = 0; diff -urp --strip-trailing-cr webform623/components/select.inc webform_arian_latest/components/select.inc --- webform623/components/select.inc 2008-10-22 15:38:58.000000000 -0400 +++ webform_arian_latest/components/select.inc 2008-10-23 15:56:10.000000000 -0400 @@ -301,12 +301,14 @@ function _webform_theme_select() { function _webform_analysis_rows_select($component) { $options = _webform_select_options($component['extra']['items']); - $query = 'SELECT data, count(data) as datacount '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d '. - " AND data != '0' AND data != '' ". - ' GROUP BY data '; + $query = 'SELECT sd.data, count(sd.data) as datacount '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + ' AND s.is_draft != 1'. + " AND sd.data != '0' AND sd.data != ''". + ' GROUP BY sd.data'; $result = db_query($query, $component['nid'], $component['cid']); $rows = array(); while ($data = db_fetch_array($result)) { diff -urp --strip-trailing-cr webform623/components/textarea.inc webform_arian_latest/components/textarea.inc --- webform623/components/textarea.inc 2008-09-06 16:51:40.000000000 -0400 +++ webform_arian_latest/components/textarea.inc 2008-10-20 16:48:34.000000000 -0400 @@ -138,10 +138,12 @@ function _webform_help_textarea($section * submissions. */ function _webform_analysis_rows_textarea($component) { - $query = 'SELECT data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d'; + $query = 'SELECT sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + ' AND s.is_draft != 1'; $nonblanks = 0; $submissions = 0; $wordcount = 0; diff -urp --strip-trailing-cr webform623/components/textfield.inc webform_arian_latest/components/textfield.inc --- webform623/components/textfield.inc 2008-10-14 14:20:02.000000000 -0400 +++ webform_arian_latest/components/textfield.inc 2008-10-23 15:58:36.000000000 -0400 @@ -195,10 +195,13 @@ function _webform_help_textfield($sectio * submissions. */ function _webform_analysis_rows_textfield($component) { - $query = 'SELECT data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d'; + $query = 'SELECT sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + ' AND s.is_draft != 1'; + $nonblanks = 0; $submissions = 0; $wordcount = 0; diff -urp --strip-trailing-cr webform623/components/time.inc webform_arian_latest/components/time.inc --- webform623/components/time.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/components/time.inc 2008-10-23 15:59:30.000000000 -0400 @@ -270,11 +270,13 @@ function _webform_theme_time() { * submissions. */ function _webform_analysis_rows_time($component) { - $query = 'SELECT no,data '. - ' FROM {webform_submitted_data} '. - ' WHERE nid = %d '. - ' AND cid = %d '. - ' ORDER BY sid,no ASC '; + $query = 'SELECT sd.no,sd.data '. + ' FROM {webform_submitted_data} sd'. + ' JOIN {webform_submissions} s ON sd.sid = s.sid'. + ' WHERE sd.nid = %d'. + ' AND sd.cid = %d'. + ' AND s.is_draft != 1'. + ' ORDER BY sd.sid,sd.no ASC'; $result = db_query($query, $component['nid'], $component['cid']); // build an array of timestamps from entered values. diff -urp --strip-trailing-cr webform623/webform.install webform_arian_latest/webform.install --- webform623/webform.install 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/webform.install 2008-10-23 16:04:52.000000000 -0400 @@ -804,7 +804,24 @@ function webform_update_20() { } return $ret; } - +/* + * Update if running 'Save Draft' Webform patch + */ +function webform_update_21() { + $ret = array(); + switch ($GLOBALS['db_type']) { + case 'mysqli': + case 'mysql': + $ret[] = update_sql("ALTER TABLE {webform_submissions} ADD is_draft tinyint not null default '0'"); + $ret[] = update_sql("ALTER TABLE {webform} ADD allow_draft tinyint not null default '0'"); + break; + case 'pgsql': + $ret[] = update_sql("ALTER TABLE {webform_submissions} ADD is_draft smallint not null default '0'"); + $ret[] = update_sql("ALTER TABLE {webform} ADD allow_draft smallint not null default '0'"); + break; + } + return $ret; +} /** * Set the upgrade */ diff -urp --strip-trailing-cr webform623/webform.module webform_arian_latest/webform.module --- webform623/webform.module 2008-10-14 17:51:42.000000000 -0400 +++ webform_arian_latest/webform.module 2008-10-31 11:40:46.243750000 -0400 @@ -89,10 +89,10 @@ function webform_menu() { ); // Node page tabs. - $items['node/%webform_menu/done'] = array( + $items['node/%webform_menu/done/submission/%sid'] = array( // prefer not sending the sid in GET, just a preference 'title' => 'Webform confirmation', 'page callback' => '_webform_confirmation', - 'page arguments' => array(1), + 'page arguments' => array(1, 4), 'access callback' => 'node_access', 'access arguments' => array('view', 1), 'type' => MENU_CALLBACK, @@ -119,7 +119,7 @@ function webform_menu() { // Node component forms. $items['node/%webform_menu/edit/components/%webform_menu_component'] = array( - 'load arguments' => array(1, 5), + 'load arguments' => array(1, 5), //5 needed in this item? is NULL anyway? 'page arguments' => array('webform_component_edit_form', 1, 4, FALSE), 'access callback' => 'node_access', 'access arguments' => array('update', 1), @@ -216,7 +216,7 @@ function webform_menu() { 'title' => 'Webform submission', 'load arguments' => array(1), 'page callback' => 'webform_client_form_load', - 'page arguments' => array(1, 3, FALSE, FALSE), + 'page arguments' => array(1, 3, FALSE, FALSE), // array('webform_client_form_'. $node->nid, $node, $submission, FALSE, FALSE, FALSE, $submission->is_draft), // call is_draft via loading the submission which should have is_draft added 'access callback' => 'webform_submission_access', 'access arguments' => array(1, 3, 'view'), 'type' => MENU_CALLBACK, @@ -252,6 +252,28 @@ function webform_menu() { 'type' => MENU_LOCAL_TASK, ); + $items['node/%webform_menu/draft_saved/%webform_menu_submission']= array( + 'title' => t('Draft saved'), + 'load arguments' => array(1), + 'page callback' => '_webform_draft_confirmation', + 'page arguments' => array(1, 3), + 'access callback' => 'webform_submission_access', + 'access arguments' => array(1, 3, 'view'), + 'type' => MENU_CALLBACK, + ); + + $items['node/%webform_menu/%webform_menu_submission/%/%/delete_file']= array( + 'title' => t('Delete Webform File'), + 'load arguments' => array(1), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('_webform_delete_file_confirm', 1, 2, 3, 4), // node, question id, file id args (node id should be loaded automatically, calling _load) + 'access callback' => 'webform_submission_access', + 'access arguments' => array(1, 2, 'edit'), // make sure if going to this link that user is allowed to update the webform if he is allowed to update it + 'file' => 'file.inc', + 'file path' => drupal_get_path('module', 'webform') .'/components/', + 'type' => MENU_CALLBACK, + ); + return $items; } @@ -295,19 +317,32 @@ function webform_menu_component_load($ci return $component; } -function webform_submission_access($node, $submission, $op = 'view', $account = NULL) { +function webform_submission_access($node, $submission = NULL, $op = 'view', $account = NULL) { // might not need submission if checking is should access submissions page ('list'), or in 'edit' if should have rights to 'edit webform submissions', aka not just /edit page for a specific submission, but all your submissons in general for that $node global $user; $account = isset($account) ? $account : $user; + $draft_count = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1", $node->nid, $user->uid)); // get number of drafts for user. if he has more than 1, show him a list to other drafts. if just one dont show him the list as he doesnt need it (the last draft prepopulates the webform anyway). use $user btw as we never check for someone elses priveledges in 'list' situations + + $allowed_roles = array(); + foreach ($node->webform['roles'] as $rid) { + $allowed_roles[$rid] = isset($user->roles[$rid]) ? TRUE : FALSE; + } + if ((array_search(TRUE, $allowed_roles) === FALSE) && ($user->uid != 1)) { + $role_is_not_allowed = TRUE; + } + module_load_include('inc', 'webform', 'webform_submissions'); + $limit_exceeded = _webform_submission_limit_check($node); + $access_errors = $role_is_not_allowed || $limit_exceeded; // for determinging if user should have access to drafts + switch ($op) { case 'view': - return user_access('access webform results') || (user_access('access own webform submissions') && ($account->uid == $submission->uid)); + return user_access('access webform results') || (user_access('access own webform submissions') && ($account->uid == $submission->uid)) || ($submission->is_draft && ($account->uid == $submission->uid) && (!$access_errors)); // even if doesnt have rights to access own finalized webform submission, at this point if it is a draft, allow them to view still or delete file they uploaded in a draft case 'edit': - return user_access('edit webform submissions') || (user_access('edit own webform submissions') && ($account->uid == $submission->uid)); + return user_access('edit webform submissions') || (user_access('edit own webform submissions') && ($account->uid == $submission->uid)) || ($submission->is_draft && ($account->uid == $submission->uid) && (!$access_errors)); // even if doesnt have rights to edit own finalized webform submission, at this point if it is a draft, allow them to edit still if they are allowed to submit the form case 'delete': - return user_access('edit webform submissions') || (user_access('edit own webform submissions') && ($account->uid == $submission->uid)) || user_access('clear webform results'); - case 'list': - return user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid); + return user_access('edit webform submissions') || (user_access('edit own webform submissions') && ($account->uid == $submission->uid)) || user_access('clear webform results') || ($submission->is_draft && ($account->uid == $submission->uid) && (!$access_errors)); // allow user to delete the submission if its still in draft form and user is allowed to submit form (no link to do this currently if user has 1 draft saved but shouldnt be needed as if will submit form, the draft prepopulated for him. only issue if wants to clear data as he doesnt want his info in our system. he can just manually clear all his data for now, until admin deletes non-completed webforms) + case 'list':// get submissions for this user or all users + return (($draft_count > 1) && $user->uid && (!$access_errors)) || user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid); // allow non-anonymous users that have drafts saved to goto Submissions page to at least see their unsubmitted drafts, edge case as last saved draft always gets populated and have to submit it, although administrator may put a saved submission back into Draft state (submitted submissions on that page may be not seen if they dont have 'access webform submissions' rights to see them. Change in webform_results_submissions() to take this into account) } } @@ -315,7 +350,8 @@ function webform_submission_access($node * Implementation of hook_perm(). */ function webform_perm() { - return array("create webforms", "edit own webforms", "edit webforms", "access webform results", "clear webform results", "access own webform submissions", "edit own webform submissions", "edit webform submissions", "use PHP for additional processing"); + return array("create webforms", "edit own webforms", "edit webforms", "access webform results", "clear webform results", "access own webform submissions", "edit own webform submissions", "edit webform submissions", "use PHP for additional processing"); // "access webform submissions" is still used in the module?, maybe cause "access webform results" seems to have replaced it. I use both just in case in my checks and left in hook_perm + } /** @@ -328,7 +364,7 @@ function webform_theme() { 'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'form' => NULL, 'enabled' => NULL), ), 'webform_view_messages' => array( - 'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'limit_exceeded' => NULL, 'allowed_roles' => NULL), + 'arguments' => array('node' => NULL, 'teaser' => NULL, 'page' => NULL, 'submission_count' => NULL, 'limit_exceeded' => NULL, 'allowed_roles' => NULL, 'submission' => NULL), ), 'webform_form' => array( 'arguments' => array('form' => NULL), @@ -387,6 +423,9 @@ function webform_theme() { 'webform_results_table' => array( 'arguments' => array('node' => NULL, 'components' => NULL, 'submissions' => NULL, 'node' => NULL), ), + 'webform_draft_confirmation' => array( + 'arguments' => array('node' => NULL, 'sid' => NULL), + ), ); // Theme functions in all components. $components = webform_load_components(TRUE); @@ -444,12 +483,60 @@ function webform_forms($form_id) { * * Only allow users with view webform submissions to download files. */ -function webform_file_download($file) { +function webform_file_download($file) { // possibly check access rights first to save time? + $file = file_check_location(file_directory_path() .'/'. $file, file_directory_path() .'/webform/'); - if ($file && user_access('access webform results')) { - $info = image_get_info(file_create_path($file)); - return array('Content-type: '. $info['mime_type']); + $pathinfo_file_path_url = pathinfo($file); + // Makes sense that if they save their submission and want to recome back to it to check on it... that they can dl and view their file to see if they want to replace it, also give access to anyone who can edit webform submissions + global $user; + if ( strpos( realpath($pathinfo_file_path_url['dirname']), realpath(file_directory_path() .'/webform/') ) === 0 ) { // scope it to webform module, do not reject other module's file download + $webform_access_download = ( user_access('access webform results') + || (user_access('edit own webform submissions') && ($file_uid==$user->uid)) + || (user_access('access own webform submissions') && ($file_uid==$user->uid)) + || user_access('edit webform submissions') ); + + if (!$webform_access_download) + return -1; + + // Get sid of file so can figure out uid of who submitted this file as an answer (to base permissions off of) + $result = db_query("SELECT * FROM {files} f where status=1"); // No need to get files from other modules setting temp files, Possibly create a webform_files db too, for faster look ups on what nid,sid belongs to an fid, Or can just not look at {files} and use webform_submitted_data for files because going to look in there anyway if a file match is found + $num_files = 0; + while ( $row = db_fetch_object($result) ) { + $path_info_db = pathinfo($row->filepath); + // So making sure the database entry is same filepath if requested file + if ( (realpath($pathinfo_file_path_url['dirname'])==realpath($path_info_db['dirname'])) && ($pathinfo_file_path_url['basename']==$path_info_db['basename']) ) { // base on filedir, and then filename cause some modules dont normalize data and might have funky filenames like C:\files/webform/1/2/node1submissionfile.blah, checks to make sure same file in same directory... Maybe can substitute file_check_path but sometimes makes mistakes like c: and C:, so hence why i just use realpath() + $fid = $row->fid; + $num_files++; + if ($num_files>1) // Drupal says their are 2 matching filepaths in its system. Shouldnt be possible, maybe a module forgot to delete its file. One should be deleted. + break; + } + } + + if ( $num_files == 1 ) { // Found a match, now lets see if the user can access it by being an webformadmin-type person or if matches the user who submitted that file + $result = db_query("SELECT wsd.`data`, wsd.nid, wsd.sid FROM {webform_submitted_data} wsd JOIN {webform_component} wc ON wsd.cid=wc.cid WHERE wc.`type`='file'"); + while ( $row = db_fetch_object($result)) { // search for this node + $data = unserialize($row->data); + if ( intval($data['fid']) == intval($fid) ) { + $sid = $row->sid; + $nid = $row->nid; + break; + } + } + + if (($nid>0) && ($sid>0)) { + $file_uid = db_result(db_query("SELECT uid FROM {webform_submissions} ws WHERE sid=%d AND nid=%d", $sid, $nid)); // Could also use {files} uid, but sticking with webforms data + if ($file && $webform_access_download) { + $info = image_get_info(file_create_path($file)); + return array('Content-type: '. $info['mime_type']); + } + } + } + else if ( $num_files > 1) + drupal_set_message( 'There are 2 of files with same filepath in the database for some reason so we are not sure if you are allowed to access this file. Please ask website administrator to fix so you may get access.' ) ; + else + drupal_set_message( 'File cannot be found. Contact Website administrator.'); // Not sure if ever gets here, in drupal 5, if file not found, hook wont get called it seems? } + return NULL; // explicitly saying dont handle download. not needed. } /** @@ -464,7 +551,7 @@ function webform_insert($node) { } // Insert the Webform. - db_query("INSERT INTO {webform} (nid, confirmation, teaser, submit_text, submit_limit, submit_interval, email, email_from_name, email_from_address, email_subject, additional_validate, additional_submit) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s')", $node->nid, $node->webform['confirmation'], $node->webform['teaser'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval'], $node->webform['email'], $node->webform['email_from_name'], $node->webform['email_from_address'], $node->webform['email_subject'], $node->webform['additional_validate'], $node->webform['additional_submit']); + db_query("INSERT INTO {webform} (nid, confirmation, teaser, submit_text, submit_limit, submit_interval, email, email_from_name, email_from_address, email_subject, additional_validate, additional_submit, allow_draft) VALUES (%d, '%s', %d, '%s', %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', %d)", $node->nid, $node->webform['confirmation'], $node->webform['teaser'], $node->webform['submit_text'], $node->webform['submit_limit'], $node->webform['submit_interval'], $node->webform['email'], $node->webform['email_from_name'], $node->webform['email_from_address'], $node->webform['email_subject'], $node->webform['additional_validate'], $node->webform['additional_submit'], $node->webform['allow_draft']); // Insert the components into the database. Used with clone.module. if (isset($node->webform['components']) && !empty($node->webform['components'])) { @@ -819,6 +906,14 @@ function webform_form(&$node, &$param) { '#default_value' => $node->webform['submit_text'], '#description' => t('By default the submit button on this form will have the label Submit. Enter a new title here to override the default.'), ); + + // Allow save draft + $form['webform']['advanced']['allow_draft'] = array( + '#type' => 'checkbox', + '#title' => t('Allow users to save a draft'), + '#default_value' => $node->webform['allow_draft'], + ); + if (user_access('use PHP for additional processing')) { $form['webform']['advanced']['additional_validate'] = array( '#type' => 'textarea', @@ -1076,13 +1171,40 @@ function webform_view(&$node, $teaser = } // Get a count of previous submissions by this user. - if ($user->uid && (user_access('access own webform submissions') || user_access('access webform results') || user_access('access webform submissions'))) { - $submission_count = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d", $node->nid, $user->uid)); + $submission_count = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft <> 1", $node->nid, $user->uid)); // get submission count where not a draft, otherwise user with 1 draft and 1 form limit + + // Check if this user has a draft for this webform. + $is_draft = FALSE; + if ($node->webform['allow_draft'] && $user->uid != 0) { // drafts shouldnt be allowed for anonymous users, so dont autofill last saved draft form for them + // Draft found - display form with draft data for further editing. + if ($_draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) { + // if is_draft, but dont have rights + include_once(drupal_get_path('module', 'webform') ."/webform_submissions.inc"); + $submission = webform_get_submission($node->nid, $_draft_sid); + if ( webform_submission_access($node, $submission, 'edit') ) { + $enabled = TRUE; + $is_draft = TRUE; + } + else { + $enabled = FALSE; + $is_draft = TRUE; + } + + } } // Render the form and generate the output. - $form = drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview); - $output = theme('webform_view', $node, $teaser, $page, $form, $enabled); + if ( ($submission->is_draft && webform_submission_access($node, $submission, 'view')) ) { // is a draft and has access so at least display the draft (maybe be able to submit based if $enabled set above for drafts) + $form = drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview, NULL, $is_draft); + $output = theme('webform_view', $node, $teaser, $page, $form, $enabled); + } + else if ( ($submission->is_draft && (!webform_submission_access($node, $submission, 'view'))) ) { // does not have access to see draft prefilled out + $node->body = 'Webform not available'; + } + else { // regular case + $form = drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview, NULL, $is_draft); + $output = theme('webform_view', $node, $teaser, $page, $form, $enabled); + } // Remove the surrounding
tag if this is a preview. if ($preview) { @@ -1091,7 +1213,7 @@ function webform_view(&$node, $teaser = // Print out messages for the webform. if (!$preview && !$logging_in) { - theme('webform_view_messages', $node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles); + theme('webform_view_messages', $node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles, $submission); } // Add the output to the node. @@ -1141,49 +1263,123 @@ function theme_webform_view($node, $teas * @param $allowed_roles * A list of user roles that are allowed to submit this webform. */ -function theme_webform_view_messages($node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles) { +function theme_webform_view_messages($node, $teaser, $page, $submission_count, $limit_exceeded, $allowed_roles, $submission = NULL) { global $user; $type = 'notice'; - // If not allowed to submit the form, give an explaination. - if (array_search(TRUE, $allowed_roles) === FALSE && $user->uid != 1) { - if (empty($allowed_roles)) { - // No roles are allowed to submit the form. - $message = t('Submissions for this form are closed.'); + $is_draft = isset($submission) ? $submission->is_draft : FALSE; // if webform is prefilled with submission, allows some better messages + + // If not allowed to submit the form, give an explanation. + $role_not_in_list = (array_search(TRUE, $allowed_roles)===FALSE); + $is_not_admin = ($user->uid != 1); + $role_is_not_allowed = ($role_not_in_list && $is_admin); + if (empty($allowed_roles)) { + if ($user->uid == 1) { + $message .= t('Restriction Bypassed: Submissions for this form are closed but you, the super-user, may still add and edit submissions.'); // clarifies to super-user form submissions are over on view page + } + else if ( user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid) ) { + $message .= t('Restriction Bypassed: Submissions for this form are closed but you may still view '); + if ( user_access('edit webform submissions') || (user_access('edit own webform submissions') && $user->uid) ) { + $message .= t(' and edit'); + } + $message .= t(' your submissions.'); + } + else { + $message .= t('Restriction: Submissions for this form are closed.'); + } + } + else if ( $role_not_in_list ) { + if (!$is_not_admin) { + $message .= t('Restriction Bypassed: Your user role was not allowed to access this form. But because of super-user permissions you may still view and edit your submissions.'); } - elseif (isset($allowed_roles[2])) { + else if (isset($allowed_roles[2])) { // The "authenticated user" role is allowed to submit and the user is currently logged-out. - $message = t('You must login or register to view this form.', array('!login' => url('user/login'), '!register' => url('user/register'))); + $message .= t('You must login or register to view this form.', array('!login' => url('user/login'), '!register' => url('user/register'))); + } + else if ( user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid) ) { + $message .= t('Restriction Bypassed: Your user role was not allowed to access this form. But because of user permissions you may still view '); + if ( user_access('edit webform submissions') || (user_access('edit own webform submissions') && $user->uid) ) { + $message .= t(' and edit'); + } + $message .= t(' your submissions.'); } else { // The user must be some other role to submit. - $message = t('You do not have permission to view this form.'); + $message .= t('Restriction: You do not have permission to view this form.'); } } // If the user has exceeded the limit of submissions, explain the limit. if ($limit_exceeded) { + if (($user->uid == 1) || user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid) ) + $message .= '
'. t("Restriction Bypassed: "); + else + $message .= '
'. t("Restriction:"); + if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] > 1) { - $message = t("You have submitted this form the maximum number of times (@count).", array('@count' => $node->webform['submit_limit'])); + $message .= ' '. t("You have submitted this form the maximum number of times (@count).", array('@count' => $node->webform['submit_limit'])); } - elseif ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) { - $message = t("You have already submitted this form."); + else if ($node->webform['submit_interval'] == -1 && $node->webform['submit_limit'] == 1) { + $message .= ' '. t("You have already submitted this form."); } else { - $message = t("You may not submit another entry at this time."); + $message .= ' '. t("You are trying to submit another form too soon. You may not submit another entry at this time but may try again later when the time limit for you expires."); } - $type = 'error'; + + if ($user->uid == 1) { + $message .= ' '. t('However you, the super-user, may still add new/edit submissions.'); // clarifies to super-user form submissions are over on view page + } + else if ( user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid) ) { // TODO possibly add edit perms in the else if too, in case admin accidentally let user edit but not view (which wouldnt make sense) + $message .= ' '. t('However, based on permissions, you may still view'); + if ( user_access('edit webform submissions') || (user_access('edit own webform submissions') && $user->uid) ) { + $message .= t(' and edit'); + } + $message .= t(' submissions.'); + } + // $type = 'error'; // the above messages are informative when user lands on page. not sure if warrants a red error message as user sees it when arrives to page, not when posting info + } + + // If the user has submitted before, give them a link to their submissions so they can review or submit other saved version + $draft_count = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1", $node->nid, $user->uid)); + + $allowed_to_access_submissions = webform_submission_access($node, NULL, 'list'); // has saved ones, may want to review or is allowed to see submitted ones + $access_errors = $role_is_not_allowed || $limit_exceeded; + + if ($submission_count>0) { + $message .= '
'. t('Status: You have submitted this form ') . $submission_count .' time'. (($submission_count>1) ? 's' : ''); + if ($draft_count>0) + $message .= t(' and have ') . $draft_count . t(' saved draft') . (($draft_count>1) ? 's' : '') . t(' as well.'); + else + $message .= '.'; } - - // If the user has submitted before, give them a link to their submissions. - if ($submission_count > 0) { - if (empty($message)) { - $message = t('You have already submitted this form.') .' '. t('View your previous submissions.', array('!url' => url('node/'. $node->nid .'/submissions'))); - } - else { - $message .= ' '. t('View your previous submissions.', array('!url' => url('node/'. $node->nid .'/submissions'))); - } + else { + if ($draft_count==1) + $message .= '
'. t('Status: You have a saved draft of this form.'); + else if ($draft_count>1) // edge case + $message .= '
'. t('Status: You have other drafts of this form already saved.'); + else // if ($draft_count==0) + $message .= '
'. t('Status: You have not submitted this form yet.'); + } + + if ( ($submission_count>0) && (!$is_draft) && webform_submission_access($node, $submission, 'edit') )// current one however is a draft + $message .= ' '. t('You may submit a new version of this form again below.'); + else if ( ($submission_count>0) && ($submission==FALSE) && (!$access_errors) ) + $message .= ' '. t('You may submit a new version of this form again below.'); + else if ( $is_draft && webform_submission_access($node, $submission, 'edit') ) + $message .= ' '. t('Your last saved draft is prefilled below for your convenience.'); + + + if ($allowed_to_access_submissions) { // allowed 'list' access to your /node/%node/submissions page, show link to view them (make note user can edit if has access to edit this webform. we dont care about a specific access to a submission, hence why can pass NULL for $submission) + $message .= '
'; + $message_link = t('View'); + if ( user_access('edit webform submissions') || (user_access('edit own webform submissions') && $user->uid) ) { // only users>0 should be admins + $message_link .= t(' and Edit'); + } + $message_link .= t(' your submissions'); + $message_link .= ($draft_count>0) ? ( t(' (including saved drafts ') . ( webform_submission_access($node, NULL, 'edit') ? t('you may want to review and submit)') : t('you may want to review)')) ) : ''; // Note: user is allowed access to the 'submissions page' if has saved draft if has over 1 saved draft and no reason why he cant submit it (if user has just 1 saved draft, it will prepopulated current form, hence why dont give access to that page if just 1) + $message .= l( $message_link, 'node/'. $node->nid .'/submissions'); + $message .= t(' for this form.'); } if ($page && isset($message)) { @@ -1332,7 +1528,7 @@ function theme_webform_admin_settings($f * Menu callback to load the appropriate node form. */ function webform_client_form_load($node, $submission, $enabled, $preview) { - return drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview); + return drupal_get_form('webform_client_form_'. $node->nid, $node, $submission, $enabled, $preview, NULL, $submission->is_draft); } /** @@ -1351,21 +1547,20 @@ function webform_client_form_load($node, * If displaying a result, specify if form elements are enabled for * editing. */ -function webform_client_form($form_state, &$node, $submission, $enabled = FALSE, $preview = FALSE) { +function webform_client_form($form_state, &$node, $submission, $enabled = FALSE, $preview = FALSE, $form_values = NULL, $is_draft = NULL) { module_load_include('inc', 'webform', 'webform_components'); webform_load_components(); - if (isset($submission->sid)) { - drupal_set_title(t('Submission #@sid', array('@sid' => $submission->sid))); + drupal_set_title( t( ($is_draft ? 'Saved ' : '') .'Submission #@sid', array('@sid' => $submission->sid))); } // Set a header for navigating results. - if ($submission && user_access('access webform results')) { + if ($submission && user_access('access webform results')) { // RECHECK: do you want only finalized submissions to look through or all forms including drafts? Then add && (!$is_draft) // Add CSS to display submission info. Don't preprocess because this CSS file is used rarely. drupal_add_css(drupal_get_path('module', 'webform') .'/webform.css', 'module', 'all', FALSE); - $previous = db_result(db_query('SELECT MAX(sid) FROM {webform_submissions} WHERE nid = %d AND sid < %d', array($node->nid, $submission->sid))); - $next = db_result(db_query('SELECT MIN(sid) FROM {webform_submissions} WHERE nid = %d AND sid > %d', array($node->nid, $submission->sid))); + $previous = db_result(db_query('SELECT MAX(sid) FROM {webform_submissions} WHERE nid = %d AND sid < %d AND is_draft <> 1', array($node->nid, $submission->sid))); // RECHECK: decide if user has access to results, if the previous node should be allowed to include drafts. If not, add to query 'AND is_draft != 1' + $next = db_result(db_query('SELECT MIN(sid) FROM {webform_submissions} WHERE nid = %d AND sid > %d AND is_draft <> 1', array($node->nid, $submission->sid))); // RECHECK: decide if user has access to results, if the next node should be allowed to include drafts $form['submission'] = array( '#type' => 'value', @@ -1383,7 +1578,7 @@ function webform_client_form($form_state ); $form['submission_info'] = array( - '#title' => t('Submission Information'), + '#title' => ($is_draft ? t('Draft Information') : t('Submission Information') ), '#type' => 'fieldset', '#collapsible' => FALSE, ); @@ -1395,7 +1590,7 @@ function webform_client_form($form_state '#value' => '
'. t('Form: !form', array('!form' => l($node->title, 'node/'. $node->nid))) .'
', ); $form['submission_info']['submitted'] = array( - '#value' => '
'. t('Submitted by !name', array('!name' => theme('username', $account))) .'
', + '#value' => '
'. ($is_draft ? t('Saved by !name', array('!name' => theme('username', $account))) : t('Submitted by !name', array('!name' => theme('username', $account))) ) .'
', ); $form['submission_info']['time'] = array( '#value' => '
'. format_date($submission->submitted, 'large') .'
', @@ -1426,7 +1621,7 @@ function webform_client_form($form_state '#tree' => TRUE ); $form['details'] = array( - '#tree' => true, + '#tree' => TRUE, ); // Put the components into a tree structure. @@ -1494,13 +1689,24 @@ function webform_client_form($form_state '#weight' => 1000, ); } + + // Save draft button + global $user; + if ($node->webform['allow_draft'] && $user->uid != 0) { // adds Save Draft Button if webform allows saving. Anonymous users shouldnt be able to save as they would overwrite each other + $form['draftbutton'] = array( + '#type' => 'submit', + '#value' => t('Save Draft'), + '#weight' => 999, + ); + } + } // Recursively add components to the form. Microweights keep things in webform order. $microweight = 0.001; foreach ($component_tree['children'] as $cid => $component) { $component_value = isset($form_state['values']['submitted'][$component['form_key']]) ? $form_state['values']['submitted'][$component['form_key']] : NULL; - _webform_client_form_add_component($cid, $component, $component_value, $form['submitted'], $form, $submission, $page_num, $enabled); + _webform_client_form_add_component($cid, $component, $component_value, $form['submitted'], $form, $submission, $page_num, $enabled, ( (int) $node->webform['allow_draft']), $form_state ); $form['submitted'][$component['form_key']]['#weight'] += $microweight; $microweight += 0.001; } @@ -1535,13 +1741,40 @@ function webform_client_form($form_state return $form; } -function _webform_client_form_add_component($cid, $component, $component_value, &$parent_fieldset, &$form, $submission, $page_num, $enabled = false) { +function unset_required_fields(&$array) { + if ( is_array($array) ) { // need to check if arg is an array (in multi-step forms will not be) + foreach ( $array as $key => $value ) { + if ( is_array($value) && ($key !== '#element_validate') ) { // Dont need to loop over validate array and look for require/other validate fields + unset_required_fields( $array[$key] ); + } + else if ( ($key === '#required') ) { // #mandatory doesnt need to be changed it seems so just change #required + $array['#required'] = 0; // turn off drupals required validation if saving, problem: * wont show up then? redirect to save draft page for now. maybe restore the #required afterwards when validation is done to those fields who had it before? + } + else if ( $key === '#element_validate' ) { // value here is an array of funcnames mapped to funcparams + // go through and unset validation functions which dont have _CHECKALWAYS_ in their callback name + foreach ( $value as $key => $validation_func_name ) { // before args were passed too, not anymore i guess + $pos = strpos($validation_func_name, '_CHECKALWAYS_'); + // if this is file type, possibly unset validation function to check file validation if already submitted a file before and no new file + if ( $pos === FALSE ) { // found match + unset( $array['#element_validate'][$key] ); + } + } + + if ( count( $array['#validate'] ) <= 0) { + unset( $array['#validate'] ); + } + } + } + } +} + +function _webform_client_form_add_component($cid, $component, $component_value, &$parent_fieldset, &$form, $submission, $page_num, $enabled = FALSE, $allow_draft = FALSE, $form_state = NULL) { // Load with submission information if necessary. if (!$enabled) { // This component is display only. $display_function = "_webform_submission_display_". $component['type']; if (function_exists($display_function)) { - $parent_fieldset[$component['form_key']] = $display_function(empty($submission->data[$cid]) ? NULL : $submission->data[$cid], $component, $enabled); + $parent_fieldset[$component['form_key']] = $display_function(empty($submission->data[$cid]) ? NULL : $submission->data[$cid], $component, $enabled, $submission->sid); } } elseif ($component['page_num'] == $page_num) { @@ -1549,7 +1782,7 @@ function _webform_client_form_add_compon if (isset($submission->data)) { $display_function = "_webform_submission_display_". $component['type']; if (function_exists($display_function)) { - $parent_fieldset[$component['form_key']] = $display_function($submission->data[$cid], $component, $enabled); + $parent_fieldset[$component['form_key']] = $display_function($submission->data[$cid], $component, $enabled, $submission->sid); // sid for file display, didnt want to modify all the display functions, so just added this to end } } else { @@ -1574,11 +1807,24 @@ function _webform_client_form_add_compon } } } + + // Save drafts: If the webform is being built during a submission, and draft save has been requested, disable required fields to prevent validation warning. + //@todo This leaves the form elements in the rendered page without the 'required' markers. At the moment, that undesirable result is circumvented by having save-draft submissions redirect to a status page w/o the form. Also, this only disables 'required' field validation. Need to test/develop for other validation. + $validate = TRUE; + if ( isset($form_state['post']['op']) && ($form_state['post']['op'] == 'Save Draft') ) { // if want to can pass in form_state and use $form_state['values']['details']['sid'] or $form['#post'][details]['sid'] + $validate = FALSE; + } + + if (!$validate) { // Saving, so do not validate textfields, etc. unsets validation for those fields, except 'file' types. which still need some special sort of validation (dont need requiredness check, but still checks for if its valid file/upload limit/bad extension/etc) + unset_required_fields( $parent_fieldset[$component['form_key']] ); // check any fields which are required and unset then. special case for 'file' types. + unset($form['#redirect']); // RECHECK: Why needed for each form element since this func called recursively? Form redirect will get overridden in webform_client_form_submit() by setting $form_state['redirect']. + } + if (isset($component['children']) && is_array($component['children'])) { $microweight = 0.001; foreach ($component['children'] as $scid => $subcomponent) { $subcomponent_value = isset($component_value[$subcomponent['form_key']]) ? $component_value[$subcomponent['form_key']] : NULL; - _webform_client_form_add_component($scid, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $submission, $page_num, $enabled); + _webform_client_form_add_component($scid, $subcomponent, $subcomponent_value, $parent_fieldset[$component['form_key']], $form, $submission, $page_num, $enabled, $form_state); $parent_fieldset[$component['form_key']][$subcomponent['form_key']]['#weight'] += $microweight; $microweight += 0.001; } @@ -1592,6 +1838,8 @@ function webform_client_form_validate($f $form_state['values']['submitted_tree'] = $form_state['values']['submitted']; $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']); + _webform_check_error_and_newfile($form, $form_state, $node); // makes sure user knows that needs to new reupload file if error occured on other element. + if (trim($node->webform['additional_validate'])) { // Support for Drupal 5 validation code. $form_values =& $form_state['values']; @@ -1609,7 +1857,7 @@ function webform_client_form_submit($for // Check for a multi-page form that is not yet complete. $submit_op = empty($node->webform['submit_text']) ? t('Submit') : $node->webform['submit_text']; - if ($form_state['values']['op'] != $submit_op) { + if ( ($form_state['values']['op'] != $submit_op) && ($form_state['values']['op'] != t('Save Draft')) ) { // Clicked Next Page or Previous Page // Store values from the current page in the form state storage. if (is_array($form_state['values']['submitted'])) { foreach ($form_state['values']['submitted'] as $key => $val) { @@ -1655,24 +1903,27 @@ function webform_client_form_submit($for $form_state['values']['submitted_tree'] = $form_state['values']['submitted']; $form_state['values']['submitted'] = _webform_client_form_submit_flatten($node, $form_state['values']['submitted']); + $is_draft = ($node->webform['allow_draft'] && $form_state['values']['op'] == t('Save Draft')) ? 1 : 0; + // Convert additional email addresses into actual values. - foreach ($node->webform['additional_emails'] as $cid => $value) { - if (is_array($form_state['values']['submitted'][$cid])) { - $node->webform['additional_emails'][$cid] = array(); - foreach ($form_state['values']['submitted'][$cid] as $submitted_value) { - if ($submitted_value) { - $node->webform['additional_emails'][$cid][] = $submitted_value; + if (!$is_draft) { // dont want to send emails if saving a draft + foreach ($node->webform['additional_emails'] as $cid => $value) { + if (is_array($form_state['values']['submitted'][$cid])) { + $node->webform['additional_emails'][$cid] = array(); + foreach ($form_state['values']['submitted'][$cid] as $submitted_value) { + if ($submitted_value) { + $node->webform['additional_emails'][$cid][] = $submitted_value; + } } } - } - else { - $node->webform['additional_emails'][$cid] = $form_state['values']['submitted'][$cid]; - } - if (empty($node->webform['additional_emails'][$cid])) { - unset($node->webform['additional_emails'][$cid]); + else { + $node->webform['additional_emails'][$cid] = $form_state['values']['submitted'][$cid]; + } + if (empty($node->webform['additional_emails'][$cid])) { + unset($node->webform['additional_emails'][$cid]); + } } } - // Perform additional submit processing. if (trim($node->webform['additional_submit'])) { // Support for Drupal 5 validation code. @@ -1684,7 +1935,7 @@ function webform_client_form_submit($for // Save the submission to the database. if (empty($form_state['values']['details']['sid'])) { // No sid was found thus insert it in the datatabase. - $form_state['values']['details']['sid'] = webform_submission_insert($node, $form_state['values']['submitted']); + $form_state['values']['details']['sid'] = webform_submission_insert($node, $form_state['values']['submitted'], $is_draft); $form_state['values']['details']['is_new'] = TRUE; // Set a cookie including the server's submission time. @@ -1697,14 +1948,14 @@ function webform_client_form_submit($for } else { // Sid was found thus update the existing sid in the datatbase. - webform_submission_update($node, $form_state['values']['details']['sid'], $form_state['values']['submitted']); + webform_submission_update($node, $form_state['values']['details']['sid'], $form_state['values']['submitted'], $is_draft); $form_state['values']['details']['is_new'] = FALSE; } $sid = $form_state['values']['details']['sid']; - // Check if this form is sending an email. - if ((!empty($node->webform['email']) || !empty($node->webform['additional_emails'])) && $form_state['values']['details']['is_new']) { + // Check if this form is sending an email. Only send if user not saving a draft + if ( (!$is_draft) && (!empty($node->webform['email']) || !empty($node->webform['additional_emails'])) && $form_state['values']['details']['is_new']) { // Set values for the name, address, and subject for the email. $email_from_name = $node->webform['email_from_name']; @@ -1802,8 +2053,12 @@ function webform_client_form_submit($for watchdog('webform', 'Submission posted to %title. Results. !details', array('%title' => $node->title, '!url' => url('node/'. $node->nid .'/submission/'. $sid), '!results' => "
\n
". htmlentities(print_r($form_state['values'], TRUE)) ."
"), WATCHDOG_NOTICE); } + // If saving a draft, return redirect to confirmation page (no email processing). + if ($is_draft) { // maybe also pass in submission and if its a draft being submitting, have appropriate redirect to /done page + $redirect = 'node/'. $node->nid .'/draft_saved/'. $sid; + } // Check confirmation field to see if redirect should be to another node or a message. - if (isset($form_state['values']['submission'])) { + else if (isset($form_state['values']['submission'])) { drupal_set_message(t('Submission updated.')); $redirect = NULL; } @@ -1815,7 +2070,7 @@ function webform_client_form_submit($for $redirect = array(trim($path), 'sid='. $sid); } else { - $redirect = array('node/'. $node->nid .'/done', 'sid='. $sid); + $redirect = 'node/'. $node->nid .'/done/submission/'. $sid; } $form_state['redirect'] = $redirect; } @@ -1839,7 +2094,7 @@ function _webform_client_form_submit_pro if ($component['pid'] == $parent) { $submit_function = "_webform_submit_". $component['type']; if (function_exists($submit_function)) { - $submit_function($form_values[$component['form_key']], $component); // Call the component process submission function. + $submit_function($form_values[$component['form_key']], $component, $node->nid, $form_values); // Call the component process submission function. Add note and form_values to pass along for 'file' componant which needs it, other types don't } } } @@ -1870,15 +2125,49 @@ function _webform_client_form_submit_fla /** * Prints the confirmation message after a successful submission. */ -function _webform_confirmation($node) { +function _webform_confirmation($node, $sid) { drupal_set_title(check_plain($node->title)); if (empty($output)) { - $output = theme(array('webform_confirmation_'. $node->nid, 'webform_confirmation'), $node, $_GET['sid']); + $output = theme(array('webform_confirmation_'. $node->nid, 'webform_confirmation'), $node, $sid); } return $output; } /** + * Prints the confirmation message after a successful draft submission. + */ +function _webform_draft_confirmation($node, $submission) { + drupal_set_title($node->title); + drupal_set_message(t('Draft saved.')); + return theme('webform_draft_confirmation', $node, $submission->sid); +} + +/** + * Themable function for webform draft confirmation + */ +function theme_webform_draft_confirmation($node, $sid) { + $node->body = check_markup('Your draft (#'. intval($sid) .') has been successfully saved. ', $node->format, FALSE); + $node->links['webform_back'] = array( + 'href' => 'node/'. $node->nid, + 'title' => t('Go back to the form'), + ); + return theme('node', $node, FALSE, TRUE); +} + +/** + * Check if current user has a draft of this webform, and return the sid of the last saved draft. (Can goto other saved drafts of same node by clicking 'View or Edit Previous Submissions' link) + */ +function _webform_fetch_draft_sid($nid, $uid) { + $query = 'SELECT sid FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1 ORDER BY submitted DESC'; + $res = db_query($query, $nid, $uid); + $row = db_fetch_array($res); + if (isset($row['sid'])) { + return (int) $row['sid']; + } + return FALSE; +} + +/** * Prepare for theming of the webform form. */ function template_preprocess_webform_form(&$vars) { @@ -1896,7 +2185,7 @@ function template_preprocess_webform_for function template_preprocess_webform_confirmation(&$vars) { if (empty($vars['node']->webform['confirmation'])) { drupal_set_message(t('Thank you, your submission has been received.')); - drupal_goto('node/'. $vars['node']->nid); + // drupal_goto('node/'. $vars['node']->nid); // just like going to standalone page, like when the user has access to submit the webform again, he might think even though he gets a message saying webform successfully submitted, he might feel need to redo the form since its available on same page. } $vars['confirmation_message'] = check_markup($vars['node']->webform['confirmation'], $vars['node']->format, FALSE); @@ -2069,7 +2358,8 @@ function _webform_filter_values($string, } if ($strict) { - return filter_xss($string); + $allowed_tags = array('a', 'em', 'strong', 'cite', 'code', 'ul', 'ol', 'li', 'dl', 'dt', 'dd', 'div', 'b', 'i', 'span' ); // allowed tags added so i can get my popup footnotes to show up for each question which i put in the componants description, others may find this useful, others may want to turn off + return filter_xss($string, $allowed_tags); } else { return $string; @@ -2080,7 +2370,7 @@ function _webform_filter_values($string, * Filters all special tokens provided by webform, and allows basic layout in descriptions. */ function _webform_filter_descriptions($string, $node = NULL, $submission = NULL, $strict = TRUE) { - return check_markup(_webform_filter_values($string, $node = NULL, $submission = NULL, $strict = TRUE)); + return check_markup(_webform_filter_values($string, $node = NULL, $submission = NULL, $strict = TRUE), 2, FALSE); // ADDED FULL HTML input format and dont check to see if role allowed to access that filter, Full Html Filter is 2 in drupal6 while 3 in drupal5... $strict could be false here if passed by componant if needed. } /** @@ -2238,7 +2528,20 @@ function _webform_component_options($com * Convert an array of components into a tree */ function _webform_components_tree_build($src, &$tree, $parent, &$page_count) { + static $_webform_added_required_js_field; // set so when called again, knows if should add javascript smartly, otherwise adds it many times + if(!isset($_webform_added_required_js_field)) // sets the variable up the first time function is called + $_webform_added_required_js_field[] = array(); foreach ($src as $cid => $component) { + // not sure how to make this run once for the required field, hence my hackish check here and in javascript, this runs before _webform_client_form_add_component (where #required may get unset) so we are good i think adding this here, could be optimized and placed somewhere else maybe? + + if ( ($component['mandatory']== 1) && (!in_array($component['form_key'], $_webform_added_required_js_field))) { // this is for file required fields when you first visit the page... since Drupal doesnt set required * for it since we use non-drupal required-ness validation and also for when saving, we unset #required field so that we can save, but then the * wont show for ANY fields so need to check for that and add * to them for user feedback + $_webform_added_required_js_field[] = $component['form_key']; + drupal_add_js("$(document).ready(function(){ + if ( $('#webform-component-". $component['form_key'] ." label span.form-required').length == 0 )// if * doesnt exist for this required field, then add it + $('#webform-component-". $component['form_key'] ." label').append('*'); //think this hack is fine cause it catches file fields and adds * for them and also for scenario when saving, it unchecks other fields #required so drupal validation on those does not occur, but possibly file validation fails like bad filename, so still want the * to show up for those fields that we unset the #required attribute for to indicate the field is required should he fix his mistake and want to submit. + });", "inline"); + } + if ($component['pid'] == $parent) { _webform_components_tree_build($src, $component, $cid, $page_count); $tree['children'][$cid] = $component; diff -urp --strip-trailing-cr webform623/webform_report.inc webform_arian_latest/webform_report.inc --- webform623/webform_report.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/webform_report.inc 2008-10-30 16:39:17.759375000 -0400 @@ -21,7 +21,9 @@ function webform_results_submissions($no $header = theme('webform_results_submissions_header', $node); if ($user_filter) { drupal_set_title(t('Submissions for %user', array('%user' => $user->name))); - $submissions = webform_get_submissions($node->nid, $header, $user->uid); + $draft_count = db_result(db_query("SELECT count(*) FROM {webform_submissions} WHERE nid = %d AND uid = %d AND is_draft = 1", $node->nid, $user->uid)); + $only_drafts = (!(user_access('access webform results') || user_access('access webform submissions') || (user_access('access own webform submissions') && $user->uid)) && (($draft_count > 1) && $user->uid)); // just check to see if user has access to drafts only + $submissions = webform_get_submissions($node->nid, $header, $user->uid, $only_drafts);//ADD HERE CHECK IF ONLY SHOULD GET DRAFTS maybe set as global in _access so dont need to requery? } else { $submissions = webform_get_submissions($node->nid, $header); @@ -67,7 +69,7 @@ function theme_webform_results_submissio foreach ($submissions as $sid => $submission) { $row = array( $sid, - format_date($submission->submitted, 'small'), + format_date($submission->submitted, 'small') . (($submission->is_draft) ? ' (Saved, not Submitted)' : ''), ); if (user_access('access webform results')) { $row[] = theme('username', $submission); @@ -140,7 +142,7 @@ function theme_webform_results_table($no // Generate a row for each submission. foreach ($submissions as $sid => $submission) { $cell[] = l($sid, 'node/'. $node->nid .'/submission/'. $sid); - $cell[] = format_date($submission->submitted, "small"); + $cell[] = format_date($submission->submitted, "small") . (($submission->is_draft) ? ' (Saved, not Submitted)' : ''); $cell[] = theme('username', $submission); $cell[] = $submission->remote_addr; $component_headers = array(); diff -urp --strip-trailing-cr webform623/webform_submissions.inc webform_arian_latest/webform_submissions.inc --- webform623/webform_submissions.inc 2008-10-08 15:51:40.000000000 -0400 +++ webform_arian_latest/webform_submissions.inc 2008-10-31 10:15:19.993750000 -0400 @@ -9,11 +9,30 @@ * @author Nathan Haug */ -function webform_submission_update($node, $sid, $submitted) { - // Update the submission data by first removing all this submissions data. - db_query("DELETE FROM {webform_submitted_data} WHERE sid = %d", $sid); - // Then re-add it to the database. +function webform_submission_update($node, $sid, $submitted, $is_draft = 0) { + // updates previous 'saved' submission id if it exists during an update and creates new one below + db_query("UPDATE {webform_submissions} SET submitted=%d, remote_addr='%s', is_draft=%d WHERE sid = %d", time(), ip_address(), $is_draft, $sid); + + // Update the submission data by first removing all this submissions data. Then re-add it to the database. + $_submitted_cids = array(); foreach ($submitted as $cid => $value) { + // only delete so can insert new filename if new data submitted + if ( ($node->webform['components'][$cid]['type'] == 'file') ) { // only update files if new data has been submitted + if ( ! empty($_FILES['files']['name'][ $node->webform['components'][$cid]['form_key'] ]) ) { // if not empty, replace old data + $_submitted_cids[$cid] = $value; + } + } + else { + $_submitted_cids[$cid] = $value; + } + } + + if ( count($_submitted_cids) > 0 ) { // if just 1 fileupload question and resaving and didnt submit a new file, submitted_cids array will be empty + $sql = "DELETE FROM {webform_submitted_data} WHERE sid = %d AND cid IN (". db_placeholders($_submitted_cids) .")"; // secure + db_query($sql, array_merge(array($sid), array_keys($_submitted_cids))); + } + + foreach ($_submitted_cids as $cid => $value) { // Don't save pagebreaks as submitted data. if ($node->webform['components'][$cid]['type'] == 'pagebreak') { continue; @@ -34,10 +53,10 @@ function webform_submission_update($node return $sid; } -function webform_submission_insert($node, $submitted) { +function webform_submission_insert($node, $submitted, $is_draft = 0) { global $user; - $result = db_query("INSERT INTO {webform_submissions} (nid, uid, submitted, remote_addr) "." VALUES (%d, %d, %d, '%s')", $node->nid, $user->uid, time(), ip_address()); + $result = db_query("INSERT INTO {webform_submissions} (nid, uid, submitted, remote_addr, is_draft) "." VALUES (%d, %d, %d, '%s', %d)", $node->nid, $user->uid, time(), ip_address(), $is_draft); $sid = db_last_insert_id('webform_submissions', 'sid'); @@ -100,16 +119,26 @@ function webform_submission_delete_form( $form = array(); $form['node'] = array('#type' => 'value', '#value' => $node); $form['submission'] = array('#type' => 'value', '#value' => $submission); - $question = t("Are you sure you want to delete this submission?"); + $question = t("Are you sure you want to delete this ". ($submission->is_draft ? t('Saved Draft') : t('Submission') ) ."?"); - return confirm_form($form, $question, isset($_GET['destination']) ? $_GET['destination'] : 'node/'. $node->nid .'/webform-results', NULL, t('Delete'), t('Cancel')); + if ( (!user_access('edit webform submissions')) && (!user_access('clear webform results')) ) // non admin is deleting his whole saved draft, possibly cause easier to just start from scratch if large webform, or he is clearing his own submission, either way they dont have access to results page so redirecting back to webform + $dest = 'node/'. $node->nid; + else + $dest = isset($_GET['destination']) ? $_GET['destination'] : 'node/'. $node->nid .'/webform-results'; + + return confirm_form($form, $question, $dest, NULL, t('Delete'), t('Cancel')); } function webform_submission_delete_form_submit($form, &$form_state) { webform_submission_delete($form_state['values']['node'], $form_state['values']['submission']); drupal_set_message(t("Submission deleted.")); - $form_state['redirect'] = 'node/'. $form_state['values']['node']->nid .'/webform-results'; + if ( (!user_access('edit webform submissions')) && (!user_access('clear webform results')) ) + $dest = 'node/'. $form_state['values']['node']->nid; + else + $dest = 'node/'. $form_state['values']['node']->nid .'/webform-results'; + + $form_state['redirect'] = $dest; } /** @@ -125,13 +154,17 @@ function webform_submission_delete_form_ * @return $submissions * An array of submissions matching your filters. */ -function webform_get_submissions($nid, $header = NULL, $uid = NULL) { +function webform_get_submissions($nid, $header = NULL, $uid = NULL, $only_drafts = FALSE) { $query = 'SELECT s.*, sd.cid, sd.no, sd.data, u.name, u.mail, u.status '. 'FROM {webform_submissions} s '. 'LEFT JOIN {webform_submitted_data} sd ON sd.sid = s.sid '. 'LEFT JOIN {users} u ON u.uid = s.uid '. 'WHERE sd.nid = %d'; + if ($only_drafts) { // if user has access to submit form still, he may have a few drafts, i choose to submit them in the submissions page, so user has access to this page if allowed to 'edit'. user will see all his drafts then only on this page if thats all he has access to + $query .= ' AND s.is_draft = 1'; + } + if ($uid) { $query .= ' AND u.uid = %d'; } @@ -161,6 +194,7 @@ function webform_get_submissions($nid, $ $submissions[$row->sid]->uid = $row->uid; $submissions[$row->sid]->name = $row->name; $submissions[$row->sid]->status = $row->status; + $submissions[$row->sid]->is_draft = $row->is_draft; } $submissions[$row->sid]->data[$row->cid]['value'][$row->no] = $row->data; $previous = $row->sid; @@ -189,6 +223,7 @@ function webform_get_submission($nid, $s $submission->uid = $row->uid; $submission->remote_addr = $row->remote_addr; $submission->submitted = $row->submitted; + $submission->is_draft = $row->is_draft; while ($row) { $submission->data[$row->cid]['value'][$row->no] = $row->data; @@ -231,7 +266,7 @@ function _webform_submission_limit_check $query = "SELECT count(*) ". "FROM {webform_submissions} ". "WHERE (( 0 = %d AND remote_addr = '%s') OR (uid > 0 AND uid = %d)) ". - "AND submitted > %d AND nid = %d"; + "AND submitted > %d AND nid = %d AND is_draft <> 1"; // when about to submit, get all the submitted forms (and not including saved ones). Used to calculate if limit reached as you dont want to count saved submissions in the limit check. // Fetch all the entries from the database within the submit interval with this username and IP. $num_submissions_database = db_result(db_query($query, $user->uid, ip_address(), $user->uid, ($node->webform['submit_interval'] != -1) ? (time() - $node->webform['submit_interval']) : $node->webform['submit_interval'], $node->nid));