$uid)); switch ($type) { case 'reg_days': return floor((time() - $user->created) / 86400); case 'join_date': return $user->created; case 'login_days': return floor((time() - $user->access) / 86400); case 'post_days': return floor((time() - _user_stats_last_post($user)) / 86400); case 'post_count': // If the post count for this user hasn't been set then update it if (!isset($user->user_post_count)) { user_stats_post_count_update($user, 'reset'); } return $user->user_post_count; case 'online': return (round((time() - $user->access) / 60) < 15 ? TRUE : FALSE); } } /** * Drupal hook implementations */ /** * Implementation of hook_nodeapi() * * Increments post count on insert or publish * Decrements post count on delete or unpublish * Checks and updates users without post counts on view * * @param &$node * The node the action is being performed on. * @param $op * The operation being performed. We are interested in insert, delete, update and view */ function user_stats_nodeapi(&$node, $op) { switch ($op) { case 'insert': $user = user_load(array('uid' => $node->uid)); if ($node->status) { user_stats_post_count_update($user, 'increment'); } break; case 'delete': $user = user_load(array('uid' => $node->uid)); user_stats_post_count_update($user, 'decrement'); break; case 'update': $user = user_load(array('uid' => $node->uid)); // Can't think of any other way of doing this than resetting the user... user_stats_post_count_update($user, 'reset'); break; } } /** * Implementation of hook_comment() * * Increments post count on insert or publish * Decrements post count on delete or unpublish * Updates users with no post count on view * * @param $a1 * Either the form values being submitted, the module form to be displayed, * or the comment object. * @param $op * What kind of action is being performed. */ function user_stats_comment(&$a1, $op) { $comment = (object)$a1; switch ($op) { case 'insert': case 'publish': $user = user_load(array('uid' => $comment->uid)); user_stats_post_count_update($user, 'increment'); break; case 'delete': case 'unpublish': $user = user_load(array('uid' => $comment->uid)); user_stats_post_count_update($user, 'decrement'); break; } } /** * Implementation of hook_cron() * * We slowly work through all users without a post count * updating them. */ function user_stats_cron() { if (variable_get('user_stats_rebuild_stats', TRUE)) { $sql = "SELECT fid FROM {profile_fields} WHERE name='user_post_count'"; $fid = db_result(db_query($sql)); // Unfortunately this cannot be done with a JOIN because of the need to match on fid $sql = "SELECT uid FROM {users} WHERE uid NOT IN ( "; $sql .= " SELECT uid FROM {profile_values} "; $sql .= " WHERE fid=%d "; $sql .= ") "; // Update 25 users per cron run $result = db_query_range($sql, $fid, 0, 25); // If all users have been updated we'll avoid running this expensive // query again by setting the following flag! if (db_num_rows($result) == 0) { variable_set('user_stats_rebuild_stats', FALSE); } while ($update_user = db_fetch_object($result)) { $user = user_load($update_user); user_stats_post_count_update($user, 'reset'); } } // Fire workflow_ng daily anniversary event if (module_exists('workflow_ng')) { $sql = "SELECT uid FROM {users} u "; // ((last cron - created) - (time() - created)) > one day $sql .= "WHERE "; $sql .= "(FLOOR((unix_timestamp()-created)/(60*60*24))-FLOOR((%d-created)/(60*60*24)))>0 "; $sql .= "AND uid>0 "; $result = db_query($sql, variable_get('cron_last', time())); while ($update_user = db_fetch_object($result)) { $user = user_load($update_user); workflow_ng_invoke_event('user_stats_day_older', $user); } } } /** * Helper function to get the last post created by the user. * * @param $account * User object. * * @return * Unix timestamp: date of the last post (node or comment). */ function _user_stats_last_post($account) { $sql = "SELECT MAX(created) FROM {node} WHERE uid=%d"; $max_node = db_result(db_query($sql, $account->uid)); $sql = "SELECT MAX(timestamp) FROM {comments} WHERE uid=%d"; $max_comments = db_result(db_query($sql, $account->uid)); if ($max_node > $max_comments) { return $max_node; } else { return $max_comments; } } /** * Workflow-ng hooks and implementing functions */ /** * Implementation of hook_event_info() */ function user_stats_event_info() { return array( 'user_stats_post_count_increment' => array( '#label' => t('User post count incremented'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count has incremented')), ), '#module' => t('User'), ), 'user_stats_post_count_decrement' => array( '#label' => t('User post count decremented'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count has decremented')), ), '#module' => t('User'), ), 'user_stats_post_count_reset' => array( '#label' => t('User post count reset'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count has reset')), ), '#module' => t('User'), ), 'user_stats_day_older' => array( '#label' => t('User is a day older'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User who is a day older')), ), '#module' => t('User'), ), ); } /** * Implementation of hook_action_info() */ function user_stats_action_info() { return array( 'user_stats_action_post_count_increment' => array( '#label' => t('Increment user post count'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count should be incremented')), ), '#module' => t('User'), '#description' => t('Increments the post count of a user'), ), 'user_stats_action_post_count_decrement' => array( '#label' => t('Decrement user post count'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count should be decremented')), ), '#module' => t('User'), '#description' => t('Decrements the post count of a user'), ), 'user_stats_action_post_count_reset' => array( '#label' => t('Reset the user post count'), '#arguments' => array( 'user' => array('#entity' => 'user', '#label' => t('User whos post count should be reset')), ), '#module' => t('User'), '#description' => t('Resets the post count of the current user.'), ), ); } /** * hook_action_info() callback * * @param &$user * Reference to the user object * @param $settings * Workflow-ng settings parameter * @param &$arguments * Pointer to all arguments passed to the function from Workflow-ng * @param &$log * Reference to Workflow-ng's internal log */ function user_stats_action_post_count_increment(&$user, $settings, &$arguments, &$log) { user_stats_post_count_update($user, 'increment'); } /** * hook_action_info() callback * * @param &$user * Reference to the user object * @param $settings * Workflow-ng settings parameter * @param &$arguments * Pointer to all arguments passed to the function from Workflow-ng * @param &$log * Reference to Workflow-ng's internal log */ function user_stats_action_post_count_decrement(&$user, $settings, &$arguments, &$log) { user_stats_post_count_update($user, 'decrement'); } /** * hook_action_info() callback * * @param &$user * Reference to the user object * @param $settings * Workflow-ng settings parameter * @param &$arguments * Pointer to all arguments passed to the function from Workflow-ng * @param &$log * Reference to Workflow-ng's internal log */ function user_stats_action_post_count_reset(&$user, $settings, &$arguments, &$log) { user_stats_post_count_update($user, 'reset'); } /** * Token hook implementations */ /** * Implementation of hook_token_values() */ function user_stats_token_values($type, $object = NULL) { switch ($type) { case 'user': // Get the user object if (isset($object)) { $account = (object)$object; } else { global $user; $account = user_load(array('uid' => $user->uid)); } // If post count for the user hasn't been generated then update it if(!isset($account->user_post_count)) { user_stats_post_count_update($account, 'reset'); } $last_post = _user_stats_last_post($account); $values['reg-days'] = floor((time()-$account->created) / 86400); $values['login-days'] = floor((time()-$account->access) / 86400); $values['post-days'] = floor((time()-$last_post) / 86400); $values['post-count'] = $account->user_post_count; break; } return $values; } /** * Implementation of hook_token_list() */ function user_stats_token_list($type = 'all') { if ($type == 'user' || $type == 'all') { $tokens['user']['reg-days'] = t('Number of days since the user registered'); $tokens['user']['login-days'] = t('Number of days since the user logged in'); $tokens['user']['post-days'] = t('Number of days since the user posted'); $tokens['user']['post-count'] = t('User\'s post count'); } return $tokens; } /** * Manage the post count of a given user * * @param $user * Reference to user object * @param $op * Whether the user post count should be incremented, decremented, or reset. * The default is to increment. Possible values are: * 'increment' (default) * 'decrement' * 'reset' */ function user_stats_post_count_update(&$user, $op = 'increment') { // The lock stops infinite recursions, the action will still happen, but the // corresponding event (the reason for an infinite loop) will not be fired static $locked; // Shared SQL for increment and decrement $sql = "UPDATE {profile_values} INNER JOIN {profile_fields} ON "; $sql .= "{profile_values}.fid={profile_fields}.fid "; $sql .= "SET VALUE=%d WHERE name='user_post_count' AND uid=%d "; switch ($op) { case 'increment': if (!isset($user->user_post_count)) { $user = user_stats_post_count_update($user, 'reset'); } else { $user->user_post_count++; db_query($sql, $user->user_post_count, $user->uid); // We use $locked to stop infinite loops if (!$locked && module_exists('workflow_ng')) { $locked = TRUE; workflow_ng_invoke_event('user_stats_post_count_increment', $user); $locked = FALSE; } } break; case 'decrement': if (!isset($user->user_post_count)) { $user = user_stats_post_count_update($user, 'reset'); } else { $user->user_post_count--; db_query($sql, $user->user_post_count, $user->uid); if (!$locked && module_exists('workflow_ng')) { $locked = TRUE; workflow_ng_invoke_event('user_stats_post_count_decrement', $user); $locked = FALSE; } } break; case 'reset': static $fid; if (!isset($fid)) { $sql = "SELECT fid FROM {profile_fields} WHERE name='user_post_count'"; $fid = db_result(db_query($sql)); } $sql = "SELECT COUNT(*) FROM {node} WHERE uid=%d AND status<>0"; $node_count = db_result(db_query($sql, $user->uid)); $sql = "SELECT COUNT(*) FROM {comments} WHERE uid=%d AND status=0"; $comments_count = db_result(db_query($sql, $user->uid)); $total_count = $node_count + $comments_count; $sql = "DELETE {profile_values} FROM {profile_values} "; $sql .= "WHERE fid=%d AND uid=%d"; db_query($sql, $fid, $user->uid); $sql = "INSERT IGNORE INTO {profile_values} (fid, uid, value) VALUES ( "; $sql .= "%d, %d, %d "; $sql .= ") "; db_query($sql, $fid, $user->uid, $total_count); if (!$locked && module_exists('workflow_ng')) { $locked = TRUE; workflow_ng_invoke_event('user_stats_post_count_reset', $user); $locked = FALSE; } break; } // Reload the user post count $user = user_load(array('uid' => $account->uid)); }