예제 #1
0
 /**
  * initializes the record edit object
  */
 public function __construct($shortcode_atts)
 {
     // define shortcode-specific attributes to use
     $add_atts = array('module' => 'single', 'class' => $this->wrap_class, 'term' => 'id');
     // run the parent class initialization to set up the parent methods
     parent::__construct($shortcode_atts, $add_atts);
     /*
      * determine the ID of the record to show
      *
      * 'pdb' is a generic $_GET variable that indexes the record according to
      * the 'term' value, which defaults to 'id'
      *
      */
     if ($this->shortcode_atts['record_id'] !== false) {
         $id = $this->shortcode_atts['record_id'];
     } else {
         $id = 0;
     }
     // override the shortcode att if the value is in the URI
     if (isset($_GET['pdb'])) {
         $id = $_GET['pdb'];
     }
     $record_id = Participants_Db::get_record_id_by_term($this->shortcode_atts['term'], $id);
     if (false === $record_id) {
         $this->_not_found();
     } else {
         $this->participant_values = Participants_Db::get_participant($record_id);
         $this->participant_id = $record_id;
         $this->_setup_iteration();
         $this->_print_from_template();
     }
 }
 /**
  * initializes the record edit object
  */
 public function __construct($shortcode_atts)
 {
     // define shortcode-specific attributes to use
     $add_atts = array('module' => 'record', 'class' => 'edit-participant ' . $this->wrap_class, 'submit_button' => Participants_Db::plugin_setting('save_changes_button'));
     // run the parent class initialization to set up the parent methods
     parent::__construct($shortcode_atts, $add_atts);
     $this->_setup_multipage();
     // set the action URI for the form
     $this->_set_submission_page();
     if (false === $this->shortcode_atts['record_id']) {
         $this->_not_found();
     } else {
         $this->participant_id = $this->shortcode_atts['record_id'];
         $this->participant_values = Participants_Db::get_participant($this->participant_id);
         if ($this->participant_values === false) {
             $this->_not_found();
         } else {
             // update the access timestamp
             Participants_Db::set_record_access($this->participant_id);
             $this->_get_validation_errors();
             $this->_setup_iteration();
             $this->_print_from_template();
         }
     }
 }
예제 #3
0
 /**
  * instantiates a field group object
  *
  * @param object a object with all the field group's properties
  */
 public function __construct($fields, $id, $module = 'none')
 {
     $this->module = $module;
     // set up the common properties
     parent::__construct($fields);
     // get rid of unneeded properties
     unset($this->name, $this->title);
     // add the record field objects
     // this needs to by typed as array for the iterators to work
     $this->fields = (array) $fields;
     $this->record_id = $id;
     $this->values = Participants_Db::get_participant($id);
 }
예제 #4
0
 * submission processing happens in Participants_Db::process_page_request on the
 * admin_init action
 *
 */
if (!isset($participant_id)) {
    // if there is no id in the request, use the default record
    $participant_id = isset($_REQUEST['id']) ? $_REQUEST['id'] : false;
}
if (false === $participant_id) {
    $action = 'insert';
    $page_title = __('Add New Participant Record', 'participants-database');
    $participant_values = Participants_Db::get_default_record();
} else {
    $action = 'update';
    $page_title = __('Edit Existing Participant Record', 'participants-database');
    $participant_values = Participants_Db::get_participant($participant_id);
}
/*
 * if we have a valid ID or are creating a new record, show the form
 */
