/** * 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; }