/** * Listener function for the logged-in user's 'last_activity' metadata. * * Many functions use a "last active" feature to show the length of time since * the user was last active. This function will update that time as a usermeta * setting for the user every 5 minutes while the user is actively browsing the * site. * * @uses bp_update_user_meta() BP function to update user metadata in the * usermeta table. * * @return bool|null Returns false if there is nothing to do. */ function bp_core_record_activity() { // Bail if user is not logged in if (!is_user_logged_in()) { return false; } // Get the user ID $user_id = bp_loggedin_user_id(); // Bail if user is not active if (bp_is_user_inactive($user_id)) { return false; } // Get the user's last activity $activity = bp_get_user_last_activity($user_id); // Make sure it's numeric if (!is_numeric($activity)) { $activity = strtotime($activity); } // Get current time $current_time = bp_core_current_time(); // Use this action to detect the very first activity for a given member if (empty($activity)) { do_action('bp_first_activity_for_member', $user_id); } // If it's been more than 5 minutes, record a newer last-activity time if (empty($activity) || strtotime($current_time) >= strtotime('+5 minutes', $activity)) { bp_update_user_last_activity($user_id, $current_time); } }
/** * Render the Stats metabox to moderate inappropriate images. * * @since 2.0.0 * * @param WP_User|null $user The WP_User object to be edited. */ public function user_admin_stats_metabox($user = null) { // Bail if no user ID. if (empty($user->ID)) { return; } // If account is not activated last activity is the time user registered. if (isset($user->user_status) && 2 == $user->user_status) { $last_active = $user->user_registered; // Account is activated, getting user's last activity. } else { $last_active = bp_get_user_last_activity($user->ID); } $datef = __('M j, Y @ G:i', 'buddypress'); $date = date_i18n($datef, strtotime($last_active)); ?> <ul> <li class="bp-members-profile-stats"><?php printf(__('Last active: %1$s', 'buddypress'), '<strong>' . $date . '</strong>'); ?> </li> <?php // Loading other stats only if user has activated their account. if (empty($user->user_status)) { /** * Fires in the user stats metabox if the user has activated their account. * * @since 2.0.0 * * @param array $value Array holding the user ID. * @param object $user Current displayed user object. */ do_action('bp_members_admin_user_stats', array('user_id' => $user->ID), $user); } ?> </ul> <?php }
/** * Listener function for the logged-in user's 'last_activity' metadata. * * Many functions use a "last active" feature to show the length of time since * the user was last active. This function will update that time as a usermeta * setting for the user every 5 minutes while the user is actively browsing the * site. * * @uses bp_update_user_meta() BP function to update user metadata in the * usermeta table. * * @return bool|null Returns false if there is nothing to do. */ function bp_core_record_activity() { if (!is_user_logged_in()) { return false; } $user_id = bp_loggedin_user_id(); if (bp_is_user_inactive($user_id)) { return false; } $activity = bp_get_user_last_activity($user_id); if (!is_numeric($activity)) { $activity = strtotime($activity); } // Get current time $current_time = bp_core_current_time(); // Use this action to detect the very first activity for a given member if (empty($activity)) { do_action('bp_first_activity_for_member', $user_id); } if (empty($activity) || strtotime($current_time) >= strtotime('+5 minutes', $activity)) { bp_update_user_last_activity($user_id, $current_time); } }
/** * Backward compatibility for 'last_activity' usermeta fetching. * * In BuddyPress 2.0, user last_activity data was moved out of usermeta. For * backward compatibility, we continue to mirror the data there. This function * serves two purposes: it warns plugin authors of the change, and it returns * the data from the proper location. * * @since 2.0.0 * * @access private For internal use only. * * @param null $retval Null retval value. * @param int $object_id ID of the user. * @param string $meta_key Meta key being fetched. * @return mixed */ function _bp_get_user_meta_last_activity_warning($retval, $object_id, $meta_key) { static $warned = false; if ('last_activity' === $meta_key) { // Don't send the warning more than once per pageload. if (false === $warned) { _doing_it_wrong('get_user_meta( $user_id, \'last_activity\' )', __('User last_activity data is no longer stored in usermeta. Use bp_get_user_last_activity() instead.', 'buddypress'), '2.0.0'); $warned = true; } return bp_get_user_last_activity($object_id); } return $retval; }
/** * Populate the instantiated class with data based on the User ID provided. * * @uses bp_core_get_userurl() Returns the URL with no HTML markup for * a user based on their user id. * @uses bp_core_get_userlink() Returns a HTML formatted link for a * user with the user's full name as the link text. * @uses bp_core_get_user_email() Returns the email address for the * user based on user ID. * @uses bp_get_user_meta() BP function returns the value of passed * usermeta name from usermeta table. * @uses bp_core_fetch_avatar() Returns HTML formatted avatar for a user * @uses bp_profile_last_updated_date() Returns the last updated date * for a user. */ public function populate() { if (bp_is_active('xprofile')) { $this->profile_data = $this->get_profile_data(); } if (!empty($this->profile_data)) { $full_name_field_name = bp_xprofile_fullname_field_name(); $this->user_url = bp_core_get_user_domain($this->id, $this->profile_data['user_nicename'], $this->profile_data['user_login']); $this->fullname = esc_attr($this->profile_data[$full_name_field_name]['field_data']); $this->user_link = "<a href='{$this->user_url}' title='{$this->fullname}'>{$this->fullname}</a>"; $this->email = esc_attr($this->profile_data['user_email']); } else { $this->user_url = bp_core_get_user_domain($this->id); $this->user_link = bp_core_get_userlink($this->id); $this->fullname = esc_attr(bp_core_get_user_displayname($this->id)); $this->email = esc_attr(bp_core_get_user_email($this->id)); } // Cache a few things that are fetched often wp_cache_set('bp_user_fullname_' . $this->id, $this->fullname, 'bp'); wp_cache_set('bp_user_email_' . $this->id, $this->email, 'bp'); wp_cache_set('bp_user_url_' . $this->id, $this->user_url, 'bp'); $this->avatar = bp_core_fetch_avatar(array('item_id' => $this->id, 'type' => 'full', 'alt' => sprintf(__('Profile photo of %s', 'buddypress'), $this->fullname))); $this->avatar_thumb = bp_core_fetch_avatar(array('item_id' => $this->id, 'type' => 'thumb', 'alt' => sprintf(__('Profile photo of %s', 'buddypress'), $this->fullname))); $this->avatar_mini = bp_core_fetch_avatar(array('item_id' => $this->id, 'type' => 'thumb', 'alt' => sprintf(__('Profile photo of %s', 'buddypress'), $this->fullname), 'width' => 30, 'height' => 30)); $this->last_active = bp_core_get_last_activity(bp_get_user_last_activity($this->id), __('active %s', 'buddypress')); }
/** * Get the "active [x days ago]" string for a user. * * @param int $user_id ID of the user. Default: displayed user ID. * @return string */ function bp_get_last_activity($user_id = 0) { if (empty($user_id)) { $user_id = bp_displayed_user_id(); } $last_activity = bp_core_get_last_activity(bp_get_user_last_activity($user_id), __('active %s', 'buddypress')); /** * Filters the 'active [x days ago]' string for a user. * * @since 1.5.0 * * @param string $value Formatted 'active [x days ago]' string. */ return apply_filters('bp_get_last_activity', $last_activity); }
/** * @group bp_last_activity_migrate * @expectedIncorrectUsage update_user_meta( $user_id, 'last_activity' ) * @expectedIncorrectUsage get_user_meta( $user_id, 'last_activity' ) */ public function test_bp_last_activity_migrate() { // We explicitly do not want last_activity created, so use the // WP factory methods $u1 = $this->factory->user->create(); $u2 = $this->factory->user->create(); $u3 = $this->factory->user->create(); $time = time(); $t1 = date('Y-m-d H:i:s', $time - 50); $t2 = date('Y-m-d H:i:s', $time - 500); $t3 = date('Y-m-d H:i:s', $time - 5000); update_user_meta($u1, 'last_activity', $t1); update_user_meta($u2, 'last_activity', $t2); update_user_meta($u3, 'last_activity', $t3); // Create an existing entry in last_activity to test no dupes global $wpdb; $bp = buddypress(); $wpdb->query($wpdb->prepare("INSERT INTO {$bp->members->table_name_last_activity}\n\t\t\t\t(`user_id`, `component`, `type`, `action`, `content`, `primary_link`, `item_id`, `date_recorded` ) VALUES\n\t\t\t\t( %d, %s, %s, %s, %s, %s, %d, %s )", $u2, $bp->members->id, 'last_activity', '', '', '', 0, $t1)); bp_last_activity_migrate(); $expected = array($u1 => $t1, $u2 => $t2, $u3 => $t3); $found = array($u1 => '', $u2 => '', $u3 => ''); foreach ($found as $uid => $v) { $found[$uid] = bp_get_user_last_activity($uid); } $this->assertSame($expected, $found); }
public function manage_users_custom_column($empty, $column_name, $user_id) { if ('timestamps' != $column_name) { return $empty; } global $gMemberNetwork; $html = ''; $mode = empty($_REQUEST['mode']) ? 'list' : $_REQUEST['mode']; $user = get_user_by('id', $user_id); $lastlogin = get_user_meta($user_id, $this->constants['meta_lastlogin'], TRUE); $register_ip = get_user_meta($user_id, $this->constants['meta_register_ip'], TRUE); $registered = strtotime(get_date_from_gmt($user->user_registered)); $lastlogged = $lastlogin ? strtotime(get_date_from_gmt($lastlogin)) : NULL; $html .= '<table></tbody>'; $html .= '<tr><td>' . __('Registered', GMEMBER_TEXTDOMAIN) . '</td><td><code title="' . $gMemberNetwork->getDate($registered, 'timeampm') . '">' . $gMemberNetwork->getDate($registered) . '</code></td></tr>'; $html .= '<tr><td>' . __('Last Login', GMEMBER_TEXTDOMAIN) . '</td><td>' . ($lastlogin ? '<code title="' . $gMemberNetwork->getDate($lastlogged, 'timeampm') . '">' . $gMemberNetwork->getDate($lastlogged) . '</code>' : '<code>' . __('N/A', GMEMBER_TEXTDOMAIN)) . '</code></td></tr>'; if (function_exists('bp_get_user_last_activity')) { if ($lastactivity = bp_get_user_last_activity($user_id)) { $lastactive = strtotime(get_date_from_gmt($lastactivity)); } $html .= '<tr><td>' . __('Last Activity', GMEMBER_TEXTDOMAIN) . '</td><td>' . ($lastactivity ? '<code title="' . bp_core_time_since($lastactivity) . '">' . $gMemberNetwork->getDate($lastactive) : '<code>' . __('N/A', GMEMBER_TEXTDOMAIN)) . '</code></td></tr>'; } $html .= '<tr><td>' . __('Register IP', GMEMBER_TEXTDOMAIN) . '</td><td><code>' . ($register_ip ? $gMemberNetwork->getIPLookup($register_ip) : __('N/A', GMEMBER_TEXTDOMAIN)) . '</code></td></tr>'; $html .= '</tbody></table>'; echo $html; }
/** * Listener function for the logged-in user's 'last_activity' metadata. * * Many functions use a "last active" feature to show the length of time since * the user was last active. This function will update that time as a usermeta * setting for the user every 5 minutes while the user is actively browsing the * site. * * @uses bp_update_user_meta() BP function to update user metadata in the * usermeta table. * * @return bool|null Returns false if there is nothing to do. */ function bp_core_record_activity() { // Bail if user is not logged in if (!is_user_logged_in()) { return false; } // Get the user ID $user_id = bp_loggedin_user_id(); // Bail if user is not active if (bp_is_user_inactive($user_id)) { return false; } // Get the user's last activity $activity = bp_get_user_last_activity($user_id); // Make sure it's numeric if (!is_numeric($activity)) { $activity = strtotime($activity); } // Get current time $current_time = bp_core_current_time(); // Use this action to detect the very first activity for a given member if (empty($activity)) { /** * Fires inside the recording of an activity item. * * Use this action to detect the very first activity for a given member. * * @since BuddyPress (1.6.0) * * @param int $user_id ID of the user whose activity is recorded. */ do_action('bp_first_activity_for_member', $user_id); } // If it's been more than 5 minutes, record a newer last-activity time if (empty($activity) || strtotime($current_time) >= strtotime('+5 minutes', $activity)) { bp_update_user_last_activity($user_id, $current_time); } }
/** * Render the Stats metabox to moderate inappropriate images. * * @access public * @since BuddyPress (2.0.0) * * @param WP_User $user The WP_User object to be edited. */ public function user_admin_stats_metabox($user = null) { if (empty($user->ID)) { return; } // If account is not activated last activity is the time user registered if (isset($user->user_status) && 2 == $user->user_status) { $last_active = $user->user_registered; // Account is activated, getting user's last activity } else { $last_active = bp_get_user_last_activity($user->ID); } $datef = __('M j, Y @ G:i', 'buddypress'); $date = date_i18n($datef, strtotime($last_active)); ?> <ul> <li class="bp-members-profile-stats"><?php printf(__('Last active: <strong>%1$s</strong>', 'buddypress'), $date); ?> </li> <?php // Loading other stats only if user has activated their account if (empty($user->user_status)) { do_action('bp_members_admin_user_stats', array('user_id' => $user->ID), $user); } ?> </ul> <?php }
/** * Get the "active [x days ago]" string for a user. * * @param int $user_id ID of the user. Default: displayed user ID. * @return string */ function bp_get_last_activity($user_id = 0) { if (empty($user_id)) { $user_id = bp_displayed_user_id(); } $last_activity = bp_core_get_last_activity(bp_get_user_last_activity($user_id), __('active %s', 'buddypress')); return apply_filters('bp_get_last_activity', $last_activity); }
<?php } ?> <div id="item-buttons"><?php /** * Fires in the member header actions section. * * @since 1.2.6 */ do_action('bp_member_header_actions'); ?> </div><!-- #item-buttons --> <span class="activity" data-livestamp="<?php bp_core_iso8601_date(bp_get_user_last_activity(bp_displayed_user_id())); ?> "><?php bp_last_activity(bp_displayed_user_id()); ?> </span> <?php /** * Fires before the display of the member's header meta. * * @since 1.2.0 */ do_action('bp_before_member_header_meta'); ?>
/** * Process content of CSV file * * @since 0.1 **/ public function generate_data() { // Check if the user clicked on the Save, Load, or Delete Settings buttons ## if (!isset($_POST['_wpnonce-q-eud-export-user-page_export']) || isset($_POST['load_export']) || isset($_POST['save_export']) || isset($_POST['delete_export'])) { return false; } // check admin referer ## check_admin_referer('q-eud-export-user-page_export', '_wpnonce-q-eud-export-user-page_export'); // build argument array ## $args = array('fields' => isset($_POST['user_fields']) && '1' == $_POST['user_fields'] ? 'all' : array('ID'), 'role' => sanitize_text_field($_POST['role'])); // did they request a specific program ? ## if (isset($_POST['program']) && $_POST['program'] != '') { $args['meta_key'] = 'member_of_club'; $args['meta_value'] = (int) $_POST['program']; $args['meta_compare'] = '='; } // is there a range limit in place for the export ? ## if (isset($_POST['limit_total']) && $_POST['limit_total'] != '') { // let's just make sure they are integer values ## $limit_offset = isset($_POST['limit_offset']) ? (int) $_POST['limit_offset'] : 0; $limit_total = (int) $_POST['limit_total']; if (is_int($limit_offset) && is_int($limit_total)) { $args['offset'] = $limit_offset; $args['number'] = $limit_total; // number - Limit the total number of users returned ## // test it ## #wp_die( $this->pr( $args ) ); } } // pre_user query ## add_action('pre_user_query', array($this, 'pre_user_query')); $users = get_users($args); remove_action('pre_user_query', array($this, 'pre_user_query')); // test args ## #wp_die( $this->pr ( $users ) ); // no users found, so chuck an error into the args array and exit the export ## if (!$users) { wp_redirect(add_query_arg('error', 'empty', wp_get_referer())); exit; } // get sitename and clean it up ## $sitename = sanitize_key(get_bloginfo('name')); if (!empty($sitename)) { $sitename .= '.'; } // export method ? ## $export_method = 'excel'; // default to Excel export ## if (isset($_POST['format']) && $_POST['format'] != '') { $export_method = sanitize_text_field($_POST['format']); } // set export filename structure ## $filename = $sitename . 'users.' . date('Y-m-d-H-i-s'); switch ($export_method) { case 'csv': // to csv ## header('Content-Description: File Transfer'); header('Content-Disposition: attachment; filename=' . $filename . '.csv'); header('Content-Type: text/csv; charset=' . get_option('blog_charset'), true); // set a csv check flag $is_csv = true; // nothing here $doc_begin = ''; //preformat $pre = ''; // how to seperate data ## $seperator = ','; // comma for csv ## // line break ## $breaker = "\n"; // nothing here $doc_end = ''; break; case 'excel': // to xls ## header('Content-Description: File Transfer'); header("Content-Type: application/vnd.ms-excel"); header("Content-Disposition: attachment; filename={$filename}.xls"); header("Pragma: no-cache"); header("Expires: 0"); // set a csv check flag $is_csv = false; //grab the template file (for tidy formatting) include 'xml-template.php'; // open xml $doc_begin = $xml_doc_begin; //preformat $pre = $xml_pre; // how to seperate data ## $seperator = $xml_seperator; // line break ## $breaker = $xml_breaker; // close xml $doc_end = $xml_doc_end; break; } // check for selected usermeta fields ## $usermeta = isset($_POST['usermeta']) ? $_POST['usermeta'] : ''; #$this->pr( $usermeta ); $usermeta_fields = array(); if ($usermeta && is_array($usermeta)) { foreach ($usermeta as $field) { $usermeta_fields[] = sanitize_text_field($field); } } #$this->pr( $usermeta_fields ); #exit; // check for selected x profile fields ## $bp_fields = isset($_POST['bp_fields']) ? $_POST['bp_fields'] : ''; $bp_fields_passed = array(); if ($bp_fields && is_array($bp_fields)) { foreach ($bp_fields as $field) { // reverse tidy ## $field = str_replace('__', ' ', sanitize_text_field($field)); // add to array ## $bp_fields_passed[] = $field; } } // cwjordan: check for x profile fields we want update time for ## $bp_fields_update = isset($_POST['bp_fields_update_time']) ? $_POST['bp_fields_update_time'] : ''; $bp_fields_update_passed = array(); if ($bp_fields_update && is_array($bp_fields_update)) { foreach ($bp_fields_update as $field) { // reverse tidy ## $field = str_replace('__', ' ', sanitize_text_field($field)); // add to array ## $bp_fields_update_passed[] = $field . " Update Date"; } } // global wpdb object ## global $wpdb; // debug ## if (self::debug) { self::log('generate_data(): merging array'); } // compile final fields list ## $fields = array_merge(self::get_user_fields(), self::get_special_fields(), $usermeta_fields, $bp_fields_passed, $bp_fields_update_passed); // test field array ## #self::pr( $fields ); // build the document headers ## $headers = array(); foreach ($fields as $key => $field) { // rename programs field ## if ($field == 'member_of_club') { $field = 'Program'; } // grab fields to exclude from exports ## if (in_array($field, $this->get_exclude_fields())) { // ditch 'em ## unset($fields[$key]); } else { if ($is_csv) { $headers[] = '"' . $field . '"'; } else { $headers[] = $field; #echo '<script>console.log("Echoing header cell: '.$field.'")</script>'; } } } // quick check ## #if ( self::debug ) self::log( 'All Fields: '. var_dump( $fields ) ); #if ( self::debug ) self::log( '$bp_fields_passed: '. var_dump( $bp_fields_passed ) ); // no more buffering while spitting back the export data ## ob_end_flush(); // get the value in bytes allocated for Memory via php.ini ## // @link http://wordpress.org/support/topic/how-to-exporting-a-lot-of-data-out-of-memory-issue $memory_limit = $this->return_bytes(ini_get('memory_limit')) * 0.75; // we need to disable caching while exporting because we export so much data that it could blow the memory cache // if we can't override the cache here, we'll have to clear it later... if (function_exists('override_function')) { override_function('wp_cache_add', '$key, $data, $group="", $expire=0', ''); override_function('wp_cache_set', '$key, $data, $group="", $expire=0', ''); override_function('wp_cache_replace', '$key, $data, $group="", $expire=0', ''); override_function('wp_cache_add_non_persistent_groups', '$key, $data, $group="", $expire=0', ''); } elseif (function_exists('runkit_function_redefine')) { runkit_function_redefine('wp_cache_add', '$key, $data, $group="", $expire=0', ''); runkit_function_redefine('wp_cache_set', '$key, $data, $group="", $expire=0', ''); runkit_function_redefine('wp_cache_replace', '$key, $data, $group="", $expire=0', ''); runkit_function_redefine('wp_cache_add_non_persistent_groups', '$key, $data, $group="", $expire=0', ''); } // open doc wrapper.. ## echo $doc_begin; // echo headers ## echo $pre . implode($seperator, $headers) . $breaker; #wp_die( self::pr( $users ) ); // build row values for each user ## foreach ($users as $user) { #wp_die( self::pr( $user ) ); // check if we're hitting any Memory limits, if so flush them out ## // per http://wordpress.org/support/topic/how-to-exporting-a-lot-of-data-out-of-memory-issue?replies=2 if (memory_get_usage(true) > $memory_limit) { wp_cache_flush(); } // open up a new empty array ## $data = array(); #wp_die( self::pr( $GLOBALS['bp'] ) ); // BP loaded ? ## if (function_exists('bp_is_active') && bp_is_active('xprofile')) { // grab all user data ## $bp_data = BP_XProfile_ProfileData::get_all_for_user($user->ID); } // single query method - get all user_meta data ## $get_user_meta = (array) get_user_meta($user->ID); #wp_die( self::pr( $get_user_meta ) ); // Filter out empty meta data ## #$get_user_meta = array_filter( array_map( function( $a ) { # return $a[0]; #}, $get_user_meta ) ); // loop over each field ## foreach ($fields as $field) { // check if this is a BP field ## if (isset($bp_data) && isset($bp_data[$field]) && in_array($field, $bp_fields_passed)) { // old way from single BP query ## $value = $bp_data[$field]; if (is_array($value)) { $value = maybe_unserialize($value['field_data']); // suggested by @grexican ## #$value = $value['field_data']; /** * cwjordan * after unserializing it we then * need to implode it so * that we have something readable? * Going to use :: as a separator * because that's what Buddypress Members Import * expects, but we might want to make that * configurable. */ if (is_array($value)) { $value = implode("::", $value); } } $value = self::sanitize($value); // check if this is a BP field we want the updated date for ## } elseif (in_array($field, $bp_fields_update_passed)) { global $bp; $real_field = str_replace(" Update Date", "", $field); $field_id = xprofile_get_field_id_from_name($real_field); $value = $wpdb->get_var($wpdb->prepare("\n SELECT last_updated \n FROM {$bp->profile->table_name_data} \n WHERE user_id = %d AND field_id = %d\n ", $user->ID, $field_id)); // include the user's role in the export ## } elseif (isset($_POST['roles']) && '1' == $_POST['roles'] && $field == 'roles') { // add "Role" as $value ## $value = isset($user->roles[0]) ? implode($user->roles, '|') : ''; // empty value if no role found - or flat array of user roles ## // include the user's BP group in the export ## } elseif (isset($_POST['groups']) && '1' == $_POST['groups'] && $field == 'groups') { if (function_exists('groups_get_user_groups')) { // check if user is a member of any groups ## $group_ids = groups_get_user_groups($user->ID); #self::pr( $group_ids ); #wp_die( pr( 'loaded group data.' )); if (!$group_ids || $group_ids == '') { $value = ''; } else { // new empty array ## $groups = array(); // loop over all groups ## foreach ($group_ids["groups"] as $group_id) { $groups[] = groups_get_group(array('group_id' => $group_id))->name . (end($group_ids["groups"]) == $group_id ? '' : ''); } // implode it ## $value = implode($groups, '|'); } } else { $value = ''; } } elseif ($field == 'bp_latest_update') { // https://bpdevel.wordpress.com/2014/02/21/user-last_activity-data-and-buddypress-2-0/ ## $value = bp_get_user_last_activity($user->ID); // user or usermeta field ## } else { // the user_meta key isset ## if (isset($get_user_meta[$field])) { // take from the bulk get_user_meta call - this returns an array in all cases, so we take the first key ## $value = $get_user_meta[$field][0]; // standard WP_User value ## } else { // use the magically assigned value from WP_Users $value = $user->{$field}; } // the $value might be serialized ## $value = self::unserialize($value); // the value is an array ## if (is_array($value)) { // recursive implode it ## $value = self::recursive_implode($value); } } // correct program value to Program Name ## if ($field == 'member_of_club') { $value = get_the_title($value); } // wrap values in quotes and add to array ## if ($is_csv) { $data[] = '"' . str_replace('"', '""', self::special_characters($value)) . '"'; // just add to array ## } else { $data[] = self::special_characters($value); } } // echo row data ## echo $pre . implode($seperator, $data) . $breaker; } // close doc wrapper.. echo $doc_end; // stop PHP, so file can export correctly ## exit; }