if ($participant_values) {
    //error_log( basename( __FILE__).' default record:'.print_r( $participant_values,1));
    //get the groups info
    $groups = Participants_Db::get_groups();
    // get the current user's info
    get_currentuserinfo();
    $options = get_option(self::$participants_db_options);
    // set up the hidden fields
    $hidden = array('action' => $action, 'subsource' => Participants_Db::PLUGIN_NAME);
    foreach (array('id', 'private_id') as $i) {
        if (isset($participant_values[$i])) {
예제 #5
0
 /**
  * instantiates the signup form object
  *
  * this class is called by a WP shortcode
  *
  * @param array $shortcode_atts   this array supplies the display parameters for the instance
  *                 'title'   string displays a title for the form (default none)
  *
  */
 public function __construct($shortcode_atts)
 {
     // define shortcode-specific attributes to use
     $shortcode_defaults = array('module' => 'signup');
     $sent = true;
     // start by assuming the notification email has been sent
     /*
      * this is set true if the form is a multi-page form. This is so a multi-page form 
      * can't be completed by skipping back to the signup form, they must go to a page 
      * with a thanks shortcode
      */
     $redirected = false;
     if ($shortcode_atts['module'] != 'thanks' && (isset($shortcode_atts['action']) && $shortcode_atts['action'] !== '')) {
         // this is set true if the signup form is supposed to be redirected after the submission
         $redirected = true;
     }
     if (isset($_GET['m']) && $_GET['m'] == 'r' || $shortcode_atts['module'] == 'retrieve') {
         /*
          * we're proceesing a link retrieve request
          */
         $shortcode_atts['module'] = 'retrieve';
     } elseif ($this->participant_id = Participants_Db::$session->get('pdbid')) {
         /*
          * the submission is successful, clear the session
          */
         Participants_Db::$session->clear('pdbid');
         Participants_Db::$session->clear('captcha_vars');
         Participants_Db::$session->clear('captcha_result');
         $this->participant_values = Participants_Db::get_participant($this->participant_id);
         if ($this->participant_values && !$redirected) {
             // check the notification sent status of the record
             $sent = $this->check_sent_status($this->participant_id);
             $this->submitted = true;
             $shortcode_atts['module'] = 'thanks';
         }
         $shortcode_atts['id'] = $this->participant_id;
     } elseif ($shortcode_atts['module'] == 'signup') {
         /*
          * we're showing the signup form
          */
         $this->participant_values = Participants_Db::get_default_record();
     } else {
         /*
          * there was no type set
          */
         return;
     }
     // run the parent class initialization to set up the $shortcode_atts property
     parent::__construct($shortcode_atts, $shortcode_defaults);
     $this->registration_page = Participants_Db::get_record_link($this->participant_values['private_id']);
     // set up the signup form email preferences
     $this->_set_email_prefs();
     // set the action URI for the form
     $this->_set_submission_page();
     // set up the template iteration object
     $this->_setup_iteration();
     if ($this->submitted) {
         /*
          * filter provides access to the freshly-stored record and the email and thanks message properties so user feedback can be altered.
          */
         if (has_filter(Participants_Db::$prefix . 'before_signup_thanks')) {
             $signup_feedback_props = array('recipient', 'receipt_subject', 'receipt_body', 'notify_recipients', 'notify_subject', 'notify_body', 'thanks_message', 'participant_values');
             $signup_feedback = new stdClass();
             foreach ($signup_feedback_props as $prop) {
                 $signup_feedback->{$prop} =& $this->{$prop};
             }
             apply_filters(Participants_Db::$prefix . 'before_signup_thanks', $signup_feedback);
         }
         /*
          * check to see if the thanks email has been sent and send it if it has not
          */
         if ($sent === false) {
             $this->_send_email();
             // mark the record as sent
             $this->update_sent_status($this->participant_id, true);
         } else {
             return false;
         }
         // the thanks message and email have already been sent for this ID
     }
     // print the shortcode output
     $this->_print_from_template();
 }
 /**
  * sets up the fields object
  * 
  * this will use a different method for each type of object used to instantiate the class
  */
 private function _setup_fields()
 {
     $this->base_type = get_class($this->shortcode_object);
     $this->values = $this->base_type !== 'PDb_List' ? $this->shortcode_object->participant_values : Participants_Db::get_participant($this->shortcode_object->record->record_id);
     $this->set_edit_page();
     $this->set_detail_page();
     $this->module = $this->shortcode_object->module;
     $this->id = $this->values['id'];
     $this->edit_link = $this->cat_url_var($this->edit_page, 'pid', $this->values['private_id']);
     $this->detail_link = $this->cat_url_var($this->detail_page, 'pdb', $this->id);
     $this->fields = new stdClass();
     $this->groups = array();
     switch ($this->base_type) {
         case 'PDb_List':
             foreach ($this->shortcode_object->record->fields as $field_object) {
                 $name = $field_object->name;
                 $value = isset($field_object->value) ? $field_object->value : '';
                 $this->fields->{$name} = Participants_Db::get_column($name);
                 $this->fields->{$name}->module = $this->shortcode_object->module;
                 $this->fields->{$name}->value = $value;
             }
             reset($this->shortcode_object->record->fields);
             $this->_setup_list_record_object();
             break;
         case 'PDb_Signup':
         case 'PDb_Single':
         case 'PDb_Record':
         default:
             if (!isset($this->shortcode_object->record)) {
                 error_log(__METHOD__ . ' cannot instantiate ' . __CLASS__ . ' object. Class must be instantiated with full module object.');
                 break;
             }
             $this->record = clone $this->shortcode_object->record;
             foreach ($this->shortcode_object->fields as $name => $field) {
                 $this->fields->{$name} = $field;
                 $this->fields->{$name}->module = $this->shortcode_object->module;
                 $this->fields->{$name}->value = $this->values[$name];
             }
             foreach ($this->record as $name => $group) {
                 $this->groups[$name] = $this_group = new stdClass();
                 $this_group->name = $name;
                 $this_group->title = $group->title;
                 $this_group->description = $group->description;
                 $this_group->fields = array();
                 foreach ($group->fields as $group_field) {
                     $this_group->fields[] = $group_field->name;
                 }
                 reset($group->fields);
             }
             reset($this->record);
             break;
     }
     //unset($this->record->options);
 }
 /**
  * sets up the template iteration object
  *
  * this takes all the fields that are going to be displayed and organizes them
  * under their group so we can easily run through them in the template
  */
 public function _setup_iteration()
 {
     // the list query object can be modified at this point to add a custom search
     do_action(Participants_Db::$prefix . 'list_query_object', $this->list_query);
     // allow the query to be altered before the records are retrieved
     $list_query = Participants_Db::set_filter('list_query', $this->list_query->get_list_query());
     if (WP_DEBUG) {
         error_log(__METHOD__ . ' list query: ' . $this->list_query->get_list_query());
     }
     // get the $wpdb object
     global $wpdb;
     // get the number of records returned
     $this->num_records = $wpdb->get_var(preg_replace('#^SELECT.+FROM #', 'SELECT COUNT(*) FROM ', $list_query));
     $this->_set_list_limit();
     // set up the pagination object
     $pagination_defaults = array('link' => $this->prepare_page_link($this->shortcode_atts['filtering'] ? filter_input(INPUT_POST, 'pagelink') : $_SERVER['REQUEST_URI']), 'page' => $this->current_page, 'size' => $this->page_list_limit, 'total_records' => $this->num_records, 'filtering' => $this->shortcode_atts['filtering'], 'add_variables' => 'instance=' . $this->instance_index . '#' . $this->list_anchor);
     // instantiate the pagination object
     $this->pagination = new PDb_Pagination($pagination_defaults);
     /*
      * get the records for this page, adding the pagination limit clause
      *
      * this gives us an array of objects, each one a set of field->value pairs
      */
     $records = $wpdb->get_results($list_query . ' ' . $this->pagination->getLimitSql(), OBJECT);
     /*
      * build an array of record objects, indexed by ID
      */
     $this->records = array();
     foreach ($records as $record) {
         $id = $record->id;
         if (!in_array('id', $this->display_columns)) {
             unset($record->id);
         }
         $this->records[$id] = $record;
     }
     if (!empty($this->records)) {
         foreach ($this->records as $record_id => $record_fields) {
             /*
              * @version 1.6 
              * 
              * this array now contains all values for the record
              */
             // set the values for the current record
             $this->participant_values = Participants_Db::get_participant($record_id);
             foreach ($record_fields as $field => $value) {
                 /*
                  * as of 1.5.5, we don't fill up the records property with all the field properties, 
                  * just the current props, like value and link. The field properties are added when 
                  * the list is displayed to use less memory
                  */
                 $field_object = new stdClass();
                 $field_object->name = $field;
                 // set the current value of the field
                 $this->_set_field_value($field_object);
                 $this->_set_field_link($field_object);
                 // add the field to the record object
                 $this->records[$record_id]->{$field_object->name} = $field_object;
             }
         }
     }
     reset($this->records);
     /*
      * at this point, $this->records has been defined as an array of records,
      * each of which is an object that is a collection of objects: each one of
      * which is the data for a field
      */
     // error_log( __METHOD__.' all records:'.print_r( $this->records,1));
 }
 /**
  * sets up the data source
  * 
  * @param array|int $data
  * 
  * @return array
  */
 private function setup_data($data = false)
 {
     if (is_array($data)) {
         return $data;
     }
     if (is_numeric($data) && ($record = Participants_Db::get_participant($data))) {
         return $record;
     }
     return array();
 }
    /**
     * performs an update to the database if needed
     */
    public static function on_update()
    {
        global $wpdb;
        // determine the actual version status of the database
        self::set_database_real_version();
        if (WP_DEBUG) {
            error_log('participants database db version determined to be: ' . get_option(Participants_Db::$db_version_option));
        }
        if (false === get_option(Participants_Db::$db_version_option) || '0.1' == get_option(Participants_Db::$db_version_option)) {
            /*
             * updates version 0.1 database to 0.2
             *
             * adding a new column "display_column" and renaming "column" to
             * "admin_column" to accommodate the new frontend display shortcode
             */
            $sql = "ALTER TABLE " . Participants_Db::$fields_table . " ADD COLUMN `display_column` INT(3) DEFAULT 0 AFTER `validation`,";
            $sql .= "CHANGE COLUMN `column` `admin_column` INT(3)";
            if (false !== $wpdb->query($sql)) {
                // in case the option doesn't exist
                add_option(Participants_Db::$db_version_option);
                // set the version number this step brings the db to
                update_option(Participants_Db::$db_version_option, '0.2');
            }
            // load some preset values into new column
            $values = array('first_name' => 1, 'last_name' => 2, 'city' => 3, 'state' => 4);
            foreach ($values as $field => $value) {
                $wpdb->update(Participants_Db::$fields_table, array('display_column' => $value), array('name' => $field));
            }
        }
        if ('0.2' == get_option(Participants_Db::$db_version_option)) {
            /*
             * updates version 0.2 database to 0.3
             *
             * modifying the 'values' column of the fields table to allow for larger
             * select option lists
             */
            $sql = "ALTER TABLE " . Participants_Db::$fields_table . " MODIFY COLUMN `values` LONGTEXT NULL DEFAULT NULL";
            if (false !== $wpdb->query($sql)) {
                // set the version number this step brings the db to
                update_option(Participants_Db::$db_version_option, '0.3');
            }
        }
        if ('0.3' == get_option(Participants_Db::$db_version_option)) {
            /*
             * updates version 0.3 database to 0.4
             *
             * changing the 'when' field to a date field
             * exchanging all the PHP string date values to UNIX timestamps in all form_element = 'date' fields
             *
             */
            // change the 'when' field to a date field
            $wpdb->update(Participants_Db::$fields_table, array('form_element' => 'date'), array('name' => 'when', 'form_element' => 'text-line'));
            //
            $date_fields = $wpdb->get_results('SELECT f.name FROM ' . Participants_Db::$fields_table . ' f WHERE f.form_element = "date"', ARRAY_A);
            $df_string = '';
            foreach ($date_fields as $date_field) {
                if (!in_array($date_field['name'], array('date_recorded', 'date_updated'))) {
                    $df_string .= ',`' . $date_field['name'] . '` ';
                }
            }
            // skip updating the Db if there's nothing to update
            if (!empty($df_string)) {
                $query = '
						
						SELECT `id`' . $df_string . '
						FROM ' . Participants_Db::$participants_table;
                $fields = $wpdb->get_results($query, ARRAY_A);
                // now that we have all the date field values, convert them to N=UNIX timestamps
                foreach ($fields as $row) {
                    $id = $row['id'];
                    unset($row['id']);
                    $update_row = array();
                    foreach ($row as $field => $original_value) {
                        if (empty($original_value)) {
                            continue 2;
                        }
                        // if it's already a timestamp, we don't try to convert
                        $value = preg_match('#^[0-9]+$#', $original_value) > 0 ? $original_value : strtotime($original_value);
                        // if strtotime fails, revert to original value
                        $update_row[$field] = false === $value ? $original_value : $value;
                    }
                    $wpdb->update(Participants_Db::$participants_table, $update_row, array('id' => $id));
                }
            }
            // set the version number this step brings the db to
            update_option(Participants_Db::$db_version_option, '0.4');
        }
        if ('0.4' == get_option(Participants_Db::$db_version_option)) {
            /*
             * updates version 0.4 database to 0.5
             *
             * modifying the "import" column to be named more appropriately "CSV"
             */
            $sql = "ALTER TABLE " . Participants_Db::$fields_table . " CHANGE COLUMN `import` `CSV` TINYINT(1)";
            if (false !== $wpdb->query($sql)) {
                // set the version number this step brings the db to
                update_option(Participants_Db::$db_version_option, '0.5');
            }
        }
        /* this fixes an error I made in the 0.5 DB update
         */
        if ('0.5' == get_option(Participants_Db::$db_version_option) && false === Participants_Db::get_participant()) {
            // define the arrays for loading the initial db records
            $this->_define_init_arrays();
            // load the default values into the database
            $i = 0;
            unset($defaults);
            foreach (array_keys(self::$field_groups) as $group) {
                foreach (self::${$group . '_fields'} as $name => $defaults) {
                    $defaults['name'] = $name;
                    $defaults['group'] = $group;
                    $defaults['CSV'] = 'main' == $group ? 1 : 0;
                    $defaults['order'] = $i;
                    $defaults['validation'] = isset($defaults['validation']) ? $defaults['validation'] : 'no';
                    if (isset($defaults['values']) && is_array($defaults['values'])) {
                        $defaults['values'] = serialize($defaults['values']);
                    }
                    $wpdb->insert(Participants_Db::$fields_table, $defaults);
                    $i++;
                }
            }
            // set the version number this step brings the db to
            update_option(Participants_Db::$db_version_option, '0.5.1');
        }
        /*
         * this is to fix a problem with the timestamp having it's datatype
         * changed when the field attributes are edited
         */
        if ('0.51' == get_option(Participants_Db::$db_version_option)) {
            $sql = "SHOW FIELDS FROM " . Participants_Db::$participants_table . " WHERE `field` IN ('date_recorded','date_updated')";
            $field_info = $wpdb->get_results($sql);
            foreach ($field_info as $field) {
                if ($field->Type !== 'TIMESTAMP') {
                    switch ($field->Field) {
                        case 'date_recorded':
                            $column_definition = '`date_recorded` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP';
                            break;
                        case 'date_updated':
                            $column_definition = '`date_updated` TIMESTAMP NOT NULL DEFAULT 0';
                            break;
                        default:
                            $column_definition = false;
                    }
                    if (false !== $column_definition) {
                        $sql = "ALTER TABLE " . Participants_Db::$participants_table . " MODIFY COLUMN " . $column_definition;
                        $result = $wpdb->get_results($sql);
                    }
                }
            }
            // delete the default record
            $wpdb->query($wpdb->prepare("DELETE FROM " . Participants_Db::$participants_table . " WHERE private_id = '%s'", 'RPNE2'));
            // add the new private ID admin column setting because we eliminated the redundant special setting
            $options = get_option(Participants_Db::$participants_db_options);
            if ($options['show_pid']) {
                $wpdb->update(Participants_Db::$fields_table, array('admin_column' => 90), array('name' => 'private_id'));
            }
            /*
             * add the "read-only" column
             */
            $sql = "ALTER TABLE " . Participants_Db::$fields_table . " ADD COLUMN `readonly` BOOLEAN DEFAULT 0 AFTER `signup`";
            $wpdb->query($sql);
            /*
             * change the old 'textarea' field type to the new 'text-area'
             */
            $sql = "\n          UPDATE " . Participants_Db::$fields_table . "\n          SET `form_element` = replace(`form_element`, \"textarea\", \"text-area\")";
            $wpdb->query($sql);
            $sql = "\n          UPDATE " . Participants_Db::$fields_table . "\n          SET `form_element` = replace(`form_element`, \"text-field\", \"text-line\") ";
            if (false !== $wpdb->query($sql)) {
                // update the stored DB version number
                update_option(Participants_Db::$db_version_option, '0.55');
            }
        }
        /*
         * this database version adds the "last_accessed" column to the main database
         * 
         */
        if ('0.55' == get_option(Participants_Db::$db_version_option)) {
            /*
             * add the "last_accessed" column
             */
            $sql = "ALTER TABLE " . Participants_Db::$participants_table . " ADD COLUMN `last_accessed` TIMESTAMP NOT NULL AFTER `date_updated`";
            $wpdb->query($sql);
            /*
             * add the new field to the fields table
             */
            $data = array('order' => '20', 'name' => 'last_accessed', 'title' => 'Last Accessed', 'group' => 'internal', 'sortable' => '1', 'form_element' => 'date');
            if (false !== $wpdb->insert(Participants_Db::$fields_table, $data)) {
                // update the stored DB version number
                update_option(Participants_Db::$db_version_option, '0.6');
            }
        }
        if ('0.6' == get_option(Participants_Db::$db_version_option)) {
            /*
             * this database version changes the internal timestamp fields from "date" 
             * type to "timestamp" type fields, also sets the 'readonly' flag of internal 
             * fields so we don't have to treat them as a special case any more.
             * 
             * set the field type of internal timestamp fields to 'timestamp'
             */
            $sql = "UPDATE " . Participants_Db::$fields_table . " p SET p.form_element = 'timestamp', p.readonly = '1' WHERE p.name IN ('date_recorded','last_accessed','date_updated')";
            if ($wpdb->query($sql) !== false) {
                // update the stored DB version number
                update_option(Participants_Db::$db_version_option, '0.65');
            }
        }
        if ('0.65' == get_option(Participants_Db::$db_version_option)) {
            /*
             * adds a new column to the goups database so a group cna be designated as a "admin" group
             */
            $sql = "ALTER TABLE " . Participants_Db::$groups_table . " ADD COLUMN `admin` BOOLEAN NOT NULL DEFAULT 0 AFTER `order`";
            if ($wpdb->query($sql) !== false) {
                // update the stored DB version number
                update_option(Participants_Db::$db_version_option, '0.7');
            }
            $sql = "UPDATE " . Participants_Db::$groups_table . " g SET g.admin = '1' WHERE g.name ='internal'";
            $wpdb->query($sql);
        }
        if ('0.7' == get_option(Participants_Db::$db_version_option)) {
            /*
             * changes all date fields' datatype to BIGINT unless the user has modified the datatype
             */
            $sql = 'SELECT f.name FROM ' . Participants_Db::$fields_table . ' f INNER JOIN INFORMATION_SCHEMA.COLUMNS AS c ON TABLE_NAME = "' . Participants_Db::$participants_table . '" AND c.column_name = f.name COLLATE utf8_general_ci AND data_type = "TINYTEXT" WHERE f.form_element = "date"';
            $results = $wpdb->get_results($sql, ARRAY_A);
            $fields = array();
            foreach ($results as $result) {
                $fields[] = $result['name'];
            }
            if (count($fields) === 0) {
                // nothing to change, update the version number
                update_option(Participants_Db::$db_version_option, '0.8');
            } else {
                $results = $wpdb->get_results("SHOW COLUMNS FROM `" . Participants_Db::$participants_table . "`");
                $columns = array();
                foreach ($results as $result) {
                    $columns[] = $result->Field;
                }
                $fields = array_intersect($columns, $fields);
                $sql = 'ALTER TABLE ' . Participants_Db::$participants_table . ' MODIFY COLUMN `' . implode('` BIGINT NULL, MODIFY COLUMN `', $fields) . '` BIGINT NULL';
                if (false !== $wpdb->query($sql)) {
                    // set the version number this step brings the db to
                    update_option(Participants_Db::$db_version_option, '0.8');
                }
            }
        }
        if ('0.8' == get_option(Participants_Db::$db_version_option)) {
            /*
             * all field and group names need more staorage space, changing the datatype to VARCHAR(64)
             */
            $sql = "ALTER TABLE " . Participants_Db::$fields_table . " MODIFY `name` VARCHAR(64) NOT NULL, MODIFY `group` VARCHAR(64) NOT NULL";
            if (false !== $wpdb->query($sql)) {
                $sql = "ALTER TABLE " . Participants_Db::$groups_table . " MODIFY `name` VARCHAR(64) NOT NULL";
                if (false !== $wpdb->query($sql)) {
                    // set the version number this step brings the db to
                    update_option(Participants_Db::$db_version_option, '0.9');
                }
            }
        }
        if ('0.9' == get_option(Participants_Db::$db_version_option)) {
            /*
             * set TIMESTAMP fields to allow NULL and set the default to NULL
             */
            $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$participants_table . "` MODIFY COLUMN `date_updated` TIMESTAMP NULL DEFAULT NULL");
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$participants_table . "` MODIFY COLUMN `last_accessed` TIMESTAMP NULL DEFAULT NULL");
            }
            /*
             * set other "not null" columns to NULL so the empty default value won't trigger an error
             */
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$participants_table . "` MODIFY COLUMN `private_id` VARCHAR(6) NULL");
            }
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$fields_table . "` MODIFY COLUMN `name` VARCHAR(64) NULL");
            }
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$fields_table . "` MODIFY COLUMN `title` TINYTEXT NULL");
            }
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$fields_table . "` MODIFY COLUMN `group` VARCHAR(64) NULL");
            }
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$groups_table . "` MODIFY COLUMN `name` VARCHAR(64) NULL");
            }
            if ($success !== false) {
                $success = $wpdb->query("ALTER TABLE `" . Participants_Db::$groups_table . "` MODIFY COLUMN `title` TINYTEXT NULL");
            }
            //
            if ($success !== false) {
                $table_status = $wpdb->get_results("SHOW TABLE STATUS WHERE `name` = '" . Participants_Db::$participants_table . "'");
                if (current($table_status)->Collation == 'utf8_general_ci') {
                    if ($success !== false) {
                        $success = $wpdb->query("alter table `" . Participants_Db::$participants_table . "` convert to character set utf8 collate utf8_unicode_ci");
                    }
                    if ($success !== false) {
                        $success = $wpdb->query("alter table `" . Participants_Db::$fields_table . "` convert to character set utf8 collate utf8_unicode_ci");
                    }
                    if ($success !== false) {
                        $success = $wpdb->query("alter table `" . Participants_Db::$groups_table . "` convert to character set utf8 collate utf8_unicode_ci");
                    }
                }
            }
            if ($success === false) {
                error_log(__METHOD__ . ' database could not be updated: ' . $wpdb->last_error);
            } else {
                update_option(Participants_Db::$db_version_option, '1.0');
            }
        }
        if (WP_DEBUG) {
            error_log(Participants_Db::PLUGIN_NAME . ' plugin updated to Db version ' . get_option(Participants_Db::$db_version_option));
        }
    }
 /**
  * instantiates the signup form object
  *
  * @param array $shortcode_atts   this array supplies the display parameters for the instance
  *
  */
 public function __construct($shortcode_atts)
 {
     // define shortcode-specific attributes to use
     $shortcode_defaults = array('module' => 'signup', 'submit_button' => Participants_Db::plugin_setting('signup_button_text'), 'edit_record_page' => Participants_Db::plugin_setting('registration_page'));
     /*
      * status values: normal (signup form submission) or multipage
      */
     $form_status = $this->get_form_status();
     /*
      * get the record ID from the last submission or current multiform
      */
     $this->participant_id = Participants_Db::$session->get('pdbid');
     /*
      * if we've opened a regular signup form while in a multipage session, treat it 
      * as a normal signup form and terminate the multipage session
      */
     if ($shortcode_atts['module'] === 'signup' && $this->participant_id !== false && !isset($shortcode_atts['action']) && $form_status === 'multipage') {
         $this->participant_id = false;
         $this->_clear_multipage_session();
     }
     /*
      * if no ID is set, no submission has been received
      */
     if ($this->participant_id === false) {
         if (filter_input(INPUT_GET, 'm') === 'r' || $shortcode_atts['module'] == 'retrieve') {
             /*
              * we're proceesing a link retrieve request
              */
             $shortcode_atts['module'] = 'retrieve';
             add_filter('pdb-before_field_added_to_iterator', array($this, 'allow_readonly_fields_in_form'));
         }
         if ($shortcode_atts['module'] == 'signup') {
             /*
              * we're showing the signup form
              */
             $this->participant_values = Participants_Db::get_default_record();
         }
     } else {
         /*
          * if we arrive here, the form has been submitted and is complete or is a multipage 
          * form and we've come back to the signup shortcode before the form was completed: 
          * in which case we show the saved values from the record
          */
         $this->participant_values = Participants_Db::get_participant($this->participant_id);
         if ($this->participant_values && ($form_status === 'normal' || $shortcode_atts['module'] == 'thanks' && $form_status === 'multipage')) {
             /*
              * the submission is successful, clear the session and set the submitted flag
              */
             $this->_clear_multipage_session();
             $this->submitted = true;
             $shortcode_atts['module'] = 'thanks';
         }
         $shortcode_atts['id'] = $this->participant_id;
     }
     // run the parent class initialization to set up the $shortcode_atts property
     parent::__construct($shortcode_atts, $shortcode_defaults);
     // set up the signup form email preferences
     $this->_set_email_prefs();
     // set the action URI for the form
     $this->_set_submission_page();
     // set up the template iteration object
     $this->_setup_iteration();
     if ($this->submitted) {
         /*
          * filter provides access to the freshly-stored record and the email and 
          * thanks message properties so user feedback can be altered.
          * 
          * filter: pdb-before_signup_thanks
          */
         if (has_filter(Participants_Db::$prefix . 'before_signup_thanks')) {
             $signup_feedback_props = array('recipient', 'receipt_subject', 'receipt_body', 'notify_recipients', 'notify_subject', 'notify_body', 'thanks_message', 'participant_values');
             $signup_feedback = new stdClass();
             foreach ($signup_feedback_props as $prop) {
                 $signup_feedback->{$prop} =& $this->{$prop};
             }
             apply_filters(Participants_Db::$prefix . 'before_signup_thanks', $signup_feedback);
         }
         $this->_send_email();
         // form has been submitted, close it
         Participants_Db::$session->clear('form_status');
     }
     // print the shortcode output
     $this->_print_from_template();
 }