/** * intializes the object with a setup array * * @param array $config an array of optional parameters: * 'filename' => an image path, filename or URL * 'classname' => a classname for the image * 'wrap_tags' => array of open and close HTML * 'link' URI for a wrapping anchor tag * 'relstring' => string to show in the "rel" attribute of the image * 'mode' => display mode: as an image or a filename or both * 'module' => calling module */ function __construct($config) { parent::__construct($config); $this->classname .= ' ' . Participants_Db::$prefix . 'image image-field-wrap'; if (empty($this->link)) { $this->link = $this->image_defined && Participants_Db::plugin_setting_is_true('image_link') ? $this->image_uri : ''; } }
/** * construct the class * * we check the setting for using PHP session, if false, we use a WP Transient-based session * * we are just using this alternate form of session mnagement instead of PHP * sessions for now */ public function __construct() { $this->use_php_sessions = Participants_Db::plugin_setting_is_true('use_php_sessions'); $this->session_name = Participants_Db::$prefix . 'session'; if ($this->use_php_sessions) { if (!session_id()) { add_action('init', 'session_start', -2); } } else { // Use WP_Session (default) require_once plugin_dir_path(__FILE__) . 'wp-session.inc.php'; if (!defined('WP_SESSION_COOKIE')) { define('WP_SESSION_COOKIE', Participants_Db::$prefix . 'wp_session'); } } add_action('plugins_loaded', array($this, 'init'), -1); }
/** * supplies the current empty state logic value * * true - empty search allowed * false - empty searches not allowed * * @return bool */ public function empty_search_allowed() { if ($this->is_shortcode() || Participants_Db::plugin_setting_is_true('empty_search', false)) { return true; } return false; }
/** * conditionally removes line breaks from a buffered output * * @param string $input the buffer input * @return string processed string */ protected function strip_linebreaks($input) { if (Participants_Db::plugin_setting_is_true('strip_linebreaks')) { $input = str_replace(PHP_EOL, '', $input); } return $input; }
/** * indicates whether groups are to be printed in the form * * signup and record forms print group titles/descriptions only if the setting for that form is true * all other shortcodes always print groups, but they're really only seen in single record displays * * @return bool true if groups are to be printed */ public function printing_groups() { switch ($this->module) { case 'signup': $optionname = 'signup_show_group_descriptions'; break; case 'record': $optionname = 'show_group_descriptions'; break; default: return true; } return Participants_Db::plugin_setting_is_true($optionname); }
/** * processes any POST requests * * this is called on the 'init' hook * * @global object $wpdb * @return null */ public static function process_page_request() { $post_sanitize = array('subsource' => FILTER_SANITIZE_STRING, 'action' => FILTER_SANITIZE_STRING, 'pdb_data_keys' => FILTER_SANITIZE_STRING, 'submit_button' => FILTER_SANITIZE_STRING, 'filename' => FILTER_SANITIZE_STRING, 'base_filename' => FILTER_SANITIZE_STRING, 'CSV_type' => FILTER_SANITIZE_STRING, 'include_csv_titles' => FILTER_VALIDATE_BOOLEAN, 'nocookie' => FILTER_VALIDATE_BOOLEAN, 'previous_multipage' => FILTER_SANITIZE_STRING); /* * $post_input is used for control functions, not for the dataset */ $post_input = filter_input_array(INPUT_POST, $post_sanitize); // only process POST arrays from this plugin's pages if (empty($post_input['subsource']) or $post_input['subsource'] != self::PLUGIN_NAME or empty($post_input['action'])) { return; } // add a filter to check the submission before anything is done with it $check = true; self::set_filter('check_submission', $check); if ($check === false) { return; } // error_log( __METHOD__.' post:'.print_r( $_POST, true ) ); /* * the originating page for a multipage form is saved in a session value * * if this is an empty string, it is assumed the submission was not part of a multipage form series */ self::$session->set('previous_multipage', $post_input['previous_multipage']); /* * get the defined columns for the submitting shortcode (if any) * * this is needed so that validation will be performed on the expected list * of fields, not just what's found in the POST array */ $columns = false; if (!empty($post_input['pdb_data_keys'])) { $columns = self::get_data_key_columns($post_input['pdb_data_keys']); } /* * instantiate the validation object if we need to. This is necessary * because another script can instantiate the object in order to add a * feedback message * * we don't validate administrators in the admin */ if (!is_object(self::$validation_errors)) { if (Participants_Db::is_form_validated()) { self::$validation_errors = new PDb_FormValidation(); } } switch ($post_input['action']) { case 'update': case 'insert': /* * we are here for one of these cases: * a) we're adding a new record in the admin * b) a user is updating their record on the frontend * c) an admin is updating a record * * signups are processed in the case 'signup' section * * set the raw post array filters. We pass in the $_POST array, expecting * a possibly altered copy of it to be returned * * filter: pdb-before_submit_update * filter: pdb-before_submit_add */ $post_data = self::set_filter('before_submit_' . ($post_input['action'] === 'insert' ? 'add' : 'update'), $_POST); if (isset($_POST['id'])) { $id = filter_input(INPUT_POST, 'id', FILTER_VALIDATE_INT, array('options' => array('min_range' => 1))); } elseif (isset($_GET['id'])) { $id = filter_input(INPUT_GET, 'id', FILTER_VALIDATE_INT, array('options' => array('min_range' => 1))); } else { $id = false; } $participant_id = self::process_form($post_data, $post_input['action'], $id, $columns); if (false === $participant_id) { // we have errors; go back to form and show errors return; } /* * set the stored record hook. * * hook: pdb-after_submit_update * hook: pdb-after_submit_add */ $wp_hook = self::$prefix . 'after_submit_' . ($post_input['action'] == 'insert' ? 'add' : 'update'); do_action($wp_hook, self::get_participant($participant_id)); /* * if we are submitting from the frontend, set the feedback message and * send the update notification */ if (!is_admin()) { /* * if the user is an admin, the validation object won't be instantiated, * so we do that here so the feedback message can be shown. */ if (!is_object(self::$validation_errors)) { self::$validation_errors = new PDb_FormValidation(); } self::$validation_errors->add_error('', self::$plugin_options['record_updated_message']); if (self::$plugin_options['send_record_update_notify_email'] && Participants_Db::$session->get('form_status') !== 'multipage') { $sent = wp_mail(self::$plugin_options['email_signup_notify_addresses'], self::proc_tags(self::$plugin_options['record_update_email_subject'], $participant_id, 'all'), self::proc_tags(self::$plugin_options['record_update_email_body'], $participant_id, 'all'), self::$email_headers); } /* * if the "thanks page" is defined as another page, save the ID in a session variable and move to that page. */ if (isset($post_data['thanks_page']) && $post_data['thanks_page'] != $_SERVER['REQUEST_URI']) { self::$session->set('pdbid', $post_data['id']); $redirect = $post_input['action'] == 'insert' ? $post_data['thanks_page'] : self::add_uri_conjunction($post_data['thanks_page']) . 'action=update'; wp_redirect($redirect); exit; } return; } // redirect according to which submit button was used switch ($post_input['submit_button']) { case self::$i18n['apply']: $redirect = get_admin_url() . 'admin.php?page=' . self::PLUGIN_NAME . '-edit_participant&id=' . $participant_id; break; case self::$i18n['next']: $get_id = $post_input['action'] == 'update' ? '&id=' . self::next_id($participant_id) : ''; $redirect = get_admin_url() . 'admin.php?page=' . self::PLUGIN_NAME . '-edit_participant' . $get_id; break; case self::$i18n['previous']: $get_id = $post_input['action'] == 'update' ? '&id=' . self::next_id($participant_id, false) : ''; $redirect = get_admin_url() . 'admin.php?page=' . self::PLUGIN_NAME . '-edit_participant' . $get_id; break; case self::$i18n['submit']: default: $redirect = get_admin_url() . 'admin.php?page=' . self::PLUGIN_NAME; } wp_redirect($redirect); exit; case 'output CSV': $csv_role = Participants_Db::plugin_setting_is_true('editor_allowed_csv_export') ? 'editor' : 'admin'; if (!Participants_Db::current_user_has_plugin_role($csv_role, 'csv export')) { die; } $header_row = array(); $title_row = array(); $data = array(); $filename = !empty($post_input['filename']) ? $post_input['filename'] : ''; switch ($post_input['CSV_type']) { // create a blank data array case 'blank': // add the header row foreach (self::get_column_atts('CSV') as $column) { $header_row[] = $column->name; } $data[] = $header_row; $i = 2; // number of blank rows to create while ($i > 0) { $data[] = array_fill_keys($header_row, ''); $i--; } break; case 'participant list': global $wpdb; $import_columns = ''; foreach (self::get_column_atts('CSV') as $column) { $import_columns .= sprintf('`%s`,', $column->name); $header_row[] = $column->name; $title_row[] = $column->title; } $data['header'] = $header_row; if ($post_input['include_csv_titles']) { $data['titles'] = $title_row; } global $current_user; $query = get_transient(Participants_Db::$prefix . 'admin_list_query' . $current_user->ID); if ($query) { $query = str_replace('*', ' ' . trim($import_columns, ',') . ' ', $query); $data += self::_prepare_CSV_rows($wpdb->get_results($query, ARRAY_A)); } break; } // CSV type if (!empty($filename)) { $base_filename = substr($filename, 0, strpos($filename, PDb_List_Admin::filename_datestamp() . '.csv')); /* * @version 1.6 * base filename is now saved as a preference */ global $user_ID; PDb_List_Admin::$user_settings = Participants_Db::$prefix . PDb_List_Admin::$user_settings . '-' . $user_ID; PDb_List_Admin::set_admin_user_setting('csv_base_filename', $base_filename); // create a file pointer connected to the output stream $output = fopen('php://output', 'w'); //header('Content-type: application/csv'); // some sources say it should be this header('Content-Type: text/csv; charset=utf-8'); header("Cache-Control: no-store, no-cache"); header('Content-Disposition: attachment; filename="' . $filename . '"'); // output the data lines foreach ($data as $line) { fputcsv($output, $line, ',', self::$CSV_enclosure); } fclose($output); // we must terminate the script to prevent additional output being added to the CSV file exit; } return $data; case 'retrieve': if (self::nonce_check(filter_input(INPUT_POST, 'session_hash', FILTER_SANITIZE_STRING), self::$main_submission_nonce_key)) { self::_process_retrieval(); } return; case 'signup': if (!self::nonce_check(filter_input(INPUT_POST, 'session_hash', FILTER_SANITIZE_STRING), self::$main_submission_nonce_key)) { return; } $_POST['private_id'] = ''; $columns[] = 'private_id'; /* * route the $_POST data through a callback if defined * * filter: pdb-before_submit_signup */ $post_data = self::set_filter('before_submit_signup', $_POST); /* * the signup form should update the current record if it is revisited during a multipage form session */ $submit_action = 'insert'; if (self::$session->get('pdbid') !== false) { $submit_action = 'update'; } // submit the data $post_data['id'] = self::process_form($post_data, $submit_action, self::$session->get('pdbid'), $columns); if (false !== $post_data['id']) { /* * hook: pdb-after_submit_signup */ $wp_hook = self::$prefix . 'after_submit_signup'; do_action($wp_hook, self::get_participant($post_data['id'])); $redirect = $post_data['thanks_page']; self::$session->set('pdbid', $post_data['id']); wp_redirect($redirect); exit; } return; } // $_POST['action'] }
/** * prints an individual group tab content * * @param string $group current group name */ protected function print_group_tab_content($group) { $internal_group = $group === 'internal'; $hscroll = Participants_Db::plugin_setting_is_true('admin_horiz_scroll'); ?> <div id="<?php echo $group; ?> " class="manage-fields-wrap" > <form id="manage_<?php echo $group; ?> _fields" method="post" autocomplete="off"> <h3><?php echo $this->group_titles[$group], ' ', $this->i18n['fields']; ?> </h3> <p> <?php if (!$internal_group) { // "add field" functionality PDb_FormElement::print_element(array('type' => 'submit', 'value' => $this->i18n['add field'], 'name' => 'action', 'attributes' => array('class' => 'button button-default', 'disabled' => 'disabled'))); PDb_FormElement::print_element(array('type' => 'text', 'name' => 'title', 'value' => '', 'attributes' => array('placeholder' => $this->i18n['new field title'] . '…', 'class' => 'add_field'))); } // skip internal groups // number of rows in the group $num_group_rows = count($this->fields_data[$group]); $last_order = $num_group_rows > 1 ? $this->fields_data[$group][$num_group_rows - 1]['order'] + 1 : 1; PDb_FormElement::print_hidden_fields(array('group' => $group, 'order' => $last_order)); ?> </p> <?php if ($hscroll) { ?> <div class="pdb-horiz-scroll-scroller"> <div class="pdb-horiz-scroll-width"> <?php } ?> <table class="wp-list-table widefat fixed manage-fields" > <thead> <tr> <?php if (!$internal_group) { ?> <th scope="col" class="delete vertical-title"><span><?php echo $this->table_header('delete'); ?> </span></th> <?php } // internal group test foreach ($this->attribute_columns[$group] as $attribute_column) { if ($internal_group && in_array($attribute_column, array('order'))) { continue; } $column_class = $attribute_column; $column_class .= in_array($attribute_column, array('order', 'persistent', 'sortable', 'admin_column', 'display_column', 'CSV', 'signup', 'display', 'readonly')) ? ' vertical-title' : ''; $column_class .= in_array($attribute_column, array('admin_column', 'display_column')) ? ' number-column' : ''; ?> <th scope="col" class="<?php echo $column_class; ?> "><span><?php echo $this->table_header($attribute_column); ?> </span></th> <?php } ?> </tr> </thead> <tbody id="<?php echo $group; ?> _fields"> <?php if ($num_group_rows < 1) { // there are no rows in this group to show ?> <tr><td colspan="<?php echo count($this->attribute_columns[$group]) + 1; ?> "><?php _e('No fields in this group', 'participants-database'); ?> </td></tr> <?php } else { // add the rows of the group foreach ($this->fields_data[$group] as $database_row) { ?> <tr id="db_row_<?php echo $database_row['id']; ?> "> <?php if (!$internal_group) { ?> <td> <?php } // hidden field test // add the hidden fields foreach (array('id') as $attribute_column) { $value = Participants_Db::prepare_value($database_row[$attribute_column]); $element_atts = array_merge($this->get_edit_field_type($attribute_column), array('name' => 'row_' . $database_row['id'] . '[' . $attribute_column . ']', 'value' => $value)); PDb_FormElement::print_element($element_atts); } PDb_FormElement::print_element(array('type' => 'hidden', 'value' => '', 'name' => 'row_' . $database_row['id'] . '[status]', 'attributes' => array('id' => 'status_' . $database_row['id']))); if (!$internal_group) { ?> <a href="javascript:return false" title="<?php echo $database_row['id']; ?> " data-thing-name="delete_<?php echo $database_row['id']; ?> " class="delete" data-thing="<?php _e('field', 'participants-database'); ?> "><span class="glyphicon glyphicon-remove"></span></a> </td> <?php } // internal group test // list the fields for editing foreach ($this->attribute_columns[$group] as $attribute_column) { $edit_field_type = $this->get_edit_field_type($attribute_column); if ($internal_group && in_array($attribute_column, array('order'))) { continue; } // preserve backslashes in regex expressions if ($attribute_column == 'validation') { if ($database_row['name'] == 'email' && $database_row[$attribute_column] == 'email') { $database_row[$attribute_column] = 'email-regex'; } } $value = Participants_Db::prepare_value($database_row[$attribute_column]); $element_atts = array_merge($edit_field_type, array('name' => 'row_' . $database_row['id'] . '[' . $attribute_column . ']', 'value' => $this->prep_value($value, in_array($attribute_column, array('title', 'description'))))); ?> <td class="<?php echo $attribute_column; ?> "><?php PDb_FormElement::print_element($element_atts); ?> </td> <?php } // columns ?> </tr> <?php } // rows } // num group rows ?> </tbody> </table> <?php if ($hscroll) { ?> </div> </div> <?php } ?> <p class="submit"> <?php PDb_FormElement::print_element(array('type' => 'submit', 'name' => 'action', 'value' => $this->i18n['update fields'], 'class' => 'button button-primary manage-fields-update')); ?> </p> </form> </div><!-- tab content container --> <?php }
/** * returns an internationalized date string from a UNIX timestamp * * @param int $timestamp a UNIX timestamp * @param bool $time if true, adds the time of day to the format * @return string a formatted date or input string if invalid */ public static function format_date($timestamp, $time = false) { // if it's not a timestamp, we attempt to convert it to one if (!Participants_Db::is_valid_timestamp($timestamp)) { $timestamp = Participants_Db::parse_date($timestamp); } if (Participants_Db::is_valid_timestamp($timestamp)) { $format = Participants_Db::plugin_setting_is_true('strict_dates') ? Participants_Db::plugin_setting('input_date_format') : Participants_Db::$date_format; if ($time) { $format .= ' ' . get_option('time_format'); } return date_i18n($format, $timestamp); } else { // not a timestamp: return unchanged return $timestamp; } }
/** * returns a concatenated string from an array of HTML lines * * @version 1.6 added option to assemble HTML without linebreaks * * @param array $output * @return type */ public function output_HTML($output = array()) { $glue = Participants_Db::plugin_setting_is_true('strip_linebreaks') ? '' : PHP_EOL; return implode($glue, $output); }
/** * prints the main body of the list, including headers * * @param string $mode dtermines the print mode: 'noheader' skips headers, (other choices to be determined) */ private static function _main_table($mode = '') { $hscroll = Participants_Db::plugin_setting_is_true('admin_horiz_scroll'); ?> <?php if ($hscroll) { ?> <div class="pdb-horiz-scroll-scroller"> <div class="pdb-horiz-scroll-width" style="width: <?php echo count(self::$display_columns) * 10; ?> em"> <?php } ?> <table class="wp-list-table widefat fixed pages pdb-list stuffbox" cellspacing="0" > <?php $PID_pattern = '<td><a href="%2$s">%1$s</a></td>'; //template for outputting a column $col_pattern = '<td>%s</td>'; if (count(self::$participants) > 0) { if ($mode != 'noheader') { ?> <thead> <tr> <?php self::_print_header_row(); ?> </tr> </thead> <?php } // table header row // print the table footer row if there is a long list if ($mode != 'noheader' && count(self::$participants) > 10) { ?> <tfoot> <tr> <?php self::_print_header_row(); ?> </tr> </tfoot> <?php } // table footer row ?> <tbody> <?php // output the main list foreach (self::$participants as $value) { ?> <tr> <?php // print delete check ?> <td> <?php if (current_user_can(Participants_Db::plugin_capability('plugin_admin_capability', 'delete participants'))) { ?> <input type="checkbox" class="delete-check" name="pid[]" value="<?php echo $value['id']; ?> " /> <?php } ?> <a href="admin.php?page=<?php echo 'participants-database'; ?> -edit_participant&action=edit&id=<?php echo $value['id']; ?> " title="<?php _e('Edit', 'participants-database'); ?> "><span class="glyphicon glyphicon-edit"></span></a> </td> <?php foreach (self::$display_columns as $column) { // this is where we place form-element-specific text transformations for display switch ($column->form_element) { case 'image-upload': $image_params = array('filename' => basename($value[$column->name]), 'link' => '', 'mode' => Participants_Db::plugin_setting_is_true('admin_thumbnails') ? 'image' : 'filename'); if (Participants_Db::is_single_record_link($column)) { $page_link = get_permalink(Participants_Db::plugin_setting('single_record_page')); $image_params['link'] = Participants_Db::add_uri_conjunction($page_link) . 'pdb=' . $value['id']; } // this is to display the image as a linked thumbnail $image = new PDb_Image($image_params); $display_value = $image->get_image_html(); break; case 'date': case 'timestamp': if (!empty($value[$column->name])) { $format = Participants_Db::$date_format; if (Participants_Db::plugin_setting_is_true('show_time') and $column->form_element == 'timestamp') { // replace spaces with so the time value stays together on a broken line $format .= ' ' . str_replace(' ', '&\\nb\\sp;', get_option('time_format')); } $time = Participants_Db::is_valid_timestamp($value[$column->name]) ? (int) $value[$column->name] : Participants_Db::parse_date($value[$column->name], $column->name, $column->form_element == 'date'); $display_value = $value[$column->name] == '0000-00-00 00:00:00' ? '' : date_i18n($format, $time); //$display_value = date_i18n($format, $time); } else { $display_value = ''; } break; case 'multi-select-other': case 'multi-checkbox': // multi selects are displayed as comma separated lists $column->value = $value[$column->name]; $display_value = PDb_FormElement::get_field_value_display($column, false); //$display_value = is_serialized($value[$column->name]) ? implode(', ', unserialize($value[$column->name])) : $value[$column->name]; break; case 'link': $link_value = maybe_unserialize($value[$column->name]); if (count($link_value) === 1) { $link_value = array_fill(0, 2, current((array) $link_value)); } $display_value = Participants_Db::make_link($link_value[0], $link_value[1]); break; case 'rich-text': if (!empty($value[$column->name])) { $display_value = '<span class="textarea">' . $value[$column->name] . '</span>'; } else { $display_value = ''; } break; case 'text-line': if (Participants_Db::is_single_record_link($column)) { $url = get_permalink(Participants_Db::plugin_setting('single_record_page')); $template = '<a href="%1$s" >%2$s</a>'; $delimiter = false !== strpos($url, '?') ? '&' : '?'; $url = $url . $delimiter . 'pdb=' . $value['id']; $display_value = sprintf($template, $url, $value[$column->name]); } elseif (Participants_Db::plugin_setting_is_true('make_links')) { $field = new stdClass(); $field->value = $value[$column->name]; $display_value = PDb_FormElement::make_link($field); } else { $display_value = $value[$column->name] === '' ? $column->default : esc_html($value[$column->name]); } break; case 'hidden': $display_value = $value[$column->name] === '' ? '' : esc_html($value[$column->name]); break; default: $column->value = $value[$column->name]; $display_value = PDb_FormElement::get_field_value_display($column, false); } if ($column->name === 'private_id' && Participants_Db::plugin_setting_is_set('registration_page')) { printf($PID_pattern, $display_value, Participants_Db::get_record_link($display_value)); } else { printf($col_pattern, $display_value); } } ?> </tr> <?php } ?> </tbody> <?php } else { // if there are no records to show; do this ?> <tbody> <tr> <td><?php _e('No records found', 'participants-database'); ?> </td> </tr> </tbody> <?php } // participants array ?> </table> <?php if ($hscroll) { ?> </div> </div> <?php } ?> </form> <?php }
/** * outputs a "record not found" message * * the message is defined in the plugin settings */ protected function _not_found() { if (Participants_Db::plugin_setting_is_true('no_record_use_template')) { $this->_print_from_template(); } else { $this->output = empty(Participants_Db::$plugin_options['no_record_error_message']) ? '' : '<p class="alert alert-error">' . Participants_Db::plugin_setting('no_record_error_message') . '</p>'; } }
/** * prints a private link retrieval link * * @param string $linktext */ public function print_retrieve_link($linktext = '', $open_tag = '<span class="pdb-retrieve-link">', $close_tag = '</span>') { $linktext = empty($linktext) ? Participants_Db::$plugin_options['retrieve_link_text'] : $linktext; if (Participants_Db::plugin_setting_is_true('show_retrieve_link')) { $retrieve_link = Participants_Db::plugin_setting('link_retrieval_page') !== 'none' ? get_permalink(Participants_Db::plugin_setting('link_retrieval_page')) : $_SERVER['REQUEST_URI']; echo $open_tag . '<a href="' . Participants_Db::add_uri_conjunction($retrieve_link) . 'm=r">' . Participants_Db::set_filter('translate_string', $linktext) . '</a>' . $close_tag; } }