Esempio n. 1
0
 /**
  * processes a form submit
  *
  * this processes all record form submissions front-end and back-
  * 
  * @global object $wpdb
  * 
  * @param array       $post           the array of new values (typically the $_POST array)
  * @param string      $action         the db action to be performed: insert or update
  * @param int|bool    $participant_id the id of the record to update. If it is false 
  *                                    or omitted, it creates a new record, if true, it 
  *                                    creates or updates the default record.
  * @param array|bool  $column_names   array of column names to process from the $post 
  *                                    array, if false, processes a preset set of columns
  *
  * @return int|bool   int ID of the record created or updated, bool false if submission 
  *                    does not validate
  */
 public static function process_form($post, $action, $participant_id = false, $column_names = false)
 {
     if (!isset($action) || !in_array($action, array('insert', 'update')) || isset($post['subsource']) && $post['subsource'] !== Participants_Db::PLUGIN_NAME) {
         return false;
     }
     global $wpdb;
     if (!empty($_FILES) && !isset($_POST['csv_file_upload'])) {
         foreach ($_FILES as $fieldname => $attributes) {
             if (UPLOAD_ERR_NO_FILE == $attributes['error']) {
                 continue;
             }
             $filepath = self::_handle_file_upload($fieldname, $attributes, $participant_id);
             if (false !== $filepath) {
                 // place the path to the file in the field value
                 $post[$fieldname] = $filepath;
                 $_POST[$fieldname] = basename($filepath);
             }
         }
     }
     /*
      * checks for a record with a matching field so we can exercise the
      * duplicate record preference
      * 
      * 0 - create new record
      * 1 - update matching record
      * 2 - show validation error
      */
     if (isset($_POST['csv_file_upload'])) {
         // a CSV upload brings in it's own match preference
         $duplicate_record_preference = filter_input(INPUT_POST, 'match_preference', FILTER_SANITIZE_STRING);
         $match_field = filter_input(INPUT_POST, 'match_field', FILTER_SANITIZE_STRING);
     } else {
         $duplicate_record_preference = self::plugin_setting('unique_email', '0');
         $match_field = self::plugin_setting('unique_field', 'id');
     }
     if (is_admin() && self::current_user_has_plugin_role('admin', 'csv upload') && !isset($_POST['csv_file_upload'])) {
         /*
          * set the preference to 0 if current user is an admin in the admin and not 
          * importing a CSV
          * 
          * this allows administrators to create new records without being affected 
          * by the duplicate record preference
          */
         $duplicate_record_preference = '0';
     }
     if ($action == 'insert' and $duplicate_record_preference !== '0') {
         $match_field_value = isset($post[$match_field]) ? filter_var($post[$match_field], FILTER_SANITIZE_STRING) : '';
         $record_match = $match_field_value !== '' && self::field_value_exists($match_field_value, $match_field);
         // if true, the incoming record matches an existing record
         /**
          * @version 1.6
          * the $record_match status variable is made available to a filter so a custom 
          * record matching method can be implemented
          */
         $record_match = self::set_filter('incoming_record_match', $record_match, $post);
         if ($record_match) {
             /*
              * this prevents possible exposure of private data when using multipage forms
              * 
              * we don't allow the "update" preference for multipage forms
              */
             if (Participants_Db::$session->get('form_status') === 'multipage') {
                 $duplicate_record_preference = '2';
             }
             /*
              * we have found a match
              */
             switch ($duplicate_record_preference) {
                 case '1':
                     // record with same field value exists...get the id and update the existing record
                     if ('id' == strtolower($match_field)) {
                         $participant_id = intval($match_field_value);
                     } else {
                         $participant_id = self::_get_participant_id_by_term($match_field, $match_field_value);
                     }
                     // get the first one
                     if (is_array($participant_id)) {
                         $participant_id = current($participant_id);
                     }
                     // set the update mode
                     $action = 'update';
                     // empty any private ID that signup assigned, the record will already have one
                     $post['private_id'] = '';
                     break;
                 case '2':
                     /*
                      * this is skipped if we're in the middle of an unfinished multi-page form
                      */
                     if (Participants_Db::$session->get('form_status') === 'multipage') {
                         // multipage form incomplete, update the current record with the new post data
                         $participant_id = Participants_Db::$session->get('pdbid');
                         $action = 'update';
                     } else {
                         // set the error message
                         if (!is_object(self::$validation_errors)) {
                             self::$validation_errors = new PDb_FormValidation();
                         }
                         self::$validation_errors->add_error($match_field, 'duplicate');
                         $action = 'skip';
                     }
                     // go on validating the rest of the form
                     break;
             }
         } elseif ($duplicate_record_preference === '1' and strtolower($match_field) === 'id' and is_numeric($match_field_value)) {
             /*
              * if the "OVERWRITE" option is set to "id" and the record contains an id, use it to create the record
              */
             $participant_id = intval($match_field_value);
             if ($participant_id !== 0) {
                 $action = 'insert';
             } else {
                 $participant_id = false;
             }
         }
     } elseif ($action === 'insert' && $duplicate_record_preference === '0') {
         /*
          * if the setting is to add a record, matching or not, we don't allow a Record 
          * ID or a private ID to be stored, we let the plugin assign them so that they 
          * will be certain to be unique
          */
         unset($post['id']);
         $post['private_id'] = '';
     }
     // set the insert status value
     self::$insert_status = $action;
     switch ($action) {
         case 'update':
             $sql = 'UPDATE ' . self::$participants_table . ' SET `date_updated` = NOW(), ';
             $where = " WHERE id = " . $participant_id;
             break;
         case 'insert':
             $sql = 'INSERT INTO ' . self::$participants_table . ' SET ';
             if (self::import_timestamp(isset($post['date_recorded']) ? $post['date_recorded'] : '') === false) {
                 $sql .= ' `date_recorded` = NOW(), ';
             }
             if (self::import_timestamp(isset($post['date_updated']) ? $post['date_updated'] : '') === false) {
                 $sql .= ' `date_updated` = NOW(), ';
             }
             $where = '';
             break;
         case 'skip':
             return false;
     }
     /*
      * determine the set of columns to process
      * 
      */
     $new_values = array();
     $column_data = array();
     if (is_array($column_names)) {
         $column_set = $column_names;
     } else {
         if (filter_input(INPUT_POST, 'action') === 'signup') {
             $column_set = 'signup';
         } else {
             $column_set = $action == 'update' ? is_admin() ? 'backend' : 'frontend' : ($participant_id ? 'all' : 'new');
         }
     }
     $columns = self::get_column_atts($column_set);
     // gather the submit values and add them to the query
     foreach ($columns as $column) {
         // the validation object is only instantiated when this method is called
         // by a form submission
         if (is_object(self::$validation_errors)) {
             self::$validation_errors->validate(isset($post[$column->name]) ? self::deep_stripslashes($post[$column->name]) : '', $column, $post);
         }
         $new_value = false;
         // we can process individual submit values here
         switch ($column->name) {
             case 'id':
                 $new_value = $participant_id;
                 break;
             case 'date_recorded':
             case 'date_updated':
             case 'last_accessed':
                 // remove the value from the post array if it is already set in the sql
                 if (isset($post[$column->name]) && strpos($sql, $column->name) !== false) {
                     unset($post[$column->name]);
                 }
                 /*
                  * this func returns bool false if the timestamp is not present or is invalid, 
                  * returns the MySQL timestamp string otherwise
                  */
                 $new_value = @self::import_timestamp($post[$column->name]);
                 break;
             case 'private_id':
                 if (isset($post['private_id']) && strlen($post['private_id']) == self::$private_id_length) {
                     $new_value = $post['private_id'];
                 } else {
                     $new_value = $action == 'insert' ? self::generate_pid() : false;
                 }
                 break;
             default:
                 if (!isset($post[$column->name])) {
                     continue;
                 }
                 switch ($column->form_element) {
                     case 'multi-select-other':
                     case 'multi-checkbox':
                         /* match the items in the comma-separated list against the preset
                          * values of the multi-select. Any extra values are placed in an
                          * 'other' array element
                          */
                         if (isset($post[$column->name])) {
                             if (is_array($post[$column->name])) {
                                 if ($column->form_element == 'multi-select-other' && ($i = array_search('other', $post[$column->name]))) {
                                     unset($post[$column->name][$i]);
                                 }
                                 $value_array = $post[$column->name];
                                 // delete the empty placeholder value
                                 if (isset($value_array[0]) && $value_array[0] === '') {
                                     unset($value_array[0]);
                                 }
                             } else {
                                 // build the value array from the string form used in CSV files
                                 $value_array = array();
                                 $incoming_value = preg_split('#([ ]*,[ ]*)#', trim($post[$column->name]));
                                 $field_values = self::unserialize_array($column->values);
                                 foreach ($incoming_value as $v) {
                                     if (in_array($v, $field_values)) {
                                         $value_array[] = $v;
                                     } else {
                                         $value_array['other'][] = $v;
                                     }
                                 }
                                 if (isset($value_array['other']) && is_array($value_array['other'])) {
                                     $value_array['other'] = implode(',', $value_array['other']);
                                 }
                             }
                         } else {
                             $value_array = array();
                         }
                         $new_value = self::_prepare_array_mysql($value_array);
                         break;
                     case 'link':
                         /* translate the link markdown used in CSV files to the array format used in the database
                          */
                         if (!is_array($post[$column->name])) {
                             $new_value = self::_prepare_array_mysql(self::get_link_array($post[$column->name]));
                         } else {
                             $new_value = self::_prepare_array_mysql($post[$column->name]);
                         }
                         break;
                     case 'rich-text':
                         global $allowedposttags;
                         $new_value = wp_kses(stripslashes($post[$column->name]), $allowedposttags);
                         break;
                     case 'date':
                         if ($post[$column->name] !== '') {
                             $date = self::parse_date($post[$column->name], $column, true);
                             $new_value = $date ? $date : false;
                         } else {
                             $new_value = null;
                         }
                         break;
                     case 'captcha':
                         $new_value = false;
                         break;
                     case 'password':
                         if (!empty($post[$column->name])) {
                             $new_value = wp_hash_password(trim($post[$column->name]));
                         } else {
                             $new_value = false;
                         }
                         break;
                     case 'image-upload':
                     case 'file-upload':
                         if (filter_input(INPUT_POST, $column->name . '-deletefile', FILTER_SANITIZE_STRING) === 'delete') {
                             if (self::$plugin_options['file_delete'] == 1 or is_admin()) {
                                 self::delete_file($post[$column->name]);
                             }
                             unset($_POST[$column->name]);
                             $post[$column->name] = '';
                         }
                         $new_value = self::_prepare_string_mysql(trim($post[$column->name]));
                         break;
                     default:
                         if (!self::current_user_has_plugin_role('admin', 'readonly access') && $column->readonly != '0') {
                             // this prevents unauthorized users from saving readonly field data
                             $new_value = false;
                         } elseif (is_array($post[$column->name])) {
                             $new_value = self::_prepare_array_mysql($post[$column->name]);
                         } else {
                             $new_value = self::_prepare_string_mysql(trim($post[$column->name]));
                         }
                 }
                 // switch column_atts->form_element
         }
         // swtich column_atts->name
         /*
          * add the column and value to the sql; if it is bool false, skip it entirely. 
          * Nulls are added as true nulls
          */
         if ($new_value !== false) {
             if ($new_value !== null) {
                 $new_values[] = $new_value;
             }
             $column_data[] = "`" . $column->name . "` = " . ($new_value === null ? "NULL" : "%s");
         }
     }
     // columns
     // if the validation object exists and there are errors, stop here
     if (is_object(self::$validation_errors) && self::$validation_errors->errors_exist()) {
         // error_log( __METHOD__.' errors exist; returning');
         return false;
     } elseif (!empty(self::$admin_message) and 'error' == self::$admin_message_type) {
         return false;
     }
     /*
      * @version 1.6
      * 
      * add in any missing default values
      */
     if ($action == 'insert') {
         $all_columns = self::get_default_record();
         unset($all_columns['private_id'], $all_columns['date_recorded'], $all_columns['date_updated']);
         foreach ($all_columns as $name => $value) {
             $find_result = preg_grep('/' . $name . '/', $column_data);
             if (count($find_result) === 0 && $value != '') {
                 // if a field with a defined default value is missing from the submission, add it in
                 $column_data[] = "`{$name}` = %s";
                 $new_values[] = $value;
             }
         }
     }
     // add in the column names
     $sql .= implode(', ', $column_data);
     // add the WHERE clause
     $sql .= $where;
     if (WP_DEBUG) {
         error_log(__METHOD__ . ' storing record sql=' . $sql . ' values:' . print_r($new_values, true));
     }
     $result = $wpdb->query($wpdb->prepare($sql, $new_values));
     if ($result === 0) {
         $db_error_message = sprintf(self::$i18n['zero_rows_error'], $wpdb->last_query);
     } elseif ($result === false) {
         $db_error_message = sprintf(self::$i18n['database_error'], $wpdb->last_query, $wpdb->last_error);
     } else {
         // is it a new record?
         if ($action == 'insert') {
             // get the new record id for the return
             $participant_id = $wpdb->insert_id;
             /* 
              * is this record a new one created in the admin? This also applies to CSV 
              * imported new records
              */
             if (is_admin()) {
                 // if in the admin hang on to the id of the last record for an hour
                 set_transient(self::$last_record, $participant_id, 1 * 60 * 60 * 1);
             }
         }
     }
     /*
      * set up user feedback
      */
     if (!isset($_POST['csv_file_upload']) && is_admin()) {
         if ($result) {
             self::set_admin_message($action == 'insert' ? self::$i18n['added'] : self::$i18n['updated'], 'updated');
         } else {
             self::set_admin_message($db_error_message, 'error');
         }
     }
     return $participant_id;
 }
 /**
  * processes a form submit
  *
  * this processes all record form submissions front-end and back-
  * 
  * @global object $wpdb
  * 
  * @param array  $post           the array of new values (typically the $_POST array)
  * @param string $action         the db action to be performed: insert or update
  * @param int|bool   $participant_id the id of the record to update. If it is false, it 
  *                                   creates a new record, if true, it creates or updates 
  *                                   the default record.
  * @param array|bool $column_names   array of column names to process from the $post 
  *                                   array, if false, processes a preset set of columns
  *
  * @return unknown int ID of the record created or updated, bool false if 
  *                 submission does not validate
  */
 public static function process_form($post, $action, $participant_id = false, $column_names = false)
 {
     global $wpdb;
     if (!empty($_FILES) && !isset($_POST['csv_file_upload'])) {
         foreach ($_FILES as $fieldname => $attributes) {
             if (UPLOAD_ERR_NO_FILE == $attributes['error']) {
                 continue;
             }
             $filepath = self::_handle_file_upload($fieldname, $attributes);
             if (false !== $filepath) {
                 // place the path to the file in the field value
                 $post[$fieldname] = $filepath;
                 $_POST[$fieldname] = basename($filepath);
             }
         }
     }
     /*
      * checks for a record with a matching field so we can exercise the
      * duplicate record preference
      */
     if ($action == 'insert' and self::$plugin_options['unique_email'] !== 0) {
         $match_field = self::$plugin_options['unique_field'];
         if (isset($post[$match_field]) && !empty($post[$match_field]) && self::field_value_exists($post[$match_field], $match_field)) {
             /*
              * we have found a match
              */
             switch (self::$plugin_options['unique_email']) {
                 case 1:
                     // record with same field value exists...get the id and update the existing record
                     if ('id' == strtolower($match_field)) {
                         $participant_id = $post[$match_field];
                     } else {
                         $participant_id = self::_get_participant_id_by_term($match_field, $post[$match_field]);
                     }
                     // get the first one
                     if (is_array($participant_id)) {
                         $participant_id = current($participant_id);
                     }
                     // mark the record as not sent so the private link will be resent
                     PDb_Signup::update_sent_status($participant_id, false);
                     // set the update mode
                     $action = 'update';
                     // empty the private ID that signup assigned, the record will already have one
                     $post['private_id'] = '';
                     break;
                 case 2:
                     // set the error message
                     if (is_object(self::$validation_errors)) {
                         self::$validation_errors->add_error($match_field, 'duplicate');
                     }
                     $action = 'skip';
                     // go on validating the rest of the form
                     break;
             }
         } elseif (self::$plugin_options['unique_email'] == 1 and 'id' == strtolower($match_field) and isset($post[$match_field])) {
             /*
              * if the "OVERWRITE" option is set to "id" and the record contains an id, use it to create the record
              */
             $participant_id = intval($post[$match_field]);
             if (0 !== $participant_id) {
                 $action = 'insert';
                 // mark the record as not sent so the private link will be resent
                 PDb_Signup::update_sent_status($participant_id, false);
             } else {
                 $participant_id = false;
             }
         }
     }
     // set the insert status value
     self::$insert_status = $action;
     switch ($action) {
         case 'update':
             $sql = 'UPDATE ' . self::$participants_table . ' SET date_updated = NOW(), ';
             $where = " WHERE id = " . $participant_id;
             break;
         case 'insert':
             $sql = 'INSERT INTO ' . self::$participants_table . ' SET ';
             if (self::import_timestamp(isset($post['date_recorded']) ? $post['date_recorded'] : '') === false) {
                 $sql .= ' `date_recorded` = NOW(), ';
             }
             if (self::import_timestamp(isset($post['date_updated']) ? $post['date_updated'] : '') === false) {
                 $sql .= ' `date_updated` = NOW(), ';
             }
             $where = '';
             break;
         case 'skip':
             return false;
     }
     /*
      * determine the set of columns to process
      * 
      */
     $new_values = array();
     $column_data = array();
     if (isset($_POST['pdb_data_keys'])) {
         $column_names = PDb_Base::get_indexed_names(explode('.', $_POST['pdb_data_keys']));
     }
     if (is_array($column_names)) {
         $column_set = $column_names;
     } else {
         if (isset($_POST['action']) && $_POST['action'] == 'signup') {
             $column_set = 'signup';
         } else {
             $column_set = $action == 'update' ? is_admin() ? 'backend' : 'frontend' : ($participant_id ? 'all' : 'new');
         }
     }
     $columns = self::get_column_atts($column_set);
     // gather the submit values and add them to the query
     foreach ($columns as $column) {
         // the validation object is only instantiated when this method is called
         // by a form submission
         if (is_object(self::$validation_errors)) {
             self::$validation_errors->validate(isset($post[$column->name]) ? $post[$column->name] : '', $column, $post);
         }
         $new_value = false;
         // we can process individual submit values here
         switch ($column->name) {
             case 'id':
                 $new_value = $participant_id;
                 break;
             case 'date_recorded':
             case 'date_updated':
             case 'last_accessed':
                 // clear the value if it's a record update
                 if ($action == 'update' && $column->name == 'date_updated') {
                     $post['date_updated'] = '';
                 }
                 /*
                  * this func returns bool false if the timestamp is not present or is invalid, 
                  * returns the MySQL timestamp string otherwise
                  */
                 $new_value = @self::import_timestamp($post[$column->name]);
                 break;
             case 'private_id':
                 if (is_string($post['private_id']) && $post['private_id'] !== '') {
                     $new_value = $post['private_id'];
                 } else {
                     $new_value = $action == 'insert' ? self::generate_pid() : false;
                 }
                 break;
             default:
                 if (!isset($post[$column->name])) {
                     continue;
                 }
                 switch ($column->form_element) {
                     case 'multi-checkbox':
                     case 'multi-select-other':
                         /* match the items in the comma-separated list against the preset
                          * values of the multi-select. Any extra values are placed in an
                          * 'other' array element
                          */
                         if (isset($post[$column->name])) {
                             if (is_array($post[$column->name])) {
                                 if ($column->form_element == 'multi-select-other' && ($i = array_search('other', $post[$column->name]))) {
                                     unset($post[$column->name][$i]);
                                 }
                                 $value_array = $post[$column->name];
                             } else {
                                 // build the value array from the string form used in CSV files
                                 $value_array = array();
                                 $incoming_value = preg_split('#([ ]*,[ ]*)#', trim($post[$column->name]));
                                 $field_values = self::unserialize_array($column->values);
                                 foreach ($incoming_value as $v) {
                                     if (in_array($v, $field_values)) {
                                         $value_array[] = $v;
                                     } else {
                                         $value_array['other'][] = $v;
                                     }
                                 }
                                 if (isset($value_array['other']) && is_array($value_array['other'])) {
                                     $value_array['other'] = implode(',', $value_array['other']);
                                 }
                             }
                         } else {
                             $value_array = array();
                         }
                         $new_value = self::_prepare_array_mysql($value_array);
                         break;
                     case 'link':
                         /* translate the link markdown used in CSV files to the array format used in the database
                          */
                         if (!is_array($post[$column->name])) {
                             $new_value = self::_prepare_array_mysql(self::get_link_array($post[$column->name]));
                         } else {
                             $new_value = self::_prepare_array_mysql($post[$column->name]);
                         }
                         break;
                     case 'rich-text':
                         global $allowedposttags;
                         $new_value = wp_kses(stripslashes($post[$column->name]), $allowedposttags);
                         break;
                     case 'date':
                         $date = false;
                         if (isset($post[$column->name])) {
                             $date = self::parse_date($post[$column->name], $column, true);
                         }
                         $new_value = $date ? $date : false;
                         break;
                     case 'captcha':
                         $new_value = false;
                         break;
                     case 'password':
                         if (!empty($post[$column->name])) {
                             $new_value = wp_hash_password(trim($post[$column->name]));
                         } else {
                             $new_value = false;
                         }
                         break;
                     case 'image-upload':
                     case 'file-upload':
                         if (isset($_POST[$column->name . '-deletefile']) and $_POST[$column->name . '-deletefile'] === 'delete') {
                             if (self::$plugin_options['file_delete'] == 1 or is_admin()) {
                                 self::delete_file($post[$column->name]);
                             }
                             unset($_POST[$column->name]);
                             $post[$column->name] = '';
                         }
                         $new_value = self::_prepare_string_mysql(trim($post[$column->name]));
                         break;
                     default:
                         if (!self::backend_user() && $column->readonly != '0') {
                             $new_value = false;
                         } elseif (is_array($post[$column->name])) {
                             $new_value = self::_prepare_array_mysql($post[$column->name]);
                         } else {
                             $new_value = self::_prepare_string_mysql(trim($post[$column->name]));
                         }
                 }
                 // switch column_atts->form_element
         }
         // swtich column_atts->name
         /*
          * add the column and value to the sql; if it is bool false, skip it entirely. 
          * Nulls are added as true nulls
          */
         if ($new_value !== false) {
             if ($new_value !== null) {
                 $new_values[] = $new_value;
             }
             $column_data[] = "`" . $column->name . "` = " . ($new_value === null ? "NULL" : "%s");
         }
     }
     // columns
     // if the validation object exists and there are errors, stop here
     if (is_object(self::$validation_errors) && self::$validation_errors->errors_exist()) {
         // error_log( __METHOD__.' errors exist; returning');
         return false;
     } elseif (!empty(self::$admin_message) and 'error' == self::$admin_message_type) {
         return false;
     }
     // add in the column names
     $sql .= implode(', ', $column_data);
     // add the WHERE clause
     $sql .= $where;
     if (WP_DEBUG) {
         error_log(__METHOD__ . ' storing record sql=' . $sql . ' values:' . print_r($new_values, true));
     }
     $wpdb->query($wpdb->prepare($sql, $new_values));
     // is it a new record?
     if ($action == 'insert') {
         // get the new record id for the return
         $participant_id = $wpdb->insert_id;
         /* 
          * is this record a new one created in the admin? This also applies to CSV 
          * imported new records
          */
         if (is_admin()) {
             // if in the admin hang on to the id of the last record for an hour
             set_transient(self::$last_record, $participant_id, 1 * 60 * 60 * 1);
             // set the "email sent" flag for this id
             set_transient(self::$prefix . 'signup-email-sent', array($participant_id => true));
         }
     }
     self::set_admin_message($action == 'insert' ? self::$i18n['added'] : self::$i18n['updated'], 'updated');
     return $participant_id;
 